]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - doc/lua_api.txt
Docs: clarify spawn_by for decorations
[dragonfireclient.git] / doc / lua_api.txt
index 23408ff6c977fc548df21ce70f315e70aaf2b122..882c9c54c89a66c03a2dda7348610be35f0de49c 100644 (file)
@@ -62,7 +62,8 @@ Where `<gameid>` is unique to each game.
 The game directory can contain the following files:
 
 * `game.conf`, with the following keys:
-    * `name`: Required, a human readable title to address the game, e.g. `name = Minetest`.
+    * `title`: Required, a human-readable title to address the game, e.g. `title = Minetest Game`.
+    * `name`: (Deprecated) same as title.
     * `description`: Short description to be shown in the content tab
     * `allowed_mapgens = <comma-separated mapgens>`
       e.g. `allowed_mapgens = v5,v6,flat`
@@ -1544,15 +1545,12 @@ Displays a minimap on the HUD.
 Representations of simple things
 ================================
 
-Position/vector
----------------
-
-    {x=num, y=num, z=num}
+Vector (ie. a position)
+-----------------------
 
-    Note: it is highly recommended to construct a vector using the helper function:
-    vector.new(num, num, num)
+    vector.new(x, y, z)
 
-For helper functions see [Spatial Vectors].
+See [Spatial Vectors] for details.
 
 `pointed_thing`
 ---------------
@@ -1573,8 +1571,7 @@ Exact pointing location (currently only `Raycast` supports these fields):
   from 1).
 * `pointed_thing.intersection_normal`: Unit vector, points outwards of the
   selected selection box. This specifies which face is pointed at.
-  Is a null vector `{x = 0, y = 0, z = 0}` when the pointer is inside the
-  selection box.
+  Is a null vector `vector.zero()` when the pointer is inside the selection box.
 
 
 
@@ -1683,10 +1680,10 @@ wear value. Syntax:
 
 Examples:
 
-* `'default:apple'`: 1 apple
-* `'default:dirt 5'`: 5 dirt
-* `'default:pick_stone'`: a new stone pickaxe
-* `'default:pick_wood 1 21323'`: a wooden pickaxe, ca. 1/3 worn out
+* `"default:apple"`: 1 apple
+* `"default:dirt 5"`: 5 dirt
+* `"default:pick_stone"`: a new stone pickaxe
+* `"default:pick_wood 1 21323"`: a wooden pickaxe, ca. 1/3 worn out
 
 ### Table format
 
@@ -1776,21 +1773,21 @@ Groups in crafting recipes
 An example: Make meat soup from any meat, any water and any bowl:
 
     {
-        output = 'food:meat_soup_raw',
+        output = "food:meat_soup_raw",
         recipe = {
-            {'group:meat'},
-            {'group:water'},
-            {'group:bowl'},
+            {"group:meat"},
+            {"group:water"},
+            {"group:bowl"},
         },
-        -- preserve = {'group:bowl'}, -- Not implemented yet (TODO)
+        -- preserve = {"group:bowl"}, -- Not implemented yet (TODO)
     }
 
 Another example: Make red wool from white wool and red dye:
 
     {
-        type = 'shapeless',
-        output = 'wool:red',
-        recipe = {'wool:white', 'group:dye,basecolor_red'},
+        type = "shapeless",
+        output = "wool:red",
+        recipe = {"wool:white", "group:dye,basecolor_red"},
     }
 
 Special groups
@@ -2015,7 +2012,7 @@ Example definition of the capabilities of an item
         max_drop_level=1,
         groupcaps={
             crumbly={maxlevel=2, uses=20, times={[1]=1.60, [2]=1.20, [3]=0.80}}
-        }
+        },
         damage_groups = {fleshy=2},
     }
 
@@ -3252,7 +3249,7 @@ Colors
 `#RRGGBBAA` defines a color in hexadecimal format and alpha channel.
 
 Named colors are also supported and are equivalent to
