mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-11 23:04:52 +09:00
176 lines
6.4 KiB
Lua
176 lines
6.4 KiB
Lua
--[[
|
|
|
|
When a player leaves the game, serialize their inventory and upload it to the master.
|
|
|
|
When a player joins the game, send a request to the master server for the players inventory.
|
|
|
|
When the master sends an inventory, parse it and give it to the player.
|
|
|
|
]]
|
|
|
|
local serialize = require("modules/clusterio/serialize")
|
|
local clusterio_api = require("modules/clusterio/api")
|
|
local progress_dialog = require("modules/inventory_sync/progress_dialog")
|
|
|
|
local inventories = {
|
|
defines.inventory.character_main,
|
|
defines.inventory.character_guns,
|
|
defines.inventory.character_ammo,
|
|
defines.inventory.character_armor,
|
|
defines.inventory.character_vehicle,
|
|
defines.inventory.character_trash,
|
|
}
|
|
|
|
inventory_sync = {}
|
|
|
|
inventory_sync.events = {}
|
|
-- Download inventory from master
|
|
inventory_sync.events[defines.events.on_player_joined_game] = function(event)
|
|
local playerIndex = event.player_index
|
|
inventory_sync.initiateInventoryDownload(playerIndex)
|
|
end
|
|
|
|
function inventory_sync.initiateInventoryDownload(playerIndex)
|
|
local player = game.players[playerIndex]
|
|
player.print("Initiating inventory download...")
|
|
global["inv_sync_download_start_tick "..player.name] = game.tick
|
|
if global.download_cache == nil then
|
|
global.download_cache = {}
|
|
end
|
|
global.download_cache[playerIndex] = ""
|
|
clusterio_api.send_json("inventory_sync_download", {
|
|
player_name = player.name
|
|
})
|
|
|
|
-- Set player into ghost mode for duration of download
|
|
local character = player.character
|
|
player.set_controller {
|
|
type = defines.controllers.ghost,
|
|
}
|
|
if character ~= nil then character.destroy() end
|
|
end
|
|
|
|
-- This function is called by instance.js as /sc inventory_sync.downloadInventory("danielv", Escaped JSON string) with data from the master
|
|
function inventory_sync.downloadInventory(playerName, data, number, total)
|
|
local player = game.players[playerName]
|
|
if player == nil then return end
|
|
if number then
|
|
if global.download_cache[playerName] == nil then
|
|
global.download_cache[playerName] = ""
|
|
end
|
|
-- Append data to download cache
|
|
global.download_cache[playerName] = global.download_cache[playerName] .. data
|
|
end
|
|
|
|
if number ~= total then
|
|
-- Show progress in console
|
|
-- game.players[playerName].print("Downloaded "..(number + 1).."/"..total.." parts")
|
|
-- Show progress in GUI
|
|
progress_dialog(player, number+1, total)
|
|
|
|
-- Request next segment
|
|
rcon.print("Segment downloaded")
|
|
else
|
|
-- Recreate player character
|
|
player.set_controller {
|
|
type = defines.controllers.god,
|
|
}
|
|
player.create_character()
|
|
-- Load downloaded inventory
|
|
local serialized_player = game.json_to_table(global.download_cache[playerName])
|
|
global.download_cache[playerName] = nil -- remove data to lower mapsize
|
|
local player = game.players[playerName]
|
|
-- Load inventories
|
|
for _, inv in pairs(inventories) do
|
|
local inventory = player.get_inventory(inv)
|
|
if inventory ~= nil and serialized_player.inventories[tostring(inv)] ~= nil then
|
|
serialize.deserialize_inventory(inventory, serialized_player.inventories[tostring(inv)])
|
|
end
|
|
end
|
|
|
|
-- Load personal logistics slots
|
|
for i = 1, 200 do
|
|
local slot = serialized_player.personal_logistic_slots[tostring(i)]
|
|
if slot ~= nil then
|
|
player.set_personal_logistic_slot(i, slot)
|
|
end
|
|
end
|
|
|
|
-- Load hotbar, don't overwrite empty slots
|
|
for i = 1, 100 do
|
|
-- if serialized_player.hotbar[i] ~= nil then
|
|
player.set_quick_bar_slot(i, game.item_prototypes[serialized_player.hotbar[i]])
|
|
-- end
|
|
end
|
|
|
|
-- Misc
|
|
player.force = serialized_player.force -- Force by name as string
|
|
player.tag = serialized_player.tag
|
|
player.color = serialized_player.color
|
|
player.chat_color = serialized_player.chat_color
|
|
|
|
local startTick = global["inv_sync_download_start_tick "..game.players[playerName].name]
|
|
game.print("Imported inventory for "..playerName.." in "..game.tick - startTick.." ticks")
|
|
end
|
|
end
|
|
|
|
-- Upload inventory when a player leaves the game. Triggers on restart after crash if player was online during crash.
|
|
inventory_sync.events[defines.events.on_player_left_game] = function(event)
|
|
local playerIndex = event.player_index
|
|
inventory_sync.uploadInventory(playerIndex)
|
|
global.download_cache[playerIndex] = "" -- Clear cache to avoid leaking memory to people quitting during download
|
|
end
|
|
|
|
function inventory_sync.uploadInventory(playerIndex)
|
|
local player = game.players[playerIndex]
|
|
|
|
local serialized_player = {
|
|
name = player.name,
|
|
inventories = {},
|
|
hotbar = {},
|
|
color = player.color,
|
|
chat_color = player.chat_color,
|
|
tag = player.tag,
|
|
-- admin = player.admin, -- This is handled by other parts of clusterio
|
|
personal_logistic_slots = {},
|
|
force = player.force.name,
|
|
}
|
|
|
|
-- Serialize hotbar
|
|
for i = 1, 100 do
|
|
serialized_player.hotbar[i] = {}
|
|
local slot = player.get_quick_bar_slot(i)
|
|
if slot ~= nil and slot.name ~= nil then
|
|
serialized_player.hotbar[i] = slot.name
|
|
end
|
|
end
|
|
|
|
-- Serialize personal logistics slots
|
|
for i = 1, 200 do -- Nobody will have more than 200 logistics slots, right?
|
|
local slot = player.get_personal_logistic_slot(i)
|
|
game.print(serpent.block(slot))
|
|
if slot.name ~= nil then
|
|
-- We leave [1] empty to force the JSON function to parse it as an object.
|
|
-- When its parsed as an array we get inconsistent key datatypes depending
|
|
-- on whether or not it is sparse.
|
|
serialized_player.personal_logistic_slots[i+1] = {
|
|
name = slot.name,
|
|
min = slot.min,
|
|
max = slot.max,
|
|
}
|
|
end
|
|
end
|
|
|
|
-- Serialize inventories
|
|
for _, inv in pairs(inventories) do
|
|
local inventory = player.get_inventory(inv)
|
|
if inventory ~= nil then
|
|
local serialized_inventory = serialize.serialize_inventory(inventory)
|
|
serialized_player.inventories[inv] = serialized_inventory
|
|
end
|
|
end
|
|
clusterio_api.send_json("inventory_sync_upload", serialized_player)
|
|
end
|
|
|
|
return inventory_sync
|