1
0
mirror of https://github.com/Uberi/Minetest-WorldEdit.git synced 2024-09-27 15:10:17 +02:00
This commit is contained in:
khonkhortisan 2014-05-17 16:27:46 +00:00
commit f8a15876a9
5 changed files with 574 additions and 233 deletions

View File

@ -1 +1,2 @@
worldedit worldedit
worldedit_protection

View File

@ -82,8 +82,8 @@ minetest.register_chatcommand("/about", {
minetest.register_chatcommand("/inspect", { minetest.register_chatcommand("/inspect", {
params = "on/off/1/0/true/false/yes/no/enable/disable/<blank>", params = "on/off/1/0/true/false/yes/no/enable/disable/<blank>",
description = "Enable or disable node inspection", description = "Enable or disable node inspection",
privs = {worldedit=true}, privs = {},
func = function(name, param) func = worldedit.privs(function(name, param)
if param == "on" or param == "1" or param == "true" or param == "yes" or param == "enable" or param == "" then if param == "on" or param == "1" or param == "true" or param == "yes" or param == "enable" or param == "" then
worldedit.inspect[name] = true worldedit.inspect[name] = true
local axis, sign = worldedit.player_axis(name) local axis, sign = worldedit.player_axis(name)
@ -95,18 +95,19 @@ minetest.register_chatcommand("/inspect", {
else else
worldedit.player_notify(name, "invalid usage: " .. param) worldedit.player_notify(name, "invalid usage: " .. param)
end end
end, end),
}) })
minetest.register_on_punchnode(function(pos, node, puncher) minetest.register_on_punchnode(function(pos, node, puncher)
local name = puncher:get_player_name() local name = puncher:get_player_name()
if worldedit.inspect[name] then if worldedit.inspect[name] then
if minetest.check_player_privs(name, {worldedit=true}) then if worldedit.privs() then
local axis, sign = worldedit.player_axis(name) local axis, sign = worldedit.player_axis(name)
message = string.format("inspector: %s at %s (param1=%d, param2=%d) punched by %s facing the %s axis", message = string.format("inspector: %s at %s (param1=%d, param2=%d) punched by %s facing the %s axis",
node.name, minetest.pos_to_string(pos), node.param1, node.param2, name, axis .. (sign > 0 and "+" or "-")) node.name, minetest.pos_to_string(pos), node.param1, node.param2, name, axis .. (sign > 0 and "+" or "-"))
else else
message = "inspector: worldedit privileges required" message = "inspector: worldedit privileges required"
worldedit.inspect[name] = nil
end end
worldedit.player_notify(name, message) worldedit.player_notify(name, message)
end end
@ -115,33 +116,33 @@ end)
minetest.register_chatcommand("/reset", { minetest.register_chatcommand("/reset", {
params = "", params = "",
description = "Reset the region so that it is empty", description = "Reset the region so that it is empty",
privs = {worldedit=true}, privs = {},
func = function(name, param) func = worldedit.privs(function(name, param)
worldedit.pos1[name] = nil worldedit.pos1[name] = nil
worldedit.pos2[name] = nil worldedit.pos2[name] = nil
worldedit.mark_pos1(name) worldedit.mark_pos1(name)
worldedit.mark_pos2(name) worldedit.mark_pos2(name)
worldedit.set_pos[name] = nil worldedit.set_pos[name] = nil
worldedit.player_notify(name, "region reset") worldedit.player_notify(name, "region reset")
end, end),
}) })
minetest.register_chatcommand("/mark", { minetest.register_chatcommand("/mark", {
params = "", params = "",
description = "Show markers at the region positions", description = "Show markers at the region positions",
privs = {worldedit=true}, privs = {},
func = function(name, param) func = worldedit.privs(function(name, param)
worldedit.mark_pos1(name) worldedit.mark_pos1(name)
worldedit.mark_pos2(name) worldedit.mark_pos2(name)
worldedit.player_notify(name, "region marked") worldedit.player_notify(name, "region marked")
end, end),
}) })
minetest.register_chatcommand("/unmark", { minetest.register_chatcommand("/unmark", {
params = "", params = "",
description = "Hide markers if currently shown", description = "Hide markers if currently shown",
privs = {worldedit=true}, privs = {},
func = function(name, param) func = worldedit.privs(function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
worldedit.pos1[name] = nil worldedit.pos1[name] = nil
worldedit.pos2[name] = nil worldedit.pos2[name] = nil
@ -150,40 +151,40 @@ minetest.register_chatcommand("/unmark", {
worldedit.pos1[name] = pos1 worldedit.pos1[name] = pos1
worldedit.pos2[name] = pos2 worldedit.pos2[name] = pos2
worldedit.player_notify(name, "region unmarked") worldedit.player_notify(name, "region unmarked")
end, end),
}) })
minetest.register_chatcommand("/pos1", { minetest.register_chatcommand("/pos1", {
params = "", params = "",
description = "Set WorldEdit region position 1 to the player's location", description = "Set WorldEdit region position 1 to the player's location",
privs = {worldedit=true}, privs = {},
func = function(name, param) func = worldedit.privs(function(name, param)
local pos = minetest.get_player_by_name(name):getpos() local pos = minetest.get_player_by_name(name):getpos()
pos.x, pos.y, pos.z = math.floor(pos.x + 0.5), math.floor(pos.y + 0.5), math.floor(pos.z + 0.5) pos.x, pos.y, pos.z = math.floor(pos.x + 0.5), math.floor(pos.y + 0.5), math.floor(pos.z + 0.5)
worldedit.pos1[name] = pos worldedit.pos1[name] = pos
worldedit.mark_pos1(name) worldedit.mark_pos1(name)
worldedit.player_notify(name, "position 1 set to " .. minetest.pos_to_string(pos)) worldedit.player_notify(name, "position 1 set to " .. minetest.pos_to_string(pos))
end, end),
}) })
minetest.register_chatcommand("/pos2", { minetest.register_chatcommand("/pos2", {
params = "", params = "",
description = "Set WorldEdit region position 2 to the player's location", description = "Set WorldEdit region position 2 to the player's location",
privs = {worldedit=true}, privs = {},
func = function(name, param) func = worldedit.privs(function(name, param)
local pos = minetest.get_player_by_name(name):getpos() local pos = minetest.get_player_by_name(name):getpos()
pos.x, pos.y, pos.z = math.floor(pos.x + 0.5), math.floor(pos.y + 0.5), math.floor(pos.z + 0.5) pos.x, pos.y, pos.z = math.floor(pos.x + 0.5), math.floor(pos.y + 0.5), math.floor(pos.z + 0.5)
worldedit.pos2[name] = pos worldedit.pos2[name] = pos
worldedit.mark_pos2(name) worldedit.mark_pos2(name)
worldedit.player_notify(name, "position 2 set to " .. minetest.pos_to_string(pos)) worldedit.player_notify(name, "position 2 set to " .. minetest.pos_to_string(pos))
end, end),
}) })
minetest.register_chatcommand("/p", { minetest.register_chatcommand("/p", {
params = "set/set1/set2/get", params = "set/set1/set2/get",
description = "Set WorldEdit region, WorldEdit position 1, or WorldEdit position 2 by punching nodes, or display the current WorldEdit region", description = "Set WorldEdit region, WorldEdit position 1, or WorldEdit position 2 by punching nodes, or display the current WorldEdit region",
privs = {worldedit=true}, privs = {},
func = function(name, param) func = worldedit.privs(function(name, param)
if param == "set" then --set both WorldEdit positions if param == "set" then --set both WorldEdit positions
worldedit.set_pos[name] = "pos1" worldedit.set_pos[name] = "pos1"
worldedit.player_notify(name, "select positions by punching two nodes") worldedit.player_notify(name, "select positions by punching two nodes")
@ -207,14 +208,14 @@ minetest.register_chatcommand("/p", {
else else
worldedit.player_notify(name, "unknown subcommand: " .. param) worldedit.player_notify(name, "unknown subcommand: " .. param)
end end
end, end),
}) })
minetest.register_chatcommand("/fixedpos", { minetest.register_chatcommand("/fixedpos", {
params = "set1/set2 x y z", params = "set1/set2 x y z",
description = "Set a WorldEdit region position to the position at (<x>, <y>, <z>)", description = "Set a WorldEdit region position to the position at (<x>, <y>, <z>)",
privs = {worldedit=true}, privs = {},
func = function(name, param) func = worldedit.privs(function(name, param)
local found, _, flag, x, y, z = param:find("^(set[12])%s+([+-]?%d+)%s+([+-]?%d+)%s+([+-]?%d+)$") local found, _, flag, x, y, z = param:find("^(set[12])%s+([+-]?%d+)%s+([+-]?%d+)%s+([+-]?%d+)$")
if found == nil then if found == nil then
worldedit.player_notify(name, "invalid usage: " .. param) worldedit.player_notify(name, "invalid usage: " .. param)
@ -230,7 +231,7 @@ minetest.register_chatcommand("/fixedpos", {
worldedit.mark_pos2(name) worldedit.mark_pos2(name)
worldedit.player_notify(name, "position 2 set to " .. minetest.pos_to_string(pos)) worldedit.player_notify(name, "position 2 set to " .. minetest.pos_to_string(pos))
end end
end, end),
}) })
minetest.register_on_punchnode(function(pos, node, puncher) minetest.register_on_punchnode(function(pos, node, puncher)
@ -261,8 +262,8 @@ end)
minetest.register_chatcommand("/volume", { minetest.register_chatcommand("/volume", {
params = "", params = "",
description = "Display the volume of the current WorldEdit region", description = "Display the volume of the current WorldEdit region",
privs = {worldedit=true}, privs = {},
func = function(name, param) func = worldedit.privs(function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if pos1 == nil or pos2 == nil then if pos1 == nil or pos2 == nil then
worldedit.player_notify(name, "no region selected") worldedit.player_notify(name, "no region selected")
@ -275,7 +276,7 @@ minetest.register_chatcommand("/volume", {
.. abs(pos2.x - pos1.x) + 1 .. "*" .. abs(pos2.x - pos1.x) + 1 .. "*"
.. abs(pos2.y - pos1.y) + 1 .. "*" .. abs(pos2.y - pos1.y) + 1 .. "*"
.. abs(pos2.z - pos1.z) + 1 .. ")") .. abs(pos2.z - pos1.z) + 1 .. ")")
end, end),
}) })
local check_set = function(name, param) local check_set = function(name, param)
@ -287,13 +288,15 @@ end
minetest.register_chatcommand("/set", { minetest.register_chatcommand("/set", {
params = "<node>", params = "<node>",
description = "Set the current WorldEdit region to <node>", description = "Set the current WorldEdit region to <node>",
privs = {worldedit=true}, privs = {},
func = safe_region(function(name, param) func = worldedit.privs(safe_region(function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if worldedit.can_edit_volume(name, {pos1, pos2}) then
local node = get_node(name, param) local node = get_node(name, param)
local count = worldedit.set(pos1, pos2, node) local count = worldedit.set(pos1, pos2, node)
worldedit.player_notify(name, count .. " nodes set") worldedit.player_notify(name, count .. " nodes set")
end, check_set), end
end, check_set)),
}) })
local check_replace = function(name, param) local check_replace = function(name, param)
@ -318,29 +321,33 @@ end
minetest.register_chatcommand("/replace", { minetest.register_chatcommand("/replace", {
params = "<search node> <replace node>", params = "<search node> <replace node>",
description = "Replace all instances of <search node> with <replace node> in the current WorldEdit region", description = "Replace all instances of <search node> with <replace node> in the current WorldEdit region",
privs = {worldedit=true}, privs = {},
func = safe_region(function(name, param) func = worldedit.privs(safe_region(function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if worldedit.can_edit_volume(name, {pos1, pos2}) then
local found, _, searchnode, replacenode = param:find("^([^%s]+)%s+(.+)$") local found, _, searchnode, replacenode = param:find("^([^%s]+)%s+(.+)$")
local newsearchnode = worldedit.normalize_nodename(searchnode) local newsearchnode = worldedit.normalize_nodename(searchnode)
local newreplacenode = worldedit.normalize_nodename(replacenode) local newreplacenode = worldedit.normalize_nodename(replacenode)
local count = worldedit.replace(pos1, pos2, newsearchnode, newreplacenode) local count = worldedit.replace(pos1, pos2, newsearchnode, newreplacenode)
worldedit.player_notify(name, count .. " nodes replaced") worldedit.player_notify(name, count .. " nodes replaced")
end, check_replace), end
end, check_replace)),
}) })
minetest.register_chatcommand("/replaceinverse", { minetest.register_chatcommand("/replaceinverse", {
params = "<search node> <replace node>", params = "<search node> <replace node>",
description = "Replace all nodes other than <search node> with <replace node> in the current WorldEdit region", description = "Replace all nodes other than <search node> with <replace node> in the current WorldEdit region",
privs = {worldedit=true}, privs = {},
func = safe_region(function(name, param) func = worldedit.privs(safe_region(function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if worldedit.can_edit_volume(name, {pos1, pos2}) then
local found, _, searchnode, replacenode = param:find("^([^%s]+)%s+(.+)$") local found, _, searchnode, replacenode = param:find("^([^%s]+)%s+(.+)$")
local newsearchnode = worldedit.normalize_nodename(searchnode) local newsearchnode = worldedit.normalize_nodename(searchnode)
local newreplacenode = worldedit.normalize_nodename(replacenode) local newreplacenode = worldedit.normalize_nodename(replacenode)
local count = worldedit.replaceinverse(pos1, pos2, searchnode, replacenode) local count = worldedit.replaceinverse(pos1, pos2, searchnode, replacenode)
worldedit.player_notify(name, count .. " nodes replaced") worldedit.player_notify(name, count .. " nodes replaced")
end, check_replace), end
end, check_replace)),
}) })
local check_sphere = function(name, param) local check_sphere = function(name, param)
@ -361,27 +368,31 @@ end
minetest.register_chatcommand("/hollowsphere", { minetest.register_chatcommand("/hollowsphere", {
params = "<radius> <node>", params = "<radius> <node>",
description = "Add hollow sphere centered at WorldEdit position 1 with radius <radius>, composed of <node>", description = "Add hollow sphere centered at WorldEdit position 1 with radius <radius>, composed of <node>",
privs = {worldedit=true}, privs = {},
func = safe_region(function(name, param) func = worldedit.privs(safe_region(function(name, param)
local pos = worldedit.pos1[name] local pos = worldedit.pos1[name]
local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$") local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$")
if worldedit.can_edit_volume(name, {{x=pos.x-radius, y=pos.y-radius, z=pos.z-radius}, {x=pos.x+radius, y=pos.y+radius, z=pos.z+radius}}) then
local node = get_node(name, nodename) local node = get_node(name, nodename)
local count = worldedit.hollow_sphere(pos, tonumber(radius), node) local count = worldedit.hollow_sphere(pos, tonumber(radius), node)
worldedit.player_notify(name, count .. " nodes added") worldedit.player_notify(name, count .. " nodes added")
end, check_sphere), end
end, check_sphere)),
}) })
minetest.register_chatcommand("/sphere", { minetest.register_chatcommand("/sphere", {
params = "<radius> <node>", params = "<radius> <node>",
description = "Add sphere centered at WorldEdit position 1 with radius <radius>, composed of <node>", description = "Add sphere centered at WorldEdit position 1 with radius <radius>, composed of <node>",
privs = {worldedit=true}, privs = {},
func = safe_region(function(name, param) func = worldedit.privs(safe_region(function(name, param)
local pos = worldedit.pos1[name] local pos = worldedit.pos1[name]
local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$") local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$")
if worldedit.can_edit_volume(name, {{x=pos.x-radius, y=pos.y-radius, z=pos.z-radius}, {x=pos.x+radius, y=pos.y+radius, z=pos.z+radius}}) then
local node = get_node(name, nodename) local node = get_node(name, nodename)
local count = worldedit.sphere(pos, tonumber(radius), node) local count = worldedit.sphere(pos, tonumber(radius), node)
worldedit.player_notify(name, count .. " nodes added") worldedit.player_notify(name, count .. " nodes added")
end, check_sphere), end
end, check_sphere)),
}) })
local check_dome = function(name, param) local check_dome = function(name, param)
@ -402,27 +413,31 @@ end
minetest.register_chatcommand("/hollowdome", { minetest.register_chatcommand("/hollowdome", {
params = "<radius> <node>", params = "<radius> <node>",
description = "Add hollow dome centered at WorldEdit position 1 with radius <radius>, composed of <node>", description = "Add hollow dome centered at WorldEdit position 1 with radius <radius>, composed of <node>",
privs = {worldedit=true}, privs = {},
func = safe_region(function(name, param) func = worldedit.privs(safe_region(function(name, param)
local pos = worldedit.pos1[name] local pos = worldedit.pos1[name]
local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$") local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$")
if worldedit.can_edit_volume(name, {{x=pos.x-radius, y=pos.y, z=pos.z-radius}, {x=pos.x+radius, y=pos.y+radius, z=pos.z+radius}}) then
local node = get_node(name, nodename) local node = get_node(name, nodename)
local count = worldedit.hollow_dome(pos, tonumber(radius), node) local count = worldedit.hollow_dome(pos, tonumber(radius), node)
worldedit.player_notify(name, count .. " nodes added") worldedit.player_notify(name, count .. " nodes added")
end, check_dome), end
end, check_dome)),
}) })
minetest.register_chatcommand("/dome", { minetest.register_chatcommand("/dome", {
params = "<radius> <node>", params = "<radius> <node>",
description = "Add dome centered at WorldEdit position 1 with radius <radius>, composed of <node>", description = "Add dome centered at WorldEdit position 1 with radius <radius>, composed of <node>",
privs = {worldedit=true}, privs = {},
func = safe_region(function(name, param) func = worldedit.privs(safe_region(function(name, param)
local pos = worldedit.pos1[name] local pos = worldedit.pos1[name]
local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$") local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$")
if worldedit.can_edit_volume(name, {{x=pos.x-radius, y=pos.y, z=pos.z-radius}, {x=pos.x+radius, y=pos.y+radius, z=pos.z+radius}}) then
local node = get_node(name, nodename) local node = get_node(name, nodename)
local count = worldedit.dome(pos, tonumber(radius), node) local count = worldedit.dome(pos, tonumber(radius), node)
worldedit.player_notify(name, count .. " nodes added") worldedit.player_notify(name, count .. " nodes added")
end, check_dome), end
end, check_dome)),
}) })
local check_cylinder = function(name, param) local check_cylinder = function(name, param)
@ -443,54 +458,63 @@ end
minetest.register_chatcommand("/hollowcylinder", { minetest.register_chatcommand("/hollowcylinder", {
params = "x/y/z/? <length> <radius> <node>", params = "x/y/z/? <length> <radius> <node>",
description = "Add hollow cylinder at WorldEdit position 1 along the x/y/z/? axis with length <length> and radius <radius>, composed of <node>", description = "Add hollow cylinder at WorldEdit position 1 along the x/y/z/? axis with length <length> and radius <radius>, composed of <node>",
privs = {worldedit=true}, privs = {},
func = safe_region(function(name, param) func = worldedit.privs(safe_region(function(name, param)
local pos = worldedit.pos1[name] local pos = worldedit.pos1[name]
local found, _, axis, length, radius, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(.+)$") local found, _, axis, length, radius, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(.+)$")
length = tonumber(length) length = tonumber(length)
sign = 1
if axis == "?" then if axis == "?" then
axis, sign = worldedit.player_axis(name) axis, sign = worldedit.player_axis(name)
length = length * sign length = length * sign
end end
if worldedit.can_edit_volume(name, {{x=pos.x-(axis~="x" and radius or 0), y=pos.y-(axis~="y" and radius or 0), z=pos.z-(axis~="z" and radius or 0)}, {x=pos.x+(axis~="x" and radius or length), y=pos.y+(axis~="y" and radius or length-1*sign), z=pos.z+(axis~="z" and radius or length-1*sign)}}) then
local node = get_node(name, nodename) local node = get_node(name, nodename)
local count = worldedit.hollow_cylinder(pos, axis, length, tonumber(radius), node) local count = worldedit.hollow_cylinder(pos, axis, length, tonumber(radius), node)
worldedit.player_notify(name, count .. " nodes added") worldedit.player_notify(name, count .. " nodes added")
end, check_cylinder), end
end, check_cylinder)),
}) })
minetest.register_chatcommand("/cylinder", { minetest.register_chatcommand("/cylinder", {
params = "x/y/z/? <length> <radius> <node>", params = "x/y/z/? <length> <radius> <node>",
description = "Add cylinder at WorldEdit position 1 along the x/y/z/? axis with length <length> and radius <radius>, composed of <node>", description = "Add cylinder at WorldEdit position 1 along the x/y/z/? axis with length <length> and radius <radius>, composed of <node>",
privs = {worldedit=true}, privs = {},
func = safe_region(function(name, param) func = worldedit.privs(safe_region(function(name, param)
local pos = worldedit.pos1[name] local pos = worldedit.pos1[name]
local found, _, axis, length, radius, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(.+)$") local found, _, axis, length, radius, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(.+)$")
length = tonumber(length) length = tonumber(length)
sign = 1
if axis == "?" then if axis == "?" then
axis, sign = worldedit.player_axis(name) axis, sign = worldedit.player_axis(name)
length = length * sign length = length * sign
end end
if worldedit.can_edit_volume(name, {{x=pos.x-(axis~="x" and radius or 0), y=pos.y-(axis~="y" and radius or 0), z=pos.z-(axis~="z" and radius or 0)}, {x=pos.x+(axis~="x" and radius or length-1*sign), y=pos.y+(axis~="y" and radius or length-1*sign), z=pos.z+(axis~="z" and radius or length-1*sign)}}) then
local node = get_node(name, nodename) local node = get_node(name, nodename)
local count = worldedit.cylinder(pos, axis, length, tonumber(radius), node) local count = worldedit.cylinder(pos, axis, length, tonumber(radius), node)
worldedit.player_notify(name, count .. " nodes added") worldedit.player_notify(name, count .. " nodes added")
end, check_cylinder), end
end, check_cylinder)),
}) })
minetest.register_chatcommand("/pyramid", { minetest.register_chatcommand("/pyramid", {
params = "x/y/z/? <height> <node>", params = "x/y/z/? <height> <node>",
description = "Add pyramid centered at WorldEdit position 1 along the x/y/z/? axis with height <height>, composed of <node>", description = "Add pyramid centered at WorldEdit position 1 along the x/y/z/? axis with height <height>, composed of <node>",
privs = {worldedit=true}, privs = {},
func = safe_region(function(name, param) func = worldedit.privs(safe_region(function(name, param)
local pos = get_position(name) local pos = get_position(name)
local found, _, axis, height, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(.+)$") local found, _, axis, height, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(.+)$")
height = tonumber(height) height = tonumber(height)
sign = 1
if axis == "?" then if axis == "?" then
axis, sign = worldedit.player_axis(name) axis, sign = worldedit.player_axis(name)
height = height * sign height = height * sign
end end
if worldedit.can_edit_volume(name, {{x=pos.x-(axis~="x" and height-1*sign or 0), y=pos.y-(axis~="y" and height-1*sign or 0), z=pos.z-(axis~="y" and height-1*sign or 0)}, {x=pos.x+height-1*sign, y=pos.y+height-1*sign, z=pos.z+height-1*sign}}) then
local node = get_node(name, nodename) local node = get_node(name, nodename)
local count = worldedit.pyramid(pos, axis, height, node) local count = worldedit.pyramid(pos, axis, height, node)
worldedit.player_notify(name, count .. " nodes added") worldedit.player_notify(name, count .. " nodes added")
end
end, end,
function(name, param) function(name, param)
if worldedit.pos1[name] == nil then if worldedit.pos1[name] == nil then
@ -506,19 +530,22 @@ minetest.register_chatcommand("/pyramid", {
if not node then return nil end if not node then return nil end
height = tonumber(height) height = tonumber(height)
return math.ceil(((height * 2 + 1) ^ 2) * height / 3) return math.ceil(((height * 2 + 1) ^ 2) * height / 3)
end), end)),
}) })
minetest.register_chatcommand("/spiral", { minetest.register_chatcommand("/spiral", {
params = "<length> <height> <space> <node>", params = "<length> <height> <space> <node>",
description = "Add spiral centered at WorldEdit position 1 with side length <length>, height <height>, space between walls <space>, composed of <node>", description = "Add spiral centered at WorldEdit position 1 with side length <length>, height <height>, space between walls <space>, composed of <node>",
privs = {worldedit=true}, privs = {},
func = safe_region(function(name, param) func = worldedit.privs(safe_region(function(name, param)
local pos = worldedit.pos1[name] local pos = worldedit.pos1[name]
local found, _, length, height, space, nodename = param:find("^(%d+)%s+(%d+)%s+(%d+)%s+(.+)$") local found, _, length, height, space, nodename = param:find("^(%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
local extent = math.ceil(length / 2)
if worldedit.can_edit_volume(name, {{x=0.5+pos.x-0.5-extent, y=pos.y, z=0.5+pos.z-0.5-extent}, {x=0.5+pos.x+0.5+extent, y=pos.y+height-1, z=0.5+pos.z+0.5+extent}}) then
local node = get_node(name, nodename) local node = get_node(name, nodename)
local count = worldedit.spiral(pos, tonumber(length), tonumber(height), tonumber(space), node) local count = worldedit.spiral(pos, tonumber(length), tonumber(height), tonumber(space), node)
worldedit.player_notify(name, count .. " nodes added") worldedit.player_notify(name, count .. " nodes added")
end
end, end,
function(name, param) function(name, param)
if worldedit.pos1[name] == nil then if worldedit.pos1[name] == nil then
@ -533,14 +560,14 @@ minetest.register_chatcommand("/spiral", {
local node = get_node(name, nodename) local node = get_node(name, nodename)
if not node then return nil end if not node then return nil end
return check_region(name, param) return check_region(name, param)
end), end)),
}) })
minetest.register_chatcommand("/copy", { minetest.register_chatcommand("/copy", {
params = "x/y/z/? <amount>", params = "x/y/z/? <amount>",
description = "Copy the current WorldEdit region along the x/y/z/? axis by <amount> nodes", description = "Copy the current WorldEdit region along the x/y/z/? axis by <amount> nodes",
privs = {worldedit=true}, privs = {},
func = safe_region(function(name, param) func = worldedit.privs(safe_region(function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
local found, _, axis, amount = param:find("^([xyz%?])%s+([+-]?%d+)$") local found, _, axis, amount = param:find("^([xyz%?])%s+([+-]?%d+)$")
if found == nil then if found == nil then
@ -548,25 +575,27 @@ minetest.register_chatcommand("/copy", {
return return
end end
amount = tonumber(amount) amount = tonumber(amount)
sign = 1
if axis == "?" then if axis == "?" then
axis, sign = worldedit.player_axis(name) axis, sign = worldedit.player_axis(name)
amount = amount * sign amount = amount * sign
end end
if worldedit.can_edit_volume(name, {{x=pos1.x+(axis=="x" and amount or 0), y=pos1.y+(axis=="y" and amount or 0), z=pos1.z+(axis=="z" and amount or 0)}, {x=pos2.x+(axis=="x" and amount or 0), y=pos2.y+(axis=="y" and amount or 0), z=pos2.z+(axis=="z" and amount or 0)}}) then
local count = worldedit.copy(pos1, pos2, axis, amount) local count = worldedit.copy(pos1, pos2, axis, amount)
worldedit.player_notify(name, count .. " nodes copied") worldedit.player_notify(name, count .. " nodes copied")
end
end, end,
function(name, param) function(name, param)
local volume = check_region(name, param) local volume = check_region(name, param)
return volume and volume * 2 or volume return volume and volume * 2 or volume
end), end)),
}) })
minetest.register_chatcommand("/move", { minetest.register_chatcommand("/move", {
params = "x/y/z/? <amount>", params = "x/y/z/? <amount>",
description = "Move the current WorldEdit region along the x/y/z/? axis by <amount> nodes", description = "Move the current WorldEdit region along the x/y/z/? axis by <amount> nodes",
privs = {worldedit=true}, privs = {},
func = safe_region(function(name, param) func = worldedit.privs(safe_region(function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
local found, _, axis, amount = param:find("^([xyz%?])%s+([+-]?%d+)$") local found, _, axis, amount = param:find("^([xyz%?])%s+([+-]?%d+)$")
if found == nil then if found == nil then
@ -574,11 +603,12 @@ minetest.register_chatcommand("/move", {
return return
end end
amount = tonumber(amount) amount = tonumber(amount)
sign = 1
if axis == "?" then if axis == "?" then
axis, sign = worldedit.player_axis(name) axis, sign = worldedit.player_axis(name)
amount = amount * sign amount = amount * sign
end end
if worldedit.can_edit_volume(name, {pos1, pos2, {x=pos1.x+(axis=="x" and amount or 0), y=pos1.y+(axis=="y" and amount or 0), z=pos1.z+(axis=="z" and amount or 0)}, {x=pos2.x+(axis=="x" and amount or 0), y=pos2.y+(axis=="y" and amount or 0), z=pos2.z+(axis=="z" and amount or 0)}}) then
local count = worldedit.move(pos1, pos2, axis, amount) local count = worldedit.move(pos1, pos2, axis, amount)
pos1[axis] = pos1[axis] + amount pos1[axis] = pos1[axis] + amount
@ -586,23 +616,27 @@ minetest.register_chatcommand("/move", {
worldedit.mark_pos1(name) worldedit.mark_pos1(name)
worldedit.mark_pos2(name) worldedit.mark_pos2(name)
worldedit.player_notify(name, count .. " nodes moved") worldedit.player_notify(name, count .. " nodes moved")
end, check_region), end
end, check_region)),
}) })
minetest.register_chatcommand("/stack", { minetest.register_chatcommand("/stack", {
params = "x/y/z/? <count>", params = "x/y/z/? <count>",
description = "Stack the current WorldEdit region along the x/y/z/? axis <count> times", description = "Stack the current WorldEdit region along the x/y/z/? axis <count> times",
privs = {worldedit=true}, privs = {},
func = safe_region(function(name, param) func = worldedit.privs(safe_region(function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
local found, _, axis, repetitions = param:find("^([xyz%?])%s+([+-]?%d+)$") local found, _, axis, repetitions = param:find("^([xyz%?])%s+([+-]?%d+)$")
repetitions = tonumber(repetitions) repetitions = tonumber(repetitions)
sign = 1
if axis == "?" then if axis == "?" then
axis, sign = worldedit.player_axis(name) axis, sign = worldedit.player_axis(name)
repetitions = repetitions * sign repetitions = repetitions * sign
end end
if worldedit.can_edit_volume(name, {{x=(axis=="x" and pos2.x+1*sign or pos1.x), y=(axis=="y" and pos2.y+1*sign or pos1.x), z=(axis=="z" and pos2.z+1*sign or pos1.z)}, {x=pos2.x+repetitions*(axis=="x" and pos2.x-pos1.x+1*sign or 0), y=pos2.y+repetitions*(axis=="y" and pos2.y-pos1.y+1*sign or 0), z=pos2.z+repetitions*(axis=="z" and pos2.z-pos1.z+1*sign or 0)}}) then
local count = worldedit.stack(pos1, pos2, axis, repetitions) local count = worldedit.stack(pos1, pos2, axis, repetitions)
worldedit.player_notify(name, count .. " nodes stacked") worldedit.player_notify(name, count .. " nodes stacked")
end
end, end,
function(name, param) function(name, param)
local found, _, axis, repetitions = param:find("^([xyz%?])%s+([+-]?%d+)$") local found, _, axis, repetitions = param:find("^([xyz%?])%s+([+-]?%d+)$")
@ -612,17 +646,18 @@ minetest.register_chatcommand("/stack", {
local count = check_region(name, param) local count = check_region(name, param)
if count then return (tonumber(repetitions) + 1) * count end if count then return (tonumber(repetitions) + 1) * count end
return nil return nil
end), end)),
}) })
minetest.register_chatcommand("/stretch", { minetest.register_chatcommand("/stretch", {
params = "<stretchx> <stretchy> <stretchz>", params = "<stretchx> <stretchy> <stretchz>",
description = "Scale the current WorldEdit positions and region by a factor of <stretchx>, <stretchy>, <stretchz> along the X, Y, and Z axes, repectively, with position 1 as the origin", description = "Scale the current WorldEdit positions and region by a factor of <stretchx>, <stretchy>, <stretchz> along the X, Y, and Z axes, repectively, with position 1 as the origin",
privs = {worldedit=true}, privs = {},
func = safe_region(function(name, param) func = worldedit.privs(safe_region(function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
local found, _, stretchx, stretchy, stretchz = param:find("^(%d+)%s+(%d+)%s+(%d+)$") local found, _, stretchx, stretchy, stretchz = param:find("^(%d+)%s+(%d+)%s+(%d+)$")
stretchx, stretchy, stretchz = tonumber(stretchx), tonumber(stretchy), tonumber(stretchz) stretchx, stretchy, stretchz = tonumber(stretchx), tonumber(stretchy), tonumber(stretchz)
if worldedit.can_edit_volume(name, {pos1, {x=pos1.x + (pos2.x - pos1.x) * stretchx, y=pos1.y + (pos2.y - pos1.y) * stretchy, z=pos1.z + (pos2.z - pos1.z) * stretchz}}) then
local count, pos1, pos2 = worldedit.stretch(pos1, pos2, stretchx, stretchy, stretchz) local count, pos1, pos2 = worldedit.stretch(pos1, pos2, stretchx, stretchy, stretchz)
--reset markers to scaled positions --reset markers to scaled positions
@ -632,6 +667,7 @@ minetest.register_chatcommand("/stretch", {
worldedit.mark_pos2(name) worldedit.mark_pos2(name)
worldedit.player_notify(name, count .. " nodes stretched") worldedit.player_notify(name, count .. " nodes stretched")
end
end, end,
function(name, param) function(name, param)
local found, _, stretchx, stretchy, stretchz = param:find("^(%d+)%s+(%d+)%s+(%d+)$") local found, _, stretchx, stretchy, stretchz = param:find("^(%d+)%s+(%d+)%s+(%d+)$")
@ -646,18 +682,31 @@ minetest.register_chatcommand("/stretch", {
local count = check_region(name, param) local count = check_region(name, param)
if count then return tonumber(stretchx) * tonumber(stretchy) * tonumber(stretchz) * count end if count then return tonumber(stretchx) * tonumber(stretchy) * tonumber(stretchz) * count end
return nil return nil
end), end)),
}) })
minetest.register_chatcommand("/transpose", { minetest.register_chatcommand("/transpose", {
params = "x/y/z/? x/y/z/?", params = "x/y/z/? x/y/z/?",
description = "Transpose the current WorldEdit region along the x/y/z/? and x/y/z/? axes", description = "Transpose the current WorldEdit region along the x/y/z/? and x/y/z/? axes",
privs = {worldedit=true}, privs = {},
func = safe_region(function(name, param) func = worldedit.privs(safe_region(function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
local found, _, axis1, axis2 = param:find("^([xyz%?])%s+([xyz%?])$") local found, _, axis1, axis2 = param:find("^([xyz%?])%s+([xyz%?])$")
if axis1 == "?" then axis1 = worldedit.player_axis(name) end if axis1 == "?" then axis1 = worldedit.player_axis(name) end
if axis2 == "?" then axis2 = worldedit.player_axis(name) end if axis2 == "?" then axis2 = worldedit.player_axis(name) end
local notaxis = (axis1~="x" and axis2~="x") and "x" or (
(axis1~="y" and axis2~="y") and "y" or (
(axis1~="z" and axis2~="z") and "z" or (
assert(0)
)
)
)
local side = math.max( --does not include pos1 in the length
(axis2=="x" or "x"==axis1) and pos2.x-pos1.x or 0,
(axis2=="y" or "y"==axis1) and pos2.y-pos1.y or 0,
(axis2=="z" or "z"==axis1) and pos2.z-pos1.z or 0
)
if worldedit.can_edit_volume(name, {pos1, {x=notaxis=="x" and pos2.x or pos1.x+side, y=notaxis=="y" and pos2.y or pos1.y+side, z=notaxis=="z" and pos2.z or pos1.z+side}}) then
local count, pos1, pos2 = worldedit.transpose(pos1, pos2, axis1, axis2) local count, pos1, pos2 = worldedit.transpose(pos1, pos2, axis1, axis2)
--reset markers to transposed positions --reset markers to transposed positions
@ -667,6 +716,7 @@ minetest.register_chatcommand("/transpose", {
worldedit.mark_pos2(name) worldedit.mark_pos2(name)
worldedit.player_notify(name, count .. " nodes transposed") worldedit.player_notify(name, count .. " nodes transposed")
end
end, end,
function(name, param) function(name, param)
local found, _, axis1, axis2 = param:find("^([xyz%?])%s+([xyz%?])$") local found, _, axis1, axis2 = param:find("^([xyz%?])%s+([xyz%?])$")
@ -679,18 +729,20 @@ minetest.register_chatcommand("/transpose", {
return nil return nil
end end
return check_region(name, param) return check_region(name, param)
end), end)),
}) })
minetest.register_chatcommand("/flip", { minetest.register_chatcommand("/flip", {
params = "x/y/z/?", params = "x/y/z/?",
description = "Flip the current WorldEdit region along the x/y/z/? axis", description = "Flip the current WorldEdit region along the x/y/z/? axis",
privs = {worldedit=true}, privs = {},
func = safe_region(function(name, param) func = worldedit.privs(safe_region(function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if param == "?" then param = worldedit.player_axis(name) end if param == "?" then param = worldedit.player_axis(name) end
if worldedit.can_edit_volume(name, {pos1, pos2}) then
local count = worldedit.flip(pos1, pos2, param) local count = worldedit.flip(pos1, pos2, param)
worldedit.player_notify(name, count .. " nodes flipped") worldedit.player_notify(name, count .. " nodes flipped")
end
end, end,
function(name, param) function(name, param)
if param ~= "x" and param ~= "y" and param ~= "z" and param ~= "?" then if param ~= "x" and param ~= "y" and param ~= "z" and param ~= "?" then
@ -698,17 +750,23 @@ minetest.register_chatcommand("/flip", {
return nil return nil
end end
return check_region(name, param) return check_region(name, param)
end), end)),
}) })
minetest.register_chatcommand("/rotate", { minetest.register_chatcommand("/rotate", {
params = "<axis> <angle>", params = "<axis> <angle>",
description = "Rotate the current WorldEdit region around the axis <axis> by angle <angle> (90 degree increment)", description = "Rotate the current WorldEdit region around the axis <axis> by angle <angle> (90 degree increment)",
privs = {worldedit=true}, privs = {},
func = safe_region(function(name, param) func = worldedit.privs(safe_region(function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
local found, _, axis, angle = param:find("^([xyz%?])%s+([+-]?%d+)$") local found, _, axis, angle = param:find("^([xyz%?])%s+([+-]?%d+)$")
if axis == "?" then axis = worldedit.player_axis(name) end if axis == "?" then axis = worldedit.player_axis(name) end
local side = math.max(
axis~="x" and pos2.x-pos1.x or 0,
axis~="y" and pos2.y-pos1.y or 0,
axis~="z" and pos2.z-pos1.z or 0
)
if worldedit.can_edit_volume(name, {pos1, {x=axis=="x" and pos2.x or pos1.x+side, y=axis=="y" and pos22.y or pos1.y+side, z=axis=="z" and pos2.z or pos1.z+side}}) then
local count, pos1, pos2 = worldedit.rotate(pos1, pos2, axis, angle) local count, pos1, pos2 = worldedit.rotate(pos1, pos2, axis, angle)
--reset markers to rotated positions --reset markers to rotated positions
@ -718,6 +776,7 @@ minetest.register_chatcommand("/rotate", {
worldedit.mark_pos2(name) worldedit.mark_pos2(name)
worldedit.player_notify(name, count .. " nodes rotated") worldedit.player_notify(name, count .. " nodes rotated")
end
end, end,
function(name, param) function(name, param)
local found, _, axis, angle = param:find("^([xyz%?])%s+([+-]?%d+)$") local found, _, axis, angle = param:find("^([xyz%?])%s+([+-]?%d+)$")
@ -730,18 +789,20 @@ minetest.register_chatcommand("/rotate", {
return nil return nil
end end
return check_region(name, param) return check_region(name, param)
end), end)),
}) })
minetest.register_chatcommand("/orient", { minetest.register_chatcommand("/orient", {
params = "<angle>", params = "<angle>",
description = "Rotate oriented nodes in the current WorldEdit region around the Y axis by angle <angle> (90 degree increment)", description = "Rotate oriented nodes in the current WorldEdit region around the Y axis by angle <angle> (90 degree increment)",
privs = {worldedit=true}, privs = {},
func = safe_region(function(name, param) func = worldedit.privs(safe_region(function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
local found, _, angle = param:find("^([+-]?%d+)$") local found, _, angle = param:find("^([+-]?%d+)$")
if worldedit.can_edit_volume(name, {pos1, pos2}) then
local count = worldedit.orient(pos1, pos2, angle) local count = worldedit.orient(pos1, pos2, angle)
worldedit.player_notify(name, count .. " nodes oriented") worldedit.player_notify(name, count .. " nodes oriented")
end
end, end,
function(name, param) function(name, param)
local found, _, angle = param:find("^([+-]?%d+)$") local found, _, angle = param:find("^([+-]?%d+)$")
@ -754,72 +815,83 @@ minetest.register_chatcommand("/orient", {
return nil return nil
end end
return check_region(name, param) return check_region(name, param)
end), end)),
}) })
minetest.register_chatcommand("/fixlight", { minetest.register_chatcommand("/fixlight", {
params = "", params = "",
description = "Fix the lighting in the current WorldEdit region", description = "Fix the lighting in the current WorldEdit region",
privs = {worldedit=true}, privs = {},
func = safe_region(function(name, param) func = worldedit.privs(safe_region(function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if worldedit.can_edit_volume(name, {pos1, pos2}) then
local count = worldedit.fixlight(pos1, pos2) local count = worldedit.fixlight(pos1, pos2)
worldedit.player_notify(name, count .. " nodes updated") worldedit.player_notify(name, count .. " nodes updated")
end), end
end)),
}) })
minetest.register_chatcommand("/hide", { minetest.register_chatcommand("/hide", {
params = "", params = "",
description = "Hide all nodes in the current WorldEdit region non-destructively", description = "Hide all nodes in the current WorldEdit region non-destructively",
privs = {worldedit=true}, privs = {},
func = safe_region(function(name, param) func = worldedit.privs(safe_region(function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if worldedit.can_edit_volume(name, {pos1, pos2}) then
local count = worldedit.hide(pos1, pos2) local count = worldedit.hide(pos1, pos2)
worldedit.player_notify(name, count .. " nodes hidden") worldedit.player_notify(name, count .. " nodes hidden")
end), end
end)),
}) })
minetest.register_chatcommand("/suppress", { minetest.register_chatcommand("/suppress", {
params = "<node>", params = "<node>",
description = "Suppress all <node> in the current WorldEdit region non-destructively", description = "Suppress all <node> in the current WorldEdit region non-destructively",
privs = {worldedit=true}, privs = {},
func = safe_region(function(name, param) func = worldedit.privs(safe_region(function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if worldedit.can_edit_volume(name, {pos1, pos2}) then
local node = get_node(name, param) local node = get_node(name, param)
local count = worldedit.suppress(pos1, pos2, node) local count = worldedit.suppress(pos1, pos2, node)
worldedit.player_notify(name, count .. " nodes suppressed") worldedit.player_notify(name, count .. " nodes suppressed")
end, check_set), end
end, check_set)),
}) })
minetest.register_chatcommand("/highlight", { minetest.register_chatcommand("/highlight", {
params = "<node>", params = "<node>",
description = "Highlight <node> in the current WorldEdit region by hiding everything else non-destructively", description = "Highlight <node> in the current WorldEdit region by hiding everything else non-destructively",
privs = {worldedit=true}, privs = {},
func = safe_region(function(name, param) func = worldedit.privs(safe_region(function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if worldedit.can_edit_volume(name, {pos1, pos2}) then
local node = get_node(name, param) local node = get_node(name, param)
local count = worldedit.highlight(pos1, pos2, node) local count = worldedit.highlight(pos1, pos2, node)
worldedit.player_notify(name, count .. " nodes highlighted") worldedit.player_notify(name, count .. " nodes highlighted")
end, check_set), end
end, check_set)),
}) })
minetest.register_chatcommand("/restore", { minetest.register_chatcommand("/restore", {
params = "", params = "",
description = "Restores nodes hidden with WorldEdit in the current WorldEdit region", description = "Restores nodes hidden with WorldEdit in the current WorldEdit region",
privs = {worldedit=true}, privs = {},
func = safe_region(function(name, param) func = worldedit.privs(safe_region(function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if worldedit.can_edit_volume(name, {pos1, pos2}) then
local count = worldedit.restore(pos1, pos2) local count = worldedit.restore(pos1, pos2)
worldedit.player_notify(name, count .. " nodes restored") worldedit.player_notify(name, count .. " nodes restored")
end), end
end)),
}) })
minetest.register_chatcommand("/save", { minetest.register_chatcommand("/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\"",
privs = {worldedit=true}, privs = {}, --worldedit, server
func = safe_region(function(name, param) func = worldedit.privs(safe_region(function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if minetest.check_player_privs(name, {server=true}) then
if param == "" then if param == "" then
worldedit.player_notify(name, "invalid usage: " .. param) worldedit.player_notify(name, "invalid usage: " .. param)
return return
@ -828,6 +900,10 @@ minetest.register_chatcommand("/save", {
worldedit.player_notify(name, "invalid file name: " .. param) worldedit.player_notify(name, "invalid file name: " .. param)
return return
end end
else
worldedit.player_notify(name, "saving as worldedit.we (missing privileges: server)")
param = "worldedit"
end
local result, count = worldedit.serialize(pos1, pos2) local result, count = worldedit.serialize(pos1, pos2)
@ -845,14 +921,14 @@ minetest.register_chatcommand("/save", {
file:close() file:close()
worldedit.player_notify(name, count .. " nodes saved") worldedit.player_notify(name, count .. " nodes saved")
end), end)),
}) })
minetest.register_chatcommand("/allocate", { minetest.register_chatcommand("/allocate", {
params = "<file>", params = "<file>",
description = "Set the region defined by nodes from \"(world folder)/schems/<file>.we\" as the current WorldEdit region", description = "Set the region defined by nodes from \"(world folder)/schems/<file>.we\" as the current WorldEdit region",
privs = {worldedit=true}, privs = {},
func = function(name, param) func = worldedit.privs(function(name, param)
local pos = get_position(name) local pos = get_position(name)
if pos == nil then return end if pos == nil then return end
@ -886,14 +962,14 @@ minetest.register_chatcommand("/allocate", {
worldedit.mark_pos2(name) worldedit.mark_pos2(name)
worldedit.player_notify(name, count .. " nodes allocated") worldedit.player_notify(name, count .. " nodes allocated")
end, end),
}) })
minetest.register_chatcommand("/load", { minetest.register_chatcommand("/load", {
params = "<file>", params = "<file>",
description = "Load nodes from \"(world folder)/schems/<file>[.we[m]]\" with position 1 of the current WorldEdit region as the origin", description = "Load nodes from \"(world folder)/schems/<file>[.we[m]]\" with position 1 of the current WorldEdit region as the origin",
privs = {worldedit=true}, privs = {},
func = function(name, param) func = worldedit.privs(function(name, param)
local pos = get_position(name) local pos = get_position(name)
if pos == nil then return end if pos == nil then return end
@ -931,17 +1007,20 @@ minetest.register_chatcommand("/load", {
return return
end end
local nodepos1, nodepos2, _ = worldedit.allocate(pos, value)
if worldedit.can_edit_volume(name, {nodepos1, nodepos2}) then
local count = worldedit.deserialize(pos, value) local count = worldedit.deserialize(pos, value)
worldedit.player_notify(name, count .. " nodes loaded") worldedit.player_notify(name, count .. " nodes loaded")
end, end
end),
}) })
minetest.register_chatcommand("/lua", { minetest.register_chatcommand("/lua", {
params = "<code>", params = "<code>",
description = "Executes <code> as a Lua chunk in the global namespace", description = "Executes <code> as a Lua chunk in the global namespace",
privs = {worldedit=true, server=true}, privs = {server=true},
func = function(name, param) func = worldedit.privs(function(name, param)
local admin = minetest.setting_get("name") local admin = minetest.setting_get("name")
if not admin or not name == admin then if not admin or not name == admin then
worldedit.player_notify(name, "this command can only be run by the server administrator") worldedit.player_notify(name, "this command can only be run by the server administrator")
@ -953,14 +1032,14 @@ minetest.register_chatcommand("/lua", {
else else
worldedit.player_notify(name, "code successfully executed", false) worldedit.player_notify(name, "code successfully executed", false)
end end
end, end),
}) })
minetest.register_chatcommand("/luatransform", { minetest.register_chatcommand("/luatransform", {
params = "<code>", params = "<code>",
description = "Executes <code> as a Lua chunk in the global namespace with the variable pos available, for each node in the current WorldEdit region", description = "Executes <code> as a Lua chunk in the global namespace with the variable pos available, for each node in the current WorldEdit region",
privs = {worldedit=true, server=true}, privs = {server=true},
func = safe_region(function(name, param) func = worldedit.privs(safe_region(function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
local admin = minetest.setting_get("name") local admin = minetest.setting_get("name")
if not admin or not name == admin then if not admin or not name == admin then
@ -974,19 +1053,24 @@ minetest.register_chatcommand("/luatransform", {
else else
worldedit.player_notify(name, "code successfully executed", false) worldedit.player_notify(name, "code successfully executed", false)
end end
end), end)),
}) })
minetest.register_chatcommand("/mtschemcreate", { minetest.register_chatcommand("/mtschemcreate", {
params = "<file>", params = "<file>",
description = "Save the current WorldEdit region using the Minetest Schematic format to \"(world folder)/schems/<filename>.mts\"", description = "Save the current WorldEdit region using the Minetest Schematic format to \"(world folder)/schems/<filename>.mts\"",
privs = {worldedit=true}, privs = {},
func = safe_region(function(name, param) func = worldedit.privs(safe_region(function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if minetest.check_player_privs(name, {server=true}) then
if param == nil then if param == nil then
worldedit.player_notify(name, "No filename specified") worldedit.player_notify(name, "No filename specified")
return return
end end
else
worldedit.player_notify(name, "saving as worldedit.mts (missing privileges: server)")
param = "worldedit"
end
local path = minetest.get_worldpath() .. "/schems" local path = minetest.get_worldpath() .. "/schems"
local filename = path .. "/" .. param .. ".mts" local filename = path .. "/" .. param .. ".mts"
@ -1000,14 +1084,44 @@ minetest.register_chatcommand("/mtschemcreate", {
worldedit.player_notify(name, "saved Minetest schematic to " .. param, false) worldedit.player_notify(name, "saved Minetest schematic to " .. param, false)
end end
worldedit.prob_list[name] = {} worldedit.prob_list[name] = {}
end), end)),
}) })
minetest.allocate_schematic = function(originpos, schematic)
--[[
u32 MTSM
u16 version 3
u16 x
u16 y
u16 z
...
--]]
--8 bits in a byte
local offset = 0
if string.byte(schematic, offset+1, offset+32/8) == "MTSM" then --32 bits
offset = offset + 32/8
if string.byte(schematic, offset+1, offset+16/8) == "3" then
offset = offset + 16/8
local x = string.byte(schematic, offset+1, offset+16/8)
offset = offset + 16/8
local y = string.byte(schematic, offset+1, offset+16/8)
offset = offset + 16/8
local z = string.byte(schematic, offset+1, offset+16/8)
offset = offset + 16/8
return originpos, {x=originpos.x+x, y=originpos.y+y, z=originpos.z+z}
else
--not version 3
end
else
--not a schematic?
end
end
minetest.register_chatcommand("/mtschemplace", { minetest.register_chatcommand("/mtschemplace", {
params = "<file>", params = "<file>",
description = "Load nodes from \"(world folder)/schems/<file>.mts\" with position 1 of the current WorldEdit region as the origin", description = "Load nodes from \"(world folder)/schems/<file>.mts\" with position 1 of the current WorldEdit region as the origin",
privs = {worldedit=true}, privs = {},
func = function(name, param) func = worldedit.privs(function(name, param)
if param == nil then if param == nil then
worldedit.player_notify(name, "no filename specified") worldedit.player_notify(name, "no filename specified")
return return
@ -1017,20 +1131,26 @@ minetest.register_chatcommand("/mtschemplace", {
if pos == nil then return end if pos == nil then return end
local path = minetest.get_worldpath() .. "/schems/" .. param .. ".mts" local path = minetest.get_worldpath() .. "/schems/" .. param .. ".mts"
local value = file:read("*a")
file:close()
if worldedit.can_edit_volume(name, {minetest.allocate_schematic(pos, value)}) then
if minetest.place_schematic(pos, path) == nil then if minetest.place_schematic(pos, path) == nil then
worldedit.player_notify(name, "failed to place Minetest schematic", false) worldedit.player_notify(name, "failed to place Minetest schematic", false)
else else
worldedit.player_notify(name, "placed Minetest schematic " .. param .. worldedit.player_notify(name, "placed Minetest schematic " .. param ..
" at " .. minetest.pos_to_string(pos), false) " at " .. minetest.pos_to_string(pos), false)
end end
end, end
end),
}) })
minetest.register_chatcommand("/mtschemprob", { minetest.register_chatcommand("/mtschemprob", {
params = "start/finish/get", params = "start/finish/get",
description = "Begins node probability entry for Minetest schematics, gets the nodes that have probabilities set, or ends node probability entry", description = "Begins node probability entry for Minetest schematics, gets the nodes that have probabilities set, or ends node probability entry",
privs = {worldedit=true}, privs = {},
func = function(name, param) func = worldedit.privs(function(name, param)
if param == "start" then --start probability setting if param == "start" then --start probability setting
worldedit.set_pos[name] = "prob" worldedit.set_pos[name] = "prob"
worldedit.prob_list[name] = {} worldedit.prob_list[name] = {}
@ -1053,7 +1173,7 @@ minetest.register_chatcommand("/mtschemprob", {
else else
worldedit.player_notify(name, "unknown subcommand: " .. param) worldedit.player_notify(name, "unknown subcommand: " .. param)
end end
end, end),
}) })
minetest.register_on_player_receive_fields( minetest.register_on_player_receive_fields(
@ -1070,10 +1190,12 @@ minetest.register_on_player_receive_fields(
minetest.register_chatcommand("/clearobjects", { minetest.register_chatcommand("/clearobjects", {
params = "", params = "",
description = "Clears all objects within the WorldEdit region", description = "Clears all objects within the WorldEdit region",
privs = {worldedit=true}, privs = {},
func = safe_region(function(name, param) func = worldedit.privs(safe_region(function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if worldedit.can_edit_volume(name, {pos1, pos2}) then
local count = worldedit.clearobjects(pos1, pos2) local count = worldedit.clearobjects(pos1, pos2)
worldedit.player_notify(name, count .. " objects cleared") worldedit.player_notify(name, count .. " objects cleared")
end), end
end)),
}) })

View File

@ -0,0 +1,38 @@
Because worldedit can easily cause massive destruction when in the wrong hands, the only use it has seen outside of singleplayer games is when wielded by an admin or moderator.
I (khonkhortisan) intend to change that, by tying its use to, and making it respect, owned areas:
On a non-creative server (say, survival) it does not make sense to be able to create valuable ores out of thin air, so the "worldedit" privilege is required to do anything.
On a creative server that does not have any form of land ownership, the "worldedit" privilege is required to do anything. A malicious player is limited by the speed of their arm.
On a creative server that has an ownership mod (say, areas) the ability to use worldedit is more complicated than a simple "yes" or "no":
Commands that do not actually edit nodes can be used by anyone. This includes setting the region, inspecting nodes, saving (to a standard filename, see below) and copying (the initial region with //copy or //stack)
Commands that do edit nodes check the current worldedit region (or the bounding box for the command) for the player's ability both to edit manually, and through this mod: (this includes the second region with //copy or //stack)
If the region is completely contained by area assigned to you, the command succeeds.
If the whole region is not owned by anyone (with or without part of it being owned by you):
If you do not have the "worldedit" privilege, you can only edit area you fully own, and the command fails.
If you do have the "worldedit" privilege, you can edit unowned area and/or your area (but not someone else's area), and the command succeeds.
If part of the region is owned by someone else (with or without part of it being unowned, or owned by you):
If you have the "areas" privilege, you could make yourself the owner anyway, so the command succeeds.
If you do not have the "areas" privilege, the land cannot be edited by you (worldedit does not currently understand owned but shared area?)
File writes - if you can set a region, you can read from the world.
If you have the "server" privilege, you can save to an arbitrary filename, as usual.
If you do not have the "server" privilege, the filename is forced to "worldedit.we" or "worldedit.mts".
File reads (of *.we or *.mts, of course) will succeed, but must pass the region write check.
Lua commands (//lua and //luatransform) require first "server", then "admin", as usual.
minetest.register_chatcommand
privs = {worldedit}, is what it used to have, but that privilege is only required in certain cases (see above)
privs = {}, func = worldedit.privs(function(name, param)...end), passes to func a wrapper that checks first whether the worldedit privilege is required, then if the player has it, before running the inner function.
worldedit.privs can also be used in an if statement, as it returns both whether execution should continue (nil or 1), and whether a region permission check should be skipped (2) due to the areas privilege.
worldedit.can_edit_volume
It takes the player's name, then a list of either two or four points (two points per region, //move has two regions to check)
It re-runs worldedit.privs to attempt skipping a region permission check (if the player has the "areas" privilege)
This return value should probably be cached instead of running the function twice.
func = worldedit.privs(safe_region(function(name, param)...if worldedit.can_edit_volume(name, {pos1, pos2}) then...
The "wp" return value has to make it past safe_region somehow.
It returns whether the command should actually be run, along with telling the player why it wasn't.
minetest.allocate_schematic
A function for schematics corresponding with worldedit.allocate for .we files

View File

@ -0,0 +1,2 @@
worldedit
commonlib

View File

@ -0,0 +1,178 @@
--if there's no protection mod, no worldedit means no editing, worldedit means editing anywhere (old behaviour)
--if there's a protection mod (and it's creative mode), no worldedit means editing only in your area, worldedit means editing in no-man's land too, and areas means editing anywhere.
--[[
--let the other mod load first
minetest.after(0, function()
--I would use mod.soft_depend from commonlib, but there are multiple mods that could create owned land
PROTECTION_MOD_EXISTS = minetest.is_protected ~= old_is_protected
--else fall back to old behaviour, where
--worldedit privilege is permission to edit everything
end)
--]]
local PROTECTION_MOD_EXISTS = false
mod.soft_depend("areas", function()
PROTECTION_MOD_EXISTS = true
end)
--mod.soft_depend("other protection mod goes here", function()
-- PROTECTION_MOD_EXISTS = true
--end)
--[[
worldedit.privs replaces privs = {worldedit = true}, and also helps bypass volume permission checks.
Usage:
In chatcommand:
privs = {}
func = worldedit.privs(function(name, param)...end)
In if statement:
name = minetest.get_player_name(node)
if worldedit.privs() then
Returns:
nil ( false) for no permission to worldedit anywhere,
1 ( true ) for permission to worldedit at least somewhere, and
2 (extra true ) for worldediting everywhere without checking for further permission.
--]]
--I wanted this function to directly choose the privileges for the chat command, but it only applies once.
--privs={worldedit=true [, server=true]}
--privs={worldedit=worldedit.priv() [, server=true]}
--instead, I had to wrap the rest of func = .
worldedit.privs = function(privsfunc) --returns a function (the argument function wrapped in another) which takes the arguments (name, param).
if privsfunc == nil then
privsfunc = function() --[[no-op]] end
end
--this silly syntax was copied from safe_region, which is actually executed on chatcommand registration, and must return a function instead of the result of a function.
--The innermost anonymous function is declared. Then safe_region executes, adding a function wrapper around that function. Then worldedit.privs gets that as an argument, and adds another wrapper. The doubly-wrapped function is the one registered as a chatcommand.
return function(name, param)
if minetest.check_player_privs(name, {areas=true}) then
--You can set areas, so you are allowed to worldedit them too.
--The ability to set the whole world as owned by yourself is already potentially destructive, what's more destructive capability?
privsfunc(name, param)
return 2 --edit everywhere without checks
end
local is_creative = minetest.setting_getbool("creative_mode")
if not is_creative or not PROTECTION_MOD_EXISTS then
--no protection mod, or not the kind of world where people can just create nodes out of thin air,
--worldedit privilege means editing anywhere
if minetest.check_player_privs(name, {worldedit=true}) then
privsfunc(name, param)
return 2 --edit everywhere without checks
else
--default chatcommand failure message
minetest.chat_send_player(name, "You don't have permission to run this command (missing privileges: worldedit)\nReasons:".. (is_creative and "" or " (not creative mode)") .. (PROTECTION_MOD_EXISTS and "" or " (no protection mod)"))
--func(name, param) placeholder
return nil --edit nowhere
end
else
--protection mod, can edit inside your area without worldedit privilege
--(worldedit and areas let you edit in no-man's land and other-owned area)
privsfunc(name, param)
return 1 --edit at least somewhere, with checks
end
end
end
--this is... within chatcommands that actually change land
--(should be the same functions as safe_region)
--also check for permission when region is set? no, //stack goes outside the boundaries.
--so the region is defined per-command on exec.
--//move has disconnected sections, so it's passed as a list of points.
--which are deduplicated.
worldedit.can_edit_volume = function(name, volume) --does not return a function like .privs does
--debug info, remove after testing
minetest.chat_send_player(name, minetest.pos_to_string(volume[1])..minetest.pos_to_string(volume[2]))
if volume[3] ~= nil then
minetest.chat_send_player(name, minetest.pos_to_string(volume[3])..minetest.pos_to_string(volume[4]))
end
--volume is before func, unlike safe_region having func before count
--because func may be removed to have can_edit_volume in an if statement
--like worldedit.privs can be
--worldedit.privs was run once to prevent safe_region large area warnings,
--then safe_region was run to prevent unnecessary large-scale permission checks
--then can_edit_volume is run before //set to honor areas
--then worldedit.privs is run again to attempt skipping checks (resusing the same code)
--then set is finally run.
--<==>--local returnfunc = function(name, param)
--worldedit.privs said that 'name' can use worldedit at least somewhere
-- return value 1 (or 2) before this function was run.
--Try skipping volume permission checks.
local wp = worldedit.privs(nil)(name, nil)
if wp == 2 then
--volfunc(name, param)
return true
elseif wp == nil then
--safety feature in case worldedit.can_edit_volume is ever run alone, without being surrounded by worldedit.privs()
--Shouldn't ever get here.
--Any volume-changing function is surrounded by this, then safe_region, then worldedit.privs()
--volfunc(name, param) placeholder
return false
end
--[[I need to use a special per-command region (think /stack, or even worse, /move), the same one safe_region uses, but corner points instead of count... instead of a for loop interpolating between pos1 and pos2]]--
--cache the result of this function for later
local has_worldedit = minetest.check_player_privs(name, {worldedit=true})
--all or nothing here
--volume may be a single region (//set) or two possibly overlapping regions (//move)
--an overlapping //move is checked twice - any attempt to make it more efficient (by combining the volumes into a single two-pos one) may take into account that the format is //move axis amount, and break if the format is changed to //move xamount yamount zamount
for v = 1,3,2 do --volume[1] and volume[2]
if volume[v] ~= nil then --volume[3] and volume[4]
volume[v], volume[v+1] = worldedit.sort_pos(volume[v], volume[v+1])
for y = volume[v].y, volume[v+1].y do
for z = volume[v].z, volume[v+1].z do
for x = volume[v].x, volume[v+1].x do
local node = {x=x, y=y, z=z}
--[[
THIS SECTION IGNORES the distinction of area that is owned by someone else, but still editable
this is treated as area owned by the editor, or no-man's land depending on if it's shared with one person or everyone
If it was treated differently (it's not), then single edits would not be able to cross the border between someone else's editable land, and no-man's land, to prevent accidental writes. It may cross the border between multiple people's editable land (or should it?), such as to create a bridge between two skyscrapers that were previously built separately.
This needs testing for the other changes first.
--]]
--Is it owned?
if minetest.is_protected(node, "") then
--Is it someone else's?
if minetest.is_protected(node, name) then
--already checked the ability to make it mine (areas)
minetest.chat_send_player(name, "You don't have permission to run this command "..
"(Region "..minetest.pos_to_string(volume[1]).."-"..minetest.pos_to_string(volume[2])..
(volume[3] ~= nil and ("+"..minetest.pos_to_string(volume[3]).."-"..minetest.pos_to_string(volume[4])) or "")..
" overlaps (first match) areaname: (x, y, z)-(x, y, z) owned by playername)"..
"\nReasons: (missing areas privilege)"
)
--volfunc(name, param) placeholder
return false
end
--it's mine
--continue
--no-man's land
--can I edit that?
elseif not has_worldedit then --using cached check
minetest.chat_send_player(name, "You don't have permission to run this command (missing worldedit privilege)\nReasons: (At least part of this area is unowned (not owned by you)) (missing areas privilege)")
--volfunc(name, param) placeholder
return false
end
end -- for x
end -- for z
end -- for y
end -- if not nil
end -- for v
--the whole thing is
--a) owned by me, and/or
--b) owned by no one and I have the worldedit privilege.
--c) I have the areas privilege and it's possibly owned by someone else. (returned earlier)
--volfunc(name, param)
return true
--<==>--end
end