Merge branch 'main' into VorTechnix

This commit is contained in:
VorTechnix 2022-05-21 07:04:09 -07:00
commit 8789b39d1d
79 changed files with 713 additions and 253 deletions

@ -6,6 +6,7 @@ Note to self: See the bottom of this file for the release template text.
## v1.14: The untitled update (unreleased) ## v1.14: The untitled update (unreleased)
- Add `//dome+`, which allows you to change the direction the dome is pointing in, and also create multiple domes at once - Add `//dome+`, which allows you to change the direction the dome is pointing in, and also create multiple domes at once
- Add `//metaball`, which renders 2 or more [metaballs](https://en.wikipedia.org/wiki/Metaballs) in Minetest
- Migrate from `depends.txt` to `mod.conf` - Migrate from `depends.txt` to `mod.conf`

@ -37,7 +37,7 @@ When actually implementing stuff, here are a few guidelines that I recommend to
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ████ ██ ██ ██ ██ ███████ -- ██ ████ ██ ██ ██ ██ ███████
local wea = worldeditadditions local wea = worldeditadditions
worldedit.register_command("{name}", { worldeditadditions_core.register_command("{name}", {
params = "<argument> <argument=default> <option1|option2|...> [<optional_argument> <optional_argument2> ...] | [<optional_argument> [<optional_argument2>]]", params = "<argument> <argument=default> <option1|option2|...> [<optional_argument> <optional_argument2> ...] | [<optional_argument> [<optional_argument2>]]",
description = "A **brief** description of what this command does", description = "A **brief** description of what this command does",
privs = { worldedit = true }, privs = { worldedit = true },

@ -59,11 +59,11 @@ dofile(wea.modpath.."/lib/scale.lua")
dofile(wea.modpath.."/lib/spiral_square.lua") dofile(wea.modpath.."/lib/spiral_square.lua")
dofile(wea.modpath.."/lib/spiral_circle.lua") dofile(wea.modpath.."/lib/spiral_circle.lua")
dofile(wea.modpath.."/lib/dome.lua") dofile(wea.modpath.."/lib/dome.lua")
dofile(wea.modpath.."/lib/metaballs.lua")
dofile(wea.modpath.."/lib/conv/conv.lua") dofile(wea.modpath.."/lib/conv/conv.lua")
dofile(wea.modpath.."/lib/erode/erode.lua") dofile(wea.modpath.."/lib/erode/erode.lua")
dofile(wea.modpath.."/lib/noise/init.lua") dofile(wea.modpath.."/lib/noise/init.lua")
wea.sculpt = dofile(wea.modpath.."/lib/sculpt/init.lua") wea.sculpt = dofile(wea.modpath.."/lib/sculpt/init.lua")
wea.metaballs = dofile(wea.modpath.."/lib/metaballs/init.lua")
dofile(wea.modpath.."/lib/copy.lua") dofile(wea.modpath.."/lib/copy.lua")
dofile(wea.modpath.."/lib/move.lua") dofile(wea.modpath.."/lib/move.lua")

@ -0,0 +1,16 @@
local wea = worldeditadditions
local wea_m = wea.modpath .. "/lib/metaballs/"
local playerdata = dofile(wea_m.."playerdata.lua")
local metaballs_ns = {
render = dofile(wea_m.."render.lua"),
add = playerdata.add,
remove = playerdata.remove,
list = playerdata.list,
list_pretty = playerdata.list_pretty,
clear = playerdata.clear,
volume = playerdata.volume
}
return metaballs_ns

@ -0,0 +1,125 @@
local wea = worldeditadditions
local Vector3 = wea.Vector3
local metaballdata = {}
--- Adds a new metaball for a given player at the specified position with a specified radius.
-- @param player_name string The name of the player.
-- @param pos Vector3 The position of the metaball.
-- @param radius number The radius of the metaball.
-- @returns bool,number The number of metaballs now defined for the given player.
local function add(player_name, pos, radius)
local pos = Vector3.clone(pos)
if type(player_name) ~= "string" then
return false, "Error: Invalid player name specified."
end
if type(radius) ~= "number" then
return false, "Error: Expected the radius to be of type number, but got value of type "..type(radius).." instead."
end
if radius < 1 then
return false, "The minimum radius of a metaball is 1, but got a radius of "..tostring(radius).."."
end
if not metaballdata[player_name] then
metaballdata[player_name] = {}
end
-- TODO: Limit the number of metaballs that can be defined?
table.insert(metaballdata[player_name], {
pos = pos,
radius = radius
})
return true, #metaballdata[player_name]
end
--- Returns a list of all metaballs defined for the given player.
-- @param player_name string The name of the player.
-- @returns bool,[{ pos: Vector3, radius: number }, ...] A list of metaballs for the given player.
local function list(player_name)
if type(player_name) ~= "string" then
return false, "Error: Invalid player name specified."
end
if not metaballdata[player_name] then return {} end
return true, metaballdata[player_name]
end
--- Returns a pretty-printed list of metaballs for the given player.
-- @param player_name string The name of the player.
-- @returns bool,string A pretty-printed list of metaballs for the given player.
local function list_pretty(player_name)
local success, metaball_list = list(player_name)
if not success then return success, metaball_list end
local rows = { { "Index", "Position", "Radius" } }
for i,metaball in ipairs(metaball_list) do
table.insert(rows, {
i,
metaball.pos,
metaball.radius
})
end
return true, wea.format.make_ascii_table(rows).."\n---------------------------\nTotal "..tostring(#metaball_list).." metaballs"
end
--- Removes the metaball with the specified index for a given player.
-- @param player_name string The name of the player.
-- @param index number The index of the metaball to remove.
-- @returns bool,number The number of metaballs now defined for the given player.
local function remove(player_name, index)
local success, metaball_list = list(player_name)
if not success then return success, metaball_list end
if index > #metaball_list then
return false, "Error: Requested the removal of metaball "..tostring(index)..", but there are "..tostring(#metaball_list).." metaballs defined."
end
table.remove(metaball_list, index)
return #metaball_list
end
--- Removes all the currently defined metaballs for the given player.
-- @param player_name string The name of the player.
-- @returns bool,number The number of metaballs that WERE defined for the given player.
local function clear(player_name)
local success, metaball_list = list(player_name)
if not success then return success, metaball_list end
metaballdata[player_name] = {}
return #metaball_list
end
--- Calculates the total volume that the currently defined metaballs are expected to take up.
-- @param player_name string The name of the player.
-- @returns bool,number The total volume that the currently defined metaballs are expected to take up.
local function volume(player_name)
local success, metaball_list = list(player_name)
if not success then return success, metaball_list end
if #metaball_list == 0 then return 0 end
local pos1 = metaball_list[1].pos
local pos2 = pos1
for i,metaball in ipairs(metaball_list) do
pos1 = Vector3.min(pos1, metaball.pos - metaball.radius)
pos2 = Vector3.max(pos2, metaball.pos + metaball.radius)
end
return (pos2 - pos1):area()
end
return {
add = add,
list = list,
list_pretty = list_pretty,
remove = remove,
clear = clear,
volume = volume
}

@ -10,7 +10,7 @@ local Vector3 = wea.Vector3
-- direction the point should point. -- direction the point should point.
-- @param metaballs [{pos: Vector3, radius: number}] Aa list of metaballs to render. Each metaball should be a table with 2 properties: pos - the position of the centre of the metaball as a Vector3, and radius - the radius of the metaball. -- @param metaballs [{pos: Vector3, radius: number}] Aa list of metaballs to render. Each metaball should be a table with 2 properties: pos - the position of the centre of the metaball as a Vector3, and radius - the radius of the metaball.
-- @param replace_node string The fully qualified name of the node to use to make the dome with. -- @param replace_node string The fully qualified name of the node to use to make the dome with.
function worldeditadditions.metaballs(metaballs, replace_node, threshold) local function render(metaballs, replace_node, threshold)
local pos1, pos2 local pos1, pos2
if not threshold then threshold = 1 end if not threshold then threshold = 1 end
@ -48,7 +48,7 @@ function worldeditadditions.metaballs(metaballs, replace_node, threshold)
metaball_sum = metaball_sum + falloff metaball_sum = metaball_sum + falloff
end end
if metaball_sum <= threshold then if metaball_sum >= threshold then
data[area:index(x, y, z)] = node_id_replace data[area:index(x, y, z)] = node_id_replace
replaced = replaced + 1 replaced = replaced + 1
end end
@ -61,3 +61,6 @@ function worldeditadditions.metaballs(metaballs, replace_node, threshold)
return true, replaced return true, replaced
end end
return render

@ -1,4 +1,4 @@
name = worldeditadditions name = worldeditadditions
description = Extra tools and commands to extend WorldEdit for Minetest description = Extra tools and commands to extend WorldEdit for Minetest
depends = worldedit depends = worldedit
optional_depends = bonemeal, cool_trees, default, moretrees, ethereal optional_depends = worldeditadditions_core, bonemeal, cool_trees, default, moretrees, ethereal

@ -0,0 +1,23 @@
local wea_c = worldeditadditions_core
wea_c.register_alias("smoothadv", "convolve")
wea_c.register_alias("conv", "convolve")
wea_c.register_alias("naturalise", "layers")
wea_c.register_alias("naturalize", "layers")
wea_c.register_alias("flora", "bonemeal")
-- Measure Tools
wea_c.register_alias("mcount", "count")
wea_c.register_alias("mfacing", "mface")
--- Overrides to core WorldEdit commands
-- These are commented out for now, as they could be potentially dangerous to stability
-- Thorough testing is required of our replacement commands before these are uncommented
-- TODO: Depend on worldeditadditions_core before uncommenting this
-- BUG: //move+ seems to be leaving stuff behind for some strange reason --@sbrl 2021-12-26
-- worldeditadditions_core.alias_override("copy", "copy+")
-- worldeditadditions_core.alias_override("move", "move+") -- MAY have issues where it doesn't overwrite the old region properly, but haven't been able to reliably reproduce this
-- worldeditadditions_core.alias_override("replace", "replacemix")

@ -6,7 +6,7 @@ local wea = worldeditadditions
-- ██████ ██ ██ ██ ██ ██ █████ ██ ████ ██ █████ ███████ ██ -- ██████ ██ ██ ██ ██ ██ █████ ██ ████ ██ █████ ███████ ██
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██████ ██████ ██ ████ ███████ ██ ██ ███████ ██ ██ ███████ -- ██████ ██████ ██ ████ ███████ ██ ██ ███████ ██ ██ ███████
worldedit.register_command("bonemeal", { worldeditadditions_core.register_command("bonemeal", {
params = "[<strength> [<chance> [<node_name> [<node_name> ...]]]]", params = "[<strength> [<chance> [<node_name> [<node_name> ...]]]]",
description = "Bonemeals everything that's bonemeal-able that has an air node directly above it. Optionally takes a strength value to use (default: 1, maximum: 4), and a chance to actually bonemeal an eligible node (positive integer; nodes have a 1-in-<chance> chance to be bonemealed; higher values mean a lower chance; default: 1 - 100% chance).", description = "Bonemeals everything that's bonemeal-able that has an air node directly above it. Optionally takes a strength value to use (default: 1, maximum: 4), and a chance to actually bonemeal an eligible node (positive integer; nodes have a 1-in-<chance> chance to be bonemealed; higher values mean a lower chance; default: 1 - 100% chance).",
privs = { worldedit = true }, privs = { worldedit = true },

@ -6,7 +6,7 @@ local Vector3 = wea.Vector3
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ █████ -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ █████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██████ ██████ ██ ████ ████ ██████ ███████ ████ ███████ -- ██████ ██████ ██ ████ ████ ██████ ███████ ████ ███████
worldedit.register_command("convolve", { worldeditadditions_core.register_command("convolve", {
params = "<kernel> [<width>[,<height>]] [<sigma>]", params = "<kernel> [<width>[,<height>]] [<sigma>]",
description = "Advanced version of //smooth from we_env. Convolves over the defined region with the given kernel. Possible kernels: box, pascal, gaussian. The width & height (if specified) must be odd integers. If the height is not specified, it defaults to the width. gaussian should give the smoothest result, but the width & height must be identical. The sigma value is only applicable to gaussian kernels, and can be thought of as the 'smoothness' to apply.", description = "Advanced version of //smooth from we_env. Convolves over the defined region with the given kernel. Possible kernels: box, pascal, gaussian. The width & height (if specified) must be odd integers. If the height is not specified, it defaults to the width. gaussian should give the smoothest result, but the width & height must be identical. The sigma value is only applicable to gaussian kernels, and can be thought of as the 'smoothness' to apply.",
privs = { worldedit = true }, privs = { worldedit = true },

@ -24,7 +24,7 @@ end
-- ██ ██ ██ ██████ ████ -- ██ ██ ██ ██████ ████
-- ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██
-- ██████ ██████ ██ ██ -- ██████ ██████ ██ ██
worldedit.register_command("copy+", { -- TODO: Make this an override worldeditadditions_core.register_command("copy+", { -- TODO: Make this an override
params = "<axis:x|y|z|-x|-y|-z|?|front|back|left|right|up|down> <count> [<axis> <count> [...]]", params = "<axis:x|y|z|-x|-y|-z|?|front|back|left|right|up|down> <count> [<axis> <count> [...]]",
description = "Copies the defined region to another location - potentially across multiple axes at once.", description = "Copies the defined region to another location - potentially across multiple axes at once.",
privs = { worldedit = true }, privs = { worldedit = true },

@ -3,7 +3,7 @@
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██████ ██████ ██████ ██ ████ ██ -- ██████ ██████ ██████ ██ ████ ██
worldedit.register_command("count", { worldeditadditions_core.register_command("count", {
params = "", params = "",
description = "Counts all the nodes in the defined region.", description = "Counts all the nodes in the defined region.",
privs = { worldedit = true }, privs = { worldedit = true },

@ -25,7 +25,7 @@ end
-- ██ ██ ██ ██ ██ ████ ██ █████ -- ██ ██ ██ ██ ██ ████ ██ █████
-- ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██
-- ██████ ██████ ██ ██ ███████ -- ██████ ██████ ██ ██ ███████
worldedit.register_command("dome+", { -- TODO: Make this an override worldeditadditions_core.register_command("dome+", { -- TODO: Make this an override
params = "<radius> <replace_node> [<pointing_dir:x|y|z|-x|-y|-z|?|front|back|left|right|up|down> ...] [h[ollow]]", params = "<radius> <replace_node> [<pointing_dir:x|y|z|-x|-y|-z|?|front|back|left|right|up|down> ...] [h[ollow]]",
description = "Creates a dome shape with a specified radius of the defined node, optionally specifying the direction it should be pointing in (defaults to the positive y direction).", description = "Creates a dome shape with a specified radius of the defined node, optionally specifying the direction it should be pointing in (defaults to the positive y direction).",
privs = { worldedit = true }, privs = { worldedit = true },
@ -46,7 +46,7 @@ worldedit.register_command("dome+", { -- TODO: Make this an override
return false, "Error: Invalid radius '"..parts[1].."'. The radius must be a positive integer." return false, "Error: Invalid radius '"..parts[1].."'. The radius must be a positive integer."
end end
if radius < 1 then if radius < 1 then
return false, "Error: The minimum radius size is 1, but you entered"..tostring(radius).."." return false, "Error: The minimum radius size is 1, but you entered "..tostring(radius).."."
end end
if not replace_node then if not replace_node then

@ -30,7 +30,7 @@ local function parse_params_ellipsoid(params_text)
return true, replace_node, radius, hollow return true, replace_node, radius, hollow
end end
worldedit.register_command("ellipsoid", { worldeditadditions_core.register_command("ellipsoid", {
params = "<rx> <ry> <rz> <replace_node> [h[ollow]]", params = "<rx> <ry> <rz> <replace_node> [h[ollow]]",
description = "Creates a 3D ellipsoid with a radius of (rx, ry, rz) at pos1, filled with <replace_node>.", description = "Creates a 3D ellipsoid with a radius of (rx, ry, rz) at pos1, filled with <replace_node>.",
privs = { worldedit = true }, privs = { worldedit = true },
@ -53,7 +53,7 @@ worldedit.register_command("ellipsoid", {
}) })
-- TODO: This duplicates a lot of code. Perhaps we can trim it down a bit? -- TODO: This duplicates a lot of code. Perhaps we can trim it down a bit?
worldedit.register_command("hollowellipsoid", { worldeditadditions_core.register_command("hollowellipsoid", {
params = "<rx> <ry> <rz> <replace_node>", params = "<rx> <ry> <rz> <replace_node>",
description = "Creates a 3D hollow ellipsoid with a radius of (rx, ry, rz) at pos1, made out of <replace_node>.", description = "Creates a 3D hollow ellipsoid with a radius of (rx, ry, rz) at pos1, made out of <replace_node>.",
privs = { worldedit = true }, privs = { worldedit = true },

@ -5,7 +5,7 @@
-- ███████ ███████ ███████ ██ ██ ███████ ██████ ██ ██████ -- ███████ ███████ ███████ ██ ██ ███████ ██████ ██ ██████
local wea = worldeditadditions local wea = worldeditadditions
worldedit.register_command("ellipsoid2", { worldeditadditions_core.register_command("ellipsoid2", {
params = "[<replace_node:dirt> [h[ollow]]]", params = "[<replace_node:dirt> [h[ollow]]]",
description = "Creates am optionally hollow 3D ellipsoid that fills the defined region, filled with <replace_node>.", description = "Creates am optionally hollow 3D ellipsoid that fills the defined region, filled with <replace_node>.",
privs = { worldedit = true }, privs = { worldedit = true },

@ -3,7 +3,7 @@
-- █████ ██████ ██ ██ ██ ██ █████ -- █████ ██████ ██ ██ ██ ██ █████
-- ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██
-- ███████ ██ ██ ██████ ██████ ███████ -- ███████ ██ ██ ██████ ██████ ███████
worldedit.register_command("erode", { worldeditadditions_core.register_command("erode", {
params = "[<snowballs|river> [<key_1> [<value_1>]] [<key_2> [<value_2>]] ...]", params = "[<snowballs|river> [<key_1> [<value_1>]] [<key_2> [<value_2>]] ...]",
description = "**experimental** Runs the specified erosion algorithm over the given defined region. This may occur in 2d or 3d. Currently implemented algorithms: snowballs (default;2d hydraulic-like). Also optionally takes an arbitrary set of key - value pairs representing parameters to pass to the algorithm. See the full documentation for details.", description = "**experimental** Runs the specified erosion algorithm over the given defined region. This may occur in 2d or 3d. Currently implemented algorithms: snowballs (default;2d hydraulic-like). Also optionally takes an arbitrary set of key - value pairs representing parameters to pass to the algorithm. See the full documentation for details.",
privs = { worldedit = true }, privs = { worldedit = true },

@ -3,7 +3,7 @@
-- ██████ ███████ ███████ █████ ██ ██ ██ ███████ ██ ████ ██ █████ -- ██████ ███████ ███████ █████ ██ ██ ██ ███████ ██ ████ ██ █████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██████ ██ ██ ███████ ███████ ██ ████ ██ ██ ██ ██ ███████ -- ██████ ██ ██ ███████ ███████ ██ ████ ██ ██ ██ ██ ███████
worldedit.register_command("basename", { worldeditadditions_core.register_command("basename", {
params = "<nodealias>", params = "<nodealias>",
description = "Returns the base name of nodes that use a given alias.", description = "Returns the base name of nodes that use a given alias.",
privs = {worldedit = true}, privs = {worldedit = true},

@ -3,7 +3,7 @@
-- █████ ██ ██ ██ ██ ███████ ██ ██ █████ ███████ -- █████ ██ ██ ██ ██ ███████ ██ ██ █████ ███████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ███████ ███████ ██████ ██ ██ ████ ███████ ███████ -- ██ ██ ███████ ███████ ██████ ██ ██ ████ ███████ ███████
worldedit.register_command("fillcaves", { worldeditadditions_core.register_command("fillcaves", {
params = "[<node_name>]", params = "[<node_name>]",
description = "Fills in all airlike nodes beneath the first non-airlike node detected in each column.", description = "Fills in all airlike nodes beneath the first non-airlike node detected in each column.",
privs = { worldedit = true }, privs = { worldedit = true },

@ -3,7 +3,7 @@
-- █████ ██ ██ ██ ██ ██ ██ ██ █████ ██ ██ ██ -- █████ ██ ██ ██ ██ ██ ██ ██ █████ ██ ██ ██
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ███████ ██████ ██████ ██████ ██ ██ ███████ ███████ -- ██ ███████ ██████ ██████ ██████ ██ ██ ███████ ███████
worldedit.register_command("floodfill", { worldeditadditions_core.register_command("floodfill", {
params = "[<replace_node> [<radius>]]", params = "[<replace_node> [<radius>]]",
description = "Floods all connected nodes of the same type starting at pos1 with <replace_node> (which defaults to `water_source`), in a sphere with a radius of <radius> (which defaults to 20).", description = "Floods all connected nodes of the same type starting at pos1 with <replace_node> (which defaults to `water_source`), in a sphere with a radius of <radius> (which defaults to 20).",
privs = { worldedit = true }, privs = { worldedit = true },

@ -6,7 +6,7 @@ local wea = worldeditadditions
-- ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██████ ██ ██ ███████ ███████ ██ -- ██ ██████ ██ ██ ███████ ███████ ██
worldedit.register_command("forest", { worldeditadditions_core.register_command("forest", {
params = "[<density>] <sapling_a> [<chance_a>] <sapling_b> [<chance_b>] [<sapling_N> [<chance_N>]] ...", params = "[<density>] <sapling_a> [<chance_a>] <sapling_b> [<chance_b>] [<sapling_N> [<chance_N>]] ...",
description = "Plants and grows trees in the defined region according to the given list of sapling names and chances and density factor. The density controls the relative density of the resulting forest, and defaults to 1 (floating-point numbers allowed). Higher chance numbers result in a lower relative chance with respect to other saplings in the list. Saplings that fail to grow are subsequently removed (this will affect pre-existing saplings too).", description = "Plants and grows trees in the defined region according to the given list of sapling names and chances and density factor. The density controls the relative density of the resulting forest, and defaults to 1 (floating-point numbers allowed). Higher chance numbers result in a lower relative chance with respect to other saplings in the list. Saplings that fail to grow are subsequently removed (this will affect pre-existing saplings too).",
privs = { worldedit = true }, privs = { worldedit = true },

@ -3,7 +3,7 @@
-- ███████ ██ ██ ██ ██ ██ ██ ██ █ ██ -- ███████ ██ ██ ██ ██ ██ ██ ██ █ ██
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ███ ██ -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ███ ██
-- ██ ██ ██████ ███████ ███████ ██████ ███ ███ -- ██ ██ ██████ ███████ ███████ ██████ ███ ███
worldedit.register_command("hollow", { worldeditadditions_core.register_command("hollow", {
params = "[<wall_thickness>]", params = "[<wall_thickness>]",
description = "Replaces nodes inside the defined region with air, but leaving a given number of nodes near the outermost edges alone. In other words, it makes the defined region hollow leaving walls of a given thickness (default: 1)", description = "Replaces nodes inside the defined region with air, but leaving a given number of nodes near the outermost edges alone. In other words, it makes the defined region hollow leaving walls of a given thickness (default: 1)",
privs = { worldedit = true }, privs = { worldedit = true },

@ -26,7 +26,7 @@ end
-- ██ ██ ██ ██ █████ ██████ ██ ███████ ████ -- ██ ██ ██ ██ █████ ██████ ██ ███████ ████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██████ ████ ███████ ██ ██ ███████ ██ ██ ██ -- ██████ ████ ███████ ██ ██ ███████ ██ ██ ██
worldedit.register_command("layers", { worldeditadditions_core.register_command("layers", {
params = "[<max_slope|min_slope..max_slope>] [<node_name_1> [<layer_count_1>]] [<node_name_2> [<layer_count_2>]] ...", params = "[<max_slope|min_slope..max_slope>] [<node_name_1> [<layer_count_1>]] [<node_name_2> [<layer_count_2>]] ...",
description = "Replaces the topmost non-airlike nodes with layers of the given nodes from top to bottom. Like WorldEdit for MC's //naturalize command. Optionally takes a maximum or minimum and maximum slope value. If a column's slope value falls outside the defined range, then it's skipped. Default: dirt_with_grass dirt 3", description = "Replaces the topmost non-airlike nodes with layers of the given nodes from top to bottom. Like WorldEdit for MC's //naturalize command. Optionally takes a maximum or minimum and maximum slope value. If a column's slope value falls outside the defined range, then it's skipped. Default: dirt_with_grass dirt 3",
privs = { worldedit = true }, privs = { worldedit = true },

@ -3,7 +3,7 @@
-- ██ ██ ██ ██ ██ █████ -- ██ ██ ██ ██ ██ █████
-- ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██
-- ███████ ██ ██ ████ ███████ -- ███████ ██ ██ ████ ███████
worldedit.register_command("line", { worldeditadditions_core.register_command("line", {
params = "[<replace_node> [<radius>]]", params = "[<replace_node> [<radius>]]",
description = "Draws a line of a given radius (default: 1) from pos1 to pos2 in the given node (default: dirt).", description = "Draws a line of a given radius (default: 1) from pos1 to pos2 in the given node (default: dirt).",
privs = { worldedit = true }, privs = { worldedit = true },

@ -1,5 +1,5 @@
local wea = worldeditadditions local wea = worldeditadditions
local we_c = worldeditadditions_commands local wea_c = worldeditadditions_core
local Vector3 = worldeditadditions.Vector3 local Vector3 = worldeditadditions.Vector3
local function parse_params_maze(params_text, is_3d) local function parse_params_maze(params_text, is_3d)
@ -61,7 +61,7 @@ end
-- ██ ██ ██ ██ ██ ███ ██ -- ██ ██ ██ ██ ██ ███ ██
-- ██ ██ ██ ██ ███████ ███████ -- ██ ██ ██ ██ ███████ ███████
worldedit.register_command("maze", { wea_c.register_command("maze", {
params = "<replace_node> [<path_length> [<path_width> [<seed>]]]", params = "<replace_node> [<path_length> [<path_width> [<seed>]]]",
description = "Generates a maze covering the currently selected area (must be at least 3x3 on the x,z axes) with replace_node as the walls. Optionally takes a (integer) seed and the path length and width (see the documentation in the worldeditadditions README for more information).", description = "Generates a maze covering the currently selected area (must be at least 3x3 on the x,z axes) with replace_node as the walls. Optionally takes a (integer) seed and the path length and width (see the documentation in the worldeditadditions README for more information).",
privs = { worldedit = true }, privs = { worldedit = true },
@ -100,7 +100,7 @@ worldedit.register_command("maze", {
-- ██ ██ ██ ██ ██ ███ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ███ ██ ██ ██ ██
-- ██ ██ ██ ██ ███████ ███████ ██████ ██████ -- ██ ██ ██ ██ ███████ ███████ ██████ ██████
worldedit.register_command("maze3d", { wea_c.register_command("maze3d", {
params = "<replace_node> [<path_length> [<path_width> [<path_depth> [<seed>]]]]", params = "<replace_node> [<path_length> [<path_width> [<path_depth> [<seed>]]]]",
description = "Generates a 3d maze covering the currently selected area (must be at least 3x3x3) with replace_node as the walls. Optionally takes a (integer) seed and the path length, width, and depth (see the documentation in the worldeditadditions README for more information).", description = "Generates a 3d maze covering the currently selected area (must be at least 3x3x3) with replace_node as the walls. Optionally takes a (integer) seed and the path length, width, and depth (see the documentation in the worldeditadditions README for more information).",
privs = { worldedit = true }, privs = { worldedit = true },

@ -4,7 +4,7 @@
-- ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ██ ██ ██ ██████ ███████ -- ██ ██ ██ ██ ██ ██████ ███████
local wea = worldeditadditions local wea = worldeditadditions
worldedit.register_command("mface", { worldeditadditions_core.register_command("mface", {
params = "", params = "",
description = "Return player facing axis.", description = "Return player facing axis.",
privs = { worldedit = true }, privs = { worldedit = true },

@ -4,7 +4,7 @@
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ██ ██████ ██ ██████ ███████ -- ██ ██ ██ ██████ ██ ██████ ███████
local wea = worldeditadditions local wea = worldeditadditions
worldedit.register_command("midpos", { worldeditadditions_core.register_command("midpos", {
params = "", params = "",
description = "Return the mid point of current selection.", description = "Return the mid point of current selection.",
privs = { worldedit = true }, privs = { worldedit = true },

@ -4,7 +4,7 @@
-- ██ ██ ██ ██ ██ ███ ██ -- ██ ██ ██ ██ ██ ███ ██
-- ██ ██ ███████ ██ ███████ ███████ -- ██ ██ ███████ ██ ███████ ███████
local wea = worldeditadditions local wea = worldeditadditions
worldedit.register_command("msize", { worldeditadditions_core.register_command("msize", {
params = "", params = "",
description = "Return the length of each axis of current selection.", description = "Return the length of each axis of current selection.",
privs = { worldedit = true }, privs = { worldedit = true },

@ -5,7 +5,7 @@
-- ██ ██ ██ ██ ██ ██ ██████ -- ██ ██ ██ ██ ██ ██ ██████
local wea = worldeditadditions local wea = worldeditadditions
local v3 = worldeditadditions.Vector3 local v3 = worldeditadditions.Vector3
worldedit.register_command("mtrig", { worldeditadditions_core.register_command("mtrig", {
params = "", params = "",
description = "Return the length of and angles of an imginary line between pos1 and pos2 in the selection.", description = "Return the length of and angles of an imginary line between pos1 and pos2 in the selection.",
privs = { worldedit = true }, privs = { worldedit = true },

@ -3,9 +3,9 @@
-- ███████ ██ ██████ ███████ ██████ ██████ ██ ████ -- ███████ ██ ██████ ███████ ██████ ██████ ██ ████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ███████ ██ -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ███████ ██
local wea_c = worldeditadditions_core
worldeditadditions_core.register_command("airapply", {
worldedit.register_command("airapply", {
params = "<command_name> <args>", params = "<command_name> <args>",
description = "Executes the given command (automatically prepending '//'), but only on non-air nodes within the defined region.", description = "Executes the given command (automatically prepending '//'), but only on non-air nodes within the defined region.",
privs = { worldedit = true }, privs = { worldedit = true },
@ -20,7 +20,7 @@ worldedit.register_command("airapply", {
end end
-- Note that we search the worldedit commands here, not the minetest ones -- Note that we search the worldedit commands here, not the minetest ones
local cmd_we = worldedit.registered_commands[cmd_name] local cmd_we = wea_c.fetch_command_def(cmd_name)
if cmd_we == nil then if cmd_we == nil then
return false, "Error: "..cmd_name.." isn't a valid command." return false, "Error: "..cmd_name.." isn't a valid command."
end end

@ -3,8 +3,9 @@
-- █████ ██ ██ ██ ██████ ███████ █████ ███████ ██████ ██████ ██ ████ -- █████ ██ ██ ██ ██████ ███████ █████ ███████ ██████ ██████ ██ ████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ███████ ███████ ███████ ██ ██ ███████ ███████ ██ ██ ██ ██ ███████ ██ -- ███████ ███████ ███████ ██ ██ ███████ ███████ ██ ██ ██ ██ ███████ ██
local wea_c = worldeditadditions_core
worldedit.register_command("ellipsoidapply", { worldeditadditions_core.register_command("ellipsoidapply", {
params = "<command_name> <args>", params = "<command_name> <args>",
description = "Executes the given command (automatically prepending '//'), clipping the result with an ellipse given by the defined region.", description = "Executes the given command (automatically prepending '//'), clipping the result with an ellipse given by the defined region.",
privs = { worldedit = true }, privs = { worldedit = true },
@ -21,7 +22,7 @@ worldedit.register_command("ellipsoidapply", {
-- print("cmd_name", cmd_name, "args_text", args_text) -- print("cmd_name", cmd_name, "args_text", args_text)
-- Note that we search the worldedit commands here, not the minetest ones -- Note that we search the worldedit commands here, not the minetest ones
local cmd_we = worldedit.registered_commands[cmd_name] local cmd_we = wea_c.fetch_command_def(cmd_name)
if cmd_we == nil then if cmd_we == nil then
return false, "Error: "..cmd_name.." isn't a valid command." return false, "Error: "..cmd_name.." isn't a valid command."
end end

@ -45,7 +45,7 @@ local function step(params)
end end
end end
worldedit.register_command("for", { worldeditadditions_core.register_command("for", {
params = "<value1> <value2> <value3>... do //<command> <arg> %% <arg>", params = "<value1> <value2> <value3>... do //<command> <arg> %% <arg>",
description = "Executes a chat command for each value before \" do \" replacing any instances of \"%%\" with those values. The forward slashes at the beginning of the chat command must be the same as if you were executing it normally.", description = "Executes a chat command for each value before \" do \" replacing any instances of \"%%\" with those values. The forward slashes at the beginning of the chat command must be the same as if you were executing it normally.",
privs = { worldedit = true }, privs = { worldedit = true },

@ -45,7 +45,7 @@ local function step(params)
end end
end end
worldedit.register_command("macro", { worldeditadditions_core.register_command("macro", {
params = "<file> [<delay=0>]", params = "<file> [<delay=0>]",
description = "Load commands from \"(world folder)/macros/<file>[.weamac | .wmac]\" with position 1 of the current WorldEdit region as the origin.", description = "Load commands from \"(world folder)/macros/<file>[.weamac | .wmac]\" with position 1 of the current WorldEdit region as the origin.",
privs = {worldedit=true}, privs = {worldedit=true},

@ -3,9 +3,10 @@
-- ██ ██ ██ ██ ██ ██ ███████ █████ ███████ ██████ ██████ ██ ████ █████ ██ ██ -- ██ ██ ██ ██ ██ ██ ███████ █████ ███████ ██████ ██████ ██ ████ █████ ██ ██
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ████ ██████ ██ ███████ ███████ ██ ██ ██ ██ ███████ ██ ███████ ██████ -- ██ ████ ██████ ██ ███████ ███████ ██ ██ ██ ██ ███████ ██ ███████ ██████
local wea_c = worldeditadditions_core
worldedit.register_command("noiseapply2d", { worldeditadditions_core.register_command("noiseapply2d", {
params = "<threshold> <scale> <command_name> <args>", params = "<threshold> <scale> <command_name> <args>",
description = "Executes the given command (automatically prepending '//'), but uses a 2d noise function with both a threshold value (a number between 0 and 1) and a scale value (number, 1 = normal scale, for small areas 10+ is recommended) to filter where in the defined region it's applied.", description = "Executes the given command (automatically prepending '//'), but uses a 2d noise function with both a threshold value (a number between 0 and 1) and a scale value (number, 1 = normal scale, for small areas 10+ is recommended) to filter where in the defined region it's applied.",
privs = { worldedit = true }, privs = { worldedit = true },
@ -19,7 +20,7 @@ worldedit.register_command("noiseapply2d", {
end end
-- Note that we search the worldedit commands here, not the minetest ones -- Note that we search the worldedit commands here, not the minetest ones
local cmd_we = worldedit.registered_commands[cmd_name] local cmd_we = wea_c.fetch_command_def(cmd_name)
if cmd_we == nil then if cmd_we == nil then
return false, "Error: "..cmd_name.." isn't a valid command." return false, "Error: "..cmd_name.." isn't a valid command."
end end

@ -1,11 +1,12 @@
local wea = worldeditadditions local wea = worldeditadditions
local wea_c = worldeditadditions_core
-- Test command: -- Test command:
-- //multi //fp set1 1330 60 5455 //fp set2 1355 35 5430 //subdivide 10 10 10 fixlight //y -- //multi //fp set1 1330 60 5455 //fp set2 1355 35 5430 //subdivide 10 10 10 fixlight //y
local function will_trigger_saferegion(name, cmd_name, args) local function will_trigger_saferegion(name, cmd_name, args)
if not worldedit.registered_commands[cmd_name] then return nil, "Error: That worldedit command could not be found (perhaps it hasn't been upgraded to worldedit.register_command() yet?)" end local def = wea_c.fetch_command_def(cmd_name)
local def = worldedit.registered_commands[cmd_name] if not def then return nil, "Error: That worldedit command could not be found (perhaps it hasn't been upgraded to worldedit.register_command() yet?)" end
if not def.parse then return nil, "Error: No parse method found (this is a bug)." end if not def.parse then return nil, "Error: No parse method found (this is a bug)." end
local parsed = {def.parse(args)} local parsed = {def.parse(args)}
@ -29,7 +30,7 @@ local function emerge_stats_tostring(tbl_emerge)
return table.concat(result, ", ") return table.concat(result, ", ")
end end
worldedit.register_command("subdivide", { worldeditadditions_core.register_command("subdivide", {
params = "<size_x> <size_y> <size_z> <command> <params>", params = "<size_x> <size_y> <size_z> <command> <params>",
description = "Subdivides the given worldedit area into chunks and runs a worldedit command multiple times to cover the defined region. Note that the given command must NOT be prepended with any forward slashes - just like //cubeapply.", description = "Subdivides the given worldedit area into chunks and runs a worldedit command multiple times to cover the defined region. Note that the given command must NOT be prepended with any forward slashes - just like //cubeapply.",
privs = { worldedit = true }, privs = { worldedit = true },
@ -57,7 +58,7 @@ worldedit.register_command("subdivide", {
local cmd_name = parts[4] local cmd_name = parts[4]
if not worldedit.registered_commands[cmd_name] then if not wea_c.fetch_command_def(cmd_name) then
return false, "Error: The worldedit command '"..parts[4].."' does not exist (try /help)." return false, "Error: The worldedit command '"..parts[4].."' does not exist (try /help)."
end end
@ -73,7 +74,7 @@ worldedit.register_command("subdivide", {
local pos1, pos2 = worldedit.sort_pos(worldedit.pos1[name], worldedit.pos2[name]) local pos1, pos2 = worldedit.sort_pos(worldedit.pos1[name], worldedit.pos2[name])
local volume = worldedit.volume(pos1, pos2) local volume = worldedit.volume(pos1, pos2)
local cmd = worldedit.registered_commands[cmd_name] local cmd = wea_c.fetch_command_def(cmd_name)
-- Note that we don't need to check for //multi privileges, as it does it at runtime -- Note that we don't need to check for //multi privileges, as it does it at runtime
if not minetest.check_player_privs(name, cmd.privs) then if not minetest.check_player_privs(name, cmd.privs) then
return false, "Error: Your privileges are unsufficient to run '"..cmd_name.."'." return false, "Error: Your privileges are unsufficient to run '"..cmd_name.."'."

@ -0,0 +1,155 @@
local wea = worldeditadditions
local Vector3 = wea.Vector3
-- ██████ ██████ ███ ███ ███████
-- ██ ██ ██ ██ ████ ████ ██
-- ██ ██ ██ ██ ██ ████ ██ █████
-- ██ ██ ██ ██ ██ ██ ██ ██
-- ██████ ██████ ██ ██ ███████
worldeditadditions_core.register_command("metaball", {
params = "add <radius> | remove <index> | render <replace_node> [<threshold=1>] | list | clear | volume",
description = "Defines and creates metaballs. After using the add subcommand to define 1 or more metaballs (uses pos1), the render subcommand can then be used to create the metaballs as nodes.",
privs = { worldedit = true },
require_pos = 1,
parse = function(params_text)
if not params_text then params_text = "" end
local parts = wea.split_shell(params_text)
if #parts < 1 then
return false, "Error: Not enough arguments (see /help /dome for usage information)."
end
local subcommand = parts[1]
local subargs = {}
if subcommand == "delete" then subcommand = "remove" end
if subcommand == "deleteall" then subcommand = "clear" end
if subcommand == "append" then subcommand = "add" end
if subcommand == "list" then subcommand = "list" end
if subcommand == "make" then subcommand = "render" end
if subcommand == "generate" then subcommand = "render" end
if subcommand == "create" then subcommand = "render" end
if subcommand == "add" then
if #parts < 2 then
return false, "Error: Not enough arguments."
end
local radius = tonumber(parts[2])
if not radius then
return false, "Error: Invalid radius '"..parts[2].."'. The radius must be a positive integer."
end
if radius < 1 then
return false, "Error: The minimum radius size is 1, but you entered "..tostring(radius).."."
end
table.insert(subargs, radius)
elseif subcommand == "remove" then
if #parts < 2 then
return false, "Error: Not enough arguments."
end
local index = tonumber(parts[2])
if not index then
return false, "Error: Invalid index '"..parts[2].."'. The index to remove must be a positive integer."
end
if index < 1 then
return false, "Error: The minimum index size is 1, but you entered "..tostring(index).."."
end
table.insert(subargs, index)
elseif subcommand == "render" then
if #parts < 2 then
return false, "Error: Not enough arguments."
end
local replace_node = worldedit.normalize_nodename(parts[2])
local threshold = 1
if not replace_node then
return false, "Error: Invalid replace_node '"..parts[2].."'."
end
if #parts >= 3 then
threshold = tonumber(parts[3])
if not threshold then
return false, "Error: The threshold value must be a valid number (a floating-point number is ok)."
end
end
table.insert(subargs, replace_node)
table.insert(subargs, threshold)
elseif subcommand ~= "list" and subcommand ~= "clear" and subcommand ~= "volume" then
return false, "Error: Unknown subcommand '"..parts[1].."'."
end
return true, subcommand, subargs
end,
nodes_needed = function(name, subcommand)
if subcommand == "render" then
return wea.metaballs.volume(name)
else
return 0
end
end,
func = function(name, subcommand, subargs)
local start_time = wea.get_ms_time()
local message = ""
local append_time = false
if subcommand == "list" then
local success, list = wea.metaballs.list_pretty(name)
if not success then return success, list end
message = list
elseif subcommand == "volume" then
local success, metaballs_list = wea.metaballs.list(name)
if not success then return success, metaballs_list end
local success2, volume = wea.metaballs.volume(name)
if not success2 then return success2, volume end
message = #metaballs_list.." will take up to "..tostring(volume).." nodes of space"
elseif subcommand == "clear" then
local success, metaballs_cleared = wea.metaballs.clear(name)
if not success then return success, metaballs_cleared end
message = tostring(metaballs_cleared).." cleared"
elseif subcommand == "remove" then
local index = subargs[1]
local success, metaballs_count = wea.metaballs.remove(name, index)
if not success then return success, metaballs_count end
message = "metaball at index "..tostring(index).." removed - "..metaballs_count.." metaballs remain"
elseif subcommand == "add" then
local pos = Vector3.clone(worldedit.pos1[name])
local radius = subargs[1]
local success, metaballs_count = wea.metaballs.add(name, pos, radius)
if not success then return success, metaballs_count end
message = "added metaball at "..pos.." with radius "..tostring(radius).." - "..metaballs_count.." metaballs are now defined"
append_time = false
elseif subcommand == "render" then
local replace_node = subargs[1]
local threshold = subargs[2]
local success, metaballs = wea.metaballs.list(name)
if not success then return success, metaballs end
if #metaballs < 2 then
return false, "Error: At least 2 metaballs must be defined to render them."
end
local success2, nodes_replaced = wea.metaballs.render(metaballs, replace_node, threshold)
if not success2 then return success2, nodes_replaced end
message = nodes_replaced.." nodes replaced using "..tostring(#metaballs).." metaballs"
append_time = true
end
local time_taken = wea.get_ms_time() - start_time
if append_time then
message = message.." in "..wea.format.human_time(time_taken)
end
minetest.log("action", name.." used //metaballs "..subcommand.." in "..wea.format.human_time(time_taken))
return true, message
end
})

@ -24,7 +24,7 @@ end
-- ██ ████ ██ ██ ██ ██ ██ █████ -- ██ ████ ██ ██ ██ ██ ██ █████
-- ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ██████ ████ ███████ -- ██ ██ ██████ ████ ███████
worldedit.register_command("move+", { -- TODO: Make this an override worldeditadditions_core.register_command("move+", { -- TODO: Make this an override
params = "<axis:x|y|z|-x|-y|-z|?|front|back|left|right|up|down> <count> [<axis> <count> [...]]", params = "<axis:x|y|z|-x|-y|-z|?|front|back|left|right|up|down> <count> [<axis> <count> [...]]",
description = "Moves the defined region to another location - potentially across multiple axes at once.", description = "Moves the defined region to another location - potentially across multiple axes at once.",
privs = { worldedit = true }, privs = { worldedit = true },

@ -1,7 +1,7 @@
local wea = worldeditadditions local wea = worldeditadditions
worldedit.register_command("noise2d", { worldeditadditions_core.register_command("noise2d", {
params = "[<key_1> [<value_1>]] [<key_2> [<value_2>]] ...]", params = "[<key_1> [<value_1>]] [<key_2> [<value_2>]] ...]",
description = "Applies 2d random noise to the terrain as a 2d heightmap in the defined region. Optionally takes an arbitrary set of key - value pairs representing parameters that control the properties of the noise and how it's applied. See the full documentation for details of these parameters and what they do.", description = "Applies 2d random noise to the terrain as a 2d heightmap in the defined region. Optionally takes an arbitrary set of key - value pairs representing parameters that control the properties of the noise and how it's applied. See the full documentation for details of these parameters and what they do.",
privs = { worldedit = true }, privs = { worldedit = true },

@ -3,7 +3,7 @@
-- ██ ██ ██ ██ █████ ██████ ██ ███████ ████ -- ██ ██ ██ ██ █████ ██████ ██ ███████ ████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██████ ████ ███████ ██ ██ ███████ ██ ██ ██ -- ██████ ████ ███████ ██ ██ ███████ ██ ██ ██
worldedit.register_command("overlay", { worldeditadditions_core.register_command("overlay", {
params = "<replace_node_a> [<chance_a>] <replace_node_b> [<chance_b>] [<replace_node_N> [<chance_N>]] ...", params = "<replace_node_a> [<chance_a>] <replace_node_b> [<chance_b>] [<replace_node_N> [<chance_N>]] ...",
description = "Places <replace_node_a> in the last contiguous air space encountered above the first non-air node. In other words, overlays all top-most nodes in the specified area with <replace_node_a>. Optionally supports a mix of nodes and chances, as in //mix and //replacemix.", description = "Places <replace_node_a> in the last contiguous air space encountered above the first non-air node. In other words, overlays all top-most nodes in the specified area with <replace_node_a>. Optionally supports a mix of nodes and chances, as in //mix and //replacemix.",
privs = { worldedit = true }, privs = { worldedit = true },

@ -5,7 +5,7 @@ local wea = worldeditadditions
-- ██████ █████ ██████ ██ ███████ ██ █████ ██ ████ ██ ██ ███ -- ██████ █████ ██████ ██ ███████ ██ █████ ██ ████ ██ ██ ███
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ███████ ██ ███████ ██ ██ ██████ ███████ ██ ██ ██ ██ ██ -- ██ ██ ███████ ██ ███████ ██ ██ ██████ ███████ ██ ██ ██ ██ ██
worldedit.register_command("replacemix", { worldeditadditions_core.register_command("replacemix", {
params = "<target_node> [<chance>] <replace_node_a> [<chance_a>] [<replace_node_b> [<chance_b>]] [<replace_node_N> [<chance_N>]] ...", params = "<target_node> [<chance>] <replace_node_a> [<chance_a>] [<replace_node_b> [<chance_b>]] [<replace_node_N> [<chance_N>]] ...",
description = "Replaces target_node with a mix of other nodes. Functions simmilarly to //mix. <chance> is optional and the chance to replace the target node at all. replace_node_a is the node to replace target_node with. If multiple nodes are specified in a space separated list, then when replacing an instance of target_node one is randomly chosen from the list. Just like with //mix, if a positive integer is present after a replace_node, that adds a weighting to that particular node making it more common.", description = "Replaces target_node with a mix of other nodes. Functions simmilarly to //mix. <chance> is optional and the chance to replace the target node at all. replace_node_a is the node to replace target_node with. If multiple nodes are specified in a space separated list, then when replacing an instance of target_node one is randomly chosen from the list. Just like with //mix, if a positive integer is present after a replace_node, that adds a weighting to that particular node making it more common.",
privs = { worldedit = true }, privs = { worldedit = true },

@ -22,7 +22,7 @@ end
-- ███████ ██ ███████ ██ █████ -- ███████ ██ ███████ ██ █████
-- ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██
-- ███████ ██████ ██ ██ ███████ ███████ -- ███████ ██████ ██ ██ ███████ ███████
worldedit.register_command("scale", { worldeditadditions_core.register_command("scale", {
params = "<axis> <scale_factor> | <factor_x> [<factor_y> <factor_z> [<anchor_x> <anchor_y> <anchor_z>]]", params = "<axis> <scale_factor> | <factor_x> [<factor_y> <factor_z> [<anchor_x> <anchor_y> <anchor_z>]]",
description = "Combined scale up / down. Takes either an axis name + a scale factor (e.g. y 3 or -z 2; negative values swap the anchor point for the scale operation), or 3 scale factor values for x, y, and z respectively. In the latter mode, a set of anchors can also be specified, which indicate which size the scale operation should be anchored to.", description = "Combined scale up / down. Takes either an axis name + a scale factor (e.g. y 3 or -z 2; negative values swap the anchor point for the scale operation), or 3 scale factor values for x, y, and z respectively. In the latter mode, a set of anchors can also be specified, which indicate which size the scale operation should be anchored to.",
privs = { worldedit = true }, privs = { worldedit = true },

@ -7,7 +7,7 @@ local Vector3 = wea.Vector3
-- ███████ ██ ██ ██ ██ ██████ ██ -- ███████ ██ ██ ██ ██ ██████ ██
-- ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██
-- ███████ ██████ ██████ ███████ ██ ██ -- ███████ ██████ ██████ ███████ ██ ██
worldedit.register_command("sculpt", { worldeditadditions_core.register_command("sculpt", {
params = "[<brush_name=default> [<height=5> [<brush_size=10>]]]", params = "[<brush_name=default> [<height=5> [<brush_size=10>]]]",
description = "Applies a sculpting brush to the terrain with a given height. See //sculptlist to list all available brushes. Note that while the brush size is configurable, the actual brush size you end up with may be slightly different to that which you request due to brush size restrictions.", description = "Applies a sculpting brush to the terrain with a given height. See //sculptlist to list all available brushes. Note that while the brush size is configurable, the actual brush size you end up with may be slightly different to that which you request due to brush size restrictions.",
privs = { worldedit = true }, privs = { worldedit = true },

@ -4,7 +4,7 @@
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ███████ ██████ ███████ ██ ████ ██ ███████ ██ ██ -- ███████ ██████ ███████ ██ ████ ██ ███████ ██ ██
local wea = worldeditadditions local wea = worldeditadditions
worldedit.register_command("scentre", { worldeditadditions_core.register_command("scentre", {
params = "", params = "",
description = "Set WorldEdit region positions 1 and 2 to the centre of the current selection.", description = "Set WorldEdit region positions 1 and 2 to the centre of the current selection.",
privs = {worldedit=true}, privs = {worldedit=true},

@ -14,7 +14,7 @@ minetest.register_on_punchnode(function(pos, node, puncher)
else wea.add_pos[name] = nil end else wea.add_pos[name] = nil end
end end
end) end)
worldedit.register_command("scloud", { worldeditadditions_core.register_command("scloud", {
params = "<0-6|stop|reset>", params = "<0-6|stop|reset>",
description = "Set and add to WorldEdit region by punching up to six nodes that define the maximums of your target", description = "Set and add to WorldEdit region by punching up to six nodes that define the maximums of your target",
privs = {worldedit=true}, privs = {worldedit=true},

@ -4,7 +4,7 @@
-- ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██
-- ███████ ██████ ██████ ███████ -- ███████ ██████ ██████ ███████
local wea = worldeditadditions local wea = worldeditadditions
worldedit.register_command("scol", { worldeditadditions_core.register_command("scol", {
params = "[<axis1>] <length>", params = "[<axis1>] <length>",
description = "Set WorldEdit region position 2 at a set distance along 1 axis.", description = "Set WorldEdit region position 2 at a set distance along 1 axis.",
privs = {worldedit=true}, privs = {worldedit=true},

@ -4,7 +4,7 @@
-- ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██
-- ███████ ██████ ██████ ██████ ███████ -- ███████ ██████ ██████ ██████ ███████
local wea = worldeditadditions local wea = worldeditadditions
worldedit.register_command("scube", { worldeditadditions_core.register_command("scube", {
params = "[<axis1> [<axis2> [<axis3>]]] <length>", params = "[<axis1> [<axis2> [<axis3>]]] <length>",
description = "Set WorldEdit region position 2 at a set distance along 3 axes.", description = "Set WorldEdit region position 2 at a set distance along 3 axes.",
privs = { worldedit = true }, privs = { worldedit = true },

@ -4,7 +4,7 @@
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ███████ ██ ██ ██ ██████ ██ ██████ ██ ██ -- ███████ ██ ██ ██ ██████ ██ ██████ ██ ██
local wea = worldeditadditions local wea = worldeditadditions
worldedit.register_command("sfactor", { worldeditadditions_core.register_command("sfactor", {
params = "<mode> <factor> [<target=xz>]", params = "<mode> <factor> [<target=xz>]",
description = "Make the length of one or more target axes of the current selection to be multiple(s) of <factor>.", description = "Make the length of one or more target axes of the current selection to be multiple(s) of <factor>.",
privs = { worldedit = true }, privs = { worldedit = true },

@ -4,7 +4,7 @@
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ███████ ██ ██ ██ ██ ██ ██ ███████ -- ███████ ██ ██ ██ ██ ██ ██ ███████
local wea = worldeditadditions local wea = worldeditadditions
worldedit.register_command("smake", { worldeditadditions_core.register_command("smake", {
params = "<operation:odd|even|equal> <mode:grow|shrink|average> [<target=xyz> [<base>]]", params = "<operation:odd|even|equal> <mode:grow|shrink|average> [<target=xyz> [<base>]]",
description = "Make one or more axes of the current selection odd, even, or equal to another.", description = "Make one or more axes of the current selection odd, even, or equal to another.",
privs = { worldedit = true }, privs = { worldedit = true },

@ -3,7 +3,7 @@
-- ███████ ██████ ██ ██ ██████ -- ███████ ██████ ██ ██ ██████
-- ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██
-- ███████ ██ ██████ ██ -- ███████ ██ ██████ ██
worldedit.register_command("spop", { worldeditadditions_core.register_command("spop", {
params = "", params = "",
description = "Pops a region off your (per-user) selection stack.", description = "Pops a region off your (per-user) selection stack.",
privs = { worldedit = true }, privs = { worldedit = true },

@ -3,7 +3,7 @@
-- ███████ ██████ ██ ██ ███████ ███████ -- ███████ ██████ ██ ██ ███████ ███████
-- ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██
-- ███████ ██ ██████ ███████ ██ ██ -- ███████ ██ ██████ ███████ ██ ██
worldedit.register_command("spush", { worldeditadditions_core.register_command("spush", {
params = "", params = "",
description = "Pushes the currently defined region onto your (per-user) selection stack.", description = "Pushes the currently defined region onto your (per-user) selection stack.",
privs = { worldedit = true }, privs = { worldedit = true },

@ -4,7 +4,7 @@
-- ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██
-- ███████ ██ ██ ███████ ██████ ██ -- ███████ ██ ██ ███████ ██████ ██
local wea = worldeditadditions local wea = worldeditadditions
worldedit.register_command("srect", { worldeditadditions_core.register_command("srect", {
params = "[<axis1> [<axis2>]] <length>", params = "[<axis1> [<axis2>]] <length>",
description = "Set WorldEdit region position 2 at a set distance along 2 axes.", description = "Set WorldEdit region position 2 at a set distance along 2 axes.",
privs = { worldedit = true }, privs = { worldedit = true },

@ -21,7 +21,7 @@ local function parse_with_name(name,args)
until not args:find("([%l%s+-]+%d+)%s*", i) until not args:find("([%l%s+-]+%d+)%s*", i)
return true, vec return true, vec
end end
worldedit.register_command("srel", { worldeditadditions_core.register_command("srel", {
params = "<axis1> <length1> [<axis2> <length2> [<axis3> <length3>]]", params = "<axis1> <length1> [<axis2> <length2> [<axis3> <length3>]]",
description = "Set WorldEdit region position 2 at set distances along 3 axes.", description = "Set WorldEdit region position 2 at set distances along 3 axes.",
privs = { worldedit = true }, privs = { worldedit = true },

@ -22,7 +22,7 @@ local function parse_with_name(name,args)
until not args:find("([%l%s+-]+%d+)%s*", i) until not args:find("([%l%s+-]+%d+)%s*", i)
return true, vec return true, vec
end end
worldedit.register_command("sshift", { worldeditadditions_core.register_command("sshift", {
params = "<axis1> <distance1> [<axis2> <distance2> [<axis3> <distance3>]]", params = "<axis1> <distance1> [<axis2> <distance2> [<axis3> <distance3>]]",
description = "Shift the WorldEdit region in 3 dimensions.", description = "Shift the WorldEdit region in 3 dimensions.",
privs = { worldedit = true }, privs = { worldedit = true },

@ -3,7 +3,7 @@
-- ███████ ███████ ██ ███████ ██ █████ -- ███████ ███████ ██ ███████ ██ █████
-- ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██
-- ███████ ███████ ██ ██ ██ ██████ ██ ██ -- ███████ ███████ ██ ██ ██ ██████ ██ ██
worldedit.register_command("sstack", { worldeditadditions_core.register_command("sstack", {
params = "", params = "",
description = "Displays the contents of your (per-user) selection stack.", description = "Displays the contents of your (per-user) selection stack.",
privs = { worldedit = true }, privs = { worldedit = true },

@ -2,7 +2,7 @@
local wea = worldeditadditions local wea = worldeditadditions
local Vector3 = wea.Vector3 local Vector3 = wea.Vector3
worldedit.register_command("spiral2", { worldeditadditions_core.register_command("spiral2", {
params = "[<circle|square>] [<replace_node=dirt> [<interval=3> [<acceleration=0>] ] ]", params = "[<circle|square>] [<replace_node=dirt> [<interval=3> [<acceleration=0>] ] ]",
description = "Generates a spiral that fills the defined region using the specified replace node. The spiral is either square (default) or circular in shape. The interval specifies the distance between the walls of the spiral, and the acceleration specifies how quickly this value should increase.", description = "Generates a spiral that fills the defined region using the specified replace node. The spiral is either square (default) or circular in shape. The interval specifies the distance between the walls of the spiral, and the acceleration specifies how quickly this value should increase.",
privs = { worldedit = true }, privs = { worldedit = true },

@ -52,7 +52,7 @@ local function parse_params_torus(params_text)
return true, replace_node, major_radius, minor_radius, axes, hollow return true, replace_node, major_radius, minor_radius, axes, hollow
end end
worldedit.register_command("torus", { worldeditadditions_core.register_command("torus", {
params = "<major_radius> <minor_radius> <replace_node> [<axes=xy> [h[ollow]]]", params = "<major_radius> <minor_radius> <replace_node> [<axes=xy> [h[ollow]]]",
description = "Creates a 3D torus with a major radius of <major_radius> and a minor radius of <minor_radius> at pos1, filled with <replace_node>, on axes <axes> (i.e. 2 axis names: xz, zy, etc).", description = "Creates a 3D torus with a major radius of <major_radius> and a minor radius of <minor_radius> at pos1, filled with <replace_node>, on axes <axes> (i.e. 2 axis names: xz, zy, etc).",
privs = { worldedit = true }, privs = { worldedit = true },
@ -81,7 +81,7 @@ worldedit.register_command("torus", {
}) })
-- TODO: This duplicates a lot of code. Perhaps we can trim it down a bit? -- TODO: This duplicates a lot of code. Perhaps we can trim it down a bit?
worldedit.register_command("hollowtorus", { worldeditadditions_core.register_command("hollowtorus", {
params = "<major_radius> <minor_radius> <replace_node> [<axes=xy>]", params = "<major_radius> <minor_radius> <replace_node> [<axes=xy>]",
description = "Creates a 3D hollow torus with a major radius of <major_radius> and a minor radius of <minor_radius> at pos1, made out of <replace_node>, on axes <axes> (i.e. 2 axis names: xz, zy, etc).", description = "Creates a 3D hollow torus with a major radius of <major_radius> and a minor radius of <minor_radius> at pos1, made out of <replace_node>, on axes <axes> (i.e. 2 axis names: xz, zy, etc).",
privs = { worldedit = true }, privs = { worldedit = true },

@ -3,7 +3,7 @@
-- ██ █ ██ ███████ ██ ██ ███████ -- ██ █ ██ ███████ ██ ██ ███████
-- ██ ███ ██ ██ ██ ██ ██ ██ -- ██ ███ ██ ██ ██ ██ ██ ██
-- ███ ███ ██ ██ ███████ ███████ ███████ -- ███ ███ ██ ██ ███████ ███████ ███████
worldedit.register_command("walls", { worldeditadditions_core.register_command("walls", {
params = "[<replace_node=dirt> [<thickness=1>]]", params = "[<replace_node=dirt> [<thickness=1>]]",
description = "Creates vertical walls of <replace_node> around the inside edges of the defined region. Optionally specifies a thickness for the walls to be created (defaults to 1)", description = "Creates vertical walls of <replace_node> around the inside edges of the defined region. Optionally specifies a thickness for the walls to be created (defaults to 1)",
privs = { worldedit = true }, privs = { worldedit = true },

@ -5,7 +5,7 @@
-- ███ ███ ██████ ██████ ██ ██ -- ███ ███ ██████ ██████ ██ ██
local wea = worldeditadditions local wea = worldeditadditions
local v3 = worldeditadditions.Vector3 local v3 = worldeditadditions.Vector3
worldedit.register_command("wbox", { worldeditadditions_core.register_command("wbox", {
params = "<replace_node>", params = "<replace_node>",
description = "Sets the edges of the current selection to <replace_node>", description = "Sets the edges of the current selection to <replace_node>",
privs = {worldedit=true}, privs = {worldedit=true},

@ -4,7 +4,7 @@
-- ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ███ ███ ██████ ██████ ██ ██ ██ ██ ██ ███████ ███████ -- ███ ███ ██████ ██████ ██ ██ ██ ██ ██ ███████ ███████
local wea = worldeditadditions local wea = worldeditadditions
worldedit.register_command("wcompass", { worldeditadditions_core.register_command("wcompass", {
params = "<replace_node> [<bead_node>]", params = "<replace_node> [<bead_node>]",
description = "Creates a compass around pos1 with a single node bead pointing north (+Z).", description = "Creates a compass around pos1 with a single node bead pointing north (+Z).",
privs = {worldedit=true}, privs = {worldedit=true},

@ -4,7 +4,7 @@
-- ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ███ ███ ██████ ██████ ██ ██ ██ ████ ███████ ██ ██ -- ███ ███ ██████ ██████ ██ ██ ██ ████ ███████ ██ ██
local wea = worldeditadditions local wea = worldeditadditions
worldedit.register_command("wcorner", { worldeditadditions_core.register_command("wcorner", {
params = "<replace_node>", params = "<replace_node>",
description = "Set the corners of the current selection to <replace_node>", description = "Set the corners of the current selection to <replace_node>",
privs = {worldedit=true}, privs = {worldedit=true},

@ -11,7 +11,7 @@
-- The strategy here is not to have duplicate content, but to pull data from -- The strategy here is not to have duplicate content, but to pull data from
-- existing sources. -- existing sources.
-- Long-form article descriptions: Chat-Command-Reference.md -- Long-form article descriptions: Chat-Command-Reference.md
-- Short descriptions: Undecided, but maybe worldedit.registered_commands -- Short descriptions: Undecided, but maybe from the registered command definition?
worldeditadditions.doc = {} worldeditadditions.doc = {}

@ -39,7 +39,7 @@ dofile(we_c.modpath.."/commands/spiral2.lua")
dofile(we_c.modpath.."/commands/copy.lua") dofile(we_c.modpath.."/commands/copy.lua")
dofile(we_c.modpath.."/commands/move.lua") dofile(we_c.modpath.."/commands/move.lua")
dofile(we_c.modpath.."/commands/dome.lua") dofile(we_c.modpath.."/commands/dome.lua")
dofile(we_c.modpath.."/commands/metaball.lua")
dofile(we_c.modpath.."/commands/count.lua") dofile(we_c.modpath.."/commands/count.lua")
dofile(we_c.modpath.."/commands/sculpt.lua") dofile(we_c.modpath.."/commands/sculpt.lua")
@ -80,24 +80,4 @@ end
-- end -- end
worldedit.alias_command("smoothadv", "convolve") dofile(we_c.modpath.."/aliases.lua")
worldedit.alias_command("conv", "convolve")
worldedit.alias_command("naturalise", "layers")
worldedit.alias_command("naturalize", "layers")
worldedit.alias_command("flora", "bonemeal")
-- Measure Tools
worldedit.alias_command("mcount", "count")
worldedit.alias_command("mfacing", "mface")
--- Overrides to core WorldEdit commands
-- These are commented out for now, as they could be potentially dangerous to stability
-- Thorough testing is required of our replacement commands before these are uncommented
-- TODO: Depend on worldeditadditions_core before uncommenting this
-- BUG: //move+ seems to be leaving stuff behind for some strange reason --@sbrl 2021-12-26
-- worldeditadditions_core.alias_override("copy", "copy+")
-- worldeditadditions_core.alias_override("move", "move+") -- MAY have issues where it doesn't overwrite the old region properly, but haven't been able to reliably reproduce this
-- worldeditadditions_core.alias_override("replace", "replacemix")

@ -1,4 +1,4 @@
name = worldeditadditions_commands name = worldeditadditions_commands
description = worldeditadditions: chat command interfaces description = worldeditadditions: chat command interfaces
depends = worldeditadditions, worldedit_commands, worldedit_shortcommands, worldedit depends = worldeditadditions, worldeditadditions_core, worldedit_commands, worldedit_shortcommands, worldedit
optional_depends = worldeditdebug, bonemeal optional_depends = worldeditdebug, bonemeal

@ -1,3 +1,5 @@
# worldeditadditions_core # worldeditadditions_core
This mod's purpose is to provide a solid base upon which the rest of WorldEditAdditions can function. Once it is complete, we will be able to mark our dependency on `worldedit` itself optional. To get to that point though will still require a significant effort in implementing enhanced versions of all existing WorldEdit commands. If you've got some free time and a great idea for a command, please do open a pull request! :D This mod's purpose is to provide a solid base upon which the rest of WorldEditAdditions can function. Once it is complete, we will be able to mark our dependency on `worldedit` itself optional. To get to that point though will still require a significant effort in implementing enhanced versions of all existing WorldEdit commands. If you've got some free time and a great idea for a command, please do open a pull request! :D
In short, `worldeditadditions_core` is a reimplementation of a number of underlying parts of the worldedit engine. Parts of `worldeditadditions_core` are likely to look very similar to parts of WorldEdit - this is because inspiration was taken from Uberi/WorldEdit when implementing `worldeditadditions_core`.

@ -0,0 +1,20 @@
--- Fetches the definition of a WorldEditAdditions or WorldEdit command
-- Does not support fetching generic Minetest commands - check
-- minetest.chatcommands for this.
-- @param cmdname string The name of the command to fetch the definition for.
local function fetch_command_def(cmdname)
local wea_c = worldeditadditions_core
if wea_c.registered_commands[cmdname] then
return wea_c.registered_commands[cmdname]
end
if minetest.global_exists("worldedit") and worldedit.registered_commands and worldedit.registered_commands[cmdname] then
return worldedit.registered_commands[cmdname]
end
return nil
end
return fetch_command_def

@ -0,0 +1,8 @@
local wea_c = worldeditadditions_core
--- WorldEdit shim just in case WorldEdit doesn't exist.
-- Eventually this will go away.
worldedit = {
-- Note that you want worldeditadditions_core.registered_commands, and not worldedit.registered_commands! This table is not guaranteed to contain all command definitions in the future, whereas worldedit command definitions are guaranteed to be imported into this worldeditadditions_core.registered_commands.
registered_commands = { }
}

@ -0,0 +1,15 @@
local wea_c = worldeditadditions_core
for name,definition in pairs(worldedit.registered_commands) do
-- This check should not be needed since worldeditadditions_commands
-- depends on this mod (so it will overwrite any worldedit definitions,
-- since worldedit is loaded first), but it's here just in case
if not wea_c.registered_commands[name] then
-- The Minetest chat command should already be imported here, so we
-- just need to import worldedit chat command definition here
wea_c.registered_commands[name] = definition
else
minetest.log("info", "Skipping registration of worldedit command "..name..", as it's already a registered worldeditadditions command")
end
end

@ -0,0 +1,17 @@
-- TODO: This is duplicated from worldeditadditions/utils. We should consider moving worldeditadditions/utils to worldeditadditions_core. Perhaps also we could move the definition of the worldeditadditions namespace to worldeditadditions_core too?
--- Formats (usually large) numbers as human-readable strings.
-- Ported from PHP: https://github.com/sbrl/Pepperminty-Wiki/blob/0a81c940c5803856db250b29f54658476bc81e21/core/05-functions.php#L67
-- @param n number The number to format.
-- @param decimals number The number of decimal places to show.
-- @return string A formatted string that represents the given input number.
local function human_size(n, decimals)
local sizes = { "", "K", "M", "G", "T", "P", "E", "Y", "Z" }
local factor = math.floor((#tostring(n) - 1) / 3)
local multiplier = 10^(decimals or 0)
local result = math.floor(0.5 + (n / math.pow(1000, factor)) * multiplier) / multiplier
return result .. sizes[factor+1]
end
return human_size

@ -0,0 +1,40 @@
local wea_c = worldeditadditions_core
local function register_alias(cmdname_target, cmdname_source, override)
if override == nil then override = false end
local def_source = wea_c.fetch_command_def(cmdname_source)
if not def_source then
minetest.log("error", "worldeditadditions_core: Failed to register alias for "..cmdname_source..""..cmdname_target..", as the source command doesn't exist.")
return false
end
if wea_c.fetch_command_def(cmdname_target) and not override then
minetest.log("error", "worldeditadditions_core: Failed to register alias for "..cmdname_source..""..cmdname_target..", as the target command exists and override wasn't set to true.")
return false
end
print("DEBUG ALIAS source "..cmdname_source.." target "..cmdname_target)
if minetest.chatcommands["/"..cmdname_target] then
minetest.override_chatcommand(
"/"..cmdname_target,
minetest.chatcommands["/"..cmdname_source]
)
else
minetest.register_chatcommand(
"/"..cmdname_target,
minetest.chatcommands["/"..cmdname_source]
)
end
wea_c.registered_commands[cmdname_target] = wea_c.registered_commands[cmdname_source]
if minetest.global_exists("worldedit") then
worldedit.registered_commands[cmdname_target] = worldedit.registered_commands[cmdname_source]
end
end
return register_alias

@ -0,0 +1,66 @@
-- ██████ ███████ ██████ ██ ███████ ████████ ███████ ██████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██████ █████ ██ ███ ██ ███████ ██ █████ ██████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ███████ ██████ ██ ███████ ██ ███████ ██ ██
-- WorldEditAdditions chat command registration
local wea_c = worldeditadditions_core
local run_command = dofile(wea_c.modpath.."/core/run_command.lua")
local function log_error(cmdname, error_message)
minetest.log("error", "register_command("..cmdname..") error: "..error_message)
end
local function register_command(cmdname, options)
---
-- 1: Validation
---
if type(options.params) ~= "string" then
log_error(cmdname, "The params option is not a string.")
return false
end
if type(options.description) ~= "string" then
log_error(cmdname, "The description option is not a string.")
return false
end
if type(options.parse) ~= "function" then
log_error(cmdname, "The parse option is not a function.")
return false
end
if type(options.func) ~= "function" then
log_error(cmdname, "The func option is not a function.")
return false
end
if wea_c.registered_commands[cmdname] and options.override ~= true then
log_error(cmdname, "A WorldEditAdditions command with that name is registered, but the option override is not set to true.")
return false
end
---
-- 2: Normalisation
---
if not options.privs then options.privs = {} end
if not options.require_pos then options.require_pos = 0 end
if not options.nodes_needed then options.nodes_needed = function() return 0 end end
---
-- 3: Registration
---
minetest.register_chatcommand("/"..cmdname, {
params = options.params,
description = options.description,
privs = options.privs,
func = function(player_name, paramtext)
run_command(cmdname, options, player_name, paramtext)
end
})
wea_c.registered_commands[cmdname] = options
if minetest.global_exists("worldedit") then
worldedit.registered_commands[cmdname] = options
end
end
return register_command

@ -0,0 +1,55 @@
-- WARNING: safe_region MUST NOT be imported more than once, as it defines chat commands. If you want to import it again elsewhere, check first that multiple dofile() calls don't execute a file more than once.
local wea_c = worldeditadditions_core
local safe_region = dofile(wea_c.modpath.."/core/safe_region.lua")
local human_size = dofile(wea_c.modpath.."/core/lib/human_size.lua")
-- TODO: Reimplement worldedit.player_notify(player_name, msg_text)
local function run_command_stage2(player_name, func, parse_result)
local success, result_message = func(player_name, unpack(parse_result))
if result_message then
-- TODO: If we were unsuccessfull, then colour the message red
worldedit.player_notify(player_name, result_message)
end
end
local function run_command(cmdname, options, player_name, paramtext)
if options.require_pos > 0 and not worldedit.pos1[player_name] then
worldedit.player_notify(player_name, "Error: pos1 must be selected to use this command.")
return false
end
if options.require_pos > 1 and not worldedit.pos2[player_name] then
worldedit.player_notify(player_name, "Error: Both pos1 and pos2 must be selected (together making a region) to use this command.")
return false
end
local parse_result = { options.parse(paramtext) }
local success = table.remove(parse_result, 1)
if not success then
worldedit.player_notify(player_name, parse_result[1] or "Invalid usage (no further error message was provided by the command. This is probably a bug.)")
return false
end
if options.nodes_needed then
local potential_changes = options.nodes_needed(player_name, unpack(parse_result))
local limit = wea_c.safe_region_limit_default
if wea_c.safe_region_limits[player_name] then
limit = wea_c.safe_region_limits[player_name]
end
if potential_changes > limit then
worldedit.player_notify(player_name, "/"..cmdname.." "..paramtext.."' may affect up to "..human_size(potential_changes).." nodes. Type //y to continue, or //n to cancel (see the //saferegion command to control when this message appears).")
safe_region(player_name, cmdname, function()
run_command_stage2(player_name, options.func, parse_result)
end)
else
run_command_stage2(player_name, options.func, parse_result)
end
else
run_command_stage2(player_name, options.func, parse_result)
end
end
return run_command

@ -0,0 +1,65 @@
-- ███████ █████ ███████ ███████ ██████ ███████ ██████ ██ ██████ ███ ██
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██
-- ███████ ███████ █████ █████ ██████ █████ ██ ███ ██ ██ ██ ██ ██ ██
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ███████ ██ ██ ██ ███████ ██ ██ ███████ ██████ ██ ██████ ██ ████
local worldedit_command_y, worldedit_command_n
if minetest.global_exists("worldedit") then
worldedit_command_y = minetest.chatcommands["/y"].func
worldedit_command_n = minetest.chatcommands["/n"].func
end
--- A table that holds at most 1 pending function call per player.
local pending_calls = {}
--- Captures the given function in the safe_region subsystem for later execution.
-- @param player_name string The name of the player.
-- @param cmdname string The name of the command being executed.
-- @param func function The function to execute later. Will be passed NO ARGUMENTS should it ever get executed in the future (this is not guaranteed).
-- @returns nil
local function safe_region(player_name, cmdname, func)
pending_calls[player_name] = {
cmdname = cmdname,
func = func
}
end
minetest.override_chatcommand("/y", {
params = "",
description = "Run a pending operation that was captured by the safe region system earlier.",
func = function(player_name)
if pending_calls[player_name] == nil then
if minetest.global_exists("worldedit") and worldedit_command_y ~= nil then
worldedit_command_y(player_name)
else
worldedit.player_notify(player_name, "There aren't any pending operations at the moment.")
end
else
pending_calls[player_name].func()
pending_calls[player_name] = nil
end
end
})
minetest.override_chatcommand("/n", {
params = "",
description = "Abort a pending operation that was captured by the safe region system.",
func = function(player_name)
if pending_calls[player_name] == nil then
if minetest.global_exists("worldedit") and worldedit_command_y ~= nil then
worldedit_command_n(player_name)
else
worldedit.player_notify(player_name, "There aren't any operations pending, so there's nothing to abort.")
end
else
worldedit.player_notify(player_name, "Aborting captured command /"..pending_calls[player_name].cmdname..".")
pending_calls[player_name] = nil
end
end
})
return safe_region

@ -5,14 +5,32 @@
-- @license Mozilla Public License, 2.0 -- @license Mozilla Public License, 2.0
-- @author Starbeamrainbowlabs and VorTechnix -- @author Starbeamrainbowlabs and VorTechnix
worldeditadditions_core = {} -- local temp = true
local we_c = worldeditadditions_core -- if temp then return end
-- This mod isn't finished yet, so it will not be executed for now.
local modpath = minetest.get_modpath("worldeditadditions_core")
worldeditadditions_core = {
modpath = modpath,
registered_commands = {},
-- Storage for per-player node limits before safe_region kicks in.
-- TODO: Persist these to disk.
safe_region_limits = {},
-- The default limit for new players on the number of potential nodes changed before safe_region kicks in.
safe_region_limit_default = 100000,
}
local wea_c = worldeditadditions_core
wea_c.register_command = dofile(modpath.."/core/register_command.lua")
wea_c.fetch_command_def = dofile(modpath.."/core/fetch_command_def.lua")
wea_c.register_alias = dofile(modpath.."/core/register_alias.lua")
we_c.modpath = minetest.get_modpath("worldeditadditions_core")
-- Initialise WorldEdit stuff if the WorldEdit mod is not present -- Initialise WorldEdit stuff if the WorldEdit mod is not present
if not minetest.get_modpath("worldedit") then if minetest.global_exists("worldedit") then
dofile(we_c.modpath.."/worldedit/init.lua") dofile(wea_c.modpath.."/core/integrations/worldedit.lua")
else
dofile(wea_c.modpath.."/core/integrations/noworldedit.lua")
end end
dofile(we_c.modpath.."/register/init.lua")

@ -1,3 +1,3 @@
name = worldeditadditions_core name = worldeditadditions_core
description = worldeditadditions: core components description = worldeditadditions: core components
optional_depends = worldedit optional_depends = worldedit, worldedit_commands, worldedit_shortcommands

@ -1,26 +0,0 @@
function worldeditadditions_core.check_command(name, def)
if not (name and #name > 0) then
return false, "Error: No command name."
end
if not def.privs then
return false, "Error: privs is nill. Expected table."
end
def.require_pos = def.require_pos or 0
if not (def.require_pos >= 0 and def.require_pos < 3) then
return false, "Error: require_pos must be greater than -1 and less than 3."
end
if not def.parse then
if def.params == "" then
def.parse = function(params_text) return true end
else
return false, "Error: parse function is invalid."
end
end
if not (def.nodes_needed == nil or type(def.nodes_needed) == "function") then
return false, "Error: nodes_needed must be nil or function."
end
if not def.func then
return false, "Error: main function is invalid."
end
return true
end

@ -1,40 +0,0 @@
function worldeditadditions_core.chatcommand_handler(cmd_name, name, param)
local def = assert(worldedit.registered_commands[cmd_name], "Error: Failed to locate worldedit command definition for command '"..name.."' (this is probably a bug).")
if def.require_pos == 2 then
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if pos1 == nil or pos2 == nil then
worldedit.player_notify(name, "no region selected")
return
end
elseif def.require_pos == 1 then
local pos1 = worldedit.pos1[name]
if pos1 == nil then
worldedit.player_notify(name, "no position 1 selected")
return
end
end
local parsed = {def.parse(param)}
local success = table.remove(parsed, 1)
if not success then
worldedit.player_notify(name, parsed[1] or "invalid usage")
return
end
if def.nodes_needed then
local count = def.nodes_needed(name, unpack(parsed))
safe_region(name, count, function()
local success, msg = def.func(name, unpack(parsed))
if msg then
minetest.chat_send_player(name, msg)
end
end)
else
-- no "safe region" check
local success, msg = def.func(name, unpack(parsed))
if msg then
minetest.chat_send_player(name, msg)
end
end
end

@ -1,14 +0,0 @@
-- ██████ ███████ ██████ ██ ███████ ████████ ███████ ██████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██████ █████ ██ ███ ██ ███████ ██ █████ ██████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ███████ ██████ ██ ███████ ██ ███████ ██ ██
-- WorldEditAdditions Register/Overwrite Functions
local we_cm = worldeditadditions_core.modpath .. "/register/"
dofile(we_cm.."check.lua")
dofile(we_cm.."handler.lua")
dofile(we_cm.."register.lua")
dofile(we_cm.."override.lua")

@ -1,35 +0,0 @@
local we_c = worldeditadditions_core
function we_c.override_command(name, def)
local def = table.copy(def)
local success, err = we_c.check_command(name, def)
if not success then
error(err)
return false
end
minetest.override_chatcommand("/" .. name, {
privs = def.privs,
params = def.params,
description = def.description,
func = function(player_name, param)
return we_c.chatcommand_handler(name, player_name, param)
end,
})
worldedit.registered_commands[name] = def
end
function we_c.alias_override(alias, original)
if not worldedit.registered_commands[original] then
minetest.log("error", "worldedit_shortcommands: original " .. original .. " does not exist")
return
end
if minetest.chatcommands["/" .. alias] then
minetest.override_chatcommand("/" .. alias, minetest.chatcommands["/" .. original])
worldedit.registered_commands[alias] = worldedit.registered_commands[original]
else
minetest.register_chatcommand("/" .. alias, minetest.chatcommands["/" .. original])
worldedit.registered_commands[alias] = worldedit.registered_commands[original]
end
end

@ -1,32 +0,0 @@
local we_c = worldeditadditions_core
function we_c.register_command(name, def)
local def = table.copy(def)
local success, err = we_c.check_command(name, def)
if not success then
return false, err
end
minetest.register_chatcommand("/" .. name, {
privs = def.privs,
params = def.params,
description = def.description,
func = function(player_name, param)
return we_c.chatcommand_handler(name, player_name, param)
end,
})
worldedit.registered_commands[name] = def
end
function we_c.alias_command(alias, original)
if not worldedit.registered_commands[original] then
minetest.log("error", "worldedit_shortcommands: original " .. original .. " does not exist")
return
end
if minetest.chatcommands["/" .. alias] then
minetest.log("error", "worldedit_shortcommands: alias " .. alias .. " already exists")
return
end
minetest.register_chatcommand("/" .. alias, minetest.chatcommands["/" .. original])
worldedit.registered_commands[alias] = worldedit.registered_commands[original]
end

@ -1,5 +0,0 @@
--- WorldEdit shim just in case WorldEdit doesn't exist
worldedit = {
registered_commands = { }
}