Update Worldedit

This commit is contained in:
Ombridride 2015-03-02 23:59:23 +01:00
parent f0645f5ebe
commit 3cf1a2318f
23 changed files with 3770 additions and 3947 deletions

2
mods/WorldEdit/.gitignore vendored Executable file
View File

@ -0,0 +1,2 @@
*~

View File

@ -98,6 +98,12 @@ Display the volume of the current WorldEdit region.
//volume //volume
### `//deleteblocks`
Delete the MapBlocks (16x16x16 units) that contain the selected region. This means that mapgen will be invoked for that area. As only whole MapBlocks get removed, the deleted area is usually larger than the selected one. Also, mapgen can trigger mechanisms like mud reflow or cavegen, which affects nodes (up to 112 nodes away) outside the MapBlock, so dont use this near buildings.
//deleteblocks
### `//set <node>` ### `//set <node>`
Set the current WorldEdit region to `<node>`. Set the current WorldEdit region to `<node>`.

View File

@ -1,4 +1,4 @@
WorldEdit v1.0 for Minetest 0.4.8+ WorldEdit v1.1 for Minetest 0.4.8+
================================== ==================================
The ultimate in-game world editing tool for [Minetest](http://minetest.net/)! Tons of functionality to help with building, fixing, and more. The ultimate in-game world editing tool for [Minetest](http://minetest.net/)! Tons of functionality to help with building, fixing, and more.
@ -41,9 +41,9 @@ Interface
--------- ---------
WorldEdit is accessed in-game in two main ways. WorldEdit is accessed in-game in two main ways.
The GUI adds a screen to each player's inventory that gives access to various WorldEdit functions. The [tutorial](Tutorial.md) and the [Chat Commands Reference](Chat Commands.md) may be helpful in learning to use it. The GUI adds a screen to each player's inventory that gives access to various WorldEdit functions. The [tutorial](Tutorial.md) and the [Chat Commands Reference](ChatCommands.md) may be helpful in learning to use it.
The chat interface adds many chat commands that perform various WorldEdit powered tasks. It is documented in the [Chat Commands Reference](Chat Commands.md). The chat interface adds many chat commands that perform various WorldEdit powered tasks. It is documented in the [Chat Commands Reference](ChatCommands.md).
Compatibility Compatibility
------------- -------------
@ -109,20 +109,24 @@ WorldEdit supports two different types of schematics.
The first is the WorldEdit Schematic format, with the file extension ".we", and in some older versions, ".wem". There have been several previous versions of the WorldEdit Schematic format, but WorldEdit is capable of loading any past versions, and will always support them - there is no need to worry about schematics becoming obselete. The first is the WorldEdit Schematic format, with the file extension ".we", and in some older versions, ".wem". There have been several previous versions of the WorldEdit Schematic format, but WorldEdit is capable of loading any past versions, and will always support them - there is no need to worry about schematics becoming obselete.
The current version of the WorldEdit Schematic format, internally known as version 4, is essentially an array of node data tables in Lua 5.2 table syntax. Specifically: As of version 5, WorldEdit schematics include a header. The header is seperated from the content by a colon (`:`). It contains fields seperated by commas (`,`). Currently only one field is used, which contains the version in ASCII decimal.
The current version of the WorldEdit Schematic format is essentially an array of node data tables in Lua 5.1 table syntax preceded by a header.
Specifically it looks like this:
5:return {
{
y = <y-axis coordinate>,
x = <x-axis coordinate>,
z = <z-axis coordinate>,
name = <node name>,
param1 = <param1 value>,
param2 = <param2 value>,
meta = <metadata table>,
},
<...>
}
return {
{
["y"] = <y-axis coordinate>,
["x"] = <x-axis coordinate>,
["name"] = <node name>,
["z"] = <z-axis coordinate>,
["meta"] = <metadata table>,
["param2"] = <param2 value>,
["param1"] = <y-axis coordinate>,
},
<...>
}
The ordering of the values and minor aspects of the syntax, such as trailing commas or newlines, are not guaranteed to stay the same in future versions. The ordering of the values and minor aspects of the syntax, such as trailing commas or newlines, are not guaranteed to stay the same in future versions.

View File

@ -107,7 +107,7 @@ Step 4: Other commands
---------------------- ----------------------
### Chat Commands ### Chat Commands
There are many more commands than what is shown here. See the [Chat Commands Reference](Chat Commands.md) for a detailed list of them, along with descriptions and examples for every single one. There are many more commands than what is shown here. See the [Chat Commands Reference](ChatCommands.md) for a detailed list of them, along with descriptions and examples for every single one.
If you're in-game and forgot how a command works, just use the `/help <command name>` command, without the first forward slash. For example, to see some information about the `//set <node>` command mentioned earlier, simply use `/help /set`. If you're in-game and forgot how a command works, just use the `/help <command name>` command, without the first forward slash. For example, to see some information about the `//set <node>` command mentioned earlier, simply use `/help /set`.
@ -115,6 +115,6 @@ A very useful command to check out is the `//save <schematic>` command, which ca
### WorldEdit GUI ### WorldEdit GUI
This only scratches the surface of what WorldEdit is capable of. Most of the functions in the WorldEdit GUI correspond to chat commands, and so the [Chat Commands Reference](Chat Commands.md) may be useful if you get stuck. This only scratches the surface of what WorldEdit is capable of. Most of the functions in the WorldEdit GUI correspond to chat commands, and so the [Chat Commands Reference](ChatCommands.md) may be useful if you get stuck.
It is helpful to explore the various buttons in the interface and check out what they do. Learning the chat command interface is also useful if you use WorldEdit intensively - an experienced chat command user can usually work faster than an experienced WorldEdit GUI user. It is helpful to explore the various buttons in the interface and check out what they do. Learning the chat command interface is also useful if you use WorldEdit intensively - an experienced chat command user can usually work faster than an experienced WorldEdit GUI user.

View File

@ -21,9 +21,9 @@ Manipulations
------------- -------------
Contained in manipulations.lua, this module allows several node operations to be applied over a region. Contained in manipulations.lua, this module allows several node operations to be applied over a region.
### count = worldedit.set(pos1, pos2, nodename) ### count = worldedit.set(pos1, pos2, node_name)
Sets a region defined by positions `pos1` and `pos2` to `nodename`. To clear a region, use "air" as the value of `nodename`. Sets a region defined by positions `pos1` and `pos2` to `node_name`. To clear a region, use "air" as the value of `node_name`.
Returns the number of nodes set. Returns the number of nodes set.
@ -109,51 +109,33 @@ Primitives
---------- ----------
Contained in primitives.lua, this module allows the creation of several geometric primitives. Contained in primitives.lua, this module allows the creation of several geometric primitives.
### count = worldedit.hollow_sphere(pos, radius, nodename) ### count = worldedit.sphere(pos, radius, node_name, hollow)
Adds a hollow sphere centered at `pos` with radius `radius`, composed of `nodename`. Adds a sphere centered at `pos` with radius `radius`, composed of `node_name`.
Returns the number of nodes added. Returns the number of nodes added.
### count = worldedit.sphere(pos, radius, nodename) ### count = worldedit.dome(pos, radius, node_name, hollow)
Adds a sphere centered at `pos` with radius `radius`, composed of `nodename`. Adds a dome centered at `pos` with radius `radius`, composed of `node_name`.
Returns the number of nodes added. Returns the number of nodes added.
### count = worldedit.hollow_dome(pos, radius, nodename) ### count = worldedit.cylinder(pos, axis, length, radius, node_name, hollow)
Adds a hollow dome centered at `pos` with radius `radius`, composed of `nodename`. Adds a cylinder at `pos` along the `axis` axis ("x" or "y" or "z") with length `length` and radius `radius`, composed of `node_name`.
Returns the number of nodes added. Returns the number of nodes added.
### count = worldedit.dome(pos, radius, nodename) ### count = worldedit.pyramid(pos, axis, height, node_name)
Adds a dome centered at `pos` with radius `radius`, composed of `nodename`.
Returns the number of nodes added.
### count = worldedit.hollow_cylinder(pos, axis, length, radius, nodename)
Adds a hollow cylinder at `pos` along the `axis` axis ("x" or "y" or "z") with length `length` and radius `radius`, composed of `nodename`.
Returns the number of nodes added.
### count = worldedit.cylinder(pos, axis, length, radius, nodename)
Adds a cylinder at `pos` along the `axis` axis ("x" or "y" or "z") with length `length` and radius `radius`, composed of `nodename`.
Returns the number of nodes added.
### count = worldedit.pyramid(pos, axis, height, nodename)
Adds a pyramid centered at `pos` along the `axis` axis ("x" or "y" or "z") with height `height`. Adds a pyramid centered at `pos` along the `axis` axis ("x" or "y" or "z") with height `height`.
Returns the number of nodes added. Returns the number of nodes added.
### count = worldedit.spiral(pos, length, height, spacer, nodename) ### count = worldedit.spiral(pos, length, height, spacer, node_name)
Adds a spiral centered at `pos` with side length `length`, height `height`, space between walls `spacer`, composed of `nodename`. Adds a spiral centered at `pos` with side length `length`, height `height`, space between walls `spacer`, composed of `node_name`.
Returns the number of nodes added. Returns the number of nodes added.
@ -173,15 +155,15 @@ Hides all nodes in a region defined by positions `pos1` and `pos2` by non-destru
Returns the number of nodes hidden. Returns the number of nodes hidden.
### count = worldedit.suppress(pos1, pos2, nodename) ### count = worldedit.suppress(pos1, pos2, node_name)
Suppresses all instances of `nodename` in a region defined by positions `pos1` and `pos2` by non-destructively replacing them with invisible nodes. Suppresses all instances of `node_name` in a region defined by positions `pos1` and `pos2` by non-destructively replacing them with invisible nodes.
Returns the number of nodes suppressed. Returns the number of nodes suppressed.
### count = worldedit.highlight(pos1, pos2, nodename) ### count = worldedit.highlight(pos1, pos2, node_name)
Highlights all instances of `nodename` in a region defined by positions `pos1` and `pos2` by non-destructively hiding all other nodes. Highlights all instances of `node_name` in a region defined by positions `pos1` and `pos2` by non-destructively hiding all other nodes.
Returns the number of nodes found. Returns the number of nodes found.
@ -195,29 +177,30 @@ Serialization
------------- -------------
Contained in serialization.lua, this module allows regions of nodes to be serialized and deserialized to formats suitable for use outside MineTest. Contained in serialization.lua, this module allows regions of nodes to be serialized and deserialized to formats suitable for use outside MineTest.
### version = worldedit.valueversion(value) ### version, extra_fields, content = worldedit.read_header(value)
Determines the version of serialized data `value`. Reads the header from serialized data `value`.
Returns the version as a positive integer or 0 for unknown versions. Returns the version as a positive integer (nil for unknown versions),
extra header fields (nil if not supported), and the content after the header.
### data, count = worldedit.serialize(pos1, pos2) ### data, count = worldedit.serialize(pos1, pos2)
Converts the region defined by positions `pos1` and `pos2` into a single string. Converts the region defined by positions `pos1` and `pos2` into a single string.
Returns the serialized data and the number of nodes serialized. Returns the serialized data and the number of nodes serialized, or nil.
### pos1, pos2, count = worldedit.allocate(originpos, value) ### pos1, pos2, count = worldedit.allocate(origin_pos, value)
Determines the volume the nodes represented by string `value` would occupy if deserialized at `originpos`. Determines the volume the nodes represented by string `value` would occupy if deserialized at `origin_pos`.
Returns the two corner positions and the number of nodes. Returns the two corner positions and the number of nodes, or nil.
### count = worldedit.deserialize(originpos, value) ### count = worldedit.deserialize(origin_pos, value)
Loads the nodes represented by string `value` at position `originpos`. Loads the nodes represented by string `value` at position `origin_pos`.
Returns the number of nodes deserialized. Returns the number of nodes deserialized or nil.
Code Code
---- ----
@ -233,4 +216,4 @@ Returns an error if the code fails or nil otherwise.
Executes `code` as a Lua chunk in the global namespace with the variable `pos` available, for each node in a region defined by positions `pos1` and `pos2`. Executes `code` as a Lua chunk in the global namespace with the variable `pos` available, for each node in a region defined by positions `pos1` and `pos2`.
Returns an error if the code fails or nil otherwise. Returns an error if the code fails or nil otherwise.

12
mods/WorldEdit/config.ld Executable file
View File

@ -0,0 +1,12 @@
project = "WorldEdit"
title = "WorldEdit API Documentation"
description = "Minetest mod to mass-modify nodes"
format = "markdown"
file = {"worldedit"}
topics = {
"README.md",
"Tutorial.md",
"ChatCommands.md",
"LICENSE.txt"
}

View File

@ -1,70 +1,52 @@
worldedit = worldedit or {} --- Lua code execution functions.
local minetest = minetest -- local copy of global -- @module worldedit.code
-- Copies and modifies positions `pos1` and `pos2` so that each component of --- Executes `code` as a Lua chunk in the global namespace.
-- `pos1` is less than or equal to the corresponding component of `pos2`. -- @return An error message if the code fails, or nil on success.
-- Returns the new positions. function worldedit.lua(code)
worldedit.sort_pos = function(pos1, pos2) local func, err = loadstring(code)
pos1 = {x=pos1.x, y=pos1.y, z=pos1.z} if not func then -- Syntax error
pos2 = {x=pos2.x, y=pos2.y, z=pos2.z} return err
if pos1.x > pos2.x then end
pos2.x, pos1.x = pos1.x, pos2.x local good, err = pcall(func)
end if not good then -- Runtime error
if pos1.y > pos2.y then return err
pos2.y, pos1.y = pos1.y, pos2.y end
end return nil
if pos1.z > pos2.z then end
pos2.z, pos1.z = pos1.z, pos2.z
end
return pos1, pos2 --- Executes `code` as a Lua chunk in the global namespace with the variable
end -- pos available, for each node in a region defined by positions `pos1` and
-- `pos2`.
-- Executes `code` as a Lua chunk in the global namespace, -- @return An error message if the code fails, or nil on success.
-- returning an error if the code fails, or nil otherwise. function worldedit.luatransform(pos1, pos2, code)
worldedit.lua = function(code) pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local func, err = loadstring(code)
if not func then -- Syntax error local factory, err = loadstring("return function(pos) " .. code .. " end")
return err if not factory then -- Syntax error
end return err
local good, err = pcall(operation) end
if not good then -- Runtime error local func = factory()
return err
end worldedit.keep_loaded(pos1, pos2)
return nil
end local pos = {x=pos1.x, y=0, z=0}
while pos.x <= pos2.x do
-- Executes `code` as a Lua chunk in the global namespace with the variable pos.y = pos1.y
-- pos available, for each node in a region defined by positions `pos1` and while pos.y <= pos2.y do
-- `pos2`, returning an error if the code fails, or nil otherwise pos.z = pos1.z
worldedit.luatransform = function(pos1, pos2, code) while pos.z <= pos2.z do
pos1, pos2 = worldedit.sort_pos(pos1, pos2) local good, err = pcall(func, pos)
if not good then -- Runtime error
local factory, err = loadstring("return function(pos) " .. code .. " end") return err
if not factory then -- Syntax error end
return err pos.z = pos.z + 1
end end
local func = factory() pos.y = pos.y + 1
end
-- Keep area loaded pos.x = pos.x + 1
local manip = minetest.get_voxel_manip() end
manip:read_from_map(pos1, pos2) return nil
end
local pos = {x=pos1.x, y=0, z=0}
while pos.x <= pos2.x do
pos.y = pos1.y
while pos.y <= pos2.y do
pos.z = pos1.z
while pos.z <= pos2.z do
local good, err = pcall(func, pos)
if not good then -- Runtime error
return err
end
pos.z = pos.z + 1
end
pos.y = pos.y + 1
end
pos.x = pos.x + 1
end
return nil
end

View File

@ -0,0 +1,114 @@
--- Common functions [INTERNAL]. All of these functions are internal!
-- @module worldedit.common
--- Copies and modifies positions `pos1` and `pos2` so that each component of
-- `pos1` is less than or equal to the corresponding component of `pos2`.
-- Returns the new positions.
function worldedit.sort_pos(pos1, pos2)
pos1 = {x=pos1.x, y=pos1.y, z=pos1.z}
pos2 = {x=pos2.x, y=pos2.y, z=pos2.z}
if pos1.x > pos2.x then
pos2.x, pos1.x = pos1.x, pos2.x
end
if pos1.y > pos2.y then
pos2.y, pos1.y = pos1.y, pos2.y
end
if pos1.z > pos2.z then
pos2.z, pos1.z = pos1.z, pos2.z
end
return pos1, pos2
end
--- Determines the volume of the region defined by positions `pos1` and `pos2`.
-- @return The volume.
function worldedit.volume(pos1, pos2)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
return (pos2.x - pos1.x + 1) *
(pos2.y - pos1.y + 1) *
(pos2.z - pos1.z + 1)
end
--- Gets other axes given an axis.
-- @raise Axis must be x, y, or z!
function worldedit.get_axis_others(axis)
if axis == "x" then
return "y", "z"
elseif axis == "y" then
return "x", "z"
elseif axis == "z" then
return "x", "y"
else
error("Axis must be x, y, or z!")
end
end
function worldedit.keep_loaded(pos1, pos2)
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos2)
end
local mh = {}
worldedit.manip_helpers = mh
--- Generates an empty VoxelManip data table for an area.
-- @return The empty data table.
function mh.get_empty_data(area)
-- Fill emerged area with ignore so that blocks in the area that are
-- only partially modified aren't overwriten.
local data = {}
local c_ignore = minetest.get_content_id("ignore")
for i = 1, worldedit.volume(area.MinEdge, area.MaxEdge) do
data[i] = c_ignore
end
return data
end
function mh.init(pos1, pos2)
local manip = minetest.get_voxel_manip()
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
return manip, area
end
function mh.init_radius(pos, radius)
local pos1 = vector.subtract(pos, radius)
local pos2 = vector.add(pos, radius)
return mh.init(pos1, pos2)
end
function mh.init_axis_radius(base_pos, axis, radius)
return mh.init_axis_radius_length(base_pos, axis, radius, radius)
end
function mh.init_axis_radius_length(base_pos, axis, radius, length)
local other1, other2 = worldedit.get_axis_others(axis)
local pos1 = {
[axis] = base_pos[axis],
[other1] = base_pos[other1] - radius,
[other2] = base_pos[other2] - radius
}
local pos2 = {
[axis] = base_pos[axis] + length,
[other1] = base_pos[other1] + radius,
[other2] = base_pos[other2] + radius
}
return mh.init(pos1, pos2)
end
function mh.finish(manip, data)
-- Update map
manip:set_data(data)
manip:write_to_map()
manip:update_map()
end

