From aa1bab21564cdac01ed2eaed6c2284fc1497626a Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sun, 25 May 2025 15:43:59 +0200 Subject: [PATCH] Implement API to create empty VoxelManip --- doc/lua_api.md | 33 +++++++++++++++--------- src/script/lua_api/l_vmanip.cpp | 45 +++++++++++++++++++++++++++++---- src/script/lua_api/l_vmanip.h | 1 + 3 files changed, 62 insertions(+), 17 deletions(-) diff --git a/doc/lua_api.md b/doc/lua_api.md index cd7707343..3a6da1bdb 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -5005,7 +5005,8 @@ A VoxelManip object can be created any time using either: 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()`. +VoxelManip object using `VoxelManip:read_from_map()`, or an empty one created +with `VoxelManip:initialize()`. Note that `VoxelManip:read_from_map()` returns two position vectors. The region formed by these positions indicate the minimum and maximum (respectively) @@ -5016,14 +5017,14 @@ 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. +requested. This 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_light_data()` for node param (usually light levels), and * `VoxelManip:get_param2_data()` for the node type-dependent "param2" values. See section [Flat array format] for more details. @@ -5038,17 +5039,16 @@ 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. +* `VoxelManip:set_data()` or +* `VoxelManip:set_light_data()` or +* `VoxelManip:set_param2_data()` 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()` etc. 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()` +changes can be committed back to the map by calling `VoxelManip:write_to_map()`. ### Flat array format @@ -5180,15 +5180,22 @@ inside the VoxelManip. Methods ------- -* `read_from_map(p1, p2)`: Loads a chunk of map into the VoxelManip object +* `read_from_map(p1, p2)`: Loads a part of the map into the VoxelManip object containing the region formed by `p1` and `p2`. - * returns actual emerged `pmin`, actual emerged `pmax` + * returns actual emerged `pmin`, actual emerged `pmax` (MapBlock-aligned) * Note that calling this multiple times will *add* to the area loaded in the VoxelManip, and not reset it. +* `initialize(p1, p2, [node])`: Clears and resizes the VoxelManip object to + comprise the region formed by `p1` and `p2`. + * **No data** is read from the map, so you can use this to treat `VoxelManip` + objects as general containers of node data. + * `node`: if present the data will be filled with this node; if not it will + be uninitialized + * returns actual emerged `pmin`, actual emerged `pmax` (MapBlock-aligned) + * (introduced in 5.13.0) * `write_to_map([light])`: Writes the data loaded from the `VoxelManip` back to the map. - * **important**: data must be set using `VoxelManip:set_data()` before - calling this. + * **important**: you should call `set_data()` before this, or nothing will change. * if `light` is true, then lighting is automatically recalculated. The default value is true. If `light` is false, no light calculations happen, and you should correct @@ -5249,6 +5256,8 @@ Methods where the engine will keep the map and the VM in sync automatically. * Note: this doesn't do what you think it does and is subject to removal. Don't use it! * `get_emerged_area()`: Returns actual emerged minimum and maximum positions. + * "Emerged" does not imply that this region was actually loaded from the map, + if `initialize()` has been used. * `close()`: Frees the data buffers associated with the VoxelManip object. It will become empty. * Since Lua's garbage collector is not aware of the potentially significant diff --git a/src/script/lua_api/l_vmanip.cpp b/src/script/lua_api/l_vmanip.cpp index 09a9f9ffa..af254b105 100644 --- a/src/script/lua_api/l_vmanip.cpp +++ b/src/script/lua_api/l_vmanip.cpp @@ -44,7 +44,40 @@ int LuaVoxelManip::l_read_from_map(lua_State *L) push_v3s16(L, vm->m_area.MinEdge); push_v3s16(L, vm->m_area.MaxEdge); + return 2; +} +int LuaVoxelManip::l_initialize(lua_State *L) +{ + MAP_LOCK_REQUIRED; + + LuaVoxelManip *o = checkObject(L, 1); + MMVManip *vm = o->vm; + + if (o->is_mapgen_vm) + throw LuaError("Cannot modify mapgen VoxelManip object"); + + VoxelArea area; + { + v3s16 bp1 = getNodeBlockPos(check_v3s16(L, 2)); + v3s16 bp2 = getNodeBlockPos(check_v3s16(L, 3)); + sortBoxVerticies(bp1, bp2); + area = VoxelArea(bp1 * MAP_BLOCKSIZE, (bp2+1) * MAP_BLOCKSIZE - v3s16(1)); + } + assert(!area.hasEmptyExtent()); + + vm->clear(); + vm->addArea(area); + if (lua_istable(L, 4)) { + MapNode n = readnode(L, 4); + const u32 volume = vm->m_area.getVolume(); + for (u32 i = 0; i != volume; i++) + vm->m_data[i] = n; + vm->clearFlags(vm->m_area, VOXELFLAG_NO_DATA); + } + + push_v3s16(L, vm->m_area.MinEdge); + push_v3s16(L, vm->m_area.MaxEdge); return 2; } @@ -93,11 +126,12 @@ int LuaVoxelManip::l_set_data(lua_State *L) lua_pop(L, 1); } - // FIXME: in theory we should clear VOXELFLAG_NO_DATA here - // However there is no way to tell which values Lua code has intended to set - // (if they were VOXELFLAG_NO_DATA before), and which were just not touched. - // In practice this doesn't cause problems because read_from_map() will cause - // all covered blocks to be loaded anyway. + // Mark all data as present, since we just got it from Lua + // Note that we can't tell if the caller intended to put CONTENT_IGNORE or + // is just repeating the dummy values we push in l_get_data() in case + // VOXELFLAG_NO_DATA is set. In practice this doesn't matter since ignore + // isn't written back to the map anyway. + vm->clearFlags(vm->m_area, VOXELFLAG_NO_DATA); return 0; } @@ -449,6 +483,7 @@ void LuaVoxelManip::Register(lua_State *L) const char LuaVoxelManip::className[] = "VoxelManip"; const luaL_Reg LuaVoxelManip::methods[] = { luamethod(LuaVoxelManip, read_from_map), + luamethod(LuaVoxelManip, initialize), luamethod(LuaVoxelManip, get_data), luamethod(LuaVoxelManip, set_data), luamethod(LuaVoxelManip, get_node_at), diff --git a/src/script/lua_api/l_vmanip.h b/src/script/lua_api/l_vmanip.h index bfd83f56c..95bb82ce2 100644 --- a/src/script/lua_api/l_vmanip.h +++ b/src/script/lua_api/l_vmanip.h @@ -24,6 +24,7 @@ private: static int gc_object(lua_State *L); static int l_read_from_map(lua_State *L); + static int l_initialize(lua_State *L); static int l_get_data(lua_State *L); static int l_set_data(lua_State *L); static int l_write_to_map(lua_State *L);