mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-11 23:04:52 +09:00
Add option to bump patch number and override factorio_version in the generated mod and update the serialize library to support the inventory bar access methods of both 0.17 and 0.18.
275 lines
9.3 KiB
Lua
275 lines
9.3 KiB
Lua
-- Library for serializing items in Factorio
|
|
-- Based on code from playerManager and trainTeleports
|
|
local serialize = {}
|
|
|
|
-- 0.17 compatibility
|
|
local supports_bar, get_bar, has_bar
|
|
if (pcall(function() local mods = script.active_mods end)) then
|
|
supports_bar = "supports_bar"
|
|
get_bar = "get_bar"
|
|
set_bar = "set_bar"
|
|
else
|
|
supports_bar = "hasbar"
|
|
get_bar = "getbar"
|
|
set_bar = "setbar"
|
|
end
|
|
|
|
-- Equipment Grids are serialized into an array of equipment entries
|
|
-- where ench entry is a table with the following fields:
|
|
-- n: name
|
|
-- 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: curently burning (optional)
|
|
-- f: remaining_burning_fuel (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.shield > 0 then entry.s = equipment.shield end
|
|
if equipment.energy > 0 then entry.e = equipment.energy end
|
|
-- TODO: Test with Industrial Revolution
|
|
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.curently_burning then
|
|
entry.b = {}
|
|
serialize.serialize_item_stack(burner.curently_burning, entry.b)
|
|
entry.f = burner.remaining_burning_fuel
|
|
end
|
|
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.n,
|
|
position = entry.p,
|
|
})
|
|
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
|
|
if entry.b then serialize.deserialize_item_stack(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
|
|
-- 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 slot.health < 1 then entry.h = slot.health end
|
|
if 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.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
|
|
serialize.deserialize_equipment_grid(slot.grid, entry.g)
|
|
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 = game.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
|
|
-- XXX what if the inventory is smaller on this instance?
|
|
local index = base_index + offset
|
|
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
|