mapsync-cd2025/serialize_mapblock.lua
2022-12-19 08:12:52 +01:00

153 lines
4.2 KiB
Lua

-- collect nodes with on_timer attributes
local node_names_with_timer = {}
minetest.register_on_mods_loaded(function()
for _,node in pairs(minetest.registered_nodes) do
if node.on_timer then
table.insert(node_names_with_timer, node.name)
end
end
minetest.log("action", "[mapsync] collected " .. #node_names_with_timer .. " items with node timers")
end)
local air_content_id = minetest.get_content_id("air")
local ignore_content_id = minetest.get_content_id("ignore")
-- map of ignored node_ids (node_id => true)
local ignore_node_ids = {
[ignore_content_id] = true
}
-- search for other ignored node_ids
minetest.register_on_mods_loaded(function()
for name, node_def in pairs(minetest.registered_nodes) do
if node_def.groups and node_def.groups.mapsync_ignore then
local node_id = minetest.get_content_id(name)
ignore_node_ids[node_id] = true
end
end
end)
-- local vars for faster access
local insert = table.insert
--- Serializes the mapblock at the given position
-- @param mapblock_pos the mapblock-position
-- @return @{mapblock_data}
function mapsync.serialize_mapblock(mapblock_pos, node_mapping)
node_mapping = node_mapping or {}
local pos1, pos2 = mapsync.get_mapblock_bounds_from_mapblock(mapblock_pos)
assert((pos2.x - pos1.x) == 15)
assert((pos2.y - pos1.y) == 15)
assert((pos2.z - pos1.z) == 15)
local manip = minetest.get_voxel_manip()
local e1, e2 = manip:read_from_map(pos1, pos2)
local area = VoxelArea:new({MinEdge=e1, MaxEdge=e2})
local node_data = manip:get_data()
local param1 = manip:get_light_data()
local param2 = manip:get_param2_data()
assert(#node_data == 4096)
assert(#param1 == 4096)
assert(#param2 == 4096)
-- serialized block
local blockdata = {
empty = true,
has_metadata = false,
metadata = {},
node_ids = {},
param1 = {},
param2 = {}
}
-- id -> true
local mapped_node_ids = {}
-- collect mapped node ids
for _, node_id in pairs(node_mapping) do
mapped_node_ids[node_id] = true
end
-- loop over all blocks and fill cid,param1 and param2
for z=pos1.z,pos2.z do
for x=pos1.x,pos2.x do
for y=pos1.y,pos2.y do
local i = area:index(x,y,z)
local node_id = node_data[i]
if ignore_node_ids[node_id] then
-- replace ignored blocks with air
node_id = air_content_id
end
if node_id ~= air_content_id then
blockdata.empty = false
end
-- map node_id if not already mapped
if not mapped_node_ids[node_id] then
local nodename = minetest.get_name_from_content_id(node_id)
mapped_node_ids[node_id] = nodename
node_mapping[nodename] = node_id
end
insert(blockdata.node_ids, node_id)
insert(blockdata.param1, param1[i])
insert(blockdata.param2, param2[i])
end
end
end
-- serialize metadata
local pos_with_meta = minetest.find_nodes_with_meta(pos1, pos2)
for _, meta_pos in ipairs(pos_with_meta) do
local relative_pos = vector.subtract(meta_pos, pos1)
local meta = minetest.get_meta(meta_pos):to_table()
-- Convert metadata item stacks to item strings
for _, invlist in pairs(meta.inventory) do
for index = 1, #invlist do
local itemstack = invlist[index]
if itemstack.to_string then
invlist[index] = itemstack:to_string()
blockdata.has_metadata = true
end
end
end
-- dirty workaround for https://github.com/minetest/minetest/issues/8943
if next(meta) and (next(meta.fields) or next(meta.inventory)) then
blockdata.has_metadata = true
blockdata.metadata.meta = blockdata.metadata.meta or {}
blockdata.metadata.meta[minetest.pos_to_string(relative_pos)] = meta
end
end
-- serialize node timers
if #node_names_with_timer > 0 then
blockdata.metadata.timers = {}
local list = minetest.find_nodes_in_area(pos1, pos2, node_names_with_timer)
for _, timer_pos in pairs(list) do
local timer = minetest.get_node_timer(timer_pos)
local relative_pos = vector.subtract(timer_pos, pos1)
if timer:is_started() then
blockdata.has_metadata = true
local timeout = timer:get_timeout()
local elapsed = timer:get_elapsed()
blockdata.metadata.timers[minetest.pos_to_string(relative_pos)] = {
timeout = timeout,
-- round down elapsed timer
elapsed = math.min(math.floor(elapsed/10)*10, timeout)
}
end
end
end
return blockdata
end