Merge pull request #71 from sbrl/VorTechnix

Merge Vortechnix's branch
This commit is contained in:
Starbeamrainbowlabs 2021-11-07 16:10:15 +00:00 committed by GitHub
commit f569358229
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 960 additions and 158 deletions

@ -7,6 +7,9 @@ Note to self: See the bottom of this file for the release template text.
- Add `//sfactor` (_selection factor_) - Selection Tools by @VorTechnix are finished for now.
- Add `//mface` (_measure facing_), `//midpos` (_measure middle position_), `//msize` (_measure size_), `//mtrig` (_measure trigonometry_) - Measuring Tools implemented by @VorTechnix.
- Add `//airapply` for applying commands only to air nodes in the defined region
- Add `//wcorner` (_wireframe corners_), `//wbox` (_wireframe box_), `//compass` (_wireframe compass_) - Wireframes implemented by @VorTechnix.
- Add `//for` for executing commands while changing their arguments - Implemented by @VorTechnix.
- Add `//sshift` (_selection shift_) - WorldEdit cuboid manipulator replacements implemented by @VorTechnix.
- Add `//noise2d` for perturbing terrain with multiple different noise functions
- Add `//noiseapply2d` for running commands on columns where a noise value is over a threshold
- Add `//ellipsoid2` which creates an ellipsoid that fills the defined region

