]> git.lizzy.rs Git - worldedit.git/blob - worldedit/serialization.lua
Metadata fix for serialization.
[worldedit.git] / worldedit / serialization.lua
1 worldedit = worldedit or {}\r
2 \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
9         end\r
10         if pos1.y > pos2.y then\r
11                 pos2.y, pos1.y = pos1.y, pos2.y\r
12         end\r
13         if pos1.z > pos2.z then\r
14                 pos2.z, pos1.z = pos1.z, pos2.z\r
15         end\r
16         return pos1, pos2\r
17 end\r
18 \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
22                 return 3\r
23         elseif value:find("^[^\"']+%{%d+%}") then\r
24                 if value:find("%[\"meta\"%]") then --previous meta flat table format\r
25                         return 2\r
26                 end\r
27                 return 1 --original flat table format\r
28         elseif value:find("%{") then --current nested table format\r
29                 return 4\r
30         end\r
31         return 0 --unknown format\r
32 end\r
33 \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
38         local count = 0\r
39         local result = {}\r
40         local env = minetest.env\r
41         while pos.x <= pos2.x do\r
42                 pos.y = pos1.y\r
43                 while pos.y <= pos2.y do\r
44                         pos.z = pos1.z\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
48                                         count = count + 1\r
49                                         local meta = env:get_meta(pos):to_table()\r
50 \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
55                                                 end\r
56                                         end\r
57 \r
58                                         result[count] = {\r
59                                                 x = pos.x - pos1.x,\r
60                                                 y = pos.y - pos1.y,\r
61                                                 z = pos.z - pos1.z,\r
62                                                 name = node.name,\r
63                                                 param1 = node.param1,\r
64                                                 param2 = node.param2,\r
65                                                 meta = meta,\r
66                                         }\r
67                                 end\r
68                                 pos.z = pos.z + 1\r
69                         end\r
70                         pos.y = pos.y + 1\r
71                 end\r
72                 pos.x = pos.x + 1\r
73         end\r
74         result = minetest.serialize(result) --convert entries to a string\r
75         return result, count\r
76 end\r
77 \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
85         local count = 0\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
92                 end\r
93                 local tables = get_tables()\r
94 \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
100                                 end\r
101                         end\r
102                 end\r
103                 local nodes = tables[1]\r
104 \r
105                 --check the node array\r
106                 count = #nodes\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
118                         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
129                         end\r
130                 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
140                         count = count + 1\r
141                 end\r
142         elseif version == 4 then --current nested table format\r
143                 local nodes = minetest.deserialize(value)\r
144                 count = #nodes\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
154                 end\r
155         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
159 end\r
160 \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)\r
164         local originx, originy, originz = originpos.x, originpos.y, originpos.z\r
165         local count = 0\r
166         local env = minetest.env\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
172                         return count\r
173                 end\r
174                 local tables = get_tables()\r
175 \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
181                                 end\r
182                         end\r
183                 end\r
184                 local nodes = tables[1]\r
185 \r
186                 --load the node array\r
187                 count = #nodes\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
194                         end\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
201                         end\r
202                 end\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
210                         node.name = name\r
211                         node.param1 = param1\r
212                         node.param2 = param2\r
213                         env:add_node(pos, node)\r
214                         count = count + 1\r
215                 end\r
216         elseif version == 4 then --current nested table format\r
217                 local nodes = minetest.deserialize(value)\r
218                 count = #nodes\r
219                 for index = 1, count do\r
220                         local entry = nodes[index]\r
221                         entry.x, entry.y, entry.z = originx + entry.x, originy + entry.y, originz + entry.z\r
222                         env:add_node(entry, entry) --entry acts both as position and as node\r
223                         env:get_meta(entry):from_table(entry.meta)\r
224                 end\r
225         end\r
226         return count\r
227 end