View File

@ -1,23 +1,74 @@
worldedit = worldedit or {} --- Compatibility functions.
local minetest = minetest --local copy of global -- @module worldedit.compatibility
worldedit.allocate_old = worldedit.allocate local function deprecated(new_func)
worldedit.deserialize_old = worldedit.deserialize local info = debug.getinfo(1, "n")
worldedit.metasave = function(pos1, pos2, filename) local msg = "worldedit." .. info.name .. "() is deprecated."
local file, err = io.open(filename, "wb") if new_func then
if err then return 0 end msg = msg .. " Use worldedit." .. new_func .. "() instead."
local data, count = worldedit.serialize(pos1, pos2) end
file:write(data) minetest.log("deprecated", msg)
file:close() end
return count
end worldedit.allocate_old = worldedit.allocate
worldedit.metaload = function(originpos, filename)
filename = minetest.get_worldpath() .. "/schems/" .. file .. ".wem" worldedit.deserialize_old = worldedit.deserialize
local file, err = io.open(filename, "wb")
if err then return 0 end function worldedit.metasave(pos1, pos2, filename)
local data = file:read("*a") deprecated("save")
return worldedit.deserialize(originpos, data) local file, err = io.open(filename, "wb")
end if err then return 0 end
worldedit.scale = function(pos1, pos2, factor) local data, count = worldedit.serialize(pos1, pos2)
return worldedit.stretch(pos1, pos2, factor, factor, factor) file:write(data)
end file:close()
return count
end
function worldedit.metaload(originpos, filename)
deprecated("load")
filename = minetest.get_worldpath() .. "/schems/" .. file .. ".wem"
local file, err = io.open(filename, "wb")
if err then return 0 end
local data = file:read("*a")
return worldedit.deserialize(originpos, data)
end
function worldedit.scale(pos1, pos2, factor)
deprecated("stretch")
return worldedit.stretch(pos1, pos2, factor, factor, factor)
end
function worldedit.valueversion(value)
deprecated("read_header")
local version = worldedit.read_header(value)
if not version or version > worldedit.LATEST_SERIALIZATION_VERSION then
return 0
end
return version
end
function worldedit.replaceinverse(pos1, pos2, search_node, replace_node)
deprecated("replace")
return worldedit.replace(pos1, pos2, search_node, replace_node, true)
end
function worldedit.clearobjects(...)
deprecated("clear_objects")
return worldedit.clear_objects(...)
end
function worldedit.hollow_sphere(pos, radius, node_name)
deprecated("sphere")
return worldedit.sphere(pos, radius, node_name, true)
end
function worldedit.hollow_dome(pos, radius, node_name)
deprecated("dome")
return worldedit.dome(pos, radius, node_name, true)
end
function worldedit.hollow_cylinder(pos, axis, length, radius, node_name)
deprecated("cylinder")
return worldedit.cylinder(pos, axis, length, radius, node_name, true)
end

View File

@ -1,25 +1,44 @@
worldedit = worldedit or {} --- Worldedit.
worldedit.version = {major=1, minor=0} -- @module worldedit
worldedit.version_string = "1.0" -- @release 1.1
-- @copyright 2013 sfan5, Anthony Zhang (Uberi/Temperest), and Brett O'Donnell (cornernote).
assert(minetest.get_voxel_manip, string.rep(">", 300) .. "HEY YOU! YES, YOU OVER THERE. THIS VERSION OF WORLDEDIT REQUIRES MINETEST 0.4.8 OR LATER! YOU HAVE AN OLD VERSION." .. string.rep("<", 300)) -- @license GNU Affero General Public License version 3 (AGPLv3)
-- @author sfan5
local path = minetest.get_modpath(minetest.get_current_modname()) -- @author Anthony Zang (Uberi/Temperest)
-- @author Bret O'Donnel (cornernote)
local loadmodule = function(path) -- @author ShadowNinja
local file = io.open(path)
if not file then worldedit = {}
return worldedit.version = {1, 1, major=1, minor=1}
end worldedit.version_string = table.concat(worldedit.version, ".")
file:close()
return dofile(path) if not minetest.get_voxel_manip then
end local err_msg = "This version of WorldEdit requires Minetest 0.4.8 or later! You have an old version."
minetest.log("error", string.rep("#", 128))
loadmodule(path .. "/manipulations.lua") minetest.log("error", err_msg)
loadmodule(path .. "/primitives.lua") minetest.log("error", string.rep("#", 128))
loadmodule(path .. "/visualization.lua") error(err_msg)
loadmodule(path .. "/serialization.lua") end
loadmodule(path .. "/code.lua")
loadmodule(path .. "/compatibility.lua") local path = minetest.get_modpath(minetest.get_current_modname())
print("[MOD] WorldEdit loaded!") local function load_module(path)
local file = io.open(path)
if not file then return end
file:close()
return dofile(path)
end
dofile(path .. "/common.lua")
load_module(path .. "/manipulations.lua")
load_module(path .. "/primitives.lua")
load_module(path .. "/visualization.lua")
load_module(path .. "/serialization.lua")
load_module(path .. "/code.lua")
load_module(path .. "/compatibility.lua")
if minetest.setting_getbool("log_mods") then
print("[WorldEdit] Loaded!")
end

File diff suppressed because it is too large Load Diff

View File

