forked from mtcontrib/Minetest-WorldEdit
		
	Compare commits
	
		
			8 Commits
		
	
	
		
			abc9efeeb8
			...
			new_schems
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					d42ac33d62 | ||
| 
						 | 
					5b19c17117 | ||
| 
						 | 
					4de0eb489f | ||
| 
						 | 
					adc6e00423 | ||
| 
						 | 
					5c06ae59ef | ||
| 
						 | 
					3c5f6b9665 | ||
| 
						 | 
					e387a57e15 | ||
| 
						 | 
					f43bc5278e | 
@@ -1,9 +1,7 @@
 | 
				
			|||||||
--- Schematic serialization and deserialiation.
 | 
					--- Schematic serialization and deserialiation.
 | 
				
			||||||
-- @module worldedit.serialization
 | 
					-- @module worldedit.serialization
 | 
				
			||||||
 | 
					
 | 
				
			||||||
worldedit.LATEST_SERIALIZATION_VERSION = 5
 | 
					worldedit.LATEST_SERIALIZATION_VERSION = 6
 | 
				
			||||||
local LATEST_SERIALIZATION_HEADER = worldedit.LATEST_SERIALIZATION_VERSION .. ":"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
--[[
 | 
					--[[
 | 
				
			||||||
Serialization version history:
 | 
					Serialization version history:
 | 
				
			||||||
@@ -15,6 +13,7 @@ Serialization version history:
 | 
				
			|||||||
      `name`, `param1`, `param2`, and `meta` fields.
 | 
					      `name`, `param1`, `param2`, and `meta` fields.
 | 
				
			||||||
  5: Added header and made `param1`, `param2`, and `meta` fields optional.
 | 
					  5: Added header and made `param1`, `param2`, and `meta` fields optional.
 | 
				
			||||||
      Header format: <Version>,<ExtraHeaderField1>,...:<Content>
 | 
					      Header format: <Version>,<ExtraHeaderField1>,...:<Content>
 | 
				
			||||||
 | 
					  6: Much more complicated but also better format
 | 
				
			||||||
--]]
 | 
					--]]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -66,17 +65,105 @@ function worldedit.serialize(pos1, pos2)
 | 
				
			|||||||
		has_meta[hash_node_position(meta_positions[i])] = true
 | 
							has_meta[hash_node_position(meta_positions[i])] = true
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	local pos = {x=pos1.x, y=0, z=0}
 | 
						-- Decide axis of saved rows
 | 
				
			||||||
 | 
						local dim = vector.add(vector.subtract(pos2, pos1), 1)
 | 
				
			||||||
 | 
						local axis
 | 
				
			||||||
 | 
						if dim.x * dim.y < math.min(dim.y * dim.z, dim.x * dim.z) then
 | 
				
			||||||
 | 
							axis = "z"
 | 
				
			||||||
 | 
						elseif dim.x * dim.z < math.min(dim.x * dim.y, dim.y * dim.z) then
 | 
				
			||||||
 | 
							axis = "y"
 | 
				
			||||||
 | 
						elseif dim.y * dim.z < math.min(dim.x * dim.y, dim.x * dim.z) then
 | 
				
			||||||
 | 
							axis = "x"
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							axis = "x" -- X or Z are usually most efficient
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
						local other1, other2 = worldedit.get_axis_others(axis)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						-- Helper functions (1)
 | 
				
			||||||
 | 
						local MATCH_DIST = 8
 | 
				
			||||||
 | 
						local function match_init(array, first_value)
 | 
				
			||||||
 | 
							array[1] = first_value
 | 
				
			||||||
 | 
							return {first_value}
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
						local function match_try(cache, prev_pushed, value)
 | 
				
			||||||
 | 
							local i = #cache
 | 
				
			||||||
 | 
							while i >= 1 do
 | 
				
			||||||
 | 
								if cache[i] == value then
 | 
				
			||||||
 | 
									local ret = -(#cache - i + 1)
 | 
				
			||||||
 | 
									local was_value = type(prev_pushed) ~= "number" or prev_pushed >= 0
 | 
				
			||||||
 | 
									return ret, (was_value and ret == -1) or prev_pushed == ret
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
 | 
								i = i - 1
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
							return nil, false
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
						local function match_push(cache, match, value)
 | 
				
			||||||
 | 
							if match ~= nil then -- don't advance cache
 | 
				
			||||||
 | 
								return match
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
							local idx = #cache + 1
 | 
				
			||||||
 | 
							cache[idx] = value
 | 
				
			||||||
 | 
							if idx > MATCH_DIST then
 | 
				
			||||||
 | 
								table.remove(cache, 1)
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
							return value
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
						-- Helper functions (2)
 | 
				
			||||||
 | 
						local function cur_new(pos, pos1)
 | 
				
			||||||
 | 
							return {
 | 
				
			||||||
 | 
								a = axis,
 | 
				
			||||||
 | 
								p = {pos.x - pos1.x, pos.y - pos1.y, pos.z - pos1.z},
 | 
				
			||||||
 | 
								c = 1,
 | 
				
			||||||
 | 
								data = {},
 | 
				
			||||||
 | 
								param1 = {},
 | 
				
			||||||
 | 
								param2 = {},
 | 
				
			||||||
 | 
								meta = {},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
						local function is_emptyish(t)
 | 
				
			||||||
 | 
							-- returns true if <t> contains only one element and that one element is == 0
 | 
				
			||||||
 | 
							local seen = false
 | 
				
			||||||
 | 
							for _, value in pairs(t) do
 | 
				
			||||||
 | 
								if not seen then
 | 
				
			||||||
 | 
									if value ~= 0 then
 | 
				
			||||||
 | 
										return false
 | 
				
			||||||
 | 
									end
 | 
				
			||||||
 | 
									seen = true
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
									return false
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
						local function cur_finish(result, cur)
 | 
				
			||||||
 | 
							if is_emptyish(cur.param1) then
 | 
				
			||||||
 | 
								cur.param1 = nil
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
							if is_emptyish(cur.param2) then
 | 
				
			||||||
 | 
								cur.param2 = nil
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
							if next(cur.meta) == nil then
 | 
				
			||||||
 | 
								cur.meta = nil
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
							result[#result + 1] = cur
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						-- Serialize stuff
 | 
				
			||||||
 | 
						local pos = {}
 | 
				
			||||||
	local count = 0
 | 
						local count = 0
 | 
				
			||||||
	local result = {}
 | 
						local result = {}
 | 
				
			||||||
	while pos.x <= pos2.x do
 | 
						local cur
 | 
				
			||||||
		pos.y = pos1.y
 | 
						local cache_data, cache_param1, cache_param2
 | 
				
			||||||
		while pos.y <= pos2.y do
 | 
						local prev_data, prev_param1, prev_param2
 | 
				
			||||||
			pos.z = pos1.z
 | 
						pos[other1] = pos1[other1]
 | 
				
			||||||
			while pos.z <= pos2.z do
 | 
						while pos[other1] <= pos2[other1] do
 | 
				
			||||||
 | 
							pos[other2] = pos1[other2]
 | 
				
			||||||
 | 
							while pos[other2] <= pos2[other2] do
 | 
				
			||||||
 | 
								pos[axis] = pos1[axis]
 | 
				
			||||||
 | 
								while pos[axis] <= pos2[axis] do
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				local node = get_node(pos)
 | 
									local node = get_node(pos)
 | 
				
			||||||
				if node.name ~= "air" and node.name ~= "ignore" then
 | 
									if node.name ~= "air" and node.name ~= "ignore" then
 | 
				
			||||||
					count = count + 1
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
					local meta
 | 
										local meta
 | 
				
			||||||
					if has_meta[hash_node_position(pos)] then
 | 
										if has_meta[hash_node_position(pos)] then
 | 
				
			||||||
@@ -93,25 +180,68 @@ function worldedit.serialize(pos1, pos2)
 | 
				
			|||||||
						end
 | 
											end
 | 
				
			||||||
					end
 | 
										end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					result[count] = {
 | 
										if cur == nil then -- Start a new row
 | 
				
			||||||
						x = pos.x - pos1.x,
 | 
											cur = cur_new(pos, pos1, axis, other1, other2)
 | 
				
			||||||
						y = pos.y - pos1.y,
 | 
					
 | 
				
			||||||
						z = pos.z - pos1.z,
 | 
											cache_data = match_init(cur.data, node.name)
 | 
				
			||||||
						name = node.name,
 | 
											cache_param1 = match_init(cur.param1, node.param1)
 | 
				
			||||||
						param1 = node.param1 ~= 0 and node.param1 or nil,
 | 
											cache_param2 = match_init(cur.param2, node.param2)
 | 
				
			||||||
						param2 = node.param2 ~= 0 and node.param2 or nil,
 | 
											prev_data = cur.data[1]
 | 
				
			||||||
						meta = meta,
 | 
											prev_param1 = cur.param1[1]
 | 
				
			||||||
					}
 | 
											prev_param2 = cur.param2[1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											cur.meta[1] = meta
 | 
				
			||||||
 | 
										else -- Append to existing row
 | 
				
			||||||
 | 
											local next_c = cur.c + 1
 | 
				
			||||||
 | 
											cur.c = next_c
 | 
				
			||||||
 | 
											local value, m, can_omit
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											value = node.name
 | 
				
			||||||
 | 
											m, can_omit = match_try(cache_data, prev_data, node.name)
 | 
				
			||||||
 | 
											if not can_omit then
 | 
				
			||||||
 | 
												 prev_data = match_push(cache_data, m, value)
 | 
				
			||||||
 | 
												 cur.data[next_c] = prev_data
 | 
				
			||||||
 | 
											end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											value = node.param1
 | 
				
			||||||
 | 
											m, can_omit = match_try(cache_param1, prev_param1, value)
 | 
				
			||||||
 | 
											if not can_omit then
 | 
				
			||||||
 | 
												prev_param1 = match_push(cache_param1, m, value)
 | 
				
			||||||
 | 
												cur.param1[next_c] = prev_param1
 | 
				
			||||||
 | 
											end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											value = node.param2
 | 
				
			||||||
 | 
											m, can_omit = match_try(cache_param2, prev_param2, value)
 | 
				
			||||||
 | 
											if not can_omit then
 | 
				
			||||||
 | 
												prev_param2 = match_push(cache_param2, m, value)
 | 
				
			||||||
 | 
												cur.param2[next_c] = prev_param2
 | 
				
			||||||
 | 
											end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											cur.meta[next_c] = meta
 | 
				
			||||||
 | 
										end
 | 
				
			||||||
 | 
										count = count + 1
 | 
				
			||||||
 | 
									else
 | 
				
			||||||
 | 
										if cur ~= nil then -- Finish row
 | 
				
			||||||
 | 
											cur_finish(result, cur)
 | 
				
			||||||
 | 
											cur = nil
 | 
				
			||||||
 | 
										end
 | 
				
			||||||
				end
 | 
									end
 | 
				
			||||||
				pos.z = pos.z + 1
 | 
									pos[axis] = pos[axis] + 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			end
 | 
								end
 | 
				
			||||||
			pos.y = pos.y + 1
 | 
								if cur ~= nil then -- Finish leftover row
 | 
				
			||||||
 | 
									cur_finish(result, cur)
 | 
				
			||||||
 | 
									cur = nil
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
 | 
								pos[other2] = pos[other2] + 1
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
		pos.x = pos.x + 1
 | 
							pos[other1] = pos[other1] + 1
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	-- Serialize entries
 | 
						-- Serialize entries
 | 
				
			||||||
	result = minetest.serialize(result)
 | 
						result = minetest.serialize(result)
 | 
				
			||||||
	return LATEST_SERIALIZATION_HEADER .. result, count
 | 
						return tonumber(worldedit.LATEST_SERIALIZATION_VERSION) .. "," ..
 | 
				
			||||||
 | 
							string.format("%d,%d,%d:", dim.x, dim.y, dim.z) .. result, count
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- Contains code based on [table.save/table.load](http://lua-users.org/wiki/SaveTableToFile)
 | 
					-- Contains code based on [table.save/table.load](http://lua-users.org/wiki/SaveTableToFile)
 | 
				
			||||||
@@ -147,8 +277,7 @@ end
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
--- Loads the schematic in `value` into a node list in the latest format.
 | 
					--- Loads the schematic in `value` into a node list in the latest format.
 | 
				
			||||||
-- @return A node list in the latest format, or nil on failure.
 | 
					-- @return A node list in the latest format, or nil on failure.
 | 
				
			||||||
local function load_schematic(value)
 | 
					local function legacy_load_schematic(version, header, content)
 | 
				
			||||||
	local version, header, content = worldedit.read_header(value)
 | 
					 | 
				
			||||||
	local nodes = {}
 | 
						local nodes = {}
 | 
				
			||||||
	if version == 1 or version == 2 then -- Original flat table format
 | 
						if version == 1 or version == 2 then -- Original flat table format
 | 
				
			||||||
		local tables = minetest.deserialize(content, true)
 | 
							local tables = minetest.deserialize(content, true)
 | 
				
			||||||
@@ -190,6 +319,8 @@ local function load_schematic(value)
 | 
				
			|||||||
		end
 | 
							end
 | 
				
			||||||
	elseif version == 4 or version == 5 then -- Nested table format
 | 
						elseif version == 4 or version == 5 then -- Nested table format
 | 
				
			||||||
		nodes = deserialize_workaround(content)
 | 
							nodes = deserialize_workaround(content)
 | 
				
			||||||
 | 
						elseif version >= 6 then
 | 
				
			||||||
 | 
							error("legacy_load_schematic called for non-legacy schematic")
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
@@ -202,14 +333,29 @@ end
 | 
				
			|||||||
-- @return High corner position.
 | 
					-- @return High corner position.
 | 
				
			||||||
-- @return The number of nodes.
 | 
					-- @return The number of nodes.
 | 
				
			||||||
function worldedit.allocate(origin_pos, value)
 | 
					function worldedit.allocate(origin_pos, value)
 | 
				
			||||||
	local nodes = load_schematic(value)
 | 
						local version, header, content = worldedit.read_header(value)
 | 
				
			||||||
	if not nodes or #nodes == 0 then return nil end
 | 
						if version == 6 then
 | 
				
			||||||
	return worldedit.allocate_with_nodes(origin_pos, nodes)
 | 
							local content = deserialize_workaround(content)
 | 
				
			||||||
 | 
							local pos2 = {
 | 
				
			||||||
 | 
								x = origin_pos.x + tonumber(header[1]) - 1,
 | 
				
			||||||
 | 
								y = origin_pos.y + tonumber(header[2]) - 1,
 | 
				
			||||||
 | 
								z = origin_pos.z + tonumber(header[3]) - 1,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							local count = 0
 | 
				
			||||||
 | 
							for _, row in ipairs(content) do
 | 
				
			||||||
 | 
								count = count + row.c
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
							return origin_pos, pos2, count
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							local nodes = legacy_load_schematic(version, header, content)
 | 
				
			||||||
 | 
							if not nodes or #nodes == 0 then return nil end
 | 
				
			||||||
 | 
							return worldedit.legacy_allocate_with_nodes(origin_pos, nodes)
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- Internal
 | 
					-- Internal
 | 
				
			||||||
function worldedit.allocate_with_nodes(origin_pos, nodes)
 | 
					function worldedit.legacy_allocate_with_nodes(origin_pos, nodes)
 | 
				
			||||||
	local huge = math.huge
 | 
						local huge = math.huge
 | 
				
			||||||
	local pos1x, pos1y, pos1z = huge, huge, huge
 | 
						local pos1x, pos1y, pos1z = huge, huge, huge
 | 
				
			||||||
	local pos2x, pos2y, pos2z = -huge, -huge, -huge
 | 
						local pos2x, pos2y, pos2z = -huge, -huge, -huge
 | 
				
			||||||
@@ -232,24 +378,110 @@ end
 | 
				
			|||||||
--- Loads the nodes represented by string `value` at position `origin_pos`.
 | 
					--- Loads the nodes represented by string `value` at position `origin_pos`.
 | 
				
			||||||
-- @return The number of nodes deserialized.
 | 
					-- @return The number of nodes deserialized.
 | 
				
			||||||
function worldedit.deserialize(origin_pos, value)
 | 
					function worldedit.deserialize(origin_pos, value)
 | 
				
			||||||
	local nodes = load_schematic(value)
 | 
						local version, header, content = worldedit.read_header(value)
 | 
				
			||||||
	if not nodes then return nil end
 | 
						if version == 6 then
 | 
				
			||||||
	if #nodes == 0 then return #nodes end
 | 
							local content = deserialize_workaround(content)
 | 
				
			||||||
 | 
							local pos2 = {
 | 
				
			||||||
 | 
								x = origin_pos.x + tonumber(header[1]) - 1,
 | 
				
			||||||
 | 
								y = origin_pos.y + tonumber(header[2]) - 1,
 | 
				
			||||||
 | 
								z = origin_pos.z + tonumber(header[3]) - 1,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							worldedit.keep_loaded(origin_pos, pos2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	local pos1, pos2 = worldedit.allocate_with_nodes(origin_pos, nodes)
 | 
							return worldedit.deserialize_with_content(origin_pos, content)
 | 
				
			||||||
	worldedit.keep_loaded(pos1, pos2)
 | 
						else
 | 
				
			||||||
 | 
							local nodes = legacy_load_schematic(version, header, content)
 | 
				
			||||||
 | 
							if not nodes or #nodes == 0 then return nil end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	local origin_x, origin_y, origin_z = origin_pos.x, origin_pos.y, origin_pos.z
 | 
							local pos1, pos2 = worldedit.legacy_allocate_with_nodes(origin_pos, nodes)
 | 
				
			||||||
	local count = 0
 | 
							worldedit.keep_loaded(pos1, pos2)
 | 
				
			||||||
	local add_node, get_meta = minetest.add_node, minetest.get_meta
 | 
					
 | 
				
			||||||
	for i, entry in ipairs(nodes) do
 | 
							local origin_x, origin_y, origin_z = origin_pos.x, origin_pos.y, origin_pos.z
 | 
				
			||||||
		entry.x, entry.y, entry.z = origin_x + entry.x, origin_y + entry.y, origin_z + entry.z
 | 
							local count = 0
 | 
				
			||||||
		-- Entry acts as both position and node
 | 
							local add_node, get_meta = minetest.add_node, minetest.get_meta
 | 
				
			||||||
		add_node(entry, entry)
 | 
							for i, entry in ipairs(nodes) do
 | 
				
			||||||
		if entry.meta then
 | 
								entry.x, entry.y, entry.z = origin_x + entry.x, origin_y + entry.y, origin_z + entry.z
 | 
				
			||||||
			get_meta(entry):from_table(entry.meta)
 | 
								-- Entry acts as both position and node
 | 
				
			||||||
 | 
								add_node(entry, entry)
 | 
				
			||||||
 | 
								if entry.meta then
 | 
				
			||||||
 | 
									get_meta(entry):from_table(entry.meta)
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
 | 
							return #nodes
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
	return #nodes
 | 
					 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- Internal
 | 
				
			||||||
 | 
					function worldedit.deserialize_with_content(origin_pos, content)
 | 
				
			||||||
 | 
						-- Helper functions
 | 
				
			||||||
 | 
						local function resolve_refs(array)
 | 
				
			||||||
 | 
							-- find (and cache) highest index
 | 
				
			||||||
 | 
							local max_i = 1
 | 
				
			||||||
 | 
							for i, _ in pairs(array) do
 | 
				
			||||||
 | 
								if i > max_i then max_i = i end
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
							array.max_i = max_i
 | 
				
			||||||
 | 
							-- resolve references
 | 
				
			||||||
 | 
							local cache = {}
 | 
				
			||||||
 | 
							for i = 1, max_i do
 | 
				
			||||||
 | 
								local v = array[i]
 | 
				
			||||||
 | 
								if v ~= nil then
 | 
				
			||||||
 | 
									if type(v) == "number" and v < 0 then -- is a reference
 | 
				
			||||||
 | 
										array[i] = cache[#cache + v + 1]
 | 
				
			||||||
 | 
									else
 | 
				
			||||||
 | 
										cache[#cache + 1] = v
 | 
				
			||||||
 | 
									end
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
						local function read_in_array(array, idx)
 | 
				
			||||||
 | 
							if idx > array.max_i then
 | 
				
			||||||
 | 
								return array[array.max_i]
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
							-- go backwards until we find something
 | 
				
			||||||
 | 
							repeat
 | 
				
			||||||
 | 
								local v = array[idx]
 | 
				
			||||||
 | 
								if v ~= nil then
 | 
				
			||||||
 | 
									return v
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
 | 
								idx = idx - 1
 | 
				
			||||||
 | 
							until idx == 0
 | 
				
			||||||
 | 
							assert(false)
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						-- Actually deserialize
 | 
				
			||||||
 | 
						local count = 0
 | 
				
			||||||
 | 
						local entry = {}
 | 
				
			||||||
 | 
						local add_node, get_meta = minetest.add_node, minetest.get_meta
 | 
				
			||||||
 | 
						for _, row in ipairs(content) do
 | 
				
			||||||
 | 
							local axis = row.a
 | 
				
			||||||
 | 
							local pos = {
 | 
				
			||||||
 | 
								x = origin_pos.x + row.p[1],
 | 
				
			||||||
 | 
								y = origin_pos.y + row.p[2],
 | 
				
			||||||
 | 
								z = origin_pos.z + row.p[3],
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if row.param1 == nil then row.param1 = {0} end
 | 
				
			||||||
 | 
							if row.param2 == nil then row.param2 = {0} end
 | 
				
			||||||
 | 
							if row.meta == nil then row.meta = {} end
 | 
				
			||||||
 | 
							resolve_refs(row.data)
 | 
				
			||||||
 | 
							resolve_refs(row.param1)
 | 
				
			||||||
 | 
							resolve_refs(row.param2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for i = 1, row.c do
 | 
				
			||||||
 | 
								entry.name = read_in_array(row.data, i)
 | 
				
			||||||
 | 
								entry.param1 = read_in_array(row.param1, i)
 | 
				
			||||||
 | 
								entry.param2 = read_in_array(row.param2, i)
 | 
				
			||||||
 | 
								add_node(pos, entry)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								local meta = row.meta[i]
 | 
				
			||||||
 | 
								if meta then
 | 
				
			||||||
 | 
									get_meta(pos):from_table(meta)
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								pos[axis] = pos[axis] + 1
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							count = count + row.c
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
						return count
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1343,23 +1343,6 @@ worldedit.register_command("restore", {
 | 
				
			|||||||
	end,
 | 
						end,
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local function detect_misaligned_schematic(name, pos1, pos2)
 | 
					 | 
				
			||||||
	pos1, pos2 = worldedit.sort_pos(pos1, pos2)
 | 
					 | 
				
			||||||
	-- Check that allocate/save can position the schematic correctly
 | 
					 | 
				
			||||||
	-- The expected behaviour is that the (0,0,0) corner of the schematic stays
 | 
					 | 
				
			||||||
	-- sat pos1, this only works when the minimum position is actually present
 | 
					 | 
				
			||||||
	-- in the schematic.
 | 
					 | 
				
			||||||
	local node = minetest.get_node(pos1)
 | 
					 | 
				
			||||||
	local have_node_at_origin = node.name ~= "air" and node.name ~= "ignore"
 | 
					 | 
				
			||||||
	if not have_node_at_origin then
 | 
					 | 
				
			||||||
		worldedit.player_notify(name,
 | 
					 | 
				
			||||||
			"Warning: The schematic contains excessive free space and WILL be "..
 | 
					 | 
				
			||||||
			"misaligned when allocated or loaded. To avoid this, shrink your "..
 | 
					 | 
				
			||||||
			"area to cover exactly the nodes to be saved."
 | 
					 | 
				
			||||||
		)
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
worldedit.register_command("save", {
 | 
					worldedit.register_command("save", {
 | 
				
			||||||
	params = "<file>",
 | 
						params = "<file>",
 | 
				
			||||||
	description = "Save the current WorldEdit region to \"(world folder)/schems/<file>.we\"",
 | 
						description = "Save the current WorldEdit region to \"(world folder)/schems/<file>.we\"",
 | 
				
			||||||
@@ -1378,7 +1361,6 @@ worldedit.register_command("save", {
 | 
				
			|||||||
	func = function(name, param)
 | 
						func = function(name, param)
 | 
				
			||||||
		local result, count = worldedit.serialize(worldedit.pos1[name],
 | 
							local result, count = worldedit.serialize(worldedit.pos1[name],
 | 
				
			||||||
				worldedit.pos2[name])
 | 
									worldedit.pos2[name])
 | 
				
			||||||
		detect_misaligned_schematic(name, worldedit.pos1[name], worldedit.pos2[name])
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		local path = minetest.get_worldpath() .. "/schems"
 | 
							local path = minetest.get_worldpath() .. "/schems"
 | 
				
			||||||
		-- Create directory if it does not already exist
 | 
							-- Create directory if it does not already exist
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user