mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-11 23:04:52 +09:00
Count up to 100 empty slots past the last seen slot with data in it before stopping to look for personal logistics slots in inventory_sync. Allows unlimeted amount of logistics request slots to be used for heavily modded games.
259 lines
8.2 KiB
Lua
259 lines
8.2 KiB
Lua
local clusterio_serialize = require("modules/clusterio/serialize")
|
|
local character_inventories = require("modules/inventory_sync/define_player_inventories")
|
|
local character_stat_keys = require("modules/inventory_sync/define_player_stat_keys")
|
|
local serialize = {}
|
|
|
|
function serialize.serialize_inventories(source, inventories)
|
|
local serialized = {}
|
|
|
|
for name, index in pairs(inventories) do
|
|
local inventory = source.get_inventory(index)
|
|
if inventory ~= nil then
|
|
serialized[name] = clusterio_serialize.serialize_inventory(inventory)
|
|
end
|
|
end
|
|
|
|
return serialized
|
|
end
|
|
|
|
function serialize.deserialize_inventories(destination, serialized, inventories)
|
|
for name, index in pairs(inventories) do
|
|
local inventory = destination.get_inventory(index)
|
|
if inventory ~= nil and serialized[name] ~= nil then
|
|
inventory.clear()
|
|
clusterio_serialize.deserialize_inventory(inventory, serialized[name])
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Characters are serialized into a table with the following fields:
|
|
-- character_crafting_speed_modifier
|
|
-- character_mining_speed_modifier
|
|
-- character_additional_mining_categories
|
|
-- character_running_speed_modifier
|
|
-- character_build_distance_bonus
|
|
-- character_item_drop_distance_bonus
|
|
-- character_reach_distance_bonus
|
|
-- character_resource_reach_distance_bonus
|
|
-- character_item_pickup_distance_bonus
|
|
-- character_loot_pickup_distance_bonus
|
|
-- character_inventory_slots_bonus
|
|
-- character_trash_slot_count_bonus
|
|
-- character_maximum_following_robot_count_bonus
|
|
-- character_health_bonus
|
|
-- character_personal_logistic_requests_enabled
|
|
-- inventories: table of character inventory name to inventory content
|
|
function serialize.serialize_character(character)
|
|
local serialized = { }
|
|
|
|
-- Serialize character stats
|
|
for _, key in pairs(character_stat_keys) do
|
|
serialized[key] = character[key]
|
|
end
|
|
|
|
-- Serialize character inventories
|
|
serialized.inventories = serialize.serialize_inventories(character, character_inventories)
|
|
|
|
return serialized
|
|
end
|
|
|
|
function serialize.deserialize_character(character, serialized)
|
|
-- Deserialize character stats
|
|
for _, key in pairs(character_stat_keys) do
|
|
character[key] = serialized[key]
|
|
end
|
|
|
|
-- Deserialize character inventories
|
|
serialize.deserialize_inventories(character, serialized.inventories, character_inventories)
|
|
end
|
|
|
|
-- Personal logistic slots is a table mapping string indexes to a table with the following fields:
|
|
-- name
|
|
-- min
|
|
-- max
|
|
function serialize.serialize_personal_logistic_slots(player)
|
|
local serialized = nil
|
|
|
|
-- Serialize personal logistic slots
|
|
local last_valid = 0
|
|
for i = 1, 65536 do
|
|
local slot = player.get_personal_logistic_slot(i)
|
|
if slot.name then
|
|
last_valid = i
|
|
if not serialized then
|
|
serialized = {}
|
|
end
|
|
serialized[tostring(i)] = {
|
|
name = slot.name,
|
|
min = slot.min,
|
|
max = slot.max,
|
|
}
|
|
|
|
-- Stop after 100 empty slots
|
|
elseif last_valid + 100 <= i then
|
|
break
|
|
end
|
|
end
|
|
|
|
return serialized
|
|
end
|
|
|
|
function serialize.deserialize_personal_logistic_slots(player, serialized)
|
|
if not serialized then
|
|
return
|
|
end
|
|
|
|
-- Load personal logistic slots
|
|
for i, slot in pairs(serialized) do
|
|
if slot ~= nil then
|
|
player.set_personal_logistic_slot(tonumber(i), slot)
|
|
end
|
|
end
|
|
end
|
|
|
|
local controller_to_name = {}
|
|
for name, value in pairs(defines.controllers) do
|
|
controller_to_name[value] = name
|
|
end
|
|
|
|
-- Players are serialized into a table with the following fields:
|
|
-- name
|
|
-- controller: may not be cutscene or editor
|
|
-- color
|
|
-- chat_color
|
|
-- tag
|
|
-- force
|
|
-- cheat_mod
|
|
-- flashlight
|
|
-- ticks_to_respawn (optional)
|
|
-- character (optional)
|
|
-- inventories: non-character inventories
|
|
-- hotbar: table of string indexes to names (optional)
|
|
-- personal_logistic_slots (optional)
|
|
function serialize.serialize_player(player)
|
|
local serialized = {
|
|
controller = controller_to_name[player.controller_type],
|
|
name = player.name,
|
|
color = player.color,
|
|
chat_color = player.chat_color,
|
|
tag = player.tag,
|
|
force = player.force.name,
|
|
cheat_mode = player.cheat_mode,
|
|
flashlight = player.is_flashlight_enabled(),
|
|
ticks_to_respawn = player.ticks_to_respawn,
|
|
}
|
|
|
|
-- For the waiting to respawn state the inventory logistic requests and filters are hidden on the player
|
|
if player.controller_type == defines.controllers.ghost and player.ticks_to_respawn then
|
|
player.ticks_to_respawn = nil -- Respawn now
|
|
|
|
serialized.personal_logistic_slots = serialize.serialize_personal_logistic_slots(player)
|
|
serialized.inventories = serialize.serialize_inventories(player, character_inventories)
|
|
|
|
-- Go back to waiting for respawn
|
|
local character = player.character
|
|
player.ticks_to_respawn = serialized.ticks_to_respawn
|
|
character.destroy()
|
|
end
|
|
|
|
-- Serialize character
|
|
if player.character then
|
|
serialized.character = serialize.serialize_character(player.character)
|
|
serialized.personal_logistic_slots = serialize.serialize_personal_logistic_slots(player)
|
|
end
|
|
|
|
-- Serialize non-character inventories
|
|
if player.controller_type == defines.controllers.god then
|
|
serialized.inventories = serialize.serialize_inventories(player, { main = defines.inventory.god_main })
|
|
end
|
|
|
|
-- Serialize hotbar
|
|
for i = 1, 100 do
|
|
local slot = player.get_quick_bar_slot(i)
|
|
if slot ~= nil and slot.name ~= nil then
|
|
if not serialized.hotbar then
|
|
serialized.hotbar = {}
|
|
end
|
|
serialized.hotbar[tostring(i)] = slot.name
|
|
end
|
|
end
|
|
|
|
return serialized
|
|
end
|
|
|
|
function serialize.deserialize_player(player, serialized)
|
|
if player.controller_type ~= defines.controllers[serialized.controller] or serialized.controller == "ghost" then
|
|
-- If targeting the character or ghost controller then create a character
|
|
if serialized.controller == "character" or serialized.controller == "ghost" then
|
|
if player.controller_type == defines.controllers.ghost or player.controller_type == defines.controllers.spectator then
|
|
player.set_controller({ type = defines.controllers.god })
|
|
end
|
|
if player.controller_type == defines.controllers.god then
|
|
player.create_character()
|
|
end
|
|
|
|
-- The ghost state stores hidden logistic and filters which are only accessible in the character controller
|
|
if serialized.controller == "ghost" then
|
|
serialize.deserialize_personal_logistic_slots(player, serialized.personal_logistic_slots)
|
|
serialize.deserialize_inventories(player, serialized.inventories, character_inventories)
|
|
local character = player.character
|
|
if serialized.ticks_to_respawn then
|
|
player.ticks_to_respawn = serialized.ticks_to_respawn
|
|
else
|
|
-- We have to set ticks to respawn to save the hidden state into the player but we
|
|
-- can't unset tick_to_respawn by setting it back to nil as that triggers a respawn.
|
|
player.ticks_to_respawn = 0
|
|
player.set_controller({ type = defines.controller.god })
|
|
player.set_controller({ type = defines.controller.ghost })
|
|
end
|
|
character.destroy()
|
|
end
|
|
|
|
else
|
|
-- Targeting the god or spectator controller, if coming from the
|
|
-- character controller then destroy the character
|
|
if player.controller_type == defines.controllers.character then
|
|
player.character.destroy()
|
|
end
|
|
|
|
-- Switching to god or spectator is a matter of setting the controller
|
|
if player.controller_type ~= defines.controllers[serialized.controller] then
|
|
player.set_controller({ type = defines.controllers[serialized.controller] })
|
|
end
|
|
end
|
|
end
|
|
|
|
player.color = serialized.color
|
|
player.chat_color = serialized.chat_color
|
|
player.tag = serialized.tag
|
|
player.force = serialized.force
|
|
player.cheat_mode = serialized.cheat_mode
|
|
if serialized.flashlight then
|
|
player.enable_flashlight()
|
|
else
|
|
player.disable_flashlight()
|
|
end
|
|
|
|
-- Deserialize character
|
|
if player.character then
|
|
serialize.deserialize_character(player.character, serialized.character)
|
|
serialize.deserialize_personal_logistic_slots(player, serialized.personal_logistic_slots)
|
|
end
|
|
|
|
-- Deserialize non-character inventories
|
|
if player.controller_type == defines.controllers.god then
|
|
serialize.deserialize_inventories(player, serialized.inventories, { main = defines.inventory.god_main })
|
|
end
|
|
|
|
-- Deserialize hotbar
|
|
if serialized.hotbar then
|
|
for i = 1, 100 do
|
|
if serialized.hotbar[tostring(i)] ~= nil then
|
|
player.set_quick_bar_slot(i, serialized.hotbar[tostring(i)])
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return serialize
|