]> git.lizzy.rs Git - worldedit.git/blobdiff - worldedit/serialization.lua
Fix //load with 0 nodes (#177)
[worldedit.git] / worldedit / serialization.lua
index f129168a35ee2be9ff8411d6c73730b47442d4bf..e796fc1477653a8afa0d64047c0734747e6970a5 100644 (file)
@@ -1,5 +1,5 @@
-worldedit = worldedit or {}\r
-local minetest = minetest -- Local copy of global\r
+--- Schematic serialization and deserialiation.\r
+-- @module worldedit.serialization\r
 \r
 worldedit.LATEST_SERIALIZATION_VERSION = 5\r
 local LATEST_SERIALIZATION_HEADER = worldedit.LATEST_SERIALIZATION_VERSION .. ":"\r
@@ -52,15 +52,23 @@ end
 -- @return The serialized data.\r
 -- @return The number of nodes serialized.\r
 function worldedit.serialize(pos1, pos2)\r
-       -- Keep area loaded\r
-       local manip = minetest.get_voxel_manip()\r
-       manip:read_from_map(pos1, pos2)\r
+       pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
+\r
+       worldedit.keep_loaded(pos1, pos2)\r
+\r
+       local get_node, get_meta, hash_node_position =\r
+               minetest.get_node, minetest.get_meta, minetest.hash_node_position\r
+\r
+       -- Find the positions which have metadata\r
+       local has_meta = {}\r
+       local meta_positions = minetest.find_nodes_with_meta(pos1, pos2)\r
+       for i = 1, #meta_positions do\r
+               has_meta[hash_node_position(meta_positions[i])] = true\r
+       end\r
 \r
-       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
        local pos = {x=pos1.x, y=0, z=0}\r
        local count = 0\r
        local result = {}\r
-       local get_node, get_meta = minetest.get_node, minetest.get_meta\r
        while pos.x <= pos2.x do\r
                pos.y = pos1.y\r
                while pos.y <= pos2.y do\r
@@ -69,20 +77,19 @@ function worldedit.serialize(pos1, pos2)
                                local node = get_node(pos)\r
                                if node.name ~= "air" and node.name ~= "ignore" then\r
                                        count = count + 1\r
-                                       local meta = get_meta(pos):to_table()\r
-\r
-                                       local meta_empty = true\r
-                                       -- Convert metadata item stacks to item strings\r
-                                       for name, inventory in pairs(meta.inventory) do\r
-                                               for index, stack in ipairs(inventory) do\r
-                                                       meta_empty = false\r
-                                                       inventory[index] = stack.to_string and stack:to_string() or stack\r
-                                               end\r
-                                       end\r
-                                       for k in pairs(meta) do\r
-                                               if k ~= "inventory" then\r
-                                                       meta_empty = false\r
-                                                       break\r
+\r
+                                       local meta\r
+                                       if has_meta[hash_node_position(pos)] then\r
+                                               meta = get_meta(pos):to_table()\r
+\r
+                                               -- Convert metadata item stacks to item strings\r
+                                               for _, invlist in pairs(meta.inventory) do\r
+                                                       for index = 1, #invlist do\r
+                                                               local itemstack = invlist[index]\r
+                                                               if itemstack.to_string then\r
+                                                                       invlist[index] = itemstack:to_string()\r
+                                                               end\r
+                                                       end\r
                                                end\r
                                        end\r
 \r
@@ -93,7 +100,7 @@ function worldedit.serialize(pos1, pos2)
                                                name = node.name,\r
                                                param1 = node.param1 ~= 0 and node.param1 or nil,\r
                                                param2 = node.param2 ~= 0 and node.param2 or nil,\r
-                                               meta = not meta_empty and meta or nil,\r
+                                               meta = meta,\r
                                        }\r
                                end\r
                                pos.z = pos.z + 1\r