@ -1,470 +1,273 @@
worldedit = worldedit or {} --- Functions for creating primitive shapes.
local minetest = minetest --local copy of global -- @module worldedit.primitives
--adds a hollow sphere centered at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added local mh = worldedit.manip_helpers
worldedit.hollow_sphere = function(pos, radius, nodename)
--set up voxel manipulator
local manip = minetest.get_voxel_manip() --- Adds a sphere of `node_name` centered at `pos`.
local pos1 = {x=pos.x - radius, y=pos.y - radius, z=pos.z - radius} -- @param pos Position to center sphere at.
local pos2 = {x=pos.x + radius, y=pos.y + radius, z=pos.z + radius} -- @param radius Sphere radius.
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2) -- @param node_name Name of node to make shere of.
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2}) -- @param hollow Whether the sphere should be hollow.
-- @return The number of nodes added.
--fill emerged area with ignore function worldedit.sphere(pos, radius, node_name, hollow)
local nodes = {} local manip, area = mh.init_radius(pos, radius)
local ignore = minetest.get_content_id("ignore")
for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do local data = mh.get_empty_data(area)
nodes[i] = ignore
end -- Fill selected area with node
local node_id = minetest.get_content_id(node_name)
--fill selected area with node local min_radius, max_radius = radius * (radius - 1), radius * (radius + 1)
local node_id = minetest.get_content_id(nodename) local offset_x, offset_y, offset_z = pos.x - area.MinEdge.x, pos.y - area.MinEdge.y, pos.z - area.MinEdge.z
local min_radius, max_radius = radius * (radius - 1), radius * (radius + 1) local stride_z, stride_y = area.zstride, area.ystride
local offsetx, offsety, offsetz = pos.x - emerged_pos1.x, pos.y - emerged_pos1.y, pos.z - emerged_pos1.z local count = 0
local zstride, ystride = area.zstride, area.ystride for z = -radius, radius do
local count = 0 -- Offset contributed by z plus 1 to make it 1-indexed
for z = -radius, radius do local new_z = (z + offset_z) * stride_z + 1
local newz = (z + offsetz) * zstride + 1 --offset contributed by z plus 1 to make it 1-indexed for y = -radius, radius do
for y = -radius, radius do local new_y = new_z + (y + offset_y) * stride_y
local newy = newz + (y + offsety) * ystride for x = -radius, radius do
for x = -radius, radius do local squared = x * x + y * y + z * z
local squared = x * x + y * y + z * z if squared <= max_radius and (not hollow or squared >= min_radius) then
if squared >= min_radius and squared <= max_radius then --position is on surface of sphere -- Position is on surface of sphere
local i = newy + (x + offsetx) local i = new_y + (x + offset_x)
nodes[i] = node_id data[i] = node_id
count = count + 1 count = count + 1
end end
end end
end end
end end
--update map nodes mh.finish(manip, data)
manip:set_data(nodes)
manip:write_to_map() return count
manip:update_map() end
return count
end --- Adds a dome.
-- @param pos Position to center dome at.
--adds a sphere centered at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added -- @param radius Dome radius. Negative for concave domes.
worldedit.sphere = function(pos, radius, nodename) -- @param node_name Name of node to make dome of.
--set up voxel manipulator -- @param hollow Whether the dome should be hollow.
local manip = minetest.get_voxel_manip() -- @return The number of nodes added.
local pos1 = {x=pos.x - radius, y=pos.y - radius, z=pos.z - radius} -- TODO: Add axis option.
local pos2 = {x=pos.x + radius, y=pos.y + radius, z=pos.z + radius} function worldedit.dome(pos, radius, node_name, hollow)
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2) local min_y, max_y = 0, radius
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2}) if radius < 0 then
radius = -radius
--fill emerged area with ignore min_y, max_y = -radius, 0
local nodes = {} end
local ignore = minetest.get_content_id("ignore")
for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do local manip, area = mh.init_axis_radius(pos, "y", radius)
nodes[i] = ignore local data = mh.get_empty_data(area)
end
-- Add dome
--fill selected area with node local node_id = minetest.get_content_id(node_name)
local node_id = minetest.get_content_id(nodename) local min_radius, max_radius = radius * (radius - 1), radius * (radius + 1)
local max_radius = radius * (radius + 1) local offset_x, offset_y, offset_z = pos.x - area.MinEdge.x, pos.y - area.MinEdge.y, pos.z - area.MinEdge.z
local offsetx, offsety, offsetz = pos.x - emerged_pos1.x, pos.y - emerged_pos1.y, pos.z - emerged_pos1.z local stride_z, stride_y = area.zstride, area.ystride
local zstride, ystride = area.zstride, area.ystride local count = 0
local count = 0 for z = -radius, radius do
for z = -radius, radius do local new_z = (z + offset_z) * stride_z + 1 --offset contributed by z plus 1 to make it 1-indexed
local newz = (z + offsetz) * zstride + 1 --offset contributed by z plus 1 to make it 1-indexed for y = min_y, max_y do
for y = -radius, radius do local new_y = new_z + (y + offset_y) * stride_y
local newy = newz + (y + offsety) * ystride for x = -radius, radius do
for x = -radius, radius do local squared = x * x + y * y + z * z
if x * x + y * y + z * z <= max_radius then --position is inside sphere if squared <= max_radius and (not hollow or squared >= min_radius) then
local i = newy + (x + offsetx) -- Position is in dome
nodes[i] = node_id local i = new_y + (x + offset_x)
count = count + 1 data[i] = node_id
end count = count + 1
end end
end end
end end
end
--update map nodes
manip:set_data(nodes) mh.finish(manip, data)
manip:write_to_map()
manip:update_map() return count
end
return count
end --- Adds a cylinder.
-- @param pos Position to center base of cylinder at.
--adds a hollow dome centered at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added -- @param axis Axis ("x", "y", or "z")
worldedit.hollow_dome = function(pos, radius, nodename) -- @param length Cylinder length.
--set up voxel manipulator -- @param radius Cylinder radius.
local manip = minetest.get_voxel_manip() -- @param node_name Name of node to make cylinder of.
local pos1 = {x=pos.x - radius, y=pos.y, z=pos.z - radius} -- @param hollow Whether the cylinder should be hollow.
local pos2 = {x=pos.x + radius, y=pos.y + radius, z=pos.z + radius} -- @return The number of nodes added.
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2) function worldedit.cylinder(pos, axis, length, radius, node_name, hollow)
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2}) local other1, other2 = worldedit.get_axis_others(axis)
--fill emerged area with ignore -- Handle negative lengths
local nodes = {} local current_pos = {x=pos.x, y=pos.y, z=pos.z}
local ignore = minetest.get_content_id("ignore") if length < 0 then
for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do length = -length
nodes[i] = ignore current_pos[axis] = current_pos[axis] - length
end end
local miny, maxy = 0, radius -- Set up voxel manipulator
if radius < 0 then local manip, area = mh.init_axis_radius_length(current_pos, axis, radius, length)
radius = -radius local data = mh.get_empty_data(area)
miny, maxy = -radius, 0
end -- Add cylinder
local node_id = minetest.get_content_id(node_name)
--fill selected area with node local min_radius, max_radius = radius * (radius - 1), radius * (radius + 1)
local node_id = minetest.get_content_id(nodename) local stride = {x=1, y=area.ystride, z=area.zstride}
local min_radius, max_radius = radius * (radius - 1), radius * (radius + 1) local offset = {
local offsetx, offsety, offsetz = pos.x - emerged_pos1.x, pos.y - emerged_pos1.y, pos.z - emerged_pos1.z x = current_pos.x - area.MinEdge.x,
local zstride, ystride = area.zstride, area.ystride y = current_pos.y - area.MinEdge.y,
local count = 0 z = current_pos.z - area.MinEdge.z,
for z = -radius, radius do }
local newz = (z + offsetz) * zstride + 1 --offset contributed by z plus 1 to make it 1-indexed local min_slice, max_slice = offset[axis], offset[axis] + length - 1
for y = miny, maxy do local count = 0
local newy = newz + (y + offsety) * ystride for index2 = -radius, radius do
for x = -radius, radius do -- Offset contributed by other axis 1 plus 1 to make it 1-indexed
local squared = x * x + y * y + z * z local new_index2 = (index2 + offset[other1]) * stride[other1] + 1
if squared >= min_radius and squared <= max_radius then --position is on surface of sphere for index3 = -radius, radius do
local i = newy + (x + offsetx) local new_index3 = new_index2 + (index3 + offset[other2]) * stride[other2]
nodes[i] = node_id local squared = index2 * index2 + index3 * index3
count = count + 1 if squared <= max_radius and (not hollow or squared >= min_radius) then
end -- Position is in cylinder
end -- Add column along axis
end for index1 = min_slice, max_slice do
end local vi = new_index3 + index1 * stride[axis]
data[vi] = node_id
--update map nodes end
manip:set_data(nodes) count = count + length
manip:write_to_map() end
manip:update_map() end
end
return count
end mh.finish(manip, data)
--adds a dome centered at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added return count
worldedit.dome = function(pos, radius, nodename) end
--set up voxel manipulator
local manip = minetest.get_voxel_manip()
local pos1 = {x=pos.x - radius, y=pos.y, z=pos.z - radius} --- Adds a pyramid.
local pos2 = {x=pos.x + radius, y=pos.y + radius, z=pos.z + radius} -- @param pos Position to center base of pyramid at.
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2) -- @param axis Axis ("x", "y", or "z")
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2}) -- @param height Pyramid height.
-- @param node_name Name of node to make pyramid of.
--fill emerged area with ignore -- @return The number of nodes added.
local nodes = {} function worldedit.pyramid(pos, axis, height, node_name)
local ignore = minetest.get_content_id("ignore") local other1, other2 = worldedit.get_axis_others(axis)
for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do
nodes[i] = ignore -- Set up voxel manipulator
end local manip, area = mh.init_axis_radius(pos, axis,
height >= 0 and height or -height)
local miny, maxy = 0, radius local data = mh.get_empty_data()
if radius < 0 then
radius = -radius -- Handle inverted pyramids
miny, maxy = -radius, 0 local start_axis, end_axis, step
end if height > 0 then
height = height - 1
--fill selected area with node step = 1
local node_id = minetest.get_content_id(nodename) else
local max_radius = radius * (radius + 1) height = height + 1
local offsetx, offsety, offsetz = pos.x - emerged_pos1.x, pos.y - emerged_pos1.y, pos.z - emerged_pos1.z step = -1
local zstride, ystride = area.zstride, area.ystride end
local count = 0
for z = -radius, radius do -- Add pyramid
local newz = (z + offsetz) * zstride + 1 --offset contributed by z plus 1 to make it 1-indexed local node_id = minetest.get_content_id(node_name)
for y = miny, maxy do local stride = {x=1, y=area.ystride, z=area.zstride}
local newy = newz + (y + offsety) * ystride local offset = {
for x = -radius, radius do x = pos.x - area.MinEdge.x,
if x * x + y * y + z * z <= max_radius then --position is inside sphere y = pos.y - area.MinEdge.y,
local i = newy + (x + offsetx) z = pos.z - area.MinEdge.z,
nodes[i] = node_id }
count = count + 1 local size = height * step
end local count = 0
end -- For each level of the pyramid
end for index1 = 0, height, step do
end -- Offset contributed by axis plus 1 to make it 1-indexed
local new_index1 = (index1 + offset[axis]) * stride[axis] + 1
--update map nodes for index2 = -size, size do
manip:set_data(nodes) local new_index2 = new_index1 + (index2 + offset[other1]) * stride[other1]
manip:write_to_map() for index3 = -size, size do
manip:update_map() local i = new_index2 + (index3 + offset[other2]) * stride[other2]
data[i] = node_id
return count end
end end
count = count + (size * 2 + 1) ^ 2
--adds a hollow cylinder at `pos` along the `axis` axis ("x" or "y" or "z") with length `length` and radius `radius`, composed of `nodename`, returning the number of nodes added size = size - 1
worldedit.hollow_cylinder = function(pos, axis, length, radius, nodename) end
local other1, other2
if axis == "x" then mh.finish(manip, data)
other1, other2 = "y", "z"
elseif axis == "y" then return count
other1, other2 = "x", "z" end
else --axis == "z"
other1, other2 = "x", "y" --- Adds a spiral.
end -- @param pos Position to center spiral at.
-- @param length Spral length.
--handle negative lengths -- @param height Spiral height.
local currentpos = {x=pos.x, y=pos.y, z=pos.z} -- @param spacer Space between walls.
if length < 0 then -- @param node_name Name of node to make spiral of.
length = -length -- @return Number of nodes added.
currentpos[axis] = currentpos[axis] - length -- TODO: Add axis option.
end function worldedit.spiral(pos, length, height, spacer, node_name)
local extent = math.ceil(length / 2)
--set up voxel manipulator
local manip = minetest.get_voxel_manip() local manip, area = mh.init_axis_radius_length(pos, "y", extent, height)
local pos1 = { local data = mh.get_empty_data(area)
[axis]=currentpos[axis],
[other1]=currentpos[other1] - radius, -- Set up variables
[other2]=currentpos[other2] - radius local node_id = minetest.get_content_id(node_name)
} local stride = {x=1, y=area.ystride, z=area.zstride}
local pos2 = { local offset_x, offset_y, offset_z = pos.x - area.MinEdge.x, pos.y - area.MinEdge.y, pos.z - area.MinEdge.z
[axis]=currentpos[axis] + length - 1, local i = offset_z * stride.z + offset_y * stride.y + offset_x + 1
[other1]=currentpos[other1] + radius,
[other2]=currentpos[other2] + radius -- Add first column
} local count = height
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2) local column = i
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2}) for y = 1, height do
data[column] = node_id
--fill emerged area with ignore column = column + stride.y
local nodes = {} end
local ignore = minetest.get_content_id("ignore")
for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do -- Add spiral segments
nodes[i] = ignore local stride_axis, stride_other = stride.x, stride.z
end local sign = -1
local segment_length = 0
--fill selected area with node spacer = spacer + 1
local node_id = minetest.get_content_id(nodename) -- Go through each segment except the last
local min_radius, max_radius = radius * (radius - 1), radius * (radius + 1) for segment = 1, math.floor(length / spacer) * 2 do
local stride = {x=1, y=area.ystride, z=area.zstride} -- Change sign and length every other turn starting with the first
local offset = {x=currentpos.x - emerged_pos1.x, y=currentpos.y - emerged_pos1.y, z=currentpos.z - emerged_pos1.z} if segment % 2 == 1 then
local min_slice, max_slice = offset[axis], offset[axis] + length - 1 sign = -sign
local count = 0 segment_length = segment_length + spacer
for index2 = -radius, radius do end
local newindex2 = (index2 + offset[other1]) * stride[other1] + 1 --offset contributed by other axis 1 plus 1 to make it 1-indexed -- Fill segment
for index3 = -radius, radius do for index = 1, segment_length do
local newindex3 = newindex2 + (index3 + offset[other2]) * stride[other2] -- Move along the direction of the segment
local squared = index2 * index2 + index3 * index3 i = i + stride_axis * sign
if squared >= min_radius and squared <= max_radius then --position is on surface of cylinder local column = i
for index1 = min_slice, max_slice do --add column along axis -- Add column
local i = newindex3 + index1 * stride[axis] for y = 1, height do
nodes[i] = node_id data[column] = node_id
end column = column + stride.y
count = count + length end
end end
end count = count + segment_length * height
end stride_axis, stride_other = stride_other, stride_axis -- Swap axes
end
--update map nodes
manip:set_data(nodes) -- Add shorter final segment
manip:write_to_map() sign = -sign
manip:update_map() for index = 1, segment_length do
i = i + stride_axis * sign
return count local column = i
end -- Add column
for y = 1, height do
--adds a cylinder at `pos` along the `axis` axis ("x" or "y" or "z") with length `length` and radius `radius`, composed of `nodename`, returning the number of nodes added data[column] = node_id
worldedit.cylinder = function(pos, axis, length, radius, nodename) column = column + stride.y
local other1, other2 end
if axis == "x" then end
other1, other2 = "y", "z" count = count + segment_length * height
elseif axis == "y" then
other1, other2 = "x", "z" mh.finish(manip, data)
else --axis == "z"
other1, other2 = "x", "y" return count
end end
--handle negative lengths
local currentpos = {x=pos.x, y=pos.y, z=pos.z}
if length < 0 then
length = -length
currentpos[axis] = currentpos[axis] - length
end
--set up voxel manipulator
local manip = minetest.get_voxel_manip()
local pos1 = {
[axis]=currentpos[axis],
[other1]=currentpos[other1] - radius,
[other2]=currentpos[other2] - radius
}
local pos2 = {
[axis]=currentpos[axis] + length - 1,
[other1]=currentpos[other1] + radius,
[other2]=currentpos[other2] + radius
}
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
--fill emerged area with ignore
local nodes = {}
local ignore = minetest.get_content_id("ignore")
for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do
nodes[i] = ignore
end
--fill selected area with node
local node_id = minetest.get_content_id(nodename)
local max_radius = radius * (radius + 1)
local stride = {x=1, y=area.ystride, z=area.zstride}
local offset = {x=currentpos.x - emerged_pos1.x, y=currentpos.y - emerged_pos1.y, z=currentpos.z - emerged_pos1.z}
local min_slice, max_slice = offset[axis], offset[axis] + length - 1
local count = 0
for index2 = -radius, radius do
local newindex2 = (index2 + offset[other1]) * stride[other1] + 1 --offset contributed by other axis 1 plus 1 to make it 1-indexed
for index3 = -radius, radius do
local newindex3 = newindex2 + (index3 + offset[other2]) * stride[other2]
if index2 * index2 + index3 * index3 <= max_radius then --position is within cylinder
for index1 = min_slice, max_slice do --add column along axis
local i = newindex3 + index1 * stride[axis]
nodes[i] = node_id
end
count = count + length
end
end
end
--update map nodes
manip:set_data(nodes)
manip:write_to_map()
manip:update_map()
return count
end
--adds a pyramid centered at `pos` with height `height`, composed of `nodename`, returning the number of nodes added
worldedit.pyramid = function(pos, axis, height, nodename)
local other1, other2
if axis == "x" then
other1, other2 = "y", "z"
elseif axis == "y" then
other1, other2 = "x", "z"
else --axis == "z"
other1, other2 = "x", "y"
end
local pos1 = {x=pos.x - height, y=pos.y - height, z=pos.z - height}
local pos2 = {x=pos.x + height, y=pos.y + height, z=pos.z + height}
--handle inverted pyramids
local startaxis, endaxis, step
if height > 0 then
height = height - 1
step = 1
pos1[axis] = pos[axis] --upper half of box
else
height = height + 1
step = -1
pos2[axis] = pos[axis] --lower half of box
end
--set up voxel manipulator
local manip = minetest.get_voxel_manip()
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
--fill emerged area with ignore
local nodes = {}
local ignore = minetest.get_content_id("ignore")
for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do
nodes[i] = ignore
end
--fill selected area with node
local node_id = minetest.get_content_id(nodename)
local stride = {x=1, y=area.ystride, z=area.zstride}
local offset = {x=pos.x - emerged_pos1.x, y=pos.y - emerged_pos1.y, z=pos.z - emerged_pos1.z}
local size = height * step
local count = 0
for index1 = 0, height, step do --go through each level of the pyramid
local newindex1 = (index1 + offset[axis]) * stride[axis] + 1 --offset contributed by axis plus 1 to make it 1-indexed
for index2 = -size, size do
local newindex2 = newindex1 + (index2 + offset[other1]) * stride[other1]
for index3 = -size, size do
local i = newindex2 + (index3 + offset[other2]) * stride[other2]
nodes[i] = node_id
end
end
count = count + (size * 2 + 1) ^ 2
size = size - 1
end
--update map nodes
manip:set_data(nodes)
manip:write_to_map()
manip:update_map()
return count
end
--adds a spiral centered at `pos` with side length `length`, height `height`, space between walls `spacer`, composed of `nodename`, returning the number of nodes added
worldedit.spiral = function(pos, length, height, spacer, nodename)
local extent = math.ceil(length / 2)
local pos1 = {x=pos.x - extent, y=pos.y, z=pos.z - extent}
local pos2 = {x=pos.x + extent, y=pos.y + height, z=pos.z + extent}
--set up voxel manipulator
local manip = minetest.get_voxel_manip()
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
--fill emerged area with ignore
local nodes = {}
local ignore = minetest.get_content_id("ignore")
for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do
nodes[i] = ignore
end
--set up variables
local node_id = minetest.get_content_id(nodename)
local stride = {x=1, y=area.ystride, z=area.zstride}
local offsetx, offsety, offsetz = pos.x - emerged_pos1.x, pos.y - emerged_pos1.y, pos.z - emerged_pos1.z
local i = offsetz * stride.z + offsety * stride.y + offsetx + 1
--add first column
local count = height
local column = i
for y = 1, height do
nodes[column] = node_id
column = column + stride.y
end
--add spiral segments
local strideaxis, strideother = stride.x, stride.z
local sign = -1
local segment_length = 0
spacer = spacer + 1
for segment = 1, math.floor(length / spacer) * 2 do --go through each segment except the last
if segment % 2 == 1 then --change sign and length every other turn starting with the first
sign = -sign
segment_length = segment_length + spacer
end
for index = 1, segment_length do --fill segment
i = i + strideaxis * sign --move along the direction of the segment
local column = i
for y = 1, height do --add column
nodes[column] = node_id
column = column + stride.y
end
end
count = count + segment_length * height
strideaxis, strideother = strideother, strideaxis --swap axes
end
--add shorter final segment
sign = -sign
for index = 1, segment_length do
i = i + strideaxis * sign
local column = i
for y = 1, height do --add column
nodes[column] = node_id
column = column + stride.y
end
end
count = count + segment_length * height
--update map nodes
manip:set_data(nodes)
manip:write_to_map()
manip:update_map()
return count
end