@ -193,7 +193,32 @@ Floods all connected nodes of the same type starting at _pos1_ with `<replace_no
```
### `//scale <axis> <scale_factor> | <factor_x> [<factor_y> <factor_z> [<anchor_x> <anchor_y> <anchor_z>`
## `//wbox <replace_node>`
Sets the edges of the current selection to `<replace_node>`to create an outline of a rectangular prism. Useful for roughing in walls.
```weacmd
//wbox silver_sandstone
//wbox dirt
```
## `//wcompass <replace_node> [<bead_node>]`
Creates a compass around pos1 with a single node bead pointing north (+Z).
```weacmd
//wcompass meselamp
//wcompass desert_cobble torch
//wcompass gold diamond
```
## `//wcorner <replace_node>`
Set the corners of the current selection to `<replace_node>`. Useful for outlining building sites and setting boundaries.
```weacmd
//wcorner glass
//wcorner stone_with_iron
```
## `//scale <axis> <scale_factor> | <factor_x> [<factor_y> <factor_z> [<anchor_x> <anchor_y> <anchor_z>`
Advanced version of [`//stretch` from WorldEdit](https://github.com/Uberi/Minetest-WorldEdit/blob/master/ChatCommands.md#stretch-stretchx-stretchy-stretchz) that can scale both up and down at the same time by transparently splitting it into 2 different operations. Scaling up is *always* done before scaling down.
Although the syntax looks complicated, it's really quite simple. The key concept to understand is that of the scale factor. It refers to how much the defined region should be scaled up or down by, and can be specified in multiple different ways:
@ -286,6 +311,8 @@ This command is best explained with examples:
The above functions just like `//replace` - nothing special going on here. It replaces all `dirt` nodes with `stone`.
Let's make it more interesting:
```weacmd
@ -728,17 +755,25 @@ Short for _select center_. Sets pos1 and pos2 to the centre point(s) of the curr
//scentre
```
### `//srel <axis1> <length1> [<axis2> <length2> [<axis3> <length3>]]`
## `//srel <axis1> <length1> [<axis2> <length2> [<axis3> <length3>]]`
Short for _select relative_. Sets the pos2 at set distances along 3 axes relative to pos1. If pos1 is not set it will default to the node directly under the player. The axis arguments accept `x, y, z` as well as `up, down, left, right, front, back`. Left, right, front and back are relative to player facing direction. Negative (`-`) can be applied to the axis, the length or both. Implementation thanks to @VorTechnix.
```weacmd
//srel front 5
//srel y 12 right -2
//srel left 3 up 5 -front 7
//scube -z 12 -y -2 x -2
//srel -z 12 -y -2 x -2
```
## `//sshift <axis1> <length1> [<axis2> <length2> [<axis3> <length3>]]`
Short for _selection shift_. Shifts the WorldEdit region along 3 axes. The axis arguments accept `x, y, z` as well as `up, down, left, right, front, back`. Left, right, front and back are relative to player facing direction. Negative (`-`) can be applied to the axis, the length or both. Implementation thanks to @VorTechnix.
```weacmd
//sshift back 4
//sshift right -2 up 2
//sshift -left 2 z -7 -y -4
//sshift -z 12 -y -2 x -2
```
### `//smake <operation:odd|even|equal> <mode:grow|shrink|average> [<target=xz> [<base>]]`
Short for _selection make_. Modifies existing selection by moving pos2. Allows you to make the selection an odd or even length on one or more axes or set two or more axes equal to each other or the longest, shortest or average of them. Implementation thanks to @VorTechnix.

@ -9,6 +9,8 @@ worldeditadditions = {}
local wea = worldeditadditions
wea.modpath = minetest.get_modpath("worldeditadditions")
wea.Set = dofile(wea.modpath.."/utils/set.lua")
wea.Vector3 = dofile(wea.modpath.."/utils/vector3.lua")
wea.Mesh, wea.Face = dofile(wea.modpath.."/utils/mesh.lua")
@ -30,7 +32,7 @@ dofile(wea.modpath.."/utils/nodes.lua")
dofile(wea.modpath.."/utils/node_identification.lua")
dofile(wea.modpath.."/utils/terrain.lua")
dofile(wea.modpath.."/utils/raycast_adv.lua") -- For the farwand
dofile(wea.modpath.."/utils/axes.lua")
dofile(wea.modpath.."/utils/player.lua") -- Player info functions
dofile(wea.modpath.."/lib/compat/saplingnames.lua")
@ -68,5 +70,9 @@ dofile(wea.modpath.."/lib/airapply.lua")
dofile(wea.modpath.."/lib/noiseapply2d.lua")
dofile(wea.modpath.."/lib/subdivide.lua")
dofile(wea.modpath.."/lib/selection/stack.lua")
dofile(wea.modpath.."/lib/selection/cloud.lua")
dofile(wea.modpath.."/lib/selection/init.lua") -- Helpers for selections
dofile(wea.modpath.."/lib/wireframe/corner_set.lua")
dofile(wea.modpath.."/lib/wireframe/make_compass.lua")
dofile(wea.modpath.."/lib/wireframe/wire_box.lua")

@ -1,48 +0,0 @@
-- ██████ ██ ██████ ██ ██ ██████
-- ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ██ ██ ██ ██ ██ ██
-- ██████ ███████ ██████ ██████ ██████
worldeditadditions.add_pos = {}
worldeditadditions.selection = {}
function worldeditadditions.selection.add_point(name, pos)
if pos ~= nil then
local is_new = not worldedit.pos1[name] and not worldedit.pos2[name]
-- print("[set_pos1]", name, "("..pos.x..", "..pos.y..", "..pos.z..")")
if not worldedit.pos1[name] then worldedit.pos1[name] = vector.new(pos) end
if not worldedit.pos2[name] then worldedit.pos2[name] = vector.new(pos) end
worldedit.marker_update(name)
local volume_before = worldedit.volume(worldedit.pos1[name], worldedit.pos2[name])
worldedit.pos1[name], worldedit.pos2[name] = worldeditadditions.vector.expand_region(worldedit.pos1[name], worldedit.pos2[name], pos)
local volume_after = worldedit.volume(worldedit.pos1[name], worldedit.pos2[name])
local volume_difference = volume_after - volume_before
worldedit.marker_update(name)
print("DEBUG volume_before", volume_before, "volume_after", volume_after)
if is_new then
local msg = "Created new region of "..volume_after.." node"
if volume_after ~= 1 then msg = msg.."s" end
worldedit.player_notify(name, msg)
else
local msg = "Expanded region by "..volume_difference.." node"
if volume_difference ~= 1 then msg = msg.."s" end
worldedit.player_notify(name, msg)
end
else
worldedit.player_notify(name, "Error: Too far away (try raising your maxdist with //farwand maxdist <number>)")
-- print("[set_pos1]", name, "nil")
end
end
function worldeditadditions.selection.clear_points(name)
worldedit.pos1[name] = nil
worldedit.pos2[name] = nil
worldedit.marker_update(name)
worldedit.set_pos[name] = nil
worldedit.player_notify(name, "Region cleared")
end

@ -0,0 +1,7 @@
local wea = worldeditadditions
local wea_m = wea.modpath .. "/lib/selection/"
wea.add_pos = {}
wea.selection = dofile(wea_m.."selection.lua")
dofile(wea_m.."stack.lua")

@ -0,0 +1,68 @@
-- ███████ ███████ ██ ███████ ██████ ████████ ██ ██████ ███ ██
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██
-- ███████ █████ ██ █████ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ███████ ███████ ███████ ███████ ██████ ██ ██ ██████ ██ ████
---Selection helpers and modifiers
local selection = {}
--- Additively adds a point to the current selection or
-- makes a selection from the provided point.
-- @param name string Player name.
-- @param pos vector The position to include.
function selection.add_point(name, pos)
if pos ~= nil then
-- print("[set_pos1]", name, "("..pos.x..", "..pos.y..", "..pos.z..")")
if not worldedit.pos1[name] then worldedit.pos1[name] = vector.new(pos) end
if not worldedit.pos2[name] then worldedit.pos2[name] = vector.new(pos) end
worldedit.marker_update(name)
local volume_before = worldedit.volume(worldedit.pos1[name], worldedit.pos2[name])
worldedit.pos1[name], worldedit.pos2[name] = worldeditadditions.vector.expand_region(worldedit.pos1[name], worldedit.pos2[name], pos)
local volume_after = worldedit.volume(worldedit.pos1[name], worldedit.pos2[name])
local volume_difference = volume_after - volume_before
worldedit.marker_update(name)
worldedit.player_notify(name, "Expanded region by "..volume_difference.." nodes")
else
worldedit.player_notify(name, "Error: Too far away (try raising your maxdist with //farwand maxdist <number>)")
-- print("[set_pos1]", name, "nil")
end
end
--- Clears current selection.
-- @param name string Player name.
function selection.clear_points(name)
worldedit.pos1[name] = nil
worldedit.pos2[name] = nil
worldedit.marker_update(name)
worldedit.set_pos[name] = nil
worldedit.player_notify(name, "Region cleared")
end
--- Checks if a string is a valid axis.
-- @param str string String to check (be sure to remove any + or -).
-- @param hv bool Include "h" (general horizontal) and "v" (general vertical).
-- @return bool If string is a valid axis then true.
function selection.check_axis(str,hv)
if hv then
return (str == "x" or str == "y" or str == "z" or str == "h" or str == "v")
else
return (str == "x" or str == "y" or str == "z")
end
end
--- Checks if a string is a valid dir.
-- @param str string String to check (be sure to remove any + or -).
-- @return bool If string is a valid dir then true.
function selection.check_dir(str)
return (str == "front" or str == "back" or str == "left" or str == "right" or str == "up" or str == "down")
end
return selection

@ -0,0 +1,25 @@
-- ██████ ██████ ██████ ███ ██ ███████ ██████ ███████ ███████ ████████
-- ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ██ ██████ ██ ██ ██ █████ ██████ ███████ █████ ██
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██████ ██████ ██ ██ ██ ████ ███████ ██ ██ ███████ ███████ ██
--- Puts a node at each corner of selection box.
-- @param {Position} pos1 The 1st position defining the WorldEdit selection
-- @param {Position} pos2 The 2nd positioon defining the WorldEdit selection
-- @param {string} node Name of the node to place
function worldeditadditions.corner_set(pos1,pos2,node)
-- z y x is the preferred loop order (because CPU cache I'd guess, since then we're iterating linearly through the data array)
local counts = { replaced = 0 }
for k,z in pairs({pos1.z,pos2.z}) do
for k,y in pairs({pos1.y,pos2.y}) do
for k,x in pairs({pos1.x,pos2.x}) do
minetest.set_node(vector.new(x,y,z), {name=node})
counts.replaced = counts.replaced + 1
end
end
end
return true, counts.replaced
end

@ -0,0 +1,32 @@
-- ███ ███ █████ ██ ██ ███████ ██████ ██████ ███ ███ ██████ █████ ███████ ███████
-- ████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ████ ██ ██ ██ ██ ██ ██
-- ██ ████ ██ ███████ █████ █████ ██ ██ ██ ██ ████ ██ ██████ ███████ ███████ ███████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ██ ██ ██ ██ ███████ ██████ ██████ ██ ██ ██ ██ ██ ███████ ███████
--- Makes a compass with a bead pointing north (+Z).
-- @param {Position} pos1 The 1st position defining the WorldEdit selection
-- @param {string} node1 Name of the node to place
-- @param {string} node2 Name of the node of the bead
function worldeditadditions.make_compass(pos1,node1,node2)
minetest.set_node(vector.add(pos1,vector.new(0,1,3)), {name=node2})
local counts = { replaced = 1 }
-- z y x is the preferred loop order (because CPU cache I'd guess, since then we're iterating linearly through the data array)
for z = -3,3 do
if z ~= 0 then
for k,x in pairs({math.floor(-3/math.abs(z)),0,math.ceil(3/math.abs(z))}) do
minetest.set_node(vector.new(pos1.x+x,pos1.y,pos1.z+z), {name=node1})
counts.replaced = counts.replaced + 1
end
else
for x = -3,3 do
minetest.set_node(vector.new(pos1.x+x,pos1.y,pos1.z), {name=node1})
counts.replaced = counts.replaced + 1
end
end
end
return true, counts.replaced
end

@ -0,0 +1,55 @@
-- ██ ██ ██ ██████ ███████ ██████ ██████ ██ ██
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ █ ██ ██ ██████ █████ ██████ ██ ██ ███
-- ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ███ ███ ██ ██ ██ ███████ ██████ ██████ ██ ██
--- Fills the edges of the selection box with nodes.
-- @param {Position} pos1 The 1st position defining the WorldEdit selection
-- @param {Position} pos2 The 2nd positioon defining the WorldEdit selection
-- @param {string} node Name of the node to place
local v3 = worldeditadditions.Vector3
function worldeditadditions.wire_box(pos1,pos2,node)
local node_id_replace = minetest.get_content_id(node)
local ps1, ps2 = v3.sort(pos1,pos2)
-- Fetch the nodes in the specified area
local manip, area = worldedit.manip_helpers.init(pos1, pos2)
local data = manip:get_data()
-- Using three loops to reduce the number of nodes processed
local counts = { replaced = 0 }
for z = ps1.z,ps2.z do
for _j,y in pairs({pos1.y,pos2.y}) do
for _k,x in pairs({pos1.x,pos2.x}) do
data[area:index(x, y, z)] = node_id_replace
counts.replaced = counts.replaced + 1
end
end
end
if math.abs(ps2.y-ps1.y) > 1 then
for _j,z in pairs({pos1.z,pos2.z}) do
for y = pos1.y+1,pos2.y-1 do
for _k,x in pairs({pos1.x,pos2.x}) do
data[area:index(x, y, z)] = node_id_replace
counts.replaced = counts.replaced + 1
end
end
end
end
if math.abs(ps2.x-ps1.x) > 1 then
for _j,z in pairs({pos1.z,pos2.z}) do
for _k,y in pairs({pos1.y,pos2.y}) do
for x = pos1.x+1,pos2.x-1 do
data[area:index(x, y, z)] = node_id_replace
counts.replaced = counts.replaced + 1
end
end
end
end
-- Save the modified nodes back to disk & return
worldedit.manip_helpers.finish(manip, data)
return true, counts.replaced
end

@ -1,39 +0,0 @@
-- Returns the player's facing direction on the horizontal axes only.
-- @param name string The name of the player to return facing direction of.
-- @return Returns axis name and sign multiplier.
function worldeditadditions.player_axis2d(name)
-- minetest.get_player_by_name("singleplayer"):
local dir = minetest.get_player_by_name(name):get_look_dir()
local x, z= math.abs(dir.x), math.abs(dir.z)
if x > z then return "x", dir.x > 0 and 1 or -1
else return "z", dir.z > 0 and 1 or -1 end
end
-- Returns the axis and sign of the axis to the left of the input axis.
-- @param axis string x or z.
-- @param sign int Sign multiplier.
-- @return Returns axis name and sign multiplier.
function worldeditadditions.axis_left(axis,sign)
if not axis:match("[xz]") then return false, "Error: Not a horizontal axis!"
elseif axis == "x" then return true, "z", sign
else return true, "x", -sign end
end
--- Dehumanize Direction: translates up, down, left, right, front, into xyz based on player orientation.
-- @param name string The name of the player to return facing direction of.
-- @param dir string Relative direction to translate.
-- @return Returns axis name and sign multiplier.
function worldeditadditions.dir_to_xyz(name, dir)
local axfac, drfac = worldeditadditions.player_axis2d(name)
local _, axlft, drlft = worldeditadditions.axis_left(axfac,drfac)
if dir:match("front") or dir:match("back") then
return axfac, dir:match("front") and drfac or -drfac
elseif dir:match("left") or dir:match("right") then
return axlft, dir:match("left") and drlft or -drlft
elseif dir:match("up") or dir:match("down") then
return "y", dir == "down" and -1 or 1
else return false, "\"" .. dir .. "\" not a recognized direction! Try: (up | down | left | right | front | back)" end
end
-- Tests
-- /lua print(worldeditadditions.table.unpack(worldeditadditions.player_axis2d(myname)))

@ -82,9 +82,12 @@ end
-- @param src string|int Input string.
-- @return string|int Returns the signed multiplier (1|-1).
function worldeditadditions.getsign(src)
if type(src) == "number" then return src < 0 and -1 or 1
if type(src) == "number" then
if src < 0 then return -1 else return 1 end
elseif type(src) ~= "string" then return 1
else return src:match('-') and -1 or 1 end
else
if src:match('-') then return -1 else return 1 end
end
end
--- Clamp a number to ensure it falls within a given range.

@ -0,0 +1,72 @@
local wea = worldeditadditions
local v3 = worldeditadditions.Vector3
--- Returns the player's position (at leg level).
-- @param name string The name of the player to return facing direction of.
-- @return vector Returns position.
function worldeditadditions.player_vector(name)
return minetest.get_player_by_name(name):get_pos()
end
--- Returns the player's facing info including relative DIRs.
-- @param name string The name of the player to return facing direction of.
-- @return table (vector3+) xyz raw values and {axis,sign} tables for facing direction and
-- relative direction keys (front, back, left, right, up, down).
function worldeditadditions.player_dir(name)
local dir = v3.clone(minetest.get_player_by_name(name):get_look_dir())
local abs = dir:abs()
-- Facing info
if abs.x > abs.z then dir.facing = {axis="x",sign=wea.getsign(dir.x)}
else dir.facing = {axis="z",sign=wea.getsign(dir.z)} end
-- Set front and back
dir.front = dir.facing
dir.back = {axis=dir.facing.axis,sign=dir.facing.sign*-1}
-- Set left and right
if dir.facing.axis == "x" then dir.left = {axis="z", sign=dir.facing.sign}
else dir.left = {axis="x", sign=dir.facing.sign*-1} end
dir.right = {axis=dir.left.axis,sign=dir.left.sign*-1}
-- Set up and down
dir.up = {axis="y",sign=1}
dir.down = {axis="y",sign=-1}
return dir
end
-- /lua print(worldeditadditions.vector.tostring(minetest.get_player_by_name(myname):get_look_dir()))
--- DEPRICATED =================================================================
-- TODO: Refactor commands that use the following functions to use player_dir then delete these functions
--- Returns the player's facing direction on the horizontal axes only.
-- @param name string The name of the player to return facing direction of.
-- @return string,int Returns axis name and sign multiplier.
function worldeditadditions.player_axis2d(name)
-- minetest.get_player_by_name("singleplayer"):
local dir = minetest.get_player_by_name(name):get_look_dir()
local x, z= math.abs(dir.x), math.abs(dir.z)
if x > z then return "x", dir.x > 0 and 1 or -1
else return "z", dir.z > 0 and 1 or -1 end
end
--- Returns the axis and sign of the axis to the left of the input axis.
-- @param axis string x or z.
-- @param sign int Sign multiplier.
-- @return string,int Returns axis name and sign multiplier.
function worldeditadditions.axis_left(axis,sign)
if not axis:match("[xz]") then return false, "Error: Not a horizontal axis!"
elseif axis == "x" then return true, "z", sign
else return true, "x", -sign end
end
--- Dehumanize Direction: translates up, down, left, right, front, into xyz based on player orientation.
-- @param name string The name of the player to return facing direction of.
-- @param dir string Relative direction to translate.
-- @return string Returns axis name and sign multiplier.
function worldeditadditions.dir_to_xyz(name, dir)
local axfac, drfac = worldeditadditions.player_axis2d(name)
local _, axlft, drlft = worldeditadditions.axis_left(axfac,drfac)
if dir:match("front") or dir:match("back") then
return axfac, dir:match("front") and drfac or -drfac
elseif dir:match("left") or dir:match("right") then
return axlft, dir:match("left") and drlft or -drlft
elseif dir:match("up") or dir:match("down") then
return "y", dir == "down" and -1 or 1
else return false, "\"" .. dir .. "\" not a recognized direction! Try: (up | down | left | right | front | back)" end
end

@ -0,0 +1,101 @@
--- Sets for lua!
-- local Set = {}
--- Option 1:
-- Set.__index = Set
-- Set.__newindex = function(tbl, key, value)
-- if not tbl.__protected[key] then
-- rawset(tbl,key,value)
-- else
-- error("Protected!")
-- end
-- end
-- Set.__protected = {
-- __protected=true,
-- new=true,
-- add=true,
-- delete=true,
-- has=true
-- }
--- Option 2:
local Set = {}
Set.__index = Set
Set.__newindex = function(tbl, key, value)
rawset(tbl.set,key,value)
end
--- Creates a new set.
-- @param i any Initial values(s) of the set.
-- @returns Set A table of keys equal to true.
function Set.new(i)
local result = {set={}}
setmetatable(result, Set)
if type(i) == "table" then
for k,v in pairs(i) do result[v] = true end
elseif i then
result[i] = true
end
return result
end
-- a = Set.new({"add","new","thing"})
--- Adds item(s) to set.
-- @param a set Set to manipulate.
-- @param i not nil Values(s) to add.
-- @returns bool Success of operation.
function Set.add(a,i)
if type(i) == "table" then
for k,v in pairs(i) do a[v] = true end
else
a[i] = true
end
return true
end
--- Deletes item(s) from set.
-- @param a set Set to manipulate.
-- @param i not nil Values(s) to delete.
-- @returns bool Success of operation.
function Set.delete(a,i)
if type(i) == "table" then
for k,v in pairs(i) do a[v] = nil end
else
a[i] = nil
end
return true
end
--- Checks if value(s) are present in set.
-- @param a set Set to inspect.
-- @param i not nil Values(s) to check.
-- @returns bool Value(s) are present?
function Set.has(a,i)
if type(i) == "table" then
for k,v in pairs(i) do
if not a[k] then return false end
end
return true
else
return a[i] ~= nil
end
end
-- ██████ ██████ ███████ ██████ █████ ████████ ██████ ██████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ██████ █████ ██████ ███████ ██ ██ ██ ██████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██████ ██ ███████ ██ ██ ██ ██ ██ ██████ ██ ██
--
-- ██████ ██ ██ ███████ ██████ ██████ ██ ██████ ███████ ███████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ██ ██ █████ ██████ ██████ ██ ██ ██ █████ ███████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██████ ████ ███████ ██ ██ ██ ██ ██ ██████ ███████ ███████
function Set.__call(i) return Set.new(i) end
-- Main Return:
return Set

@ -4,3 +4,4 @@ dofile(wea.modpath.."/utils/strings/split.lua")
dofile(wea.modpath.."/utils/strings/polyfill.lua")
dofile(wea.modpath.."/utils/strings/tochars.lua")
wea.split_shell = dofile(wea.modpath.."/utils/strings/split_shell.lua")
wea.to_boolean = dofile(wea.modpath.."/utils/strings/to_boolean.lua")

@ -42,16 +42,46 @@ function worldeditadditions.gsplit(text, pattern, plain)
end
-- Split a string into substrings separated by a pattern.
--- Split a string into substrings separated by a pattern. -- Deprecated
-- @param text string The string to iterate over
-- @param pattern string The separator pattern
-- @param plain boolean If true (or truthy), pattern is interpreted as a
-- plain string, not a Lua pattern
-- @returns table A sequence table containing the substrings
function worldeditadditions.split(text, pattern, plain)
function worldeditadditions.dsplit(text, pattern, plain)
local ret = {}
for match in worldeditadditions.gsplit(text, pattern, plain) do
table.insert(ret, match)
end
return ret
end
--- Split a string into substrings separated by a pattern.
-- @param str string The string to iterate over
-- @param dlm string The delimiter (separator) pattern
-- @param plain boolean If true (or truthy), pattern is interpreted as a
-- plain string, not a Lua pattern
-- @returns table A sequence table containing the substrings
function worldeditadditions.split (str,dlm,plain)
local pos, ret = 0, {}
local ins, i = str:find(dlm,pos,plain)
-- "if plain" shaves off some time in the while statement
if plain then
while ins do
table.insert(ret,str:sub(pos,ins - 1))
pos = ins + #dlm
ins = str:find(dlm,pos,true)
end
else
while ins do
table.insert(ret,str:sub(pos,ins - 1))
pos = i + 1
ins, i = str:find(dlm,pos)
end
end
-- print(pos..","..#str)
if str:sub(pos,#str) ~= "" then
table.insert(ret,str:sub(pos,#str))
end
return ret
end

@ -0,0 +1,12 @@
--- Converts input to a value of type Boolean.
-- @param arg any Input to convert
-- @returns boolean
local function to_boolean(arg)
local typ = type(arg)
if typ == "boolean" then return arg
elseif typ == "number" and arg > 0 then return true
elseif arg == "false" or arg == "no" then return false
elseif typ ~= "nil" then return true
else return false end
end
return to_boolean

@ -2,16 +2,27 @@
-- @param tbl table input table
-- @param sep string key value seperator
-- @param new_line string key value pair delimiter
-- @param max_depth number max recursion depth (optional)
-- @return string concatenated table pairs
local function table_tostring(tbl, sep, new_line)
local function table_tostring(tbl, sep, new_line, max_depth)
if type(sep) ~= "string" then sep = ": " end
if type(new_line) ~= "string" then new_line = ", " end
if type(max_depth) == "number" then max_depth = {depth=0,max=max_depth}
elseif type(max_depth) ~= "table" then max_depth = {depth=0,max=5} end
local ret = {}
if type(tbl) ~= "table" then return "Error: input not table!" end
for key,value in pairs(tbl) do
table.insert(ret,tostring(key) .. sep .. tostring(value) .. new_line)
if type(value) == "table" and max_depth.depth < max_depth.max then
table.insert(ret,tostring(key) .. sep ..
"{" .. table_tostring(value,sep,new_line,{max_depth.depth+1,max_depth.max}) .. "}")
else
table.insert(ret,tostring(key) .. sep .. tostring(value))
end
end
return table.concat(ret,"")
return table.concat(ret,new_line)
end
-- Test:
-- /lua v1 = { x= 0.335, facing= { axis= "z", sign= -1 } }; print(worldeditadditions.table.tostring(v1))
return table_tostring

@ -263,7 +263,7 @@ end
-- This enables convenient ingesting of positions from outside.
-- @param pos1 Vector3 The first vector to operate on.
-- @param pos2 Vector3 The second vector to operate on.
-- @returns Vector3,Vector3 The 2 sorted vectors.
-- @returns Vector3,Vector3 The 2 sorted vectors (min, max).
function Vector3.sort(pos1, pos2)
local pos1_new = Vector3.clone(pos1) -- This way we can accept non-Vector3 instances
local pos2_new = Vector3.clone(pos2) -- This way we can accept non-Vector3 instances

@ -15,6 +15,6 @@ worldedit.register_command("basename", {
end,
func = function(name, params_text)
if name == nil then return end
worldedit.player_notify(name, worldedit.normalize_nodename(params_text) or 'Error 404: "'..params_text..'" not found!')
return true, worldedit.normalize_nodename(params_text) or 'Error 404: "'..params_text..'" not found!'
end
})

@ -0,0 +1,88 @@
-- ███████ ██████ ██████
-- ██ ██ ██ ██ ██
-- █████ ██ ██ ██████
-- ██ ██ ██ ██ ██
-- ██ ██████ ██ ██
-- Process:
-- 1: Split `params_text` into two vars with unpack(wea.split(params_text,"%sdo%s"))
-- 2: Further split the two vars into two tables, one of values and the other of {command, args} sub tables
-- 3: For each entry in the values table execute each {command, args} sub table using gsub to replace "%%" in the args with the current value
-- Specs:
-- Command cluster support using ()
-- ?Basename support for values
-- ?Comma deliniation support for values
local wea = worldeditadditions
local function step(params)
-- Initialize additional params on first call
if not params.first then
params.i = 1 -- Iteration number
params.time = 0 -- Total execution time
params.first = true
end
-- Load current value to use
local v = params.values[params.i]
-- Start a timer
local start_time = wea.get_ms_time()
-- Execute command
params.cmd.func(params.player_name, params.args:gsub("%%+",v))
-- Finish timer and add to total
params.time = params.time + wea.get_ms_time() - start_time
-- Increment iteration state
params.i = params.i + 1
if params.i <= #params.values then
-- If we haven't run out of values call function again
minetest.after(0, step, params)
else
worldedit.player_notify(params.player_name, "For "..
table.concat(params.values,", ")..
", /"..params.cmd_name.." completed in " ..
wea.format.human_time(params.time))
end
end
worldedit.register_command("for", {
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.",
privs = { worldedit = true },
parse = function(params_text)
if not params_text:match("%sdo%s") then
return false, "Error: \"do\" argument is not present."
end
local parts = wea.split(params_text,"%sdo%s")
if not parts[1] == "" then
return false, "Error: No values specified."
end
if not parts[2] then
return false, "Error: No command specified."
end
local values = wea.split(parts[1],"%s")
local command, args = parts[2]:match("/([^%s]+)%s*(.*)$")
if not args then args = ""
else args = wea.trim(args) end
return true, values, command, args
end,
func = function(name, values, command, args)
local cmd = minetest.chatcommands[command]
if not cmd then
return false, "Error: "..command.." isn't a valid command."
end
if not minetest.check_player_privs(name, cmd.privs) then
return false, "Your privileges are insufficient to run /\""..command.."\"."
end
step({
player_name = name,
cmd_name = command,
values = values,
cmd = cmd,
args = args
})
end
})

@ -0,0 +1,18 @@
-- ███ ███ ███████ ████████ █████
-- ████ ████ ██ ██ ██ ██
-- ██ ████ ██ █████ ██ ███████
-- ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ███████ ██ ██ ██
-- Commands that work on other commands.
local we_cm = worldeditadditions_commands.modpath .. "/commands/meta/"
dofile(we_cm.."airapply.lua")
dofile(we_cm.."ellipsoidapply.lua")
dofile(we_cm.."for.lua")
-- dofile(we_cm.."macro.lua") -- Async bug
dofile(we_cm.."many.lua")
dofile(we_cm.."multi.lua")
dofile(we_cm.."noiseapply2d.lua")
dofile(we_cm.."subdivide.lua")

@ -0,0 +1,134 @@
-- ███ ███ █████ ██████ ██████ ██████
-- ████ ████ ██ ██ ██ ██ ██ ██ ██
-- ██ ████ ██ ███████ ██ ██████ ██ ██
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ██ ██ ██████ ██ ██ ██████
local wea = worldeditadditions
local v3 = worldeditadditions.Vector3
local function step(params)
-- Initialize additional params on first call
if not params.first then
params.i = 1 -- Iteration number
params.time = 0 -- Total execution time
params.first = true
end
-- Load current command string to use
local command, args = params.commands[params.i]:match("/([^%s]+)%s*(.*)$")
if not args then args = ""
else args = args:match("^%s*(.*)%s*$") end
-- Get command and test privs
local cmd = minetest.chatcommands[command]
if not cmd then
return false, "Error: "..command.." isn't a valid command."
end
if not minetest.check_player_privs(params.player_name, cmd.privs) then
return false, "Your privileges are insufficient to run /\""..command.."\"."
end
-- Start a timer
local start_time = wea.get_ms_time()
-- Execute command
cmd.func(params.player_name, args)
-- Finish timer and add to total
params.time = params.time + wea.get_ms_time() - start_time
-- Increment iteration state
params.i = params.i + 1
if params.i <= #params.commands then
-- If we haven't run out of values call function again
minetest.after(params.delay, step, params) -- Time is in seconds
else
worldedit.player_notify(params.player_name, "The macro \""..
params.file.."\" was completed in " ..
wea.format.human_time(params.time))
end
end
worldedit.register_command("macro", {
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.",
privs = {worldedit=true},
require_pos = 0,
parse = function(params_text)
local parts = wea.split(params_text,"%s")
local file_name, delay -- = params_text:match("^(.-)%s*(%d*%.?%d*)$")
-- Check for params and delay
if not parts[1] then
return false, "Error: Insufficient arguments. Expected: \"<file> [<delay=0>]\""
elseif not parts[#parts]:match("[^%d%.]") then
delay = table.remove(parts,#parts)
file_name = table.concat(parts," ")
else
delay = 0
file_name = table.concat(parts," ")
end
-- Check file name
if file_name:match("[!\"#%%&'%(%)%*%+,/:;<=>%?%[\\]%^`{|}]") then
return false, "Disallowed file name: " .. params_text
end
return true, file_name, delay
end,
func = function(name, file_name, delay)
if not worldedit.pos1[name] then
worldedit.pos1[name] = v3.add(wea.player_vector(name), v3.new(0.5,-0.5,0.5)):floor()
worldedit.mark_pos1(name)
end
worldedit.pos2[name] = worldedit.pos1[name]
-- Find the file in the world path
local testpaths = {
minetest.get_worldpath() .. "/macros/" .. file_name,
minetest.get_worldpath() .. "/macros/" .. file_name .. ".weamac",
minetest.get_worldpath() .. "/macros/" .. file_name .. ".wmac",
}
local file, err
for index, path in ipairs(testpaths) do
file, err = io.open(path, "rb")
if not err then break end
end
-- Check if file exists
if err then
return false, "Error: File \"" .. file_name .. "\" does not exist or is corrupt."
end
local value = file:read("*a")
file:close()
step({
player_name = name,
file = file_name:match("^[^%.]+"),
delay = delay,
commands = wea.split(value,"[\n\r]+")
})
end,
})
-- Make default macro
local function default_macro()
local path = minetest.get_worldpath() .. "/macros"
-- Create directory if it does not already exist
minetest.mkdir(path)
local writer, err = io.open(path.."/fixlight.weamac", "ab")
if not writer then return false end
writer:write("//multi //1 //2 //outset 50 //fixlight //y")
writer:flush()
writer:close()
return true
end
-- Check for default macro
local function chk_default_macro()
local path = minetest.get_worldpath() .. "/macros/fixlight.weamac"
local file, err = io.open(path, "rb")
if err then return false
else
file:close()
return true
end
end
if not chk_default_macro() then
default_macro()
end

@ -18,4 +18,8 @@ dofile(we_cm.."smake.lua")
dofile(we_cm.."spop.lua")
dofile(we_cm.."spush.lua")
dofile(we_cm.."srect.lua")
dofile(we_cm.."sshift.lua")
dofile(we_cm.."sstack.lua")
-- Aliases
worldedit.alias_command("sfac", "sfactor")

@ -36,7 +36,7 @@ worldedit.register_command("srel", {
if not _ then return false, vec end
if not worldedit.pos1[name] then
local pos = vector.add(minetest.get_player_by_name(name):get_pos(), vector.new(0.5,-0.5,0.5))
local pos = vector.add(wea.player_vector(name), vector.new(0.5,-0.5,0.5))
wea.vector.floor(pos)
worldedit.pos1[name] = pos
worldedit.mark_pos1(name)

@ -0,0 +1,51 @@
-- ███████ ███████ ██ ██ ██ ███████ ████████
-- ██ ██ ██ ██ ██ ██ ██
-- ███████ ███████ ███████ ██ █████ ██
-- ██ ██ ██ ██ ██ ██ ██
-- ███████ ███████ ██ ██ ██ ██ ██
local wea = worldeditadditions
local v3 = worldeditadditions.Vector3
local function parse_with_name(name,args)
local vec, tmp = v3.new(0, 0, 0), {}
local find, _, i = {}, 0, 0
repeat
_, i, tmp.proc = args:find("([%l%s+-]+%d+)%s*", i)
if tmp.proc:match("[xyz]") then
tmp.ax = tmp.proc:match("[xyz]")
tmp.dir = tonumber(tmp.proc:match("[+-]?%d+")) * (tmp.proc:match("-%l+") and -1 or 1)
else
tmp.ax, _ = wea.dir_to_xyz(name, tmp.proc:match("%l+"))
if not tmp.ax then return false, _ end
tmp.dir = tonumber(tmp.proc:match("[+-]?%d+")) * (tmp.proc:match("-%l+") and -1 or 1) * _
end
vec[tmp.ax] = tmp.dir
until not args:find("([%l%s+-]+%d+)%s*", i)
return true, vec
end
worldedit.register_command("sshift", {
params = "<axis1> <distance1> [<axis2> <distance2> [<axis3> <distance3>]]",
description = "Shift the WorldEdit region in 3 dimensions.",
privs = { worldedit = true },
require_pos = 2,
parse = function(params_text)
if params_text:match("([%l%s+-]+%d+)") then return true, params_text
else return false, "No acceptable params found" end
end,
func = function(name, params_text)
local _, vec = parse_with_name(name,params_text)
if not _ then return false, vec end
local pos1 = vec:add(worldedit.pos1[name])
worldedit.pos1[name] = pos1
worldedit.mark_pos1(name)
local pos2 = vec:add(worldedit.pos2[name])
worldedit.pos2[name] = pos2
worldedit.mark_pos2(name)
return true, "Region shifted by " .. (vec.x + vec.y + vec.z) .. " nodes."
end,
})
-- Tests
-- //srel front 5 left 3 y 2

@ -0,0 +1,13 @@
-- ██ ██ ██ ██████ ███████ ███████ ██████ █████ ███ ███ ███████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ████ ██
-- ██ █ ██ ██ ██████ █████ █████ ██████ ███████ ██ ████ ██ █████
-- ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ███ ███ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ ███████
-- 2d and 3d outlines of shapes.
local we_cm = worldeditadditions_commands.modpath .. "/commands/wireframe/"
dofile(we_cm.."wbox.lua")
dofile(we_cm.."wcompass.lua")
dofile(we_cm.."wcorner.lua")

@ -0,0 +1,39 @@
-- ██ ██ ██████ ██████ ██ ██
-- ██ ██ ██ ██ ██ ██ ██ ██
-- ██ █ ██ ██████ ██ ██ ███
-- ██ ███ ██ ██ ██ ██ ██ ██ ██
-- ███ ███ ██████ ██████ ██ ██
local wea = worldeditadditions
local v3 = worldeditadditions.Vector3
worldedit.register_command("wbox", {
params = "<replace_node>",
description = "Sets the edges of the current selection to <replace_node>",
privs = {worldedit=true},
require_pos = 2,
parse = function(params_text)
if params_text == "" then
return false, "Error: too few arguments! Expected: \"<replace_node>\""
end
local node = worldedit.normalize_nodename(params_text)
if not node then
return false, "invalid node name: " .. params_text
end
return true, node
end,
nodes_needed = function(name)
local delta = v3.subtract(worldedit.pos2[name], worldedit.pos1[name]):abs():add(1)
local total, mult, axes = 1, 4, {"x","y","z"}
for k,v in pairs(axes) do
if worldedit.pos1[name] ~= worldedit.pos2[name] then total = total*2
else mult = mult/2 end
end
for k,v in pairs(axes) do
if delta[v] > 2 then total = total + (delta[v] - 2)*mult end
end
return total
end,
func = function(name, node)
local _, count = wea.wire_box(worldedit.pos1[name], worldedit.pos2[name], node)
return _, count .. " nodes set"
end,
})

@ -0,0 +1,35 @@
-- ██ ██ ██████ ██████ ███ ███ ██████ █████ ███████ ███████
-- ██ ██ ██ ██ ██ ████ ████ ██ ██ ██ ██ ██ ██
-- ██ █ ██ ██ ██ ██ ██ ████ ██ ██████ ███████ ███████ ███████
-- ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ███ ███ ██████ ██████ ██ ██ ██ ██ ██ ███████ ███████
local wea = worldeditadditions
worldedit.register_command("wcompass", {
params = "<replace_node> [<bead_node>]",
description = "Creates a compass around pos1 with a single node bead pointing north (+Z).",
privs = {worldedit=true},
require_pos = 1,
parse = function(params_text)
local parts = wea.split(params_text," ",true)
if not parts[1] then
return false, "Error: too few arguments! Expected: \"<replace_node> [<bead_node>]\""
elseif not parts[2] then
parts[2] = parts[1]
end
local node1 = worldedit.normalize_nodename(parts[1])
local node2 = worldedit.normalize_nodename(parts[2])
if not node1 then
return false, "Invalid <replace_node>: " .. parts[1]
elseif not node2 then
return false, "Invalid <bead_node>: " .. parts[2]
end
return true, node1, node2
end,
nodes_needed = function(name)
return 26
end,
func = function(name, node1, node2)
local _, count = wea.make_compass(worldedit.pos1[name], node1, node2)
return _, count .. " nodes set"
end,
})

@ -0,0 +1,30 @@
-- ██ ██ ██████ ██████ ██████ ███ ██ ███████ ██████
-- ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ ██
-- ██ █ ██ ██ ██ ██ ██████ ██ ██ ██ █████ ██████
-- ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ███ ███ ██████ ██████ ██ ██ ██ ████ ███████ ██ ██
local wea = worldeditadditions
worldedit.register_command("wcorner", {
params = "<replace_node>",
description = "Set the corners of the current selection to <replace_node>",
privs = {worldedit=true},
require_pos = 2,
parse = function(params_text)
local node = worldedit.normalize_nodename(params_text)
if not node then
return false, "invalid node name: " .. params_text
end
return true, node
end,
nodes_needed = function(name)
local p1, p2, total = worldedit.pos1[name], worldedit.pos2[name], 1
for k,v in pairs({"x","y","z"}) do
if p1[v] ~= p2[v] then total = total*2 end
end
return total
end,
func = function(name, node)
local _, count = wea.corner_set(worldedit.pos1[name], worldedit.pos2[name], node)
return _, count .. " nodes set"
end,
})

@ -39,12 +39,8 @@ dofile(we_c.modpath.."/commands/spiral2.lua")
dofile(we_c.modpath.."/commands/count.lua")
dofile(we_c.modpath.."/commands/meta/multi.lua")
dofile(we_c.modpath.."/commands/meta/many.lua")
dofile(we_c.modpath.."/commands/meta/subdivide.lua")
dofile(we_c.modpath.."/commands/meta/ellipsoidapply.lua")
dofile(we_c.modpath.."/commands/meta/airapply.lua")
dofile(we_c.modpath.."/commands/meta/noiseapply2d.lua")
-- Meta Commands
dofile(we_c.modpath.."/commands/meta/init.lua")
-- Selection Tools
dofile(we_c.modpath.."/commands/selectors/init.lua")
@ -52,6 +48,9 @@ dofile(we_c.modpath.."/commands/selectors/init.lua")
-- Measure Tools
dofile(we_c.modpath.."/commands/measure/init.lua")
-- Wireframe
dofile(we_c.modpath.."/commands/wireframe/init.lua")
dofile(we_c.modpath.."/commands/extra/saplingaliases.lua")
dofile(we_c.modpath.."/commands/extra/basename.lua")
@ -84,9 +83,6 @@ worldedit.alias_command("naturalize", "layers")
worldedit.alias_command("flora", "bonemeal")
-- Selection Tools
worldedit.alias_command("sfac", "sfactor")
-- Measure Tools
worldedit.alias_command("mcount", "count")
worldedit.alias_command("mfacing", "mface")

@ -1,37 +1,26 @@
function worldeditadditions_core.register_command(name, def)
-- TODO: Implement our own deep copy function here
-- Depending on a Minetest-specific addition here makes be very uneasy
-- ...especially since it's not obvious at first glance that this isn't a
-- core feature provided by Lua itself
local def = table.copy(def)
assert(
def.privs,
"Error: No privileges specified in definition of command '"..name.."'."
)
def.require_pos = def.require_pos or 0
assert(def.require_pos >= 0 and def.require_pos < 3)
if def.params == "" and not def.parse then
def.parse = function(params_text) return true end
else
assert(
def.parse,
"Error: No parameter parsing function specified, even though parameters were specified in definition of command '"..name.."'."
)
function worldeditadditions_core.check_command(name, def)
if not (name and #name > 0) then
return false, "Error: No command name."
end
assert(
def.nodes_needed == nil or type(def.nodes_needed) == "function",
"Error: nodes_needed must be either not specified or be a function that returns the number of nodes that could potentially be changed for a given set fo parsed parameters in definition of command '"..name.."'"
)
assert(
def.func,
"Error: 'func' is not defined. It must be defined to the function to call to run the command in definition of command '"..name.."'."
)
return def
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,9 +1,11 @@
local we_c = worldeditadditions_core
function we_c.override_command(name, def)
local success, def = we_c.check(name, def)
local def = table.copy(def)
local success, err = we_c.check_command(name, def)
if not success then
return false, def
error(err)
return false
end
minetest.override_chatcommand("/" .. name, {
@ -16,3 +18,18 @@ function we_c.override_command(name, def)
})
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,9 +1,9 @@
local we_c = worldeditadditions_core
function we_c.register_command(name, def)
local success, def = we_c.check(name, def)
local def = table.copy(def)
local success, err = we_c.check_command(name, def)
if not success then
return false, def
return false, err
end
minetest.register_chatcommand("/" .. name, {
@ -16,3 +16,17 @@ function we_c.register_command(name, def)
})
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