schematic rotation (#1)

* schematic rotation

* fixes

* remove auto-rotation code

* schematic encoding v2

* testing

* working encode/decode

* rotate schematic

* spec

* rotate dialog

* skip if rotation == 0

* wip

* wip

* wip

* fix order

* fix metadata rotation

---------

Co-authored-by: BuckarooBanzay <BuckarooBanzay@users.noreply.github.com>
This commit is contained in:
Buckaroo Banzai 2024-02-29 13:15:18 +01:00 committed by GitHub
parent edbc905fe1
commit 9cd093046d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 659 additions and 92 deletions

17
.github/workflows/test.yml vendored Normal file
View File

@ -0,0 +1,17 @@
name: test
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 10
strategy:
matrix:
ENGINE_VERSION: [5.5.0, 5.6.0, 5.7.0, latest]
steps:
- uses: actions/checkout@v3
- name: test
run: docker-compose up --exit-code-from sut

View File

@ -14,5 +14,5 @@ read_globals = {
"minetest",
-- mods
"mapsync"
"mapsync", "mtt"
}

View File

@ -54,16 +54,18 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
end
if not fields.save and not fields.key_enter_field then
return false
return true
end
local playername = player:get_player_name()
if not pos1[playername] or not pos2[playername] then
return false
return true
end
-- configure and unmark
pick_and_place.configure(pos1[playername], pos2[playername], fields.name)
pos1[playername] = nil
pos2[playername] = nil
return true
end)

17
docker-compose.yml Normal file
View File

@ -0,0 +1,17 @@
version: "3.6"
services:
sut:
build:
context: ./test
args:
ENGINE_VERSION: ${ENGINE_VERSION:-5.7.0}
user: root
volumes:
- "./:/root/.minetest/worlds/world/worldmods/pick_and_place/"
- "world_data:/root/.minetest/worlds/world"
- "./test/world.mt:/root/.minetest/worlds/world/world.mt"
- "./test/minetest.conf:/minetest.conf"
volumes:
world_data: {}

101
encode.lua Normal file
View File

@ -0,0 +1,101 @@
local char, byte = string.char, string.byte
local function encode_uint16(int)
local a, b = int % 0x100, int / 0x100
return char(a, b)
end
local function decode_uint16(str, ofs)
ofs = ofs or 1
local a = byte(str, ofs)
local b = byte(str, ofs + 1)
return a + b * 0x100
end
-- nodeid -> name
local nodeid_name_mapping = {}
function pick_and_place.encode_schematic(schematic)
-- list of strings
local node_id_data = {}
local param2_data = {}
local nodeid_mapping = {}
-- nodeid -> true
local nodeids = {}
for i = 1, #schematic.node_id_data do
local node_id = schematic.node_id_data[i]
nodeids[node_id] = true
table.insert(node_id_data, encode_uint16(node_id))
table.insert(param2_data, char(schematic.param2_data[i]))
end
for nodeid in pairs(nodeids) do
local name = nodeid_name_mapping[nodeid]
if not name then
name = minetest.get_name_from_content_id(nodeid)
nodeid_name_mapping[nodeid] = name
end
nodeid_mapping[nodeid] = name
end
local serialized_data = minetest.serialize({
version = 2,
node_id_data = table.concat(node_id_data),
param2_data = table.concat(param2_data),
metadata = schematic.metadata,
nodeid_mapping = nodeid_mapping,
size = schematic.size
})
local compressed_data = minetest.compress(serialized_data, "deflate")
local encoded_data = minetest.encode_base64(compressed_data)
return encoded_data
end
-- name -> nodeid
local name_nodeid_mapping = {}
function pick_and_place.decode_schematic(encoded_data)
local compressed_data = minetest.decode_base64(encoded_data)
local serialized_data = minetest.decompress(compressed_data, "deflate")
local data = minetest.deserialize(serialized_data)
if data.version ~= 2 then
return false, "invalid version: " .. (data.version or "nil")
end
local schematic = {
node_id_data = {},
param2_data = {},
metadata = data.metadata,
size = data.size
}
-- foreign_nodeid -> local_nodeid
local localized_id_mapping = {}
for foreign_nodeid, name in pairs(data.nodeid_mapping) do
local local_nodeid = name_nodeid_mapping[name]
if not local_nodeid then
local_nodeid = minetest.get_content_id(name)
name_nodeid_mapping[name] = local_nodeid
end
localized_id_mapping[foreign_nodeid] = local_nodeid
end
for i = 1, #data.param2_data do
-- localize nodeid mapping
local foreign_nodeid = decode_uint16(data.node_id_data, 1 + ((i-1) * 2))
local local_nodeid = localized_id_mapping[foreign_nodeid]
table.insert(schematic.node_id_data, local_nodeid)
table.insert(schematic.param2_data, byte(data.param2_data, i))
end
return schematic
end