-[CSS Color Module Level 4](http://dev.w3.org/csswg/css-color/#named-colors).
+[CSS Color Module Level 4](https://www.w3.org/TR/css-color-4/#named-color).
 To specify the value of the alpha channel, append `#A` or `#AA` to the end of
 the color name (e.g. `colorname#08`).
 
@@ -3303,33 +3300,76 @@ The following functions provide escape sequences:
 
 Spatial Vectors
 ===============
-A spatial vector is similar to a position, but instead using
-absolute world coordinates, it uses *relative* coordinates, relative to
-no particular point.
-
-Internally, it is implemented as a table with the 3 fields
-`x`, `y` and `z`. Example: `{x = 0, y = 1, z = 0}`.
-However, one should *never* create a vector manually as above, such misbehavior
-is deprecated. The vector helpers set a metatable for the created vectors which
-allows indexing with numbers, calling functions directly on vectors and using
-operators (like `+`). Furthermore, the internal implementation might change in
+
+Minetest stores 3-dimensional spatial vectors in Lua as tables of 3 coordinates,
+and has a class to represent them (`vector.*`), which this chapter is about.
+For details on what a spatial vectors is, please refer to Wikipedia:
+https://en.wikipedia.org/wiki/Euclidean_vector.
+
+Spatial vectors are used for various things, including, but not limited to:
+
+* any 3D spatial vector (x/y/z-directions)
+* Euler angles (pitch/yaw/roll in radians) (Spatial vectors have no real semantic
+  meaning here. Therefore, most vector operations make no sense in this use case.)
+
+Note that they are *not* used for:
+
+* n-dimensional vectors where n is not 3 (ie. n=2)
+* arrays of the form `{num, num, num}`
+
+The API documentation may refer to spatial vectors, as produced by `vector.new`,
+by any of the following notations:
+
+* `(x, y, z)` (Used rarely, and only if it's clear that it's a vector.)
+* `vector.new(x, y, z)`
+* `{x=num, y=num, z=num}` (Even here you are still supposed to use `vector.new`.)
+
+Compatibility notes
+-------------------
+
+Vectors used to be defined as tables of the form `{x = num, y = num, z = num}`.
+Since Minetest 5.5.0, vectors additionally have a metatable to enable easier use.
+Note: Those old-style vectors can still be found in old mod code. Hence, mod and
+engine APIs still need to be able to cope with them in many places.
+
+Manually constructed tables are deprecated and highly discouraged. This interface
+should be used to ensure seamless compatibility between mods and the Minetest API.
+This is especially important to callback function parameters and functions overwritten
+by mods.
+Also, though not likely, the internal implementation of a vector might change in
 the future.
-Old code might still use vectors without metatables, be aware of this!
+In your own code, or if you define your own API, you can, of course, still use
+other representations of vectors.
+
+Vectors provided by API functions will provide an instance of this class if not
+stated otherwise. Mods should adapt this for convenience reasons.
+
+Special properties of the class
+-------------------------------
+
+Vectors can be indexed with numbers and allow method and operator syntax.
 
 All these forms of addressing a vector `v` are valid:
 `v[1]`, `v[3]`, `v.x`, `v[1] = 42`, `v.y = 13`
+Note: Prefer letter over number indexing for performance and compatibility reasons.
 
 Where `v` is a vector and `foo` stands for any function name, `v:foo(...)` does
 the same as `vector.foo(v, ...)`, apart from deprecated functionality.
 
+`tostring` is defined for vectors, see `vector.to_string`.
+
 The metatable that is used for vectors can be accessed via `vector.metatable`.
 Do not modify it!
 
 All `vector.*` functions allow vectors `{x = X, y = Y, z = Z}` without metatables.
 Returned vectors always have a metatable set.
 
-For the following functions, `v`, `v1`, `v2` are vectors,
-`p1`, `p2` are positions,
+Common functions and methods
+----------------------------
+
+For the following functions (and subchapters),
+`v`, `v1`, `v2` are vectors,
+`p1`, `p2` are position vectors,
 `s` is a scalar (a number),
 vectors are written like this: `(x, y, z)`:
 
@@ -3351,6 +3391,7 @@ vectors are written like this: `(x, y, z)`:
     * `init`: If given starts looking for the vector at this string index.
 * `vector.to_string(v)`:
     * Returns a string of the form `"(x, y, z)"`.
+    *  `tostring(v)` does the same.
 * `vector.direction(p1, p2)`:
     * Returns a vector of length 1 with direction `p1` to `p2`.
     * If `p1` and `p2` are identical, returns `(0, 0, 0)`.
@@ -3369,6 +3410,9 @@ vectors are written like this: `(x, y, z)`:
 * `vector.apply(v, func)`:
     * Returns a vector where the function `func` has been applied to each
       component.
+* `vector.combine(v, w, func)`:
+       * Returns a vector where the function `func` has combined both components of `v` and `w`
+         for each component
 * `vector.equals(v1, v2)`:
     * Returns a boolean, `true` if the vectors are identical.
 * `vector.sort(v1, v2)`:
@@ -3381,7 +3425,7 @@ vectors are written like this: `(x, y, z)`:
     * Returns the cross product of `v1` and `v2`.
 * `vector.offset(v, x, y, z)`:
     * Returns the sum of the vectors `v` and `(x, y, z)`.
-* `vector.check()`:
+* `vector.check(v)`:
     * Returns a boolean value indicating whether `v` is a real vector, eg. created
       by a `vector.*` function.
     * Returns `false` for anything else, including tables like `{x=3,y=1,z=4}`.
@@ -3403,6 +3447,9 @@ For the following functions `x` can be either a vector or a number:
     * Returns a scaled vector.
     * Deprecated: If `s` is a vector: Returns the Schur quotient.
 
+Operators
+---------
+
 Operators can be used if all of the involved vectors have metatables:
 * `v1 == v2`:
     * Returns whether `v1` and `v2` are identical.
@@ -3419,8 +3466,11 @@ Operators can be used if all of the involved vectors have metatables:
 * `v / s`:
     * Returns `v` scaled by `1 / s`.
 
+Rotation-related functions
+--------------------------
+
 For the following functions `a` is an angle in radians and `r` is a rotation
-vector ({x = <pitch>, y = <yaw>, z = <roll>}) where pitch, yaw and roll are
+vector (`{x = <pitch>, y = <yaw>, z = <roll>}`) where pitch, yaw and roll are
 angles in radians.
 
 * `vector.rotate(v, r)`:
@@ -3437,6 +3487,18 @@ angles in radians.
     * If `up` is omitted, the roll of the returned vector defaults to zero.
     * Otherwise `direction` and `up` need to be vectors in a 90 degree angle to each other.
 
+Further helpers
+---------------
+
+There are more helper functions involving vectors, but they are listed elsewhere
+because they only work on specific sorts of vectors or involve things that are not
+vectors.
+
+For example:
+
+* `minetest.hash_node_position` (Only works on node positions.)
+* `minetest.dir_to_wallmounted` (Involves wallmounted param2 values.)
+
 
 
 
@@ -3488,8 +3550,16 @@ Helper functions
 * `minetest.string_to_pos(string)`: returns a position or `nil`
     * Same but in reverse.
     * If the string can't be parsed to a position, nothing is returned.
-* `minetest.string_to_area("(X1, Y1, Z1) (X2, Y2, Z2)")`: returns two positions
+* `minetest.string_to_area("(X1, Y1, Z1) (X2, Y2, Z2)", relative_to)`:
+    * returns two positions
     * Converts a string representing an area box into two positions
+    * X1, Y1, ... Z2 are coordinates
+    * `relative_to`: Optional. If set to a position, each coordinate
+      can use the tilde notation for relative positions
+    * Tilde notation: "~": Relative coordinate
+                      "~<number>": Relative coordinate plus <number>
+    * Example: `minetest.string_to_area("(1,2,3) (~5,~-5,~)", {x=10,y=10,z=10})`
+      returns `{x=1,y=2,z=3}, {x=15,y=5,z=10}`
 * `minetest.formspec_escape(string)`: returns a string
     * escapes the characters "[", "]", "\", "," and ";", which can not be used
       in formspecs.
@@ -4634,6 +4704,8 @@ Utilities
           abm_min_max_y = true,
           -- dynamic_add_media supports passing a table with options (5.5.0)
           dynamic_add_media_table = true,
+          -- allows get_sky to return a table instead of separate values (5.6.0)
+          get_sky_as_table = true,
       }
 
 * `minetest.has_feature(arg)`: returns `boolean, missing_features`
@@ -5707,6 +5779,68 @@ Timing
 * `job:cancel()`
     * Cancels the job function from being called
 
+Async environment
+-----------------
+
+The engine allows you to submit jobs to be ran in an isolated environment
+concurrently with normal server operation.
+A job consists of a function to be ran in the async environment, any amount of
+arguments (will be serialized) and a callback that will be called with the return
+value of the job function once it is finished.
+
+The async environment does *not* have access to the map, entities, players or any
+globals defined in the 'usual' environment. Consequently, functions like
+`minetest.get_node()` or `minetest.get_player_by_name()` simply do not exist in it.
+
+Arguments and return values passed through this can contain certain userdata
+objects that will be seamlessly copied (not shared) to the async environment.
+This allows you easy interoperability for delegating work to jobs.
+
+* `minetest.handle_async(func, callback, ...)`:
+    * Queue the function `func` to be ran in an async environment.
+      Note that there are multiple persistent workers and any of them may
+      end up running a given job. The engine will scale the amount of
+      worker threads automatically.
+    * When `func` returns the callback is called (in the normal environment)
+      with all of the return values as arguments.
+    * Optional: Variable number of arguments that are passed to `func`
+* `minetest.register_async_dofile(path)`:
+    * Register a path to a Lua file to be imported when an async environment
+      is initialized. You can use this to preload code which you can then call
+      later using `minetest.handle_async()`.
+
+### List of APIs available in an async environment
+
+Classes:
+* `ItemStack`
+* `PerlinNoise`
+* `PerlinNoiseMap`
+* `PseudoRandom`
+* `PcgRandom`
+* `SecureRandom`
+* `VoxelArea`
+* `VoxelManip`
+    * only if transferred into environment; can't read/write to map
+* `Settings`
+
+Class instances that can be transferred between environments:
+* `ItemStack`
+* `PerlinNoise`
+* `PerlinNoiseMap`
+* `VoxelManip`
+
+Functions:
+* Standalone helpers such as logging, filesystem, encoding,
+  hashing or compression APIs
+* `minetest.request_insecure_environment` (same restrictions apply)
+
+Variables:
+* `minetest.settings`
+* `minetest.registered_items`, `registered_nodes`, `registered_tools`,
+  `registered_craftitems` and `registered_aliases`
+    * with all functions and userdata values replaced by `true`, calling any
+      callbacks here is obviously not possible
+
 Server
 ------
 
@@ -5982,11 +6116,11 @@ Misc.
            This is due to the fact that JSON has two distinct array and object
            values.
     * Example: `write_json({10, {a = false}})`,
-      returns `"[10, {\"a\": false}]"`
+      returns `'[10, {"a": false}]'`
 * `minetest.serialize(table)`: returns a string
     * Convert a table containing tables, strings, numbers, booleans and `nil`s
       into string form readable by `minetest.deserialize`
-    * Example: `serialize({foo='bar'})`, returns `'return { ["foo"] = "bar" }'`
+    * Example: `serialize({foo="bar"})`, returns `'return { ["foo"] = "bar" }'`
 * `minetest.deserialize(string[, safe])`: returns a table
     * Convert a string returned by `minetest.serialize` into a table
     * `string` is loaded in an empty sandbox environment.
@@ -5998,7 +6132,7 @@ Misc.
      value of `safe`. It is fine to serialize then deserialize user-provided
      data, but directly providing user input to deserialize is always unsafe.
     * Example: `deserialize('return { ["foo"] = "bar" }')`,
-      returns `{foo='bar'}`
+      returns `{foo="bar"}`
     * Example: `deserialize('print("foo")')`, returns `nil`
       (function call fails), returns
       `error:[string "print("foo")"]:1: attempt to call global 'print' (a nil value)`
@@ -6211,45 +6345,53 @@ Sorted alphabetically.
 `AreaStore`
 -----------
 
-A fast access data structure to store areas, and find areas near a given
-position or area.
-Every area has a `data` string attribute to store additional information.
-You can create an empty `AreaStore` by calling `AreaStore()`, or
-`AreaStore(type_name)`. The mod decides where to save and load AreaStore.
-If you chose the parameter-less constructor, a fast implementation will be
-automatically chosen for you.
+AreaStore is a data structure to calculate intersections of 3D cuboid volumes
+and points. The `data` field (string) may be used to store and retrieve any
+mod-relevant information to the specified area.
+
+Despite its name, mods must take care of persisting AreaStore data. They may
+use the provided load and write functions for this.
+
 
 ### Methods
 
-* `get_area(id, include_borders, include_data)`
+* `AreaStore(type_name)`
+    * Returns a new AreaStore instance
+    * `type_name`: optional, forces the internally used API.
+        * Possible values: `"LibSpatial"` (default).
+        * When other values are specified, or SpatialIndex is not available,
+          the custom Minetest functions are used.
+* `get_area(id, include_corners, include_data)`
     * Returns the area information about the specified ID.
     * Returned values are either of these:
 
             nil  -- Area not found
-            true -- Without `include_borders` and `include_data`
+            true -- Without `include_corners` and `include_data`
             {
-                min = pos, max = pos -- `include_borders == true`
+                min = pos, max = pos -- `include_corners == true`
                 data = string        -- `include_data == true`
             }
 
-* `get_areas_for_pos(pos, include_borders, include_data)`
+* `get_areas_for_pos(pos, include_corners, include_data)`
     * Returns all areas as table, indexed by the area ID.
     * Table values: see `get_area`.
-* `get_areas_in_area(edge1, edge2, accept_overlap, include_borders, include_data)`
-    * Returns all areas that contain all nodes inside the area specified by `edge1`
-      and `edge2` (inclusive).
+* `get_areas_in_area(corner1, corner2, accept_overlap, include_corners, include_data)`
+    * Returns all areas that contain all nodes inside the area specified by`
+      `corner1 and `corner2` (inclusive).
     * `accept_overlap`: if `true`, areas are returned that have nodes in
       common (intersect) with the specified area.
     * Returns the same values as `get_areas_for_pos`.
-* `insert_area(edge1, edge2, data, [id])`: inserts an area into the store.
+* `insert_area(corner1, corner2, data, [id])`: inserts an area into the store.
     * Returns the new area's ID, or nil if the insertion failed.
-    * The (inclusive) positions `edge1` and `edge2` describe the area.
+    * The (inclusive) positions `corner1` and `corner2` describe the area.
     * `data` is a string stored with the area.
     * `id` (optional): will be used as the internal area ID if it is an unique
       number between 0 and 2^32-2.
-* `reserve(count)`: reserves resources for at most `count` many contained
-  areas.
-  Only needed for efficiency, and only some implementations profit.
+* `reserve(count)`
+    * Requires SpatialIndex, no-op function otherwise.
+    * Reserves resources for `count` many contained areas to improve
+      efficiency when working with many area entries. Additional areas can still
+      be inserted afterwards at the usual complexity.
 * `remove_area(id)`: removes the area with the given id from the store, returns
   success.
 * `set_cache_params(params)`: sets params for the included prefiltering cache.
@@ -6287,9 +6429,9 @@ An `InvRef` is a reference to an inventory.
 * `set_width(listname, width)`: set width of list; currently used for crafting
 * `get_stack(listname, i)`: get a copy of stack index `i` in list
 * `set_stack(listname, i, stack)`: copy `stack` to index `i` in list
-* `get_list(listname)`: return full list
+* `get_list(listname)`: return full list (list of `ItemStack`s)
 * `set_list(listname, list)`: set full list (size will not change)
-* `get_lists()`: returns list of inventory lists
+* `get_lists()`: returns table that maps listnames to inventory lists
 * `set_lists(lists)`: sets inventory lists (size will not change)
 * `add_item(listname, stack)`: add item somewhere in list, returns leftover
   `ItemStack`.
@@ -6649,7 +6791,7 @@ object you are working with still exists.
         * Fourth column: subject looking to the right
         * Fifth column:  subject viewed from above
         * Sixth column:  subject viewed from below
-* `get_entity_name()` (**Deprecated**: Will be removed in a future version)
+* `get_entity_name()` (**Deprecated**: Will be removed in a future version, use the field `self.name` instead)
 * `get_luaentity()`
 
 #### Player only (no-op for other objects)
@@ -6762,17 +6904,18 @@ object you are working with still exists.
 * `hud_get(id)`: gets the HUD element definition structure of the specified ID
 * `hud_set_flags(flags)`: sets specified HUD flags of player.
     * `flags`: A table with the following fields set to boolean values
-        * hotbar
-        * healthbar
-        * crosshair
-        * wielditem
-        * breathbar
-        * minimap
-        * minimap_radar
+        * `hotbar`
+        * `healthbar`
+        * `crosshair`
+        * `wielditem`
+        * `breathbar`
+        * `minimap`: Modifies the client's permission to view the minimap.
+          The client may locally elect to not view the minimap.
+        * `minimap_radar`: is only usable when `minimap` is true
+        * `basic_debug`: Allow showing basic debug info that might give a gameplay advantage.
+          This includes map seed, player position, look direction, the pointed node and block bounds.
+          Does not affect players with the `debug` privilege.
     * If a flag equals `nil`, the flag is not modified
-    * `minimap`: Modifies the client's permission to view the minimap.
-      The client may locally elect to not view the minimap.
-    * `minimap_radar` is only usable when `minimap` is true
 * `hud_get_flags()`: returns a table of player HUD flags with boolean values.
     * See `hud_set_flags` for a list of flags that can be toggled.
 * `hud_set_hotbar_itemcount(count)`: sets number of items in builtin hotbar
@@ -6861,9 +7004,15 @@ object you are working with still exists.
         * `"plain"`: Uses 0 textures, `bgcolor` used
     * `clouds`: Boolean for whether clouds appear in front of `"skybox"` or
       `"plain"` custom skyboxes (default: `true`)
-* `get_sky()`: returns base_color, type, table of textures, clouds.
-* `get_sky_color()`: returns a table with the `sky_color` parameters as in
-    `set_sky`.
+* `get_sky(as_table)`:
+    * `as_table`: boolean that determines whether the deprecated version of this
+    function is being used.
+        * `true` returns a table containing sky parameters as defined in `set_sky(sky_parameters)`.
+        * Deprecated: `false` or `nil` returns base_color, type, table of textures,
+        clouds.
+* `get_sky_color()`:
+    * Deprecated: Use `get_sky(as_table)` instead.
+    * returns a table with the `sky_color` parameters as in `set_sky`.
 * `set_sun(sun_parameters)`:
     * Passing no arguments resets the sun to its default values.
     * `sun_parameters` is a table with the following optional fields:
@@ -6944,6 +7093,12 @@ object you are working with still exists.
     * Returns `false` if failed.
     * Resource intensive - use sparsely
     * To get blockpos, integer divide pos by 16
+* `set_lighting(light_definition)`: sets lighting for the player
+    * `light_definition` is a table with the following optional fields:
+      * `shadows` is a table that controls ambient shadows
+        * `intensity` sets the intensity of the shadows from 0 (no shadows, default) to 1 (blackness)
+* `get_lighting()`: returns the current state of lighting for the player.
+    * Result is a table with the same fields as `light_definition` in `set_lighting`.
 
 `PcgRandom`
 -----------
@@ -7362,7 +7517,7 @@ Used by `minetest.register_entity`.
         -- for more info) by using a '_' prefix
     }
 
-Collision info passed to `on_step`:
+Collision info passed to `on_step` (`moveresult` argument):
 
     {
         touching_ground = boolean,
@@ -7379,6 +7534,8 @@ Collision info passed to `on_step`:
             },
             ...
         }
+        -- `collisions` does not contain data of unloaded mapblock collisions
+        -- or when the velocity changes are negligibly small
     }
 
 ABM (ActiveBlockModifier) definition
@@ -8091,11 +8248,11 @@ Used by `minetest.register_craft`.
 ### Shaped
 
     {
-        output = 'default:pick_stone',
+        output = "default:pick_stone",
         recipe = {
-            {'default:cobble', 'default:cobble', 'default:cobble'},
-            {'', 'default:stick', ''},
-            {'', 'default:stick', ''},  -- Also groups; e.g. 'group:crumbly'
+            {"default:cobble", "default:cobble", "default:cobble"},
+            {"", "default:stick", ""},
+            {"", "default:stick", ""},  -- Also groups; e.g. "group:crumbly"
         },
         replacements = <list of item pairs>,
         -- replacements: replace one input item with another item on crafting
@@ -8106,7 +8263,7 @@ Used by `minetest.register_craft`.
 
     {
         type = "shapeless",
-        output = 'mushrooms:mushroom_stew',
+        output = "mushrooms:mushroom_stew",
         recipe = {
             "mushrooms:bowl",
             "mushrooms:mushroom_brown",
@@ -8403,9 +8560,8 @@ See [Decoration types]. Used by `minetest.register_decoration`.
 
         spawn_by = "default:water",
         -- Node (or list of nodes) that the decoration only spawns next to.
-        -- Checks two horizontal planes of 8 neighbouring nodes (including
-        -- diagonal neighbours), one plane level with the 'place_on' node and a
-        -- plane one node above that.
+        -- Checks the 8 neighbouring nodes on the same Y, and also the ones
+        -- at Y+1, excluding both center nodes.
 
         num_spawn_by = 1,
         -- Number of spawn_by nodes that must be surrounding the decoration