View File

@ -1,273 +1,239 @@
worldedit = worldedit or {} --- Schematic serialization and deserialiation.
local minetest = minetest --local copy of global -- @module worldedit.serialization
--modifies positions `pos1` and `pos2` so that each component of `pos1` is less than or equal to its corresponding conent of `pos2`, returning two new positions worldedit.LATEST_SERIALIZATION_VERSION = 5
worldedit.sort_pos = function(pos1, pos2) local LATEST_SERIALIZATION_HEADER = worldedit.LATEST_SERIALIZATION_VERSION .. ":"
pos1 = {x=pos1.x, y=pos1.y, z=pos1.z}
pos2 = {x=pos2.x, y=pos2.y, z=pos2.z}
if pos1.x > pos2.x then --[[
pos2.x, pos1.x = pos1.x, pos2.x Serialization version history:
end 1: Original format. Serialized Lua table with a weird linked format...
if pos1.y > pos2.y then 2: Position and node seperated into sub-tables in fields `1` and `2`.
pos2.y, pos1.y = pos1.y, pos2.y 3: List of nodes, one per line, with fields seperated by spaces.
end Format: <X> <Y> <Z> <Name> <Param1> <Param2>
if pos1.z > pos2.z then 4: Serialized Lua table containing a list of nodes with `x`, `y`, `z`,
pos2.z, pos1.z = pos1.z, pos2.z `name`, `param1`, `param2`, and `meta` fields.
end 5: Added header and made `param1`, `param2`, and `meta` fields optional.
return pos1, pos2 Header format: <Version>,<ExtraHeaderField1>,...:<Content>
end --]]
--determines the version of serialized data `value`, returning the version as a positive integer or 0 for unknown versions
worldedit.valueversion = function(value) --- Reads the header of serialized data.
if value:find("([+-]?%d+)%s+([+-]?%d+)%s+([+-]?%d+)") and not value:find("%{") then --previous list format -- @param value Serialized WorldEdit data.
return 3 -- @return The version as a positive natural number, or 0 for unknown versions.
elseif value:find("^[^\"']+%{%d+%}") then -- @return Extra header fields as a list of strings, or nil if not supported.
if value:find("%[\"meta\"%]") then --previous meta flat table format -- @return Content (data after header).
return 2 function worldedit.read_header(value)
end if value:find("^[0-9]+[%-:]") then
return 1 --original flat table format local header_end = value:find(":", 1, true)
elseif value:find("%{") then --current nested table format local header = value:sub(1, header_end - 1):split(",")
return 4 local version = tonumber(header[1])
end table.remove(header, 1)
return 0 --unknown format local content = value:sub(header_end + 1)
end return version, header, content
end
--converts the region defined by positions `pos1` and `pos2` into a single string, returning the serialized data and the number of nodes serialized -- Old versions that didn't include a header with a version number
worldedit.serialize = function(pos1, pos2) if value:find("([+-]?%d+)%s+([+-]?%d+)%s+([+-]?%d+)") and not value:find("%{") then -- List format
--make area stay loaded return 3, nil, value
local manip = minetest.get_voxel_manip() elseif value:find("^[^\"']+%{%d+%}") then
manip:read_from_map(pos1, pos2) if value:find("%[\"meta\"%]") then -- Meta flat table format
return 2, nil, value
local pos1, pos2 = worldedit.sort_pos(pos1, pos2) end
local pos = {x=pos1.x, y=0, z=0} return 1, nil, value -- Flat table format
local count = 0 elseif value:find("%{") then -- Raw nested table format
local result = {} return 4, nil, value
local get_node, get_meta = minetest.get_node, minetest.get_meta end
while pos.x <= pos2.x do return nil
pos.y = pos1.y end
while pos.y <= pos2.y do
pos.z = pos1.z
while pos.z <= pos2.z do --- Converts the region defined by positions `pos1` and `pos2`
local node = get_node(pos) -- into a single string.
if node.name ~= "air" and node.name ~= "ignore" then -- @return The serialized data.
count = count + 1 -- @return The number of nodes serialized.
local meta = get_meta(pos):to_table() function worldedit.serialize(pos1, pos2)
pos1, pos2 = worldedit.sort_pos(pos1, pos2)
--convert metadata itemstacks to itemstrings
for name, inventory in pairs(meta.inventory) do worldedit.keep_loaded(pos1, pos2)
for index, stack in ipairs(inventory) do
inventory[index] = stack.to_string and stack:to_string() or stack local pos = {x=pos1.x, y=0, z=0}
end local count = 0
end local result = {}
local get_node, get_meta = minetest.get_node, minetest.get_meta
result[count] = { while pos.x <= pos2.x do
x = pos.x - pos1.x, pos.y = pos1.y
y = pos.y - pos1.y, while pos.y <= pos2.y do
z = pos.z - pos1.z, pos.z = pos1.z
name = node.name, while pos.z <= pos2.z do
param1 = node.param1, local node = get_node(pos)
param2 = node.param2, if node.name ~= "air" and node.name ~= "ignore" then
meta = meta, count = count + 1
} local meta = get_meta(pos):to_table()
end
pos.z = pos.z + 1 local meta_empty = true
end -- Convert metadata item stacks to item strings
pos.y = pos.y + 1 for name, inventory in pairs(meta.inventory) do
end for index, stack in ipairs(inventory) do
pos.x = pos.x + 1 meta_empty = false
end inventory[index] = stack.to_string and stack:to_string() or stack
result = minetest.serialize(result) --convert entries to a string end
return result, count end
end for k in pairs(meta) do
if k ~= "inventory" then
--determines the volume the nodes represented by string `value` would occupy if deserialized at `originpos`, returning the two corner positions and the number of nodes meta_empty = false
--contains code based on [table.save/table.load](http://lua-users.org/wiki/SaveTableToFile) by ChillCode, available under the MIT license (GPL compatible) break
worldedit.allocate = function(originpos, value) end
local huge = math.huge end
local pos1x, pos1y, pos1z = huge, huge, huge
local pos2x, pos2y, pos2z = -huge, -huge, -huge result[count] = {
local originx, originy, originz = originpos.x, originpos.y, originpos.z x = pos.x - pos1.x,
local count = 0 y = pos.y - pos1.y,
local version = worldedit.valueversion(value) z = pos.z - pos1.z,
if version == 1 or version == 2 then --flat table format name = node.name,
--obtain the node table param1 = node.param1 ~= 0 and node.param1 or nil,
local get_tables = loadstring(value) param2 = node.param2 ~= 0 and node.param2 or nil,
if get_tables then --error loading value meta = not meta_empty and meta or nil,
return originpos, originpos, count }
end end
local tables = get_tables() pos.z = pos.z + 1
end
--transform the node table into an array of nodes pos.y = pos.y + 1
for i = 1, #tables do end
for j, v in pairs(tables[i]) do pos.x = pos.x + 1
if type(v) == "table" then end
tables[i][j] = tables[v[1]] -- Serialize entries
end result = minetest.serialize(result)
end return LATEST_SERIALIZATION_HEADER .. result, count
end end
local nodes = tables[1]
--check the node array --- Loads the schematic in `value` into a node list in the latest format.
count = #nodes -- Contains code based on [table.save/table.load](http://lua-users.org/wiki/SaveTableToFile)
if version == 1 then --original flat table format -- by ChillCode, available under the MIT license.
for index = 1, count do -- @return A node list in the latest format, or nil on failure.
local entry = nodes[index] local function load_schematic(value)
local pos = entry[1] local version, header, content = worldedit.read_header(value)
local x, y, z = originx - pos.x, originy - pos.y, originz - pos.z local nodes = {}
if x < pos1x then pos1x = x end if version == 1 or version == 2 then -- Original flat table format
if y < pos1y then pos1y = y end local tables = minetest.deserialize(content)
if z < pos1z then pos1z = z end if not tables then return nil end
if x > pos2x then pos2x = x end
if y > pos2y then pos2y = y end -- Transform the node table into an array of nodes
if z > pos2z then pos2z = z end for i = 1, #tables do
end for j, v in pairs(tables[i]) do
else --previous meta flat table format if type(v) == "table" then
for index = 1, count do tables[i][j] = tables[v[1]]
local entry = nodes[index] end
local x, y, z = originx - entry.x, originy - entry.y, originz - entry.z end
if x < pos1x then pos1x = x end end
if y < pos1y then pos1y = y end nodes = tables[1]
if z < pos1z then pos1z = z end
if x > pos2x then pos2x = x end if version == 1 then --original flat table format
if y > pos2y then pos2y = y end for i, entry in ipairs(nodes) do
if z > pos2z then pos2z = z end local pos = entry[1]
end entry.x, entry.y, entry.z = pos.x, pos.y, pos.z
end entry[1] = nil
elseif version == 3 then --previous list format local node = entry[2]
for x, y, z, name, param1, param2 in value:gmatch("([+-]?%d+)%s+([+-]?%d+)%s+([+-]?%d+)%s+([^%s]+)%s+(%d+)%s+(%d+)[^\r\n]*[\r\n]*") do --match node entries entry.name, entry.param1, entry.param2 = node.name, node.param1, node.param2
x, y, z = originx + tonumber(x), originy + tonumber(y), originz + tonumber(z) entry[2] = nil
if x < pos1x then pos1x = x end end
if y < pos1y then pos1y = y end end
if z < pos1z then pos1z = z end elseif version == 3 then -- List format
if x > pos2x then pos2x = x end for x, y, z, name, param1, param2 in content:gmatch(
if y > pos2y then pos2y = y end "([+-]?%d+)%s+([+-]?%d+)%s+([+-]?%d+)%s+" ..
if z > pos2z then pos2z = z end "([^%s]+)%s+(%d+)%s+(%d+)[^\r\n]*[\r\n]*") do
count = count + 1 param1, param2 = tonumber(param1), tonumber(param2)
end table.insert(nodes, {
elseif version == 4 then --current nested table format x = originx + tonumber(x),
--wip: this is a filthy hack that works surprisingly well y = originy + tonumber(y),
value = value:gsub("return%s*{", "", 1):gsub("}%s*$", "", 1) z = originz + tonumber(z),
local escaped = value:gsub("\\\\", "@@"):gsub("\\\"", "@@"):gsub("(\"[^\"]*\")", function(s) return string.rep("@", #s) end) name = name,
local startpos, startpos1, endpos = 1, 1 param1 = param1 ~= 0 and param1 or nil,
local nodes = {} param2 = param2 ~= 0 and param2 or nil,
while true do })
startpos, endpos = escaped:find("},%s*{", startpos) end
if not startpos then elseif version == 4 or version == 5 then -- Nested table format
break if not jit then
end -- This is broken for larger tables in the current version of LuaJIT
local current = value:sub(startpos1, startpos) nodes = minetest.deserialize(content)
table.insert(nodes, minetest.deserialize("return " .. current)) else
startpos, startpos1 = endpos, endpos -- XXX: This is a filthy hack that works surprisingly well - in LuaJIT, `minetest.deserialize` will fail due to the register limit
end nodes = {}
table.insert(nodes, minetest.deserialize("return " .. value:sub(startpos1))) value = value:gsub("return%s*{", "", 1):gsub("}%s*$", "", 1) -- remove the starting and ending values to leave only the node data
local escaped = value:gsub("\\\\", "@@"):gsub("\\\"", "@@"):gsub("(\"[^\"]*\")", function(s) return string.rep("@", #s) end)
--local nodes = minetest.deserialize(value) --wip: this is broken for larger tables in the current version of LuaJIT local startpos, startpos1, endpos = 1, 1
while true do -- go through each individual node entry (except the last)
count = #nodes startpos, endpos = escaped:find("},%s*{", startpos)
for index = 1, count do if not startpos then
local entry = nodes[index] break
x, y, z = originx + entry.x, originy + entry.y, originz + entry.z end
if x < pos1x then pos1x = x end local current = value:sub(startpos1, startpos)
if y < pos1y then pos1y = y end local entry = minetest.deserialize("return " .. current)
if z < pos1z then pos1z = z end table.insert(nodes, entry)
if x > pos2x then pos2x = x end startpos, startpos1 = endpos, endpos
if y > pos2y then pos2y = y end end
if z > pos2z then pos2z = z end local entry = minetest.deserialize("return " .. value:sub(startpos1)) -- process the last entry
end table.insert(nodes, entry)
end end
local pos1 = {x=pos1x, y=pos1y, z=pos1z} else
local pos2 = {x=pos2x, y=pos2y, z=pos2z} return nil
return pos1, pos2, count end
end return nodes
end
--loads the nodes represented by string `value` at position `originpos`, returning the number of nodes deserialized
--contains code based on [table.save/table.load](http://lua-users.org/wiki/SaveTableToFile) by ChillCode, available under the MIT license (GPL compatible) --- Determines the volume the nodes represented by string `value` would occupy
worldedit.deserialize = function(originpos, value) -- if deserialized at `origin_pos`.
--make area stay loaded -- @return Low corner position.
local pos1, pos2 = worldedit.allocate(originpos, value) -- @return High corner position.
local manip = minetest.get_voxel_manip() -- @return The number of nodes.
manip:read_from_map(pos1, pos2) function worldedit.allocate(origin_pos, value)
local nodes = load_schematic(value)
local originx, originy, originz = originpos.x, originpos.y, originpos.z if not nodes then return nil end
local count = 0 return worldedit.allocate_with_nodes(origin_pos, nodes)
local add_node, get_meta = minetest.add_node, minetest.get_meta end
local version = worldedit.valueversion(value)
if version == 1 or version == 2 then --original flat table format
--obtain the node table -- Internal
local get_tables = loadstring(value) function worldedit.allocate_with_nodes(origin_pos, nodes)
if not get_tables then --error loading value local huge = math.huge
return count local pos1x, pos1y, pos1z = huge, huge, huge
end local pos2x, pos2y, pos2z = -huge, -huge, -huge
local tables = get_tables() local origin_x, origin_y, origin_z = origin_pos.x, origin_pos.y, origin_pos.z
for i, entry in ipairs(nodes) do
--transform the node table into an array of nodes local x, y, z = origin_x + entry.x, origin_y + entry.y, origin_z + entry.z
for i = 1, #tables do if x < pos1x then pos1x = x end
for j, v in pairs(tables[i]) do if y < pos1y then pos1y = y end
if type(v) == "table" then if z < pos1z then pos1z = z end
tables[i][j] = tables[v[1]] if x > pos2x then pos2x = x end
end if y > pos2y then pos2y = y end
end if z > pos2z then pos2z = z end
end end
local nodes = tables[1] local pos1 = {x=pos1x, y=pos1y, z=pos1z}
local pos2 = {x=pos2x, y=pos2y, z=pos2z}
--load the node array return pos1, pos2, #nodes
count = #nodes end
if version == 1 then --original flat table format
for index = 1, count do
local entry = nodes[index] --- Loads the nodes represented by string `value` at position `origin_pos`.
local pos = entry[1] -- @return The number of nodes deserialized.
pos.x, pos.y, pos.z = originx - pos.x, originy - pos.y, originz - pos.z function worldedit.deserialize(origin_pos, value)
add_node(pos, entry[2]) local nodes = load_schematic(value)
end if not nodes then return nil end
else --previous meta flat table format
for index = 1, #nodes do local pos1, pos2 = worldedit.allocate_with_nodes(origin_pos, nodes)
local entry = nodes[index] worldedit.keep_loaded(pos1, pos2)
entry.x, entry.y, entry.z = originx + entry.x, originy + entry.y, originz + entry.z
add_node(entry, entry) --entry acts both as position and as node local origin_x, origin_y, origin_z = origin_pos.x, origin_pos.y, origin_pos.z
get_meta(entry):from_table(entry.meta) local count = 0
end local add_node, get_meta = minetest.add_node, minetest.get_meta
end for i, entry in ipairs(nodes) do
elseif version == 3 then --previous list format entry.x, entry.y, entry.z = origin_x + entry.x, origin_y + entry.y, origin_z + entry.z
local pos = {x=0, y=0, z=0} -- Entry acts as both position and node
local node = {name="", param1=0, param2=0} add_node(entry, entry)
for x, y, z, name, param1, param2 in value:gmatch("([+-]?%d+)%s+([+-]?%d+)%s+([+-]?%d+)%s+([^%s]+)%s+(%d+)%s+(%d+)[^\r\n]*[\r\n]*") do --match node entries if entry.meta then
pos.x, pos.y, pos.z = originx + tonumber(x), originy + tonumber(y), originz + tonumber(z) get_meta(entry):from_table(entry.meta)
node.name, node.param1, node.param2 = name, param1, param2 end
add_node(pos, node) end
count = count + 1 return #nodes
end end
elseif version == 4 then --current nested table format
--wip: this is a filthy hack that works surprisingly well
value = value:gsub("return%s*{", "", 1):gsub("}%s*$", "", 1)
local escaped = value:gsub("\\\\", "@@"):gsub("\\\"", "@@"):gsub("(\"[^\"]*\")", function(s) return string.rep("@", #s) end)
local startpos, startpos1, endpos = 1, 1
local nodes = {}
while true do
startpos, endpos = escaped:find("},%s*{", startpos)
if not startpos then
break
end
local current = value:sub(startpos1, startpos)
table.insert(nodes, minetest.deserialize("return " .. current))
startpos, startpos1 = endpos, endpos
end
table.insert(nodes, minetest.deserialize("return " .. value:sub(startpos1)))
--local nodes = minetest.deserialize(value) --wip: this is broken for larger tables in the current version of LuaJIT
--load the nodes
count = #nodes
for index = 1, count do
local entry = nodes[index]
entry.x, entry.y, entry.z = originx + entry.x, originy + entry.y, originz + entry.z
add_node(entry, entry) --entry acts both as position and as node
end
--load the metadata
for index = 1, count do
local entry = nodes[index]
get_meta(entry):from_table(entry.meta)
end
end
return count
end

View File

@ -1,142 +1,131 @@
worldedit = worldedit or {} --- Functions for visibly hiding nodes
local minetest = minetest --local copy of global -- @module worldedit.visualization
--modifies positions `pos1` and `pos2` so that each component of `pos1` is less than or equal to its corresponding conent of `pos2`, returning two new positions minetest.register_node("worldedit:placeholder", {
worldedit.sort_pos = function(pos1, pos2) drawtype = "airlike",
pos1 = {x=pos1.x, y=pos1.y, z=pos1.z} paramtype = "light",
pos2 = {x=pos2.x, y=pos2.y, z=pos2.z} sunlight_propagates = true,
if pos1.x > pos2.x then diggable = false,
pos2.x, pos1.x = pos1.x, pos2.x walkable = false,
end groups = {not_in_creative_inventory=1},
if pos1.y > pos2.y then })
pos2.y, pos1.y = pos1.y, pos2.y
end --- Hides all nodes in a region defined by positions `pos1` and `pos2` by
if pos1.z > pos2.z then -- non-destructively replacing them with invisible nodes.
pos2.z, pos1.z = pos1.z, pos2.z -- @return The number of nodes hidden.
end function worldedit.hide(pos1, pos2)
return pos1, pos2 pos1, pos2 = worldedit.sort_pos(pos1, pos2)
end
worldedit.keep_loaded(pos1, pos2)
--determines the volume of the region defined by positions `pos1` and `pos2`, returning the volume
worldedit.volume = function(pos1, pos2) local pos = {x=pos1.x, y=0, z=0}
local pos1, pos2 = worldedit.sort_pos(pos1, pos2) local get_node, get_meta, swap_node = minetest.get_node,
return (pos2.x - pos1.x + 1) * (pos2.y - pos1.y + 1) * (pos2.z - pos1.z + 1) minetest.get_meta, minetest.swap_node
end while pos.x <= pos2.x do
pos.y = pos1.y
minetest.register_node("worldedit:placeholder", { while pos.y <= pos2.y do
drawtype = "airlike", pos.z = pos1.z
paramtype = "light", while pos.z <= pos2.z do
sunlight_propagates = true, local node = get_node(pos)
diggable = false, if node.name ~= "air" and node.name ~= "worldedit:placeholder" then
groups = {not_in_creative_inventory=1}, -- Save the node's original name
}) get_meta(pos):set_string("worldedit_placeholder", node.name)
-- Swap in placeholder node
--hides all nodes in a region defined by positions `pos1` and `pos2` by non-destructively replacing them with invisible nodes, returning the number of nodes hidden node.name = "worldedit:placeholder"
worldedit.hide = function(pos1, pos2) swap_node(pos, node)
--make area stay loaded end
local manip = minetest.get_voxel_manip() pos.z = pos.z + 1
manip:read_from_map(pos1, pos2) end
pos.y = pos.y + 1
local pos1, pos2 = worldedit.sort_pos(pos1, pos2) end
local pos = {x=pos1.x, y=0, z=0} pos.x = pos.x + 1
local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node end
while pos.x <= pos2.x do return worldedit.volume(pos1, pos2)
pos.y = pos1.y end
while pos.y <= pos2.y do
pos.z = pos1.z --- Suppresses all instances of `node_name` in a region defined by positions
while pos.z <= pos2.z do -- `pos1` and `pos2` by non-destructively replacing them with invisible nodes.
local node = get_node(pos) -- @return The number of nodes suppressed.
if node.name ~= "worldedit:placeholder" then function worldedit.suppress(pos1, pos2, node_name)
local data = get_meta(pos):to_table() --obtain metadata of original node -- Ignore placeholder supression
data.fields.worldedit_placeholder = node.name --add the node's name if node_name == "worldedit:placeholder" then
node.name = "worldedit:placeholder" --set node name return 0
add_node(pos, node) --add placeholder node end
get_meta(pos):from_table(data) --set placeholder metadata to the original node's metadata
end pos1, pos2 = worldedit.sort_pos(pos1, pos2)
pos.z = pos.z + 1
end worldedit.keep_loaded(pos1, pos2)
pos.y = pos.y + 1
end local nodes = minetest.find_nodes_in_area(pos1, pos2, node_name)
pos.x = pos.x + 1 local get_node, get_meta, swap_node = minetest.get_node,
end minetest.get_meta, minetest.swap_node
return worldedit.volume(pos1, pos2) for _, pos in ipairs(nodes) do
end local node = get_node(pos)
-- Save the node's original name
--suppresses all instances of `nodename` in a region defined by positions `pos1` and `pos2` by non-destructively replacing them with invisible nodes, returning the number of nodes suppressed get_meta(pos):set_string("worldedit_placeholder", node.name)
worldedit.suppress = function(pos1, pos2, nodename) -- Swap in placeholder node
--ignore placeholder supression node.name = "worldedit:placeholder"
if nodename == "worldedit:placeholder" then swap_node(pos, node)
return 0 end
end return #nodes
end
--make area stay loaded
local manip = minetest.get_voxel_manip() --- Highlights all instances of `node_name` in a region defined by positions
manip:read_from_map(pos1, pos2) -- `pos1` and `pos2` by non-destructively hiding all other nodes.
-- @return The number of nodes found.
local pos1, pos2 = worldedit.sort_pos(pos1, pos2) function worldedit.highlight(pos1, pos2, node_name)
local nodes = minetest.find_nodes_in_area(pos1, pos2, nodename) pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node
for _, pos in ipairs(nodes) do worldedit.keep_loaded(pos1, pos2)
local node = get_node(pos)
local data = get_meta(pos):to_table() --obtain metadata of original node local pos = {x=pos1.x, y=0, z=0}
data.fields.worldedit_placeholder = node.name --add the node's name local get_node, get_meta, swap_node = minetest.get_node,
node.name = "worldedit:placeholder" --set node name minetest.get_meta, minetest.swap_node
add_node(pos, node) --add placeholder node local count = 0
get_meta(pos):from_table(data) --set placeholder metadata to the original node's metadata while pos.x <= pos2.x do
end pos.y = pos1.y
return #nodes while pos.y <= pos2.y do
end pos.z = pos1.z
while pos.z <= pos2.z do
--highlights all instances of `nodename` in a region defined by positions `pos1` and `pos2` by non-destructively hiding all other nodes, returning the number of nodes found local node = get_node(pos)
worldedit.highlight = function(pos1, pos2, nodename) if node.name == node_name then -- Node found
--make area stay loaded count = count + 1
local manip = minetest.get_voxel_manip() elseif node.name ~= "worldedit:placeholder" then -- Hide other nodes
manip:read_from_map(pos1, pos2) -- Save the node's original name
get_meta(pos):set_string("worldedit_placeholder", node.name)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2) -- Swap in placeholder node
local pos = {x=pos1.x, y=0, z=0} node.name = "worldedit:placeholder"
local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node swap_node(pos, node)
local count = 0 end
while pos.x <= pos2.x do pos.z = pos.z + 1
pos.y = pos1.y end
while pos.y <= pos2.y do pos.y = pos.y + 1
pos.z = pos1.z end
while pos.z <= pos2.z do pos.x = pos.x + 1
local node = get_node(pos) end
if node.name == nodename then --node found return count
count = count + 1 end
elseif node.name ~= "worldedit:placeholder" then --hide other nodes
local data = get_meta(pos):to_table() --obtain metadata of original node -- Restores all nodes hidden with WorldEdit functions in a region defined
data.fields.worldedit_placeholder = node.name --add the node's name -- by positions `pos1` and `pos2`.
node.name = "worldedit:placeholder" --set node name -- @return The number of nodes restored.
add_node(pos, node) --add placeholder node function worldedit.restore(pos1, pos2)
get_meta(pos):from_table(data) --set placeholder metadata to the original node's metadata local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
end
pos.z = pos.z + 1 worldedit.keep_loaded(pos1, pos2)
end
pos.y = pos.y + 1 local nodes = minetest.find_nodes_in_area(pos1, pos2, "worldedit:placeholder")
end local get_node, get_meta, swap_node = minetest.get_node,
pos.x = pos.x + 1 minetest.get_meta, minetest.swap_node
end for _, pos in ipairs(nodes) do
return count local node = get_node(pos)
end local meta = get_meta(pos)
local data = meta:to_table()
--restores all nodes hidden with WorldEdit functions in a region defined by positions `pos1` and `pos2`, returning the number of nodes restored node.name = data.fields.worldedit_placeholder
worldedit.restore = function(pos1, pos2) data.fields.worldedit_placeholder = nil
--make area stay loaded meta:from_table(data)
local manip = minetest.get_voxel_manip() swap_node(pos, node)
manip:read_from_map(pos1, pos2) end
return #nodes
local pos1, pos2 = worldedit.sort_pos(pos1, pos2) end
local nodes = minetest.find_nodes_in_area(pos1, pos2, "worldedit:placeholder")
local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node
for _, pos in ipairs(nodes) do
local node = get_node(pos)
local data = get_meta(pos):to_table() --obtain node metadata
node.name = data.fields.worldedit_placeholder --set node name
data.fields.worldedit_placeholder = nil --delete old nodename
add_node(pos, node) --add original node
get_meta(pos):from_table(data) --set original node metadata
end
return #nodes
end

File diff suppressed because it is too large Load Diff

View File

@ -1,161 +1,162 @@
worldedit.marker1 = {} worldedit.marker1 = {}
worldedit.marker2 = {} worldedit.marker2 = {}
worldedit.marker_region = {} worldedit.marker_region = {}
--marks worldedit region position 1 --marks worldedit region position 1
worldedit.mark_pos1 = function(name) worldedit.mark_pos1 = function(name)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if pos1 ~= nil then if pos1 ~= nil then
--make area stay loaded --make area stay loaded
local manip = minetest.get_voxel_manip() local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos1) manip:read_from_map(pos1, pos1)
end end
if worldedit.marker1[name] ~= nil then --marker already exists if worldedit.marker1[name] ~= nil then --marker already exists
worldedit.marker1[name]:remove() --remove marker worldedit.marker1[name]:remove() --remove marker
worldedit.marker1[name] = nil worldedit.marker1[name] = nil
end end
if pos1 ~= nil then if pos1 ~= nil then
--add marker --add marker
worldedit.marker1[name] = minetest.add_entity(pos1, "worldedit:pos1") worldedit.marker1[name] = minetest.add_entity(pos1, "worldedit:pos1")
if worldedit.marker1[name] ~= nil then if worldedit.marker1[name] ~= nil then
worldedit.marker1[name]:get_luaentity().name = name worldedit.marker1[name]:get_luaentity().player_name = name
end end
end end
worldedit.mark_region(name) worldedit.mark_region(name)
end end
--marks worldedit region position 2 --marks worldedit region position 2
worldedit.mark_pos2 = function(name) worldedit.mark_pos2 = function(name)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if pos2 ~= nil then if pos2 ~= nil then
--make area stay loaded --make area stay loaded
local manip = minetest.get_voxel_manip() local manip = minetest.get_voxel_manip()
manip:read_from_map(pos2, pos2) manip:read_from_map(pos2, pos2)
end end
if worldedit.marker2[name] ~= nil then --marker already exists if worldedit.marker2[name] ~= nil then --marker already exists
worldedit.marker2[name]:remove() --remove marker worldedit.marker2[name]:remove() --remove marker
worldedit.marker2[name] = nil worldedit.marker2[name] = nil
end end
if pos2 ~= nil then if pos2 ~= nil then
--add marker --add marker
worldedit.marker2[name] = minetest.add_entity(pos2, "worldedit:pos2") worldedit.marker2[name] = minetest.add_entity(pos2, "worldedit:pos2")
if worldedit.marker2[name] ~= nil then if worldedit.marker2[name] ~= nil then
worldedit.marker2[name]:get_luaentity().name = name worldedit.marker2[name]:get_luaentity().player_name = name
end end
end end
worldedit.mark_region(name) worldedit.mark_region(name)
end end
worldedit.mark_region = function(name) worldedit.mark_region = function(name)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if worldedit.marker_region[name] ~= nil then --marker already exists if worldedit.marker_region[name] ~= nil then --marker already exists
--wip: make the area stay loaded somehow --wip: make the area stay loaded somehow
for _, entity in ipairs(worldedit.marker_region[name]) do for _, entity in ipairs(worldedit.marker_region[name]) do
entity:remove() entity:remove()
end end
worldedit.marker_region[name] = nil worldedit.marker_region[name] = nil
end end
if pos1 ~= nil and pos2 ~= nil then if pos1 ~= nil and pos2 ~= nil then
local pos1, pos2 = worldedit.sort_pos(pos1, pos2) local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local thickness = 0.2 local thickness = 0.2
local sizex, sizey, sizez = (1 + pos2.x - pos1.x) / 2, (1 + pos2.y - pos1.y) / 2, (1 + pos2.z - pos1.z) / 2 local sizex, sizey, sizez = (1 + pos2.x - pos1.x) / 2, (1 + pos2.y - pos1.y) / 2, (1 + pos2.z - pos1.z) / 2
--make area stay loaded --make area stay loaded
local manip = minetest.get_voxel_manip() local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos2) manip:read_from_map(pos1, pos2)
local markers = {} local markers = {}
--XY plane markers --XY plane markers
for _, z in ipairs({pos1.z - 0.5, pos2.z + 0.5}) do for _, z in ipairs({pos1.z - 0.5, pos2.z + 0.5}) do
local marker = minetest.add_entity({x=pos1.x + sizex - 0.5, y=pos1.y + sizey - 0.5, z=z}, "worldedit:region_cube") local marker = minetest.add_entity({x=pos1.x + sizex - 0.5, y=pos1.y + sizey - 0.5, z=z}, "worldedit:region_cube")
marker:set_properties({ marker:set_properties({
visual_size={x=sizex * 2, y=sizey * 2}, visual_size={x=sizex * 2, y=sizey * 2},
collisionbox = {-sizex, -sizey, -thickness, sizex, sizey, thickness}, collisionbox = {-sizex, -sizey, -thickness, sizex, sizey, thickness},
}) })
marker:get_luaentity().name = name marker:get_luaentity().player_name = name
table.insert(markers, marker) table.insert(markers, marker)
end end
--YZ plane markers --YZ plane markers
for _, x in ipairs({pos1.x - 0.5, pos2.x + 0.5}) do for _, x in ipairs({pos1.x - 0.5, pos2.x + 0.5}) do
local marker = minetest.add_entity({x=x, y=pos1.y + sizey - 0.5, z=pos1.z + sizez - 0.5}, "worldedit:region_cube") local marker = minetest.add_entity({x=x, y=pos1.y + sizey - 0.5, z=pos1.z + sizez - 0.5}, "worldedit:region_cube")
marker:set_properties({ marker:set_properties({
visual_size={x=sizez * 2, y=sizey * 2}, visual_size={x=sizez * 2, y=sizey * 2},
collisionbox = {-thickness, -sizey, -sizez, thickness, sizey, sizez}, collisionbox = {-thickness, -sizey, -sizez, thickness, sizey, sizez},
}) })
marker:setyaw(math.pi / 2) marker:setyaw(math.pi / 2)
marker:get_luaentity().name = name marker:get_luaentity().player_name = name
table.insert(markers, marker) table.insert(markers, marker)
end end
worldedit.marker_region[name] = markers worldedit.marker_region[name] = markers
end end
end end
minetest.register_entity(":worldedit:pos1", { minetest.register_entity(":worldedit:pos1", {
initial_properties = { initial_properties = {
visual = "cube", visual = "cube",
visual_size = {x=1.1, y=1.1}, visual_size = {x=1.1, y=1.1},
textures = {"worldedit_pos1.png", "worldedit_pos1.png", textures = {"worldedit_pos1.png", "worldedit_pos1.png",
"worldedit_pos1.png", "worldedit_pos1.png", "worldedit_pos1.png", "worldedit_pos1.png",
"worldedit_pos1.png", "worldedit_pos1.png"}, "worldedit_pos1.png", "worldedit_pos1.png"},
collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55}, collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55},
physical = false, physical = false,
}, },
on_step = function(self, dtime) on_step = function(self, dtime)
if worldedit.marker1[self.name] == nil then if worldedit.marker1[self.player_name] == nil then
self.object:remove() self.object:remove()
end end
end, end,
on_punch = function(self, hitter) on_punch = function(self, hitter)
self.object:remove() self.object:remove()
worldedit.marker1[self.name] = nil worldedit.marker1[self.player_name] = nil
end, end,
}) })
minetest.register_entity(":worldedit:pos2", { minetest.register_entity(":worldedit:pos2", {
initial_properties = { initial_properties = {
visual = "cube", visual = "cube",
visual_size = {x=1.1, y=1.1}, visual_size = {x=1.1, y=1.1},
textures = {"worldedit_pos2.png", "worldedit_pos2.png", textures = {"worldedit_pos2.png", "worldedit_pos2.png",
"worldedit_pos2.png", "worldedit_pos2.png", "worldedit_pos2.png", "worldedit_pos2.png",
"worldedit_pos2.png", "worldedit_pos2.png"}, "worldedit_pos2.png", "worldedit_pos2.png"},
collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55}, collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55},
physical = false, physical = false,
}, },
on_step = function(self, dtime) on_step = function(self, dtime)
if worldedit.marker2[self.name] == nil then if worldedit.marker2[self.player_name] == nil then
self.object:remove() self.object:remove()
end end
end, end,
on_punch = function(self, hitter) on_punch = function(self, hitter)
self.object:remove() self.object:remove()
worldedit.marker2[self.name] = nil worldedit.marker2[self.player_name] = nil
end, end,
}) })
minetest.register_entity(":worldedit:region_cube", { minetest.register_entity(":worldedit:region_cube", {
initial_properties = { initial_properties = {
visual = "upright_sprite", visual = "upright_sprite",
visual_size = {x=1.1, y=1.1}, visual_size = {x=1.1, y=1.1},
textures = {"worldedit_cube.png"}, textures = {"worldedit_cube.png"},
visual_size = {x=10, y=10}, visual_size = {x=10, y=10},
physical = false, physical = false,
}, },
on_step = function(self, dtime) on_step = function(self, dtime)
if worldedit.marker_region[self.name] == nil then if worldedit.marker_region[self.player_name] == nil then
self.object:remove() self.object:remove()
return return
end end
end, end,
on_punch = function(self, hitter) on_punch = function(self, hitter)
for _, entity in ipairs(worldedit.marker_region[self.name]) do for _, entity in ipairs(worldedit.marker_region[self.player_name]) do
entity:remove() entity:remove()
end end
worldedit.marker_region[self.name] = nil worldedit.marker_region[self.player_name] = nil
end, end,
}) })

View File

@ -1,65 +1,65 @@
local safe_region_callback = {} local safe_region_callback = {}
local safe_region_param = {} local safe_region_param = {}
check_region = function(name, param) check_region = function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] --obtain positions local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] --obtain positions
if pos1 == nil or pos2 == nil then if pos1 == nil or pos2 == nil then
worldedit.player_notify(name, "no region selected") worldedit.player_notify(name, "no region selected")
return nil return nil
end end
return worldedit.volume(pos1, pos2) return worldedit.volume(pos1, pos2)
end end
--`callback` is a callback to run when the user confirms --`callback` is a callback to run when the user confirms
--`nodes_needed` is a function accepting `param`, `pos1`, and `pos2` to calculate the number of nodes needed --`nodes_needed` is a function accepting `param`, `pos1`, and `pos2` to calculate the number of nodes needed
safe_region = function(callback, nodes_needed) safe_region = function(callback, nodes_needed)
--default node volume calculation --default node volume calculation
nodes_needed = nodes_needed or check_region nodes_needed = nodes_needed or check_region
return function(name, param) return function(name, param)
--check if the operation applies to a safe number of nodes --check if the operation applies to a safe number of nodes
local count = nodes_needed(name, param) local count = nodes_needed(name, param)
if count == nil then return end --invalid command if count == nil then return end --invalid command
if count < 10000 then if count < 10000 then
return callback(name, param) return callback(name, param)
end end
--save callback to call later --save callback to call later
safe_region_callback[name], safe_region_param[name] = callback, param safe_region_callback[name], safe_region_param[name] = callback, param
worldedit.player_notify(name, "WARNING: this operation could affect up to " .. count .. " nodes; type //y to continue or //n to cancel") worldedit.player_notify(name, "WARNING: this operation could affect up to " .. count .. " nodes; type //y to continue or //n to cancel")
end end
end end
minetest.register_chatcommand("/y", { minetest.register_chatcommand("/y", {
params = "", params = "",
description = "Confirm a pending operation", description = "Confirm a pending operation",
func = function(name) func = function(name)
local callback, param = safe_region_callback[name], safe_region_param[name] local callback, param = safe_region_callback[name], safe_region_param[name]
if not callback then if not callback then
worldedit.player_notify(name, "no operation pending") worldedit.player_notify(name, "no operation pending")
return return
end end
--obtain positions --obtain positions
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if pos1 == nil or pos2 == nil then if pos1 == nil or pos2 == nil then
worldedit.player_notify(name, "no region selected") worldedit.player_notify(name, "no region selected")
return return
end end
safe_region_callback[name], safe_region_param[name] = nil, nil --reset pending operation safe_region_callback[name], safe_region_param[name] = nil, nil --reset pending operation
callback(name, param, pos1, pos2) callback(name, param, pos1, pos2)
end, end,
}) })
minetest.register_chatcommand("/n", { minetest.register_chatcommand("/n", {
params = "", params = "",
description = "Confirm a pending operation", description = "Confirm a pending operation",
func = function(name) func = function(name)
if not safe_region_callback[name] then if not safe_region_callback[name] then
worldedit.player_notify(name, "no operation pending") worldedit.player_notify(name, "no operation pending")
return return
end end
safe_region_callback[name], safe_region_param[name] = nil, nil safe_region_callback[name], safe_region_param[name] = nil, nil
end, end,
}) })

File diff suppressed because it is too large Load Diff

View File

@ -102,7 +102,10 @@ if unified_inventory then --unified inventory installed
end end
elseif inventory_plus then --inventory++ installed elseif inventory_plus then --inventory++ installed
minetest.register_on_joinplayer(function(player) minetest.register_on_joinplayer(function(player)
inventory_plus.register_button(player, "worldedit_gui", "WorldEdit") local can_worldedit = minetest.check_player_privs(player:get_player_name(), {worldedit=true})
if can_worldedit then
inventory_plus.register_button(player, "worldedit_gui", "WorldEdit")
end
end) end)
--show the form when the button is pressed and hide it when done --show the form when the button is pressed and hide it when done

View File

@ -0,0 +1 @@
worldedit?

View File

@ -1,103 +1,103 @@
worldedit = {} worldedit = rawget(_G, "worldedit") or {}
local minetest = minetest --local copy of global local minetest = minetest --local copy of global
local get_pointed = function(pos, nearest, distance) local get_pointed = function(pos, nearest, distance)
if distance > 100 then if distance > 100 then
return false return false
end end
--check for collision with node --check for collision with node
local nodename = minetest.get_node(pos).name local nodename = minetest.get_node(pos).name
if nodename ~= "air" if nodename ~= "air"
and nodename ~= "default:water_source" and nodename ~= "default:water_source"
and nodename ~= "default:water_flowing" then and nodename ~= "default:water_flowing" then
if nodename ~= "ignore" then if nodename ~= "ignore" then
return nearest return nearest
end end
return false return false
end end
end end
local use = function(itemstack, user, pointed_thing) local use = function(itemstack, user, pointed_thing)
if pointed_thing.type == "nothing" then --pointing at nothing if pointed_thing.type == "nothing" then --pointing at nothing
local placepos = worldedit.raytrace(user:getpos(), user:get_look_dir(), get_pointed) local placepos = worldedit.raytrace(user:getpos(), user:get_look_dir(), get_pointed)
if placepos then --extended reach if placepos then --extended reach
pointed_thing.type = "node" pointed_thing.type = "node"
pointed_thing.under = nil --wip pointed_thing.under = nil --wip
pointed_thing.above = nil --wip pointed_thing.above = nil --wip
end end
end end
return minetest.item_place_node(itemstack, user, pointed_thing) return minetest.item_place_node(itemstack, user, pointed_thing)
end end
-- --
worldedit.raytrace = function(pos, dir, callback) worldedit.raytrace = function(pos, dir, callback)
local base = {x=math.floor(pos.x), y=math.floor(pos.y), z=math.floor(pos.z)} local base = {x=math.floor(pos.x), y=math.floor(pos.y), z=math.floor(pos.z)}
local stepx, stepy, stepz = 0, 0, 0 local stepx, stepy, stepz = 0, 0, 0
local componentx, componenty, componentz = 0, 0, 0 local componentx, componenty, componentz = 0, 0, 0
local intersectx, intersecty, intersectz = 0, 0, 0 local intersectx, intersecty, intersectz = 0, 0, 0
if dir.x == 0 then if dir.x == 0 then
intersectx = math.huge intersectx = math.huge
elseif dir.x > 0 then elseif dir.x > 0 then
stepx = 1 stepx = 1
componentx = 1 / dir.x componentx = 1 / dir.x
intersectx = ((base.x - pos.x) + 1) * componentx intersectx = ((base.x - pos.x) + 1) * componentx
else else
stepx = -1 stepx = -1
componentx = 1 / -dir.x componentx = 1 / -dir.x
intersectx = (pos.x - base.x) * componentx intersectx = (pos.x - base.x) * componentx
end end
if dir.y == 0 then if dir.y == 0 then
intersecty = math.huge intersecty = math.huge
elseif dir.y > 0 then elseif dir.y > 0 then
stepy = 1 stepy = 1
componenty = 1 / dir.y componenty = 1 / dir.y
intersecty = ((base.y - pos.y) + 1) * componenty intersecty = ((base.y - pos.y) + 1) * componenty
else else
stepy = -1 stepy = -1
componenty = 1 / -dir.y componenty = 1 / -dir.y
intersecty = (pos.y - base.y) * componenty intersecty = (pos.y - base.y) * componenty
end end
if dir.z == 0 then if dir.z == 0 then
intersectz = math.huge intersectz = math.huge
elseif dir.z > 0 then elseif dir.z > 0 then
stepz = 1 stepz = 1
componentz = 1 / dir.z componentz = 1 / dir.z
intersectz = ((base.z - pos.z) + 1) * componentz intersectz = ((base.z - pos.z) + 1) * componentz
else else
stepz = -1 stepz = -1
componentz = 1 / -dir.z componentz = 1 / -dir.z
intersectz = (pos.z - base.z) * componentz intersectz = (pos.z - base.z) * componentz
end end
local distance = 0 local distance = 0
local nearest = {x=base.x, y=base.y, z=base.z} local nearest = {x=base.x, y=base.y, z=base.z}
while true do while true do
local values = {callback(base, nearest, distance)} local values = {callback(base, nearest, distance)}
if #values > 0 then if #values > 0 then
return unpack(values) return unpack(values)
end end
nearest.x, nearest.y, nearest.z = base.x, base.y, base.z nearest.x, nearest.y, nearest.z = base.x, base.y, base.z
if intersectx < intersecty then if intersectx < intersecty then
if intersectx < intersectz then if intersectx < intersectz then
base.x = base.x + stepx base.x = base.x + stepx
distance = intersectx distance = intersectx
intersectx = intersectx + componentx intersectx = intersectx + componentx
else else
base.z = base.z + stepz base.z = base.z + stepz
distance = intersectz distance = intersectz
intersectz = intersectz + componentz intersectz = intersectz + componentz
end end
elseif intersecty < intersectz then elseif intersecty < intersectz then
base.y = base.y + stepy base.y = base.y + stepy
distance = intersecty distance = intersecty
intersecty = intersecty + componenty intersecty = intersecty + componenty
else else
base.z = base.z + stepz base.z = base.z + stepz
distance = intersectz distance = intersectz
intersectz = intersectz + componentz intersectz = intersectz + componentz
end end
end end
end end

View File

@ -1 +1 @@
worldedit_commands worldedit_commands

View File

@ -1,50 +1,50 @@
--provides shorter names for the commands in `worldedit_commands` --provides shorter names for the commands in `worldedit_commands`
--returns true if command could not be aliased, false otherwise --returns true if command could not be aliased, false otherwise
worldedit.alias_chatcommand = function(alias, original_command) worldedit.alias_chatcommand = function(alias, original_command)
if not minetest.chatcommands[original_command] then if not minetest.chatcommands[original_command] then
minetest.log("error", "worldedit_shortcommands: original command " .. original_command .. " does not exist") minetest.log("error", "worldedit_shortcommands: original command " .. original_command .. " does not exist")
return true return true
end end
if minetest.chatcommands[alias] then if minetest.chatcommands[alias] then
minetest.log("error", "worldedit_shortcommands: alias " .. alias .. " already exists") minetest.log("error", "worldedit_shortcommands: alias " .. alias .. " already exists")
return true return true
end end
minetest.register_chatcommand(alias, minetest.chatcommands[original_command]) minetest.register_chatcommand(alias, minetest.chatcommands[original_command])
return false return false
end end
worldedit.alias_chatcommand("/i", "/inspect") worldedit.alias_chatcommand("/i", "/inspect")
worldedit.alias_chatcommand("/rst", "/reset") worldedit.alias_chatcommand("/rst", "/reset")
worldedit.alias_chatcommand("/mk", "/mark") worldedit.alias_chatcommand("/mk", "/mark")
worldedit.alias_chatcommand("/umk", "/unmark") worldedit.alias_chatcommand("/umk", "/unmark")
worldedit.alias_chatcommand("/1", "/pos1") worldedit.alias_chatcommand("/1", "/pos1")
worldedit.alias_chatcommand("/2", "/pos2") worldedit.alias_chatcommand("/2", "/pos2")
worldedit.alias_chatcommand("/fp", "/fixedpos") worldedit.alias_chatcommand("/fp", "/fixedpos")
worldedit.alias_chatcommand("/v", "/volume") worldedit.alias_chatcommand("/v", "/volume")
worldedit.alias_chatcommand("/s", "/set") worldedit.alias_chatcommand("/s", "/set")
worldedit.alias_chatcommand("/r", "/replace") worldedit.alias_chatcommand("/r", "/replace")
worldedit.alias_chatcommand("/ri", "/replaceinverse") worldedit.alias_chatcommand("/ri", "/replaceinverse")
worldedit.alias_chatcommand("/hspr", "/hollowsphere") worldedit.alias_chatcommand("/hspr", "/hollowsphere")
worldedit.alias_chatcommand("/spr", "/sphere") worldedit.alias_chatcommand("/spr", "/sphere")
worldedit.alias_chatcommand("/hdo", "/hollowdome") worldedit.alias_chatcommand("/hdo", "/hollowdome")
worldedit.alias_chatcommand("/do", "/dome") worldedit.alias_chatcommand("/do", "/dome")
worldedit.alias_chatcommand("/hcyl", "/hollowcylinder") worldedit.alias_chatcommand("/hcyl", "/hollowcylinder")
worldedit.alias_chatcommand("/cyl", "/cylinder") worldedit.alias_chatcommand("/cyl", "/cylinder")
worldedit.alias_chatcommand("/pyr", "/pyramid") worldedit.alias_chatcommand("/pyr", "/pyramid")
worldedit.alias_chatcommand("/spl", "/spiral") worldedit.alias_chatcommand("/spl", "/spiral")
worldedit.alias_chatcommand("/m", "/move") worldedit.alias_chatcommand("/m", "/move")
worldedit.alias_chatcommand("/c", "/copy") worldedit.alias_chatcommand("/c", "/copy")
worldedit.alias_chatcommand("/stk", "/stack") worldedit.alias_chatcommand("/stk", "/stack")
worldedit.alias_chatcommand("/sch", "/stretch") worldedit.alias_chatcommand("/sch", "/stretch")
worldedit.alias_chatcommand("/tps", "/transpose") worldedit.alias_chatcommand("/tps", "/transpose")
worldedit.alias_chatcommand("/fl", "/flip") worldedit.alias_chatcommand("/fl", "/flip")
worldedit.alias_chatcommand("/rot", "/rotate") worldedit.alias_chatcommand("/rot", "/rotate")
worldedit.alias_chatcommand("/ort", "/orient") worldedit.alias_chatcommand("/ort", "/orient")
worldedit.alias_chatcommand("/hi", "/hide") worldedit.alias_chatcommand("/hi", "/hide")
worldedit.alias_chatcommand("/sup", "/suppress") worldedit.alias_chatcommand("/sup", "/suppress")
worldedit.alias_chatcommand("/hlt", "/highlight") worldedit.alias_chatcommand("/hlt", "/highlight")
worldedit.alias_chatcommand("/rsr", "/restore") worldedit.alias_chatcommand("/rsr", "/restore")
worldedit.alias_chatcommand("/l", "/lua") worldedit.alias_chatcommand("/l", "/lua")
worldedit.alias_chatcommand("/lt", "/luatransform") worldedit.alias_chatcommand("/lt", "/luatransform")
worldedit.alias_chatcommand("/clro", "/clearobjects") worldedit.alias_chatcommand("/clro", "/clearobjects")