diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 86c64f9be..46b789526 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2099,6 +2099,8 @@ methods: ^ To be used only by a VoxelManip object from minetest.get_mapgen_object ^ (p1, p2) is the area in which lighting is set; defaults to the whole area if left out - update_liquids(): Update liquid flow +- was_modified(): Returns true or false if the data in the voxel manipulator had been modified since + the last read from map, due to a call to minetest.set_data() on the loaded area elsewhere VoxelArea: A helper class for voxel areas - Can be created via VoxelArea:new{MinEdge=pmin, MaxEdge=pmax} diff --git a/src/environment.cpp b/src/environment.cpp index 24a498aa9..c8af72f6b 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -778,19 +778,26 @@ bool ServerEnvironment::setNode(v3s16 p, const MapNode &n) { INodeDefManager *ndef = m_gamedef->ndef(); MapNode n_old = m_map->getNodeNoEx(p); + // Call destructor - if(ndef->get(n_old).has_on_destruct) + if (ndef->get(n_old).has_on_destruct) m_script->node_on_destruct(p, n_old); + // Replace node - bool succeeded = m_map->addNodeWithEvent(p, n); - if(!succeeded) + if (!m_map->addNodeWithEvent(p, n)) return false; + + // Update active VoxelManipulator if a mapgen thread + m_map->updateVManip(p); + // Call post-destructor - if(ndef->get(n_old).has_after_destruct) + if (ndef->get(n_old).has_after_destruct) m_script->node_after_destruct(p, n_old); + // Call constructor - if(ndef->get(n).has_on_construct) + if (ndef->get(n).has_on_construct) m_script->node_on_construct(p, n); + return true; } @@ -798,24 +805,36 @@ bool ServerEnvironment::removeNode(v3s16 p) { INodeDefManager *ndef = m_gamedef->ndef(); MapNode n_old = m_map->getNodeNoEx(p); + // Call destructor - if(ndef->get(n_old).has_on_destruct) + if (ndef->get(n_old).has_on_destruct) m_script->node_on_destruct(p, n_old); + // Replace with air // This is slightly optimized compared to addNodeWithEvent(air) - bool succeeded = m_map->removeNodeWithEvent(p); - if(!succeeded) + if (!m_map->removeNodeWithEvent(p)) return false; + + // Update active VoxelManipulator if a mapgen thread + m_map->updateVManip(p); + // Call post-destructor - if(ndef->get(n_old).has_after_destruct) + if (ndef->get(n_old).has_after_destruct) m_script->node_after_destruct(p, n_old); + // Air doesn't require constructor return true; } bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n) { - return m_map->addNodeWithEvent(p, n, false); + if (!m_map->addNodeWithEvent(p, n, false)) + return false; + + // Update active VoxelManipulator if a mapgen thread + m_map->updateVManip(p); + + return true; } std::set ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius) diff --git a/src/map.cpp b/src/map.cpp index 147bd3584..236972ae9 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -2757,6 +2757,28 @@ MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d) void ServerMap::prepareBlock(MapBlock *block) { } +// N.B. This requires no synchronization, since data will not be modified unless +// the VoxelManipulator being updated belongs to the same thread. +void ServerMap::updateVManip(v3s16 pos) +{ + Mapgen *mg = m_emerge->getCurrentMapgen(); + if (!mg) + return; + + ManualMapVoxelManipulator *vm = mg->vm; + if (!vm) + return; + + if (!vm->m_area.contains(pos)) + return; + + s32 idx = vm->m_area.index(pos); + vm->m_data[idx] = getNodeNoEx(pos); + vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA; + + vm->m_is_dirty = true; +} + s16 ServerMap::findGroundLevel(v2s16 p2d) { #if 0 @@ -3523,6 +3545,7 @@ void ServerMap::PrintInfo(std::ostream &out) ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map): VoxelManipulator(), + m_is_dirty(false), m_create_area(false), m_map(map) { @@ -3617,6 +3640,8 @@ void ManualMapVoxelManipulator::initialEmerge(v3s16 blockpos_min, m_loaded_blocks[p] = flags; } + + m_is_dirty = false; } void ManualMapVoxelManipulator::blitBackAll( diff --git a/src/map.h b/src/map.h index 9b505d8e6..1847c7ac7 100644 --- a/src/map.h +++ b/src/map.h @@ -493,6 +493,8 @@ public: // Database version void loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load=false); + void updateVManip(v3s16 pos); + // For debug printing virtual void PrintInfo(std::ostream &out); @@ -550,6 +552,8 @@ public: void blitBackAll(std::map * modified_blocks, bool overwrite_generated = true); + bool m_is_dirty; + protected: bool m_create_area; Map *m_map; diff --git a/src/script/lua_api/l_vmanip.cpp b/src/script/lua_api/l_vmanip.cpp index f3c1fa67a..2a3cfcafa 100644 --- a/src/script/lua_api/l_vmanip.cpp +++ b/src/script/lua_api/l_vmanip.cpp @@ -304,6 +304,18 @@ int LuaVoxelManip::l_update_map(lua_State *L) return 0; } +int LuaVoxelManip::l_was_modified(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaVoxelManip *o = checkobject(L, 1); + ManualMapVoxelManipulator *vm = o->vm; + + lua_pushboolean(L, vm->m_is_dirty); + + return 1; +} + LuaVoxelManip::LuaVoxelManip(ManualMapVoxelManipulator *mmvm, bool is_mg_vm) { this->vm = mmvm; @@ -396,5 +408,6 @@ const luaL_reg LuaVoxelManip::methods[] = { luamethod(LuaVoxelManip, set_light_data), luamethod(LuaVoxelManip, get_param2_data), luamethod(LuaVoxelManip, set_param2_data), + luamethod(LuaVoxelManip, was_modified), {0,0} }; diff --git a/src/script/lua_api/l_vmanip.h b/src/script/lua_api/l_vmanip.h index fc7ef30e6..70468b344 100644 --- a/src/script/lua_api/l_vmanip.h +++ b/src/script/lua_api/l_vmanip.h @@ -58,6 +58,8 @@ private: static int l_get_param2_data(lua_State *L); static int l_set_param2_data(lua_State *L); + static int l_was_modified(lua_State *L); + public: LuaVoxelManip(ManualMapVoxelManipulator *mmvm, bool is_mapgen_vm); LuaVoxelManip(Map *map);