37
encode.spec.lua Normal file
View File

@ -0,0 +1,37 @@
mtt.register("encode", function(callback)
local data = pick_and_place.encode_schematic({
node_id_data = {0, 0, 0, 0, 1, 2, 3, 4},
param2_data = {0, 0, 0, 0, 4, 3, 2, 1},
metadata = {
["(0,0,0)"] = {
meta = {
x = 1
},
inventory = {
main = {"default:stick 1"}
}
}
},
size = {
x = 2,
y = 2,
z = 2
}
})
assert(data)
local schematic, err = pick_and_place.decode_schematic(data)
assert(not err)
assert(schematic)
assert(#schematic.node_id_data, 8)
assert(#schematic.param2_data, 8)
assert(schematic.metadata["(0,0,0)"])
assert(schematic.size.x == 2)
assert(schematic.size.y == 2)
assert(schematic.size.z == 2)
callback()
end)

View File

@ -3,9 +3,15 @@ pick_and_place = {}
local MP = minetest.get_modpath("pick_and_place")
dofile(MP .. "/common.lua")
dofile(MP .. "/rotate.lua")
dofile(MP .. "/schematic_rotate.lua")
dofile(MP .. "/schematic_flip.lua")
dofile(MP .. "/schematic_orient.lua")
dofile(MP .. "/schematic_transpose.lua")
dofile(MP .. "/pointed.lua")
dofile(MP .. "/configure.lua")
dofile(MP .. "/remove.lua")
dofile(MP .. "/encode.lua")
dofile(MP .. "/serialize.lua")
dofile(MP .. "/entity.lua")
dofile(MP .. "/handle_node.lua")
@ -14,3 +20,8 @@ dofile(MP .. "/configure_tool.lua")
dofile(MP .. "/pick_tool.lua")
dofile(MP .. "/place_tool.lua")
dofile(MP .. "/preview.lua")
if minetest.get_modpath("mtt") and mtt.enabled then
dofile(MP .. "/encode.spec.lua")
dofile(MP .. "/schematic_rotate.spec.lua")
end

View File

@ -1,5 +1,5 @@
name = pick_and_place
description = Pick and place utility
depends = wield_events
optional_depends = mapsync
optional_depends = mapsync, mtt
min_minetest_version = 5.5.0

View File

@ -1,6 +1,9 @@
local FORMSPEC_NAME = "pick_and_place:place"
local has_mapsync = minetest.get_modpath("mapsync")
local function get_pos(player, size)
local function get_pos(meta, player)
local size = minetest.string_to_pos(meta:get_string("size"))
local distance = vector.distance(vector.new(), size)
local radius = math.ceil(distance / 2)
local offset = vector.round(vector.divide(size, 2))
@ -25,28 +28,39 @@ minetest.register_tool("pick_and_place:place", {
local controls = player:get_player_control()
local meta = itemstack:get_meta()
local schematic = meta:get_string("schematic")
local size = minetest.string_to_pos(meta:get_string("size"))
local pos1, pos2 = get_pos(player, size)
local pos1, pos2 = get_pos(meta, player)
if controls.aux1 then
-- removal
pick_and_place.remove_area(pos1, pos2)
else
-- placement
local schematic = meta:get_string("schematic")
local success, msg = pick_and_place.deserialize(pos1, schematic)
if not success then
minetest.chat_send_player(playername, "Placement error: " .. msg)
end
end
end,
on_secondary_use = function(_, player)
local playername = player:get_player_name()
-- show name input
minetest.show_formspec(playername, FORMSPEC_NAME, [[
size[9,1]
real_coordinates[true]
button_exit[0.1,0.1;2.8,0.8;deg90;90°]
button_exit[3.1,0.1;2.8,0.8;deg180;180°]
button_exit[6.1,0.1;2.8,0.8;deg270;270°]
]])
end,
on_step = function(itemstack, player)
local playername = player:get_player_name()
local controls = player:get_player_control()
local meta = itemstack:get_meta()
local size = minetest.string_to_pos(meta:get_string("size"))
local pos1, pos2 = get_pos(player, size)
local pos1, pos2 = get_pos(meta, player)
if controls.aux1 then
-- removal preview
@ -65,3 +79,49 @@ minetest.register_tool("pick_and_place:place", {
pick_and_place.clear_preview(playername)
end
})
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= FORMSPEC_NAME then
return false
end
local itemstack = player:get_wielded_item()
if itemstack:get_name() ~= "pick_and_place:place" then
return true
end
local rotation = 0
if fields.deg90 then
rotation = 90
elseif fields.deg180 then
rotation = 180
elseif fields.deg270 then
rotation = 270
end
if rotation == 0 then
return true
end
local meta = itemstack:get_meta()
local schematic_data = meta:get_string("schematic")
local schematic, err = pick_and_place.decode_schematic(schematic_data)
if err then
minetest.chat_send_player(player:get_player_name(), "Schematic decode error: " .. err)
return true
end
-- rotate schematic
pick_and_place.schematic_rotate(schematic, rotation)
meta:set_string("schematic", pick_and_place.encode_schematic(schematic))
-- rotate size
local size = minetest.string_to_pos(meta:get_string("size"))
size = pick_and_place.rotate_size(size, rotation)
meta:set_string("size", minetest.pos_to_string(size))
-- set tool
player:set_wielded_item(itemstack)
return true
end)

32
rotate.lua Normal file
View File

@ -0,0 +1,32 @@
-- rotates the size for a given rotation
function pick_and_place.rotate_size(size, rotation)
if rotation == 90 or rotation == 270 then
-- invert x/z
return {
x = size.z,
y = size.y,
z = size.x
}
end
-- unchanged in 180 or 0 degrees
return size
end
-- look direction in 90-degree increments
function pick_and_place.get_player_rotation(player)
local yaw = player:get_look_horizontal()
local degrees = yaw / math.pi * 180
local rotation = 0
if degrees > 45 and degrees < (90+45) then
-- x-
rotation = 180
elseif degrees > (90+45) and degrees < (180+45) then
-- z-
rotation = 90
elseif degrees < 45 or degrees > (360-45) then
-- z+
rotation = 270
end
return rotation
end

41
schematic_flip.lua Normal file
View File

@ -0,0 +1,41 @@
local function flip_data(data, size, indexFn, axis)
local pos = {x=0, y=0, z=0}
local max = { x=size.x, y=size.y, z=size.z }
local start = max[axis]
max[axis] = math.floor(max[axis] / 2)
while pos.x <= max.x do
pos.y = 0
while pos.y <= max.y do
pos.z = 0
while pos.z <= max.z do
local data_1 = data[indexFn(pos)]
local value = pos[axis] -- Save position
pos[axis] = start - value -- Shift position
local data_2 = data[indexFn(pos)]
data[indexFn(pos)] = data_1
pos[axis] = value -- Restore position
data[indexFn(pos)] = data_2
pos.z = pos.z + 1
end
pos.y = pos.y + 1
end
pos.x = pos.x + 1
end
end
function pick_and_place.schematic_flip(node_ids, param2_data, metadata, max, axis)
local min = { x=0, y=0, z=0 }
local area = VoxelArea:new({MinEdge=min, MaxEdge=max})
local vmanipIndex = function(pos) return area:indexp(pos) end
local metaIndex = function(pos) return minetest.pos_to_string(pos) end
flip_data(node_ids, max, vmanipIndex, axis)
flip_data(param2_data, max, vmanipIndex, axis)
if metadata then
flip_data(metadata, max, metaIndex, axis)
end
end

88
schematic_orient.lua Normal file
View File

@ -0,0 +1,88 @@
local node_id_to_name_cache = {}
local node_ids_rotateable = {}
local wallmounted = {
[90] = {0, 1, 5, 4, 2, 3, 0, 0},
[180] = {0, 1, 3, 2, 5, 4, 0, 0},
[270] = {0, 1, 4, 5, 3, 2, 0, 0}
}
local facedir = {
[90] = { 1, 2, 3, 0, 13, 14, 15, 12, 17, 18, 19, 16,
9, 10, 11, 8, 5, 6, 7, 4, 23, 20, 21, 22},
[180] = { 2, 3, 0, 1, 10, 11, 8, 9, 6, 7, 4, 5,
18, 19, 16, 17, 14, 15, 12, 13, 22, 23, 20, 21},
[270] = { 3, 0, 1, 2, 19, 16, 17, 18, 15, 12, 13, 14,
7, 4, 5, 6, 11, 8, 9, 10, 21, 22, 23, 20}
}
local function rotate_param2(node_name, param2, angle)
if not angle or angle == 0 then
return param2
end
local def = minetest.registered_nodes[node_name]
if def then
local wallmounted_substitution = wallmounted[angle]
local facedir_substitution = facedir[angle]
local paramtype2 = def.paramtype2
if paramtype2 == "wallmounted" or paramtype2 == "colorwallmounted" then
local orient = param2 % 8
return param2 - orient + wallmounted_substitution[orient + 1]
elseif paramtype2 == "facedir" or paramtype2 == "colorfacedir" then
local orient = param2 % 32
return param2 - orient + facedir_substitution[orient + 1]
end
end
end
local min = { x=0, y=0, z=0 }
local rotate_param2types = {
["wallmounted"] = true,
["colorwallmounted"] = true,
["facedir"] = true,
["colorfacedir"] = true
}
function pick_and_place.schematic_orient(node_ids, param2_data, max, rotation)
-- https://github.com/Uberi/Minetest-WorldEdit/blob/master/worldedit/manipulations.lua#L555
local area = VoxelArea:new({MinEdge=min, MaxEdge=max})
local pos = {x=0, y=0, z=0}
while pos.x <= max.x do
pos.y = 0
while pos.y <= max.y do
pos.z = 0
while pos.z <= max.z do
local index = area:indexp(pos)
local param2 = param2_data[index]
local node_id = node_ids[index]
local node_name = node_id_to_name_cache[node_id]
if not node_name then
-- cache association
node_name = minetest.get_name_from_content_id(node_id)
node_id_to_name_cache[node_id] = node_name
-- check if param2 is facedir
local def = minetest.registered_nodes[node_name]
node_ids_rotateable[node_id] = rotate_param2types[def.paramtype2]
end
if node_ids_rotateable[node_id] then
-- rotate only the non-disabled and supported nodes
param2 = rotate_param2(node_name, param2, rotation)
param2_data[index] = param2
end
pos.z = pos.z + 1
end
pos.y = pos.y + 1
end
pos.x = pos.x + 1
end
end

120
schematic_rotate.lua Normal file
View File

@ -0,0 +1,120 @@
local zero = { x=0, y=0, z=0 }
-- creates a buffer for the rotation
local function create_buffer(data, size, max)
local area_src = VoxelArea:new({MinEdge=zero, MaxEdge=size})
local area_dst = VoxelArea:new({MinEdge=zero, MaxEdge=max})
local buf = {}
for z=0,size.z do
for x=0,size.x do
for y=0,size.y do
local i_src = area_src:index(x,y,z)
local i_dst = area_dst:index(x,y,z)
buf[i_dst] = data[i_src]
end
end
end
return buf
end
-- extracts the rotated new size from the buffer
local function extract_from_buffer(buf, size, max, offset)
local area_src = VoxelArea:new({MinEdge=zero, MaxEdge=max})
local area_dst = VoxelArea:new({MinEdge=zero, MaxEdge=size})
local data = {}
for z=0,size.z do
for x=0,size.x do
for y=0,size.y do
local i_src = area_src:index(x+offset.x,y+offset.y,z+offset.z)
local i_dst = area_dst:index(x,y,z)
data[i_dst] = buf[i_src]
end
end
end
return data
end
local function apply_offset(metadata_map, offset)
local new_metadata_map = {}
for pos_str, metadata in pairs(metadata_map) do
local pos = minetest.string_to_pos(pos_str)
local new_pos = vector.subtract(pos, offset)
local new_pos_str = minetest.pos_to_string(new_pos)
new_metadata_map[new_pos_str] = metadata
end
return new_metadata_map
end
function pick_and_place.schematic_rotate(schematic, rotation)
if rotation <= 0 or rotation > 270 then
-- invalid or no rotation
return
end
local other1, other2 = "x", "z"
local rotated_size = pick_and_place.rotate_size(schematic.size, rotation)
local metadata = schematic.metadata
local max_xz_axis = math.max(schematic.size.x, schematic.size.z)
local max = { x=max_xz_axis-1, y=schematic.size.y-1, z=max_xz_axis-1 }
-- create transform buffers
local node_id_buf = create_buffer(schematic.node_id_data, vector.subtract(schematic.size, 1), max)
local param2_buf = create_buffer(schematic.param2_data, vector.subtract(schematic.size, 1), max)
-- rotate
if rotation == 90 then
pick_and_place.schematic_flip(node_id_buf, param2_buf, metadata, max, other1)
pick_and_place.schematic_transpose(node_id_buf, param2_buf, metadata, max, other1, other2)
elseif rotation == 180 then
pick_and_place.schematic_flip(node_id_buf, param2_buf, metadata, max, other1)
pick_and_place.schematic_flip(node_id_buf, param2_buf, metadata, max, other2)
elseif rotation == 270 then
pick_and_place.schematic_transpose(node_id_buf, param2_buf, metadata, max, other1, other2)
pick_and_place.schematic_flip(node_id_buf, param2_buf, metadata, max, other1)
end
-- extract from buffer with offset
local offset = {x=0, y=0, z=0}
local z_larger = schematic.size.z > schematic.size.x
local x_larger = schematic.size.z < schematic.size.x
local xz_diff = math.abs(schematic.size.x - schematic.size.z)
if rotation == 90 then
if z_larger then
offset.z = xz_diff
end
elseif rotation == 180 then
if x_larger then
offset.z = xz_diff
elseif z_larger then
offset.x = xz_diff
end
elseif rotation == 270 then
if x_larger then
offset.x = xz_diff
end
end
schematic.node_id_data = extract_from_buffer(node_id_buf, vector.subtract(rotated_size, 1), max, offset)
schematic.param2_data = extract_from_buffer(param2_buf, vector.subtract(rotated_size, 1), max, offset)
schematic.metadata = apply_offset(metadata, offset)
-- rotate size
schematic.size = rotated_size
-- orient rotated schematic
pick_and_place.schematic_orient(
schematic.node_id_data,
schematic.param2_data,
vector.subtract(rotated_size, 1),
rotation
)
end

30
schematic_rotate.spec.lua Normal file
View File

@ -0,0 +1,30 @@
mtt.register("rotate", function(callback)
local schematic = {
node_id_data = {0, 0, 0, 0, 1, 2, 3, 4},
param2_data = {0, 0, 0, 0, 4, 3, 2, 1},
metadata = {
["(0,0,0)"] = {
meta = {
x = 1
},
inventory = {
main = {"default:stick 1"}
}
}
},
size = {
x = 2,
y = 2,
z = 2
}
}
pick_and_place.schematic_rotate(schematic, 90)
assert(schematic.node_id_data[1] == 0)
assert(schematic.node_id_data[2] == 2)
assert(schematic.metadata["(0,0,1)"])
callback()
end)

44
schematic_transpose.lua Normal file
View File

@ -0,0 +1,44 @@
local function transpose_data(data, max, indexFn, axis1, axis2)
-- https://github.com/Uberi/Minetest-WorldEdit/blob/master/worldedit/manipulations.lua#L422
local pos = {x=0, y=0, z=0}
while pos.x <= max.x do
pos.y = 0
while pos.y <= max.y do
pos.z = 0
while pos.z <= max.z do
local extent1, extent2 = pos[axis1], pos[axis2]
if extent1 < extent2 then -- Transpose only if below the diagonal
local data_1 = data[indexFn(pos)]
local value1, value2 = pos[axis1], pos[axis2] -- Save position values
pos[axis1], pos[axis2] = extent2, extent1 -- Swap axis extents
local data_2 = data[indexFn(pos)]
data[indexFn(pos)] = data_1
pos[axis1], pos[axis2] = value1, value2 -- Restore position values
data[indexFn(pos)] = data_2
end
pos.z = pos.z + 1
end
pos.y = pos.y + 1
end
pos.x = pos.x + 1
end
end
function pick_and_place.schematic_transpose(node_ids, param2_data, metadata, max, axis1, axis2)
local min = { x=0, y=0, z=0 }
local area = VoxelArea:new({MinEdge=min, MaxEdge=max})
local vmanipIndex = function(pos) return area:indexp(pos) end
local metaIndex = function(pos) return minetest.pos_to_string(pos) end
transpose_data(node_ids, max, vmanipIndex, axis1, axis2)
transpose_data(param2_data, max, vmanipIndex, axis1, axis2)
if metadata then
transpose_data(metadata, max, metaIndex, axis1, axis2)
end
end

View File

@ -1,23 +1,5 @@
local char, byte = string.char, string.byte
local air_cid = minetest.get_content_id("air")
local function encode_uint16(int)
local a, b = int % 0x100, int / 0x100
return char(a, b)
end
local function decode_uint16(str, ofs)
ofs = ofs or 1
local a = byte(str, ofs)
local b = byte(str, ofs + 1)
return a + b * 0x100
end
-- nodeid -> name
local nodeid_name_mapping = {}
function pick_and_place.serialize(pos1, pos2)
local manip = minetest.get_voxel_manip()
local e1, e2 = manip:read_from_map(pos1, pos2)
@ -26,36 +8,20 @@ function pick_and_place.serialize(pos1, pos2)
local node_data = manip:get_data()
local param2 = manip:get_param2_data()
local mapdata = {}
local node_id_data = {}
local param2_data = {}
local metadata = {}
-- nodeid -> true
local nodeids = {}
for z=pos1.z,pos2.z do
for x=pos1.x,pos2.x do
for y=pos1.y,pos2.y do
for x=pos1.x,pos2.x do
local i = area:index(x,y,z)
nodeids[node_data[i]] = true
table.insert(mapdata, encode_uint16(node_data[i]))
table.insert(mapdata, char(param2[i]))
table.insert(node_id_data, node_data[i])
table.insert(param2_data, param2[i])
end
end
end
-- id -> name
local nodeid_mapping = {}
for nodeid in pairs(nodeids) do
local name = nodeid_name_mapping[nodeid]
if not name then
name = minetest.get_name_from_content_id(nodeid)
nodeid_name_mapping[nodeid] = name
end
nodeid_mapping[nodeid] = name
end
-- store metadata
local nodes_with_meta = minetest.find_nodes_with_meta(pos1, pos2)
for _, pos in ipairs(nodes_with_meta) do
@ -76,34 +42,24 @@ function pick_and_place.serialize(pos1, pos2)
metadata[minetest.pos_to_string(rel_pos)] = meta_table
end
local data = {
version = 1,
mapdata = table.concat(mapdata),
local schematic = {
node_id_data = node_id_data,
param2_data = param2_data,
metadata = metadata,
nodeid_mapping = nodeid_mapping,
size = vector.add(vector.subtract(pos2, pos1), 1)
}
local serialized_data = minetest.serialize(data)
local compressed_data = minetest.compress(serialized_data, "deflate")
local encoded_data = minetest.encode_base64(compressed_data)
return encoded_data
return pick_and_place.encode_schematic(schematic)
end
-- name -> nodeid
local name_nodeid_mapping = {}
function pick_and_place.deserialize(pos1, encoded_data)
local compressed_data = minetest.decode_base64(encoded_data)
local serialized_data = minetest.decompress(compressed_data, "deflate")
local data = minetest.deserialize(serialized_data)
if data.version ~= 1 then
return false, "invalid version: " .. (data.version or "nil")
local schematic, err = pick_and_place.decode_schematic(encoded_data)
if err then
return false, "Decode error: " .. err
end
local pos2 = vector.add(pos1, vector.subtract(data.size, 1))
local pos2 = vector.add(pos1, vector.subtract(schematic.size, 1))
local manip = minetest.get_voxel_manip()
local e1, e2 = manip:read_from_map(pos1, pos2)
@ -112,47 +68,31 @@ function pick_and_place.deserialize(pos1, encoded_data)
local node_data = manip:get_data()
local param2 = manip:get_param2_data()
-- foreign_nodeid -> local_nodeid
local localized_id_mapping = {}
for foreign_nodeid, name in pairs(data.nodeid_mapping) do
local local_nodeid = name_nodeid_mapping[name]
if not local_nodeid then
local_nodeid = minetest.get_content_id(name)
name_nodeid_mapping[name] = local_nodeid
end
localized_id_mapping[foreign_nodeid] = local_nodeid
end
local j = 1
for z=pos1.z,pos2.z do
for x=pos1.x,pos2.x do
for y=pos1.y,pos2.y do
for x=pos1.x,pos2.x do
local i = area:index(x,y,z)
local foreign_nodeid = decode_uint16(data.mapdata, j)
local nodeid = schematic.node_id_data[j]
-- localize nodeid mapping
local local_nodeid = localized_id_mapping[foreign_nodeid]
if local_nodeid ~= air_cid then
node_data[i] = local_nodeid
param2[i] = byte(data.mapdata, j+2)
if nodeid ~= air_cid then
node_data[i] = nodeid
param2[i] = schematic.param2_data[j]
end
j = j + 3
j = j + 1
end
end
end
-- metadata
for pos_str, meta_table in pairs(data.metadata) do
-- set metadata
for pos_str, meta_table in pairs(schematic.metadata) do
local pos = minetest.string_to_pos(pos_str)
local abs_pos = vector.add(pos1, pos)
local meta = minetest.get_meta(abs_pos)
meta:from_table(meta_table)
end
-- set nodeid's and param2
manip:set_data(node_data)
manip:set_param2_data(param2)
manip:write_to_map()

13
test/Dockerfile Normal file
View File

@ -0,0 +1,13 @@
ARG ENGINE_VERSION=5.7.0
FROM registry.gitlab.com/minetest/minetest/server:${ENGINE_VERSION}
USER root
RUN apk add --no-cache lua-dev luarocks
RUN apk add git &&\
mkdir -p /root/.minetest/worlds/world/worldmods/ &&\
git clone https://github.com/BuckarooBanzay/mtt /root/.minetest/worlds/world/worldmods/mtt &&\
git clone https://github.com/mt-mods/wield_events /root/.minetest/worlds/world/worldmods/wield_events
ENTRYPOINT minetestserver --config /minetest.conf

5
test/minetest.conf Normal file
View File

@ -0,0 +1,5 @@
default_game = minetest_game
mg_name = v7
mtt_enable = true
mtt_filter = pick_and_place
secure.trusted_mods = mtt

9
test/world.mt Normal file
View File

@ -0,0 +1,9 @@
enable_damage = false
creative_mode = true
mod_storage_backend = sqlite3
auth_backend = sqlite3
player_backend = dummy
backend = dummy
gameid = minetest
world_name = world
server_announce = false