1 worldedit = worldedit or {}
\r
3 --modifies positions `pos1` and `pos2` so that each component of `pos1` is less than or equal to its corresponding conent of `pos2`, returning two new positions
\r
4 worldedit.sort_pos = function(pos1, pos2)
\r
5 pos1 = {x=pos1.x, y=pos1.y, z=pos1.z}
\r
6 pos2 = {x=pos2.x, y=pos2.y, z=pos2.z}
\r
7 if pos1.x > pos2.x then
\r
8 pos2.x, pos1.x = pos1.x, pos2.x
\r
10 if pos1.y > pos2.y then
\r
11 pos2.y, pos1.y = pos1.y, pos2.y
\r
13 if pos1.z > pos2.z then
\r
14 pos2.z, pos1.z = pos1.z, pos2.z
\r
19 --determines the version of serialized data `value`, returning the version as a positive integer or 0 for unknown versions
\r
20 worldedit.valueversion = function(value)
\r
21 if value:find("([+-]?%d+)%s+([+-]?%d+)%s+([+-]?%d+)") and not value:find("%{") then --previous list format
\r
23 elseif value:find("^[^\"']+%{%d+%}") then
\r
24 if value:find("%[\"meta\"%]") then --previous meta flat table format
\r
27 return 1 --original flat table format
\r
28 elseif value:find("%{") then --current nested table format
\r
31 return 0 --unknown format
\r
34 --converts the region defined by positions `pos1` and `pos2` into a single string, returning the serialized data and the number of nodes serialized
\r
35 worldedit.serialize = function(pos1, pos2) --wip: check for ItemStacks and whether they can be serialized
\r
36 local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
\r
37 local pos = {x=pos1.x, y=0, z=0}
\r
40 local env = minetest.env
\r
41 while pos.x <= pos2.x do
\r
43 while pos.y <= pos2.y do
\r
45 while pos.z <= pos2.z do
\r
46 local node = env:get_node(pos)
\r
47 if node.name ~= "air" and node.name ~= "ignore" then
\r
49 local meta = env:get_meta(pos):to_table()
\r
51 --convert metadata itemstacks to itemstrings
\r
52 for name, inventory in pairs(meta.inventory) do
\r
53 for index, stack in ipairs(inventory) do
\r
54 inventory[index] = stack:to_string()
\r
63 param1 = node.param1,
\r
64 param2 = node.param2,
\r
74 result = minetest.serialize(result) --convert entries to a string
\r
75 return result, count
\r
78 --determines the volume the nodes represented by string `value` would occupy if deserialized at `originpos`, returning the two corner positions and the number of nodes
\r
79 --contains code based on [table.save/table.load](http://lua-users.org/wiki/SaveTableToFile) by ChillCode, available under the MIT license (GPL compatible)
\r
80 worldedit.allocate = function(originpos, value)
\r
81 local huge = math.huge
\r
82 local pos1x, pos1y, pos1z = huge, huge, huge
\r
83 local pos2x, pos2y, pos2z = -huge, -huge, -huge
\r
84 local originx, originy, originz = originpos.x, originpos.y, originpos.z
\r
86 local version = worldedit.valueversion(value)
\r
87 if version == 1 or version == 2 then --flat table format
\r
88 --obtain the node table
\r
89 local get_tables = loadstring(value)
\r
90 if get_tables then --error loading value
\r
91 return originpos, originpos, count
\r
93 local tables = get_tables()
\r
95 --transform the node table into an array of nodes
\r
96 for i = 1, #tables do
\r
97 for j, v in pairs(tables[i]) do
\r
98 if type(v) == "table" then
\r
99 tables[i][j] = tables[v[1]]
\r
103 local nodes = tables[1]
\r
105 --check the node array
\r
107 if version == 1 then --original flat table format
\r
108 for index = 1, count do
\r
109 local entry = nodes[index]
\r
110 local pos = entry[1]
\r
111 local x, y, z = originx - pos.x, originy - pos.y, originz - pos.z
\r
112 if x < pos1x then pos1x = x end
\r
113 if y < pos1y then pos1y = y end
\r
114 if z < pos1z then pos1z = z end
\r
115 if x > pos2x then pos2x = x end
\r
116 if y > pos2y then pos2y = y end
\r
117 if z > pos2z then pos2z = z end
\r
119 else --previous meta flat table format
\r
120 for index = 1, count do
\r
121 local entry = nodes[index]
\r
122 local x, y, z = originx - entry.x, originy - entry.y, originz - entry.z
\r
123 if x < pos1x then pos1x = x end
\r
124 if y < pos1y then pos1y = y end
\r
125 if z < pos1z then pos1z = z end
\r
126 if x > pos2x then pos2x = x end
\r
127 if y > pos2y then pos2y = y end
\r
128 if z > pos2z then pos2z = z end
\r
131 elseif version == 3 then --previous list format
\r
132 for x, y, z, name, param1, param2 in value:gmatch("([+-]?%d+)%s+([+-]?%d+)%s+([+-]?%d+)%s+([^%s]+)%s+(%d+)%s+(%d+)[^\r\n]*[\r\n]*") do --match node entries
\r
133 x, y, z = originx + tonumber(x), originy + tonumber(y), originz + tonumber(z)
\r
134 if x < pos1x then pos1x = x end
\r
135 if y < pos1y then pos1y = y end
\r
136 if z < pos1z then pos1z = z end
\r
137 if x > pos2x then pos2x = x end
\r
138 if y > pos2y then pos2y = y end
\r
139 if z > pos2z then pos2z = z end
\r
142 elseif version == 4 then --current nested table format
\r
143 local nodes = minetest.deserialize(value)
\r
145 for index = 1, count do
\r
146 local entry = nodes[index]
\r
147 x, y, z = originx + entry.x, originy + entry.y, originz + entry.z
\r
148 if x < pos1x then pos1x = x end
\r
149 if y < pos1y then pos1y = y end
\r
150 if z < pos1z then pos1z = z end
\r
151 if x > pos2x then pos2x = x end
\r
152 if y > pos2y then pos2y = y end
\r
153 if z > pos2z then pos2z = z end
\r
156 local pos1 = {x=pos1x, y=pos1y, z=pos1z}
\r
157 local pos2 = {x=pos2x, y=pos2y, z=pos2z}
\r
158 return pos1, pos2, count
\r
161 --loads the nodes represented by string `value` at position `originpos`, returning the number of nodes deserialized
\r
162 --contains code based on [table.save/table.load](http://lua-users.org/wiki/SaveTableToFile) by ChillCode, available under the MIT license (GPL compatible)
\r
163 worldedit.deserialize = function(originpos, value, env)
\r
164 local originx, originy, originz = originpos.x, originpos.y, originpos.z
\r
166 if env == nil then env = minetest.env end
\r
167 local version = worldedit.valueversion(value)
\r
168 if version == 1 or version == 2 then --original flat table format
\r
169 --obtain the node table
\r
170 local get_tables = loadstring(value)
\r
171 if not get_tables then --error loading value
\r
174 local tables = get_tables()
\r
176 --transform the node table into an array of nodes
\r
177 for i = 1, #tables do
\r
178 for j, v in pairs(tables[i]) do
\r
179 if type(v) == "table" then
\r
180 tables[i][j] = tables[v[1]]
\r
184 local nodes = tables[1]
\r
186 --load the node array
\r
188 if version == 1 then --original flat table format
\r
189 for index = 1, count do
\r
190 local entry = nodes[index]
\r
191 local pos = entry[1]
\r
192 pos.x, pos.y, pos.z = originx - pos.x, originy - pos.y, originz - pos.z
\r
193 env:add_node(pos, entry[2])
\r
195 else --previous meta flat table format
\r
196 for index = 1, #nodes do
\r
197 local entry = nodes[index]
\r
198 entry.x, entry.y, entry.z = originx + entry.x, originy + entry.y, originz + entry.z
\r
199 env:add_node(entry, entry) --entry acts both as position and as node
\r
200 env:get_meta(entry):from_table(entry.meta)
\r
203 elseif version == 3 then --previous list format
\r
204 local pos = {x=0, y=0, z=0}
\r
205 local node = {name="", param1=0, param2=0}
\r
206 for x, y, z, name, param1, param2 in value:gmatch("([+-]?%d+)%s+([+-]?%d+)%s+([+-]?%d+)%s+([^%s]+)%s+(%d+)%s+(%d+)[^\r\n]*[\r\n]*") do --match node entries
\r
207 pos.x = originx + tonumber(x)
\r
208 pos.y = originy + tonumber(y)
\r
209 pos.z = originz + tonumber(z)
\r
211 node.param1 = param1
\r
212 node.param2 = param2
\r
213 env:add_node(pos, node)
\r
216 elseif version == 4 then --current nested table format
\r
217 --wip: this is a filthy hack that works surprisingly well
\r
218 value = value:gsub("return%s*{", "", 1):gsub("}%s*$", "", 1)
\r
219 local escaped = value:gsub("\\\\", "@@"):gsub("\\\"", "@@"):gsub("(\"[^\"]*\")", function(s) return string.rep("@", #s) end)
\r
220 local startpos, startpos1, endpos = 1, 1
\r
223 startpos, endpos = escaped:find("},%s*{", startpos)
\r
224 if not startpos then
\r
227 local current = value:sub(startpos1, startpos)
\r
228 table.insert(nodes, minetest.deserialize("return " .. current))
\r
229 startpos, startpos1 = endpos, endpos
\r
232 --local nodes = minetest.deserialize(value) --wip: this is broken for larger tables in the current version of LuaJIT
\r
234 for index = 1, count do
\r
235 local entry = nodes[index]
\r
236 entry.x, entry.y, entry.z = originx + entry.x, originy + entry.y, originz + entry.z
\r
237 env:add_node(entry, entry) --entry acts both as position and as node
\r
238 env:get_meta(entry):from_table(entry.meta)
\r