@@ -112,7 +119,7 @@ end
 -- Contains code based on [table.save/table.load](http://lua-users.org/wiki/SaveTableToFile)\r
 -- by ChillCode, available under the MIT license.\r
 -- @return A node list in the latest format, or nil on failure.\r
-function worldedit.load_schematic(value)\r
+local function load_schematic(value)\r
        local version, header, content = worldedit.read_header(value)\r
        local nodes = {}\r
        if version == 1 or version == 2 then -- Original flat table format\r
@@ -145,9 +152,9 @@ function worldedit.load_schematic(value)
                                "([^%s]+)%s+(%d+)%s+(%d+)[^\r\n]*[\r\n]*") do\r
                        param1, param2 = tonumber(param1), tonumber(param2)\r
                        table.insert(nodes, {\r
-                               x = originx + tonumber(x),\r
-                               y = originy + tonumber(y),\r
-                               z = originz + tonumber(z),\r
+                               x = tonumber(x),\r
+                               y = tonumber(y),\r
+                               z = tonumber(z),\r
                                name = name,\r
                                param1 = param1 ~= 0 and param1 or nil,\r
                                param2 = param2 ~= 0 and param2 or nil,\r
@@ -160,20 +167,20 @@ function worldedit.load_schematic(value)
                else\r
                        -- XXX: This is a filthy hack that works surprisingly well - in LuaJIT, `minetest.deserialize` will fail due to the register limit\r
                        nodes = {}\r
-                       value = value:gsub("return%s*{", "", 1):gsub("}%s*$", "", 1) -- remove the starting and ending values to leave only the node data\r
-                       local escaped = value:gsub("\\\\", "@@"):gsub("\\\"", "@@"):gsub("(\"[^\"]*\")", function(s) return string.rep("@", #s) end)\r
+                       content = content:gsub("return%s*{", "", 1):gsub("}%s*$", "", 1) -- remove the starting and ending values to leave only the node data\r
+                       local escaped = content:gsub("\\\\", "@@"):gsub("\\\"", "@@"):gsub("(\"[^\"]*\")", function(s) return string.rep("@", #s) end)\r
                        local startpos, startpos1, endpos = 1, 1\r
                        while true do -- go through each individual node entry (except the last)\r
                                startpos, endpos = escaped:find("},%s*{", startpos)\r
                                if not startpos then\r
                                        break\r
                                end\r
-                               local current = value:sub(startpos1, startpos)\r
+                               local current = content:sub(startpos1, startpos)\r
                                local entry = minetest.deserialize("return " .. current)\r
                                table.insert(nodes, entry)\r
                                startpos, startpos1 = endpos, endpos\r
                        end\r
-                       local entry = minetest.deserialize("return " .. value:sub(startpos1)) -- process the last entry\r
+                       local entry = minetest.deserialize("return " .. content:sub(startpos1)) -- process the last entry\r
                        table.insert(nodes, entry)\r
                end\r
        else\r
@@ -187,15 +194,15 @@ end
 -- @return Low corner position.\r
 -- @return High corner position.\r
 -- @return The number of nodes.\r
-worldedit.allocate = function(origin_pos, value)\r
-       local nodes = worldedit.load_schematic(value)\r
+function worldedit.allocate(origin_pos, value)\r
+       local nodes = load_schematic(value)\r
        if not nodes then return nil end\r
        return worldedit.allocate_with_nodes(origin_pos, nodes)\r
 end\r
 \r
 \r
 -- Internal\r
-worldedit.allocate_with_nodes = function(origin_pos, nodes)\r
+function worldedit.allocate_with_nodes(origin_pos, nodes)\r
        local huge = math.huge\r
        local pos1x, pos1y, pos1z = huge, huge, huge\r
        local pos2x, pos2y, pos2z = -huge, -huge, -huge\r
@@ -217,14 +224,13 @@ end
 \r
 --- Loads the nodes represented by string `value` at position `origin_pos`.\r
 -- @return The number of nodes deserialized.\r
-worldedit.deserialize = function(origin_pos, value)\r
-       local nodes = worldedit.load_schematic(value)\r
+function worldedit.deserialize(origin_pos, value)\r
+       local nodes = load_schematic(value)\r
        if not nodes then return nil end\r
+       if #nodes == 0 then return #nodes end\r
 \r
-       -- Make area stay loaded\r
        local pos1, pos2 = worldedit.allocate_with_nodes(origin_pos, nodes)\r
-       local manip = minetest.get_voxel_manip()\r
-       manip:read_from_map(pos1, pos2)\r
+       worldedit.keep_loaded(pos1, pos2)\r
 \r
        local origin_x, origin_y, origin_z = origin_pos.x, origin_pos.y, origin_pos.z\r
        local count = 0\r