mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-12 07:04:52 +09:00
* Initial plan * Remove debug log statement from serialize.lua Co-authored-by: Danielv123 <6792749+Danielv123@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Danielv123 <6792749+Danielv123@users.noreply.github.com>
307 lines
8.9 KiB
Lua
307 lines
8.9 KiB
Lua
-- Library for serializing items in Factorio
|
|
-- Based on code from playerManager and trainTeleports
|
|
local compat = require("modules/clusterio/compat")
|
|
local serialize = {}
|
|
|
|
-- 0.17 compatibility
|
|
local supports_bar, get_bar, set_bar
|
|
if compat.version_ge("1.0.0") then
|
|
supports_bar = "supports_bar"
|
|
get_bar = "get_bar"
|
|
set_bar = "set_bar"
|
|
else
|
|
supports_bar = "hasbar"
|
|
get_bar = "getbar"
|
|
set_bar = "setbar"
|
|
end
|
|
|
|
local has_create_grid = compat.version_ge("1.1.7")
|
|
local has_quality = compat.version_ge("2.0.0")
|
|
|
|
-- Equipment Grids are serialized into an array of equipment entries
|
|
-- where ench entry is a table with the following fields:
|
|
-- n: name
|
|
-- g: ghost name (optional)
|
|
-- t: to_be_removed (optional)
|
|
-- p: position (array of 2 numbers corresponding to x and y)
|
|
-- s: shield (optional)
|
|
-- e: energy (optional)
|
|
-- If the equipment is a burner the following is also present:
|
|
-- i: burner inventory
|
|
-- r: result inventory
|
|
-- b: currently burning (optional)
|
|
-- f: remaining_burning_fuel (optional)
|
|
-- If the equipment has a quality the following is also present:
|
|
-- q: quality (optional)
|
|
function serialize.serialize_equipment_grid(grid)
|
|
local serialized = {}
|
|
local processed = {}
|
|
for y = 0, grid.height - 1 do
|
|
for x = 0, grid.width - 1 do
|
|
local equipment = grid.get({x, y})
|
|
if equipment ~= nil then
|
|
local pos = equipment.position
|
|
local combined_pos = pos.x + pos.y * grid.width + 1
|
|
if not processed[combined_pos] then
|
|
processed[combined_pos] = true
|
|
local entry = {
|
|
n = equipment.name,
|
|
p = {pos.x, pos.y},
|
|
}
|
|
if equipment.type == "equipment-ghost" then
|
|
if equipment.ghost_name then
|
|
entry.g = equipment.ghost_name
|
|
end
|
|
end
|
|
if equipment.shield > 0 then entry.s = equipment.shield end
|
|
if equipment.energy > 0 then entry.e = equipment.energy end
|
|
if equipment.burner then
|
|
local burner = equipment.burner
|
|
entry.i = serialize.serialize_inventory(burner.inventory)
|
|
entry.r = serialize.serialize_inventory(burner.burnt_result_inventory)
|
|
if burner.currently_burning then
|
|
entry.b = burner.currently_burning.name
|
|
entry.f = burner.remaining_burning_fuel
|
|
end
|
|
end
|
|
if equipment.quality then
|
|
entry.q = equipment.quality.name
|
|
end
|
|
if equipment.to_be_removed then
|
|
entry.t = equipment.to_be_removed
|
|
end
|
|
table.insert(serialized, entry)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return serialized
|
|
end
|
|
|
|
function serialize.deserialize_equipment_grid(grid, serialized)
|
|
grid.clear()
|
|
for _, entry in ipairs(serialized) do
|
|
local equipment = grid.put({
|
|
name = entry.g or entry.n,
|
|
quality = has_quality and entry.q or nil,
|
|
position = entry.p,
|
|
ghost = entry.g and true or nil,
|
|
})
|
|
if equipment then
|
|
if entry.s then equipment.shield = entry.s end
|
|
if entry.e then equipment.energy = entry.e end
|
|
if entry.i then
|
|
local burner = equipment.burner
|
|
if entry.b then burner.currently_burning = entry.b end
|
|
if entry.f then burner.remaining_burning_fuel = entry.f end
|
|
serialize.deserialize_inventory(burner.burnt_result_inventory, entry.r)
|
|
serialize.deserialize_inventory(burner.inventory, entry.i)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Item stacks are serialized into a table with the following fields:
|
|
-- n: name
|
|
-- c: count
|
|
-- q: quality (optional)
|
|
-- h: health (optional)
|
|
-- d: durability (optional)
|
|
-- a: ammo count (optional)
|
|
-- l: label (optional)
|
|
-- g: equipment grid (optional)
|
|
-- i: item inventory (optional)
|
|
-- If the item stack is exportable it has the following property instead
|
|
-- e: export string
|
|
-- Label is a table with the following fields:
|
|
-- t: label text (optional)
|
|
-- c: color (optional)
|
|
-- a: allow manual label change
|
|
function serialize.serialize_item_stack(slot, entry)
|
|
if
|
|
slot.is_blueprint
|
|
or slot.is_blueprint_book
|
|
or slot.is_upgrade_item
|
|
or slot.is_deconstruction_item
|
|
or slot.is_item_with_tags
|
|
then
|
|
local call_success, call_return = pcall(slot.export_stack)
|
|
if not call_success then
|
|
print("Error: '" .. call_return .. "' thrown exporting '" .. slot.name .. "'")
|
|
else
|
|
entry.e = call_return
|
|
end
|
|
|
|
return
|
|
end
|
|
|
|
entry.n = slot.name
|
|
entry.c = slot.count
|
|
if has_quality and slot.quality.level ~= 0 then entry.q = slot.quality.name end
|
|
if slot.health < 1 then entry.h = slot.health end
|
|
if slot.type == "tool" and slot.durability then entry.d = slot.durability end
|
|
if slot.type == "ammo" then entry.a = slot.ammo end
|
|
if slot.is_item_with_label then
|
|
local label = {}
|
|
if slot.label then label.t = slot.label end
|
|
if slot.label_color then label.c = slot.label_color end
|
|
label.a = slot.allow_manual_label_change
|
|
entry.l = label
|
|
end
|
|
|
|
if slot.grid then
|
|
entry.g = serialize.serialize_equipment_grid(slot.grid)
|
|
end
|
|
|
|
if slot.is_item_with_inventory then
|
|
local sub_inventory = slot.get_inventory(defines.inventory.item_main)
|
|
entry.i = serialize.serialize_inventory(sub_inventory)
|
|
end
|
|
end
|
|
|
|
function serialize.deserialize_item_stack(slot, entry)
|
|
if entry.e then
|
|
local success = slot.import_stack(entry.e)
|
|
if success == 1 then
|
|
print("Error: import of '" .. entry.e .. "' succeeded with errors")
|
|
elseif success == -1 then
|
|
print("Error: import of '" .. entry.e .. "' failed")
|
|
end
|
|
|
|
return
|
|
end
|
|
|
|
local item_stack = {
|
|
name = entry.n,
|
|
count = entry.c,
|
|
}
|
|
if entry.q then item_stack.quality = entry.q end
|
|
if entry.h then item_stack.health = entry.h end
|
|
if entry.d then item_stack.durability = entry.d end
|
|
if entry.a then item_stack.ammo = entry.a end
|
|
|
|
local call_success, call_return = pcall(slot.set_stack, item_stack)
|
|
if not call_success then
|
|
print("Error: '" .. call_return .. "' thrown setting stack ".. serpent.line(entry))
|
|
|
|
elseif not call_return then
|
|
print("Error: Failed to set stack " .. serpent.line(entry))
|
|
|
|
else
|
|
if entry.l then
|
|
-- TODO test this with AAI's unit-remote-control
|
|
local label = entry.l
|
|
if label.t then slot.label = label.t end
|
|
if label.c then slot.label_color = label.c end
|
|
slot.allow_manual_label_change = label.a
|
|
end
|
|
if entry.g then
|
|
if slot.grid then
|
|
serialize.deserialize_equipment_grid(slot.grid, entry.g)
|
|
elseif slot.type == "item-with-entity-data" and has_create_grid then
|
|
slot.create_grid()
|
|
serialize.deserialize_equipment_grid(slot.grid, entry.g)
|
|
else
|
|
print("Error: Attempt to deserialize equipment grid on an unsupported entity")
|
|
end
|
|
end
|
|
if entry.i then
|
|
local sub_inventory = slot.get_inventory(defines.inventory.item_main)
|
|
serialize.deserialize_inventory(sub_inventory, entry.i)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Inventories are serialized into a table with the following fields:
|
|
-- i: array of item stack or exportable item entries
|
|
-- b: bar position (optional)
|
|
-- Each item entry has the following fields
|
|
-- s: index (optional, equals to previous plus one if not present)
|
|
-- r: repeat count (optional)
|
|
-- f: slot filter (optional)
|
|
-- Pluss all the fields for item stacks (see deserialize_item_stack)
|
|
-- It's also possible that the slot is empty but has a slot filter.
|
|
function serialize.serialize_inventory(inventory)
|
|
local serialized = {}
|
|
if inventory[supports_bar]() and inventory[get_bar]() <= #inventory then
|
|
serialized.b = inventory[get_bar]()
|
|
end
|
|
|
|
serialized.i = {}
|
|
local previous_index = 0
|
|
local previous_serialized = nil
|
|
for i = 1, #inventory do
|
|
local item = {}
|
|
local slot = inventory[i]
|
|
if inventory.supports_filters() then
|
|
item.f = inventory.get_filter(i)
|
|
end
|
|
|
|
if slot.valid_for_read then
|
|
serialize.serialize_item_stack(slot, item)
|
|
end
|
|
|
|
if item.n or item.f or item.e then
|
|
local item_serialized = compat.table_to_json(item)
|
|
if item_serialized == previous_serialized then
|
|
local previous_item = serialized.i[#serialized.i]
|
|
previous_item.r = (previous_item.r or 0) + 1
|
|
previous_index = i
|
|
|
|
else
|
|
if i ~= previous_index + 1 then
|
|
item.s = i
|
|
end
|
|
|
|
previous_index = i
|
|
previous_serialized = item_serialized
|
|
table.insert(serialized.i, item)
|
|
end
|
|
|
|
else
|
|
-- Either an empty slot or serilization failed
|
|
previous_index = 0
|
|
previous_serialized = nil
|
|
end
|
|
end
|
|
|
|
return serialized
|
|
end
|
|
|
|
function serialize.deserialize_inventory(inventory, serialized)
|
|
if serialized.b and inventory[supports_bar]() then
|
|
inventory[set_bar](serialized.b)
|
|
end
|
|
|
|
local last_slot_index = 0
|
|
for _, entry in ipairs(serialized.i) do
|
|
local base_index = entry.s or last_slot_index + 1
|
|
|
|
local repeat_count = entry.r or 0
|
|
for offset = 0, repeat_count do
|
|
local index = base_index + offset
|
|
if index > #inventory then
|
|
print("Error: Inventory index out of bounds deserializing inventory, voiding excess slots")
|
|
break
|
|
end
|
|
local slot = inventory[index]
|
|
if entry.f then
|
|
local call_success, call_return = pcall(inventory.set_filter, index, entry.f)
|
|
if not call_success then
|
|
print("Error: '" .. call_return .. "' thrown setting filter " .. entry.f)
|
|
|
|
elseif not call_return then
|
|
print("Error: Failed to set filter " .. entry.f)
|
|
end
|
|
end
|
|
|
|
if entry.n or entry.e then
|
|
serialize.deserialize_item_stack(slot, entry)
|
|
end
|
|
end
|
|
last_slot_index = base_index + repeat_count
|
|
end
|
|
end
|
|
|
|
return serialize
|