aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--builtin/game/constants.lua5
-rw-r--r--doc/lua_api.txt167
2 files changed, 163 insertions, 9 deletions
diff --git a/builtin/game/constants.lua b/builtin/game/constants.lua
index ea3644cfb..d0b7c753c 100644
--- a/builtin/game/constants.lua
+++ b/builtin/game/constants.lua
@@ -4,6 +4,11 @@
-- Constants values for use with the Lua API
--
+-- Built-in Content IDs (for use with VoxelManip API)
+core.CONTENT_UNKNOWN = 125
+core.CONTENT_AIR = 126
+core.CONTENT_IGNORE = 127
+
-- Block emerge status constants (for use with core.emerge_area)
core.EMERGE_CANCELLED = 0
core.EMERGE_ERRORED = 1
diff --git a/doc/lua_api.txt b/doc/lua_api.txt
index 5008a5293..63e4971e5 100644
--- a/doc/lua_api.txt
+++ b/doc/lua_api.txt
@@ -2812,22 +2812,171 @@ nil, this table will be used to store the result instead of creating a new table
`noisevals = noise:getMapSlice({x=24, z=1}, {x=1, z=1})`
### `VoxelManip`
-An interface to the `MapVoxelManipulator` for Lua.
-It can be created via `VoxelManip()` or `minetest.get_voxel_manip()`.
-The map will be pre-loaded if two positions are passed to either.
+#### About VoxelManip
+VoxelManip is a scripting interface to the internal 'Map Voxel Manipulator' facility. The purpose of
+this object is for fast, low-level, bulk access to reading and writing Map content. As such, setting
+map nodes through VoxelManip will lack many of the higher level features and concepts you may be used
+to with other methods of setting nodes. For example, nodes will not have their construction and
+destruction callbacks run, and no rollback information is logged.
+
+It is important to note that VoxelManip is designed for speed, and *not* ease of use or flexibility.
+If your mod requires a map manipulation facility that will handle 100% of all edge cases, or the use
+of high level node placement features, perhaps minetest.set_node() is better suited for the job.
+
+In addition, VoxelManip might not be faster, or could even be slower, for your specific use case.
+VoxelManip is most effective when setting very large areas of map at once - for example, if only
+setting a 5x5x5 node area, a minetest.set_node() loop may be more optimal. Always profile code
+using both methods of map manipulation to determine which is most appropriate for your usage.
+
+#### Using VoxelManip
+A VoxelManip object can be created any time using either:
+`VoxelManip([p1, p2])`, or `minetest.get_voxel_manip([p1, p2])`.
+
+If the optional position parameters are present for either of these routines, the specified region
+will be pre-loaded into the VoxelManip object on creation. Otherwise, the area of map you wish to
+manipulate must first be loaded into the VoxelManip object using `VoxelManip:read_from_map()`.
+
+Note that `VoxelManip:read_from_map()` returns two position vectors. The region formed by these
+positions indicate the minimum and maximum (respectively) positions of the area actually loaded in
+the VoxelManip, which may be larger than the area requested. For convenience, the loaded area
+coordinates can also be queried any time after loading map data with `VoxelManip:get_emerged_area()`.
+
+Now that the VoxelManip object is populated with map data, your mod can fetch a copy of this data
+using either of two methods. `VoxelManip:get_node_at()`, which retrieves an individual node in a
+MapNode formatted table at the position requested is the simplest method to use, but also the slowest.
+
+Nodes in a VoxelManip object may also be read in bulk to a flat array table using:
+`VoxelManip:get_data()` for node content (in Content ID form, see section 'Content IDs'),
+`VoxelManip:get_light_data()` for node light levels, and
+`VoxelManip:get_param2_data()` for the node type-dependent "param2" values.
+
+See section 'Flat array format' for more details.
+
+It is very important to understand that the tables returned by any of the above three functions
+represent a snapshot of the VoxelManip's internal state at the time of the call. This copy of the
+data will *not* magically update itself if another function modifies the internal VoxelManip state.
+Any functions that modify a VoxelManip's contents work on the VoxelManip's internal state unless
+otherwise explicitly stated.
+
+Once the bulk data has been edited to your liking, the internal VoxelManip state can be set using:
+`VoxelManip:set_data()` for node content (in Content ID form, see section 'Content IDs'),
+`VoxelManip:set_light_data()` for node light levels, and
+`VoxelManip:set_param2_data()` for the node type-dependent "param2" values.
+
+The parameter to each of the above three functions can use any table at all in the same flat array
+format as produced by get_data() et al. and is *not required* to be a table retrieved from get_data().
+
+Once the internal VoxelManip state has been modified to your liking, the changes can be committed back
+to the map by calling `VoxelManip:write_to_map()`.
+
+Finally, a call to `VoxelManip:update_map()` is required to re-calculate lighting and set the blocks
+as being modified so that connected clients are sent the updated parts of map.
+
+
+##### Flat array format
+Let
+ `Nx = p2.X - p1.X + 1`,
+ `Ny = p2.Y - p1.Y + 1`, and
+ `Nz = p2.Z - p1.Z + 1`.
+
+Then, for a loaded region of p1..p2, this array ranges from `1` up to and including the value of
+the expression `Nx * Ny * Nz`.
+
+Positions offset from p1 are present in the array with the format of:
+```
+[
+ (0, 0, 0), (1, 0, 0), (2, 0, 0), ... (Nx, 0, 0),
+ (0, 1, 0), (1, 1, 0), (2, 1, 0), ... (Nx, 1, 0),
+ ...
+ (0, Ny, 0), (1, Ny, 0), (2, Ny, 0), ... (Nx, Ny, 0),
+ (0, 0, 1), (1, 0, 1), (2, 0, 1), ... (Nx, 0, 1),
+ ...
+ (0, Ny, 2), (1, Ny, 2), (2, Ny, 2), ... (Nx, Ny, 2),
+ ...
+ (0, Ny, Nz), (1, Ny, Nz), (2, Ny, Nz), ... (Nx, Ny, Nz)
+]
+```
+
+and the array index for a position p contained completely in p1..p2 is:
+
+`(p.Z - p1.Z) * Ny * Nx + (p.Y - p1.Y) * Nx + (p.X - p1.X) + 1`
+
+Note that this is the same "flat 3D array" format as `PerlinNoiseMap:get3dMap_flat()`.
+VoxelArea objects (see section 'VoxelArea') can be used to simplify calculation of the index
+for a single point in a flat VoxelManip array.
+
+##### Content IDs
+A Content ID is a unique integer identifier for a specific node type. These IDs are used by VoxelManip
+in place of the node name string for `VoxelManip:get_data()` and `VoxelManip:set_data()`. You can use
+`minetest.get_content_id()` to look up the Content ID for the specified node name, and
+`minetest.get_name_from_content_id()` to look up the node name string for a given Content ID.
+After registration of a node, its Content ID will remain the same throughout execution of the mod.
+Note that the node being queried needs to have already been been registered.
+
+The following builtin node types have their Content IDs defined as constants:
+```
+core.CONTENT_UNKNOWN (ID for "unknown" nodes)
+core.CONTENT_AIR (ID for "air" nodes)
+core.CONTENT_IGNORE (ID for "ignore" nodes)
+```
+
+##### Mapgen VoxelManip objects
+Inside of `on_generated()` callbacks, it is possible to retrieve the same VoxelManip object used by the
+core's Map Generator (commonly abbreviated Mapgen). Most of the rules previously described still apply
+but with a few differences:
+* The Mapgen VoxelManip object is retrieved using: `minetest.get_mapgen_object("voxelmanip")`
+* This VoxelManip object already has the region of map just generated loaded into it; it's not necessary
+ to call `VoxelManip:read_from_map()` before using a Mapgen VoxelManip.
+* The `on_generated()` callbacks of some mods may place individual nodes in the generated area using
+ non-VoxelManip map modification methods. Because the same Mapgen VoxelManip object is passed through
+ each `on_generated()` callback, it becomes necessary for the Mapgen VoxelManip object to maintain
+ consistency with the current map state. For this reason, calling any of the following functions:
+ `minetest.add_node()`, `minetest.set_node()`, or `minetest.swap_node()`
+ will also update the Mapgen VoxelManip object's internal state active on the current thread.
+* After modifying the Mapgen VoxelManip object's internal buffer, it may be necessary to update lighting
+ information using either: `VoxelManip:calc_lighting()` or `VoxelManip:set_lighting()`.
+* `VoxelManip:update_map()` does not need to be called after `write_to_map()`. The map update is performed
+ automatically after all on_generated callbacks have been run for that generated block.
+
+##### Other API functions operating on a VoxelManip
+If any VoxelManip contents were set to a liquid node, `VoxelManip:update_liquids()` must be called
+for these liquid nodes to begin flowing. It is recommended to call this function only after having
+written all buffered data back to the VoxelManip object, save for special situations where the modder
+desires to only have certain liquid nodes begin flowing.
+
+The functions `minetest.generate_ores()` and `minetest.generate_decorations()` will generate all
+registered decorations and ores throughout the full area inside of the specified VoxelManip object.
+
+`minetest.place_schematic_on_vmanip()` is otherwise identical to `minetest.place_schematic()`,
+except instead of placing the specified schematic directly on the map at the specified position, it
+will place the schematic inside of the VoxelManip.
+
+##### Notes
+* Attempting to read data from a VoxelManip object before map is read will result in a zero-length
+ array table for `VoxelManip:get_data()`, and an "ignore" node at any position for
+ `VoxelManip:get_node_at()`.
+* If either a region of map has not yet been generated or is out-of-bounds of the map, that region is
+ filled with "ignore" nodes.
+* Other mods, or the core itself, could possibly modify the area of map currently loaded into a VoxelManip
+ object. With the exception of Mapgen VoxelManips (see above section), the internal buffers are not
+ updated. For this reason, it is strongly encouraged to complete the usage of a particular VoxelManip
+ object in the same callback it had been created.
+* If a VoxelManip object will be used often, such as in an `on_generated()` callback, consider passing
+ a file-scoped table as the optional parameter to `VoxelManip:get_data()`, which serves as a static
+ buffer the function can use to write map data to instead of returning a new table each call. This
+ greatly enhances performance by avoiding unnecessary memory allocations.
#### Methods
-* `read_from_map(p1, p2)`: Reads a chunk of map from the map containing the
- region formed by `p1` and `p2`.
+* `read_from_map(p1, p2)`: Loads a chunk of map into the VoxelManip object containing
+ the region formed by `p1` and `p2`.
* returns actual emerged `pmin`, actual emerged `pmax`
* `write_to_map()`: Writes the data loaded from the `VoxelManip` back to the map.
- * **important**: data must be set using `VoxelManip:set_data` before calling this
+ * **important**: data must be set using `VoxelManip:set_data()` before calling this
* `get_node_at(pos)`: Returns a `MapNode` table of the node currently loaded in
the `VoxelManip` at that position
-* `set_node_at(pos, node)`: Sets a specific `MapNode` in the `VoxelManip` at
- that position
-* `get_data(buffer)`: Gets the data read into the `VoxelManip` object
+* `set_node_at(pos, node)`: Sets a specific `MapNode` in the `VoxelManip` at that position
+* `get_data([buffer])`: Retrieves the node content data loaded into the `VoxelManip` object
* returns raw node data in the form of an array of node content IDs
* if the param `buffer` is present, this table will be used to store the result instead
* `set_data(data)`: Sets the data contents of the `VoxelManip` object