2012-10-14 03:45:50 +02:00
worldedit = worldedit or { }
2013-07-12 20:22:30 +02:00
local minetest = minetest --local copy of global
2012-10-14 03:45:50 +02:00
--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
worldedit.sort_pos = function ( pos1 , pos2 )
pos1 = { x = pos1.x , y = pos1.y , z = pos1.z }
pos2 = { x = pos2.x , y = pos2.y , z = pos2.z }
if pos1.x > pos2.x then
pos2.x , pos1.x = pos1.x , pos2.x
end
if pos1.y > pos2.y then
pos2.y , pos1.y = pos1.y , pos2.y
end
if pos1.z > pos2.z then
pos2.z , pos1.z = pos1.z , pos2.z
end
return pos1 , pos2
end
2013-03-20 22:12:48 +01:00
--determines the version of serialized data `value`, returning the version as a positive integer or 0 for unknown versions
worldedit.valueversion = function ( value )
if value : find ( " ([+-]?%d+)%s+([+-]?%d+)%s+([+-]?%d+) " ) and not value : find ( " %{ " ) then --previous list format
return 3
elseif value : find ( " ^[^ \" ']+%{%d+%} " ) then
if value : find ( " %[ \" meta \" %] " ) then --previous meta flat table format
return 2
end
return 1 --original flat table format
elseif value : find ( " %{ " ) then --current nested table format
return 4
end
return 0 --unknown format
end
2012-10-14 03:45:50 +02:00
--converts the region defined by positions `pos1` and `pos2` into a single string, returning the serialized data and the number of nodes serialized
2013-07-29 00:15:46 +02:00
worldedit.serialize = function ( pos1 , pos2 )
2013-07-21 22:54:25 +02:00
--make area stay loaded
local manip = minetest.get_voxel_manip ( )
manip : read_from_map ( pos1 , pos2 )
2012-10-14 03:45:50 +02:00
local pos1 , pos2 = worldedit.sort_pos ( pos1 , pos2 )
local pos = { x = pos1.x , y = 0 , z = 0 }
local count = 0
local result = { }
2013-07-12 20:22:30 +02:00
local get_node , get_meta = minetest.get_node , minetest.get_meta
2012-10-14 03:45:50 +02:00
while pos.x <= pos2.x do
pos.y = pos1.y
while pos.y <= pos2.y do
pos.z = pos1.z
while pos.z <= pos2.z do
2013-07-12 20:22:30 +02:00
local node = get_node ( pos )
2012-10-14 03:45:50 +02:00
if node.name ~= " air " and node.name ~= " ignore " then
count = count + 1
2013-07-12 20:22:30 +02:00
local meta = get_meta ( pos ) : to_table ( )
2013-03-27 23:07:52 +01:00
--convert metadata itemstacks to itemstrings
for name , inventory in pairs ( meta.inventory ) do
for index , stack in ipairs ( inventory ) do
2013-04-30 01:52:00 +02:00
inventory [ index ] = stack.to_string and stack : to_string ( ) or stack
2013-03-27 23:07:52 +01:00
end
end
2013-03-20 22:12:48 +01:00
result [ count ] = {
x = pos.x - pos1.x ,
y = pos.y - pos1.y ,
z = pos.z - pos1.z ,
name = node.name ,
param1 = node.param1 ,
param2 = node.param2 ,
meta = meta ,
}
2012-10-14 03:45:50 +02:00
end
pos.z = pos.z + 1
end
pos.y = pos.y + 1
end
pos.x = pos.x + 1
end
2013-03-20 22:12:48 +01:00
result = minetest.serialize ( result ) --convert entries to a string
2012-10-14 03:45:50 +02:00
return result , count
end
2012-10-21 23:49:43 +02:00
--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
2013-03-20 22:12:48 +01:00
--contains code based on [table.save/table.load](http://lua-users.org/wiki/SaveTableToFile) by ChillCode, available under the MIT license (GPL compatible)
2012-10-21 23:49:43 +02:00
worldedit.allocate = function ( originpos , value )
local huge = math.huge
2013-03-20 22:12:48 +01:00
local pos1x , pos1y , pos1z = huge , huge , huge
local pos2x , pos2y , pos2z = - huge , - huge , - huge
2012-10-21 23:49:43 +02:00
local originx , originy , originz = originpos.x , originpos.y , originpos.z
local count = 0
2013-03-20 22:12:48 +01:00
local version = worldedit.valueversion ( value )
if version == 1 or version == 2 then --flat table format
--obtain the node table
local get_tables = loadstring ( value )
if get_tables then --error loading value
return originpos , originpos , count
end
local tables = get_tables ( )
--transform the node table into an array of nodes
for i = 1 , # tables do
for j , v in pairs ( tables [ i ] ) do
if type ( v ) == " table " then
tables [ i ] [ j ] = tables [ v [ 1 ] ]
end
end
2012-10-21 23:49:43 +02:00
end
2013-03-20 22:12:48 +01:00
local nodes = tables [ 1 ]
--check the node array
count = # nodes
if version == 1 then --original flat table format
for index = 1 , count do
local entry = nodes [ index ]
local pos = entry [ 1 ]
local x , y , z = originx - pos.x , originy - pos.y , originz - pos.z
if x < pos1x then pos1x = x end
if y < pos1y then pos1y = y end
if z < pos1z then pos1z = z end
if x > pos2x then pos2x = x end
if y > pos2y then pos2y = y end
if z > pos2z then pos2z = z end
end
else --previous meta flat table format
for index = 1 , count do
local entry = nodes [ index ]
local x , y , z = originx - entry.x , originy - entry.y , originz - entry.z
if x < pos1x then pos1x = x end
if y < pos1y then pos1y = y end
if z < pos1z then pos1z = z end
if x > pos2x then pos2x = x end
if y > pos2y then pos2y = y end
if z > pos2z then pos2z = z end
end
2012-10-21 23:49:43 +02:00
end
2013-03-20 22:12:48 +01:00
elseif version == 3 then --previous list format
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
x , y , z = originx + tonumber ( x ) , originy + tonumber ( y ) , originz + tonumber ( z )
if x < pos1x then pos1x = x end
if y < pos1y then pos1y = y end
if z < pos1z then pos1z = z end
if x > pos2x then pos2x = x end
if y > pos2y then pos2y = y end
if z > pos2z then pos2z = z end
count = count + 1
end
elseif version == 4 then --current nested table format
2013-07-21 22:54:25 +02:00
--wip: this is a filthy hack that works surprisingly well
value = value : gsub ( " return%s*{ " , " " , 1 ) : gsub ( " }%s*$ " , " " , 1 )
local escaped = value : gsub ( " \\ \\ " , " @@ " ) : gsub ( " \\ \" " , " @@ " ) : gsub ( " ( \" [^ \" ]* \" ) " , function ( s ) return string.rep ( " @ " , # s ) end )
local startpos , startpos1 , endpos = 1 , 1
local nodes = { }
while true do
startpos , endpos = escaped : find ( " },%s*{ " , startpos )
if not startpos then
break
end
local current = value : sub ( startpos1 , startpos )
table.insert ( nodes , minetest.deserialize ( " return " .. current ) )
startpos , startpos1 = endpos , endpos
end
table.insert ( nodes , minetest.deserialize ( " return " .. value : sub ( startpos1 ) ) )
--local nodes = minetest.deserialize(value) --wip: this is broken for larger tables in the current version of LuaJIT
2013-03-20 22:12:48 +01:00
count = # nodes
for index = 1 , count do
local entry = nodes [ index ]
x , y , z = originx + entry.x , originy + entry.y , originz + entry.z
if x < pos1x then pos1x = x end
if y < pos1y then pos1y = y end
if z < pos1z then pos1z = z end
if x > pos2x then pos2x = x end
if y > pos2y then pos2y = y end
if z > pos2z then pos2z = z end
2012-10-21 23:49:43 +02:00
end
end
2013-03-20 22:12:48 +01:00
local pos1 = { x = pos1x , y = pos1y , z = pos1z }
local pos2 = { x = pos2x , y = pos2y , z = pos2z }
2012-10-21 23:49:43 +02:00
return pos1 , pos2 , count
end
2012-10-14 03:45:50 +02:00
--loads the nodes represented by string `value` at position `originpos`, returning the number of nodes deserialized
2013-03-20 22:12:48 +01:00
--contains code based on [table.save/table.load](http://lua-users.org/wiki/SaveTableToFile) by ChillCode, available under the MIT license (GPL compatible)
2013-07-31 06:02:37 +02:00
worldedit.deserialize = function ( originpos , value )
2013-08-01 04:15:08 +02:00
--make area stay loaded --wip: not very performant
2013-07-31 06:02:37 +02:00
local pos1 , pos2 = worldedit.allocate ( originpos , value )
local manip = minetest.get_voxel_manip ( )
manip : read_from_map ( pos1 , pos2 )
2012-10-21 23:49:43 +02:00
local originx , originy , originz = originpos.x , originpos.y , originpos.z
2012-10-14 03:45:50 +02:00
local count = 0
2013-07-12 20:22:30 +02:00
local add_node , get_meta = minetest.add_node , minetest.get_meta
2013-03-20 22:12:48 +01:00
local version = worldedit.valueversion ( value )
if version == 1 or version == 2 then --original flat table format
--obtain the node table
local get_tables = loadstring ( value )
if not get_tables then --error loading value
return count
end
local tables = get_tables ( )
--transform the node table into an array of nodes
for i = 1 , # tables do
for j , v in pairs ( tables [ i ] ) do
if type ( v ) == " table " then
tables [ i ] [ j ] = tables [ v [ 1 ] ]
end
2012-10-23 05:17:37 +02:00
end
end
2013-03-20 22:12:48 +01:00
local nodes = tables [ 1 ]
2012-10-23 05:17:37 +02:00
2013-03-20 22:12:48 +01:00
--load the node array
count = # nodes
if version == 1 then --original flat table format
for index = 1 , count do
local entry = nodes [ index ]
local pos = entry [ 1 ]
pos.x , pos.y , pos.z = originx - pos.x , originy - pos.y , originz - pos.z
2013-07-12 20:22:30 +02:00
add_node ( pos , entry [ 2 ] )
2012-10-14 03:45:50 +02:00
end
2013-03-20 22:12:48 +01:00
else --previous meta flat table format
for index = 1 , # nodes do
local entry = nodes [ index ]
entry.x , entry.y , entry.z = originx + entry.x , originy + entry.y , originz + entry.z
2013-07-12 20:22:30 +02:00
add_node ( entry , entry ) --entry acts both as position and as node
get_meta ( entry ) : from_table ( entry.meta )
2012-10-14 03:45:50 +02:00
end
end
2013-03-20 22:12:48 +01:00
elseif version == 3 then --previous list format
local pos = { x = 0 , y = 0 , z = 0 }
local node = { name = " " , param1 = 0 , param2 = 0 }
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
2013-07-12 20:22:30 +02:00
pos.x , pos.y , pos.z = originx + tonumber ( x ) , originy + tonumber ( y ) , originz + tonumber ( z )
node.name , node.param1 , node.param2 = name , param1 , param2
add_node ( pos , node )
2013-03-20 22:12:48 +01:00
count = count + 1
end
elseif version == 4 then --current nested table format
2013-04-21 02:28:21 +02:00
--wip: this is a filthy hack that works surprisingly well
value = value : gsub ( " return%s*{ " , " " , 1 ) : gsub ( " }%s*$ " , " " , 1 )
2013-04-29 01:01:24 +02:00
local escaped = value : gsub ( " \\ \\ " , " @@ " ) : gsub ( " \\ \" " , " @@ " ) : gsub ( " ( \" [^ \" ]* \" ) " , function ( s ) return string.rep ( " @ " , # s ) end )
2013-04-21 02:28:21 +02:00
local startpos , startpos1 , endpos = 1 , 1
local nodes = { }
while true do
startpos , endpos = escaped : find ( " },%s*{ " , startpos )
if not startpos then
break
end
local current = value : sub ( startpos1 , startpos )
table.insert ( nodes , minetest.deserialize ( " return " .. current ) )
startpos , startpos1 = endpos , endpos
end
2013-04-30 01:52:00 +02:00
table.insert ( nodes , minetest.deserialize ( " return " .. value : sub ( startpos1 ) ) )
2013-04-21 02:28:21 +02:00
--local nodes = minetest.deserialize(value) --wip: this is broken for larger tables in the current version of LuaJIT
2013-04-30 01:52:00 +02:00
--load the nodes
2013-03-20 22:12:48 +01:00
count = # nodes
for index = 1 , count do
local entry = nodes [ index ]
entry.x , entry.y , entry.z = originx + entry.x , originy + entry.y , originz + entry.z
2013-07-12 20:22:30 +02:00
add_node ( entry , entry ) --entry acts both as position and as node
2013-04-30 01:52:00 +02:00
end
--load the metadata
for index = 1 , count do
local entry = nodes [ index ]
2013-07-12 20:22:30 +02:00
get_meta ( entry ) : from_table ( entry.meta )
2013-03-20 22:12:48 +01:00
end
2012-10-14 03:45:50 +02:00
end
return count
2013-04-28 19:08:09 +02:00
end