diff --git a/doc/lua_api.md b/doc/lua_api.md index e1fb93a4d..f0d9624d0 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -9479,12 +9479,17 @@ Used by `core.register_lbm`. A loading block modifier (LBM) is used to define a function that is called for specific nodes (defined by `nodenames`) when a mapblock which contains such nodes -gets activated (not loaded!). +gets activated (**not loaded!**). -Note: LBMs operate on a "snapshot" of node positions taken once before they are triggered. +*Note*: LBMs operate on a "snapshot" of node positions taken once before they are triggered. That means if an LBM callback adds a node, it won't be taken into account. -However the engine guarantees that when the callback is called that all given position(s) -contain a matching node. +However the engine guarantees that at the point in time when the callback is called +that all given positions contain a matching node. + +*Note*: For maps generated in 5.11.0 or older, many newly generated blocks did not +get a timestamp set. This means LBMs introduced between generation time and +time of first activation will never run. +Currently the only workaround is to use `run_at_every_load`. ```lua { @@ -9508,7 +9513,7 @@ contain a matching node. action = function(pos, node, dtime_s) end, -- Function triggered for each qualifying node. -- `dtime_s` is the in-game time (in seconds) elapsed since the block - -- was last active. + -- was last active (available since 5.7.0). bulk_action = function(pos_list, dtime_s) end, -- Function triggered with a list of all applicable node positions at once. diff --git a/src/emerge.cpp b/src/emerge.cpp index 71e1ed7c5..29134bccf 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -581,7 +581,8 @@ MapBlock *EmergeThread::finishGen(v3s16 pos, BlockMakeData *bmdata, Perform post-processing on blocks (invalidate lighting, queue liquid transforms, etc.) to finish block make */ - m_map->finishBlockMake(bmdata, modified_blocks); + m_map->finishBlockMake(bmdata, modified_blocks, + m_server->m_env->getGameTime()); MapBlock *block = m_map->getBlockNoCreateNoEx(pos); if (!block) { @@ -619,11 +620,6 @@ MapBlock *EmergeThread::finishGen(v3s16 pos, BlockMakeData *bmdata, m_mapgen->gennotify.clearEvents(); m_mapgen->vm = nullptr; - /* - Activate the block - */ - m_server->m_env->activateBlock(block, 0); - return block; } diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index e52bd9369..28ca0f889 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -394,10 +394,8 @@ void ActiveBlockList::update(std::vector &active_players, */ std::set newlist = m_forceloaded_list; std::set extralist; - m_abm_list = m_forceloaded_list; for (const PlayerSAO *playersao : active_players) { v3s16 pos = getNodeBlockPos(floatToInt(playersao->getBasePosition(), BS)); - fillRadiusBlock(pos, active_block_range, m_abm_list); fillRadiusBlock(pos, active_block_range, newlist); s16 player_ao_range = std::min(active_object_range, playersao->getWantedRange()); @@ -417,6 +415,8 @@ void ActiveBlockList::update(std::vector &active_players, } } + m_abm_list = newlist; + /* Find out which blocks on the new list are not on the old list */ @@ -448,6 +448,7 @@ void ActiveBlockList::update(std::vector &active_players, Do some least-effort sanity checks to hopefully catch code bugs. */ assert(newlist.size() >= extralist.size()); + assert(newlist.size() >= m_abm_list.size()); assert(blocks_removed.size() <= m_list.size()); if (!blocks_added.empty()) { assert(newlist.count(*blocks_added.begin()) > 0); @@ -1076,6 +1077,14 @@ public: } }; + +void ServerEnvironment::forceActivateBlock(MapBlock *block) +{ + assert(block); + if (m_active_blocks.add(block->getPos())) + activateBlock(block); +} + void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime) { // Reset usage timer immediately, otherwise a block that becomes active diff --git a/src/serverenvironment.h b/src/serverenvironment.h index 8b1cc98b6..6d9584044 100644 --- a/src/serverenvironment.h +++ b/src/serverenvironment.h @@ -124,6 +124,7 @@ public: // Don't call this after loadIntroductionTimes() ran. void addLBMDef(LoadingBlockModifierDef *lbm_def); + /// @param now current game time void loadIntroductionTimes(const std::string ×, IGameDef *gamedef, u32 now); @@ -150,6 +151,7 @@ private: // The key of the map is the LBM def's first introduction time. lbm_lookup_map m_lbm_lookup; + /// @return map of LBM name -> timestamp static std::unordered_map parseIntroductionTimesString(const std::string ×); @@ -186,12 +188,24 @@ public: m_list.clear(); } + /// @return true if block was newly added + bool add(v3s16 p) { + if (m_list.insert(p).second) { + m_abm_list.insert(p); + return true; + } + return false; + } + void remove(v3s16 p) { m_list.erase(p); m_abm_list.erase(p); } + // list of all active blocks std::set m_list; + // list of blocks for ABM processing + // subset of `m_list` that does not contain view cone affected blocks std::set m_abm_list; // list of blocks that are always active, not modified by this class std::set m_forceloaded_list; @@ -312,10 +326,10 @@ public: ); /* - Activate objects and dynamically modify for the dtime determined - from timestamp and additional_dtime + Force a block to become active. It will probably be deactivated + the next time active blocks are re-calculated. */ - void activateBlock(MapBlock *block, u32 additional_dtime=0); + void forceActivateBlock(MapBlock *block); /* {Active,Loading}BlockModifiers @@ -404,6 +418,9 @@ private: const std::string &savedir, const Settings &conf); static AuthDatabase *openAuthDatabase(const std::string &name, const std::string &savedir, const Settings &conf); + + void activateBlock(MapBlock *block, u32 additional_dtime=0); + /* Internal ActiveObject interface ------------------------------------------- diff --git a/src/servermap.cpp b/src/servermap.cpp index 87e886492..ab58510d2 100644 --- a/src/servermap.cpp +++ b/src/servermap.cpp @@ -199,6 +199,7 @@ bool ServerMap::blockpos_over_mapgen_limit(v3s16 p) bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data) { + assert(data); s16 csize = getMapgenParams()->chunksize; v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize); v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1); @@ -263,8 +264,10 @@ bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data) } void ServerMap::finishBlockMake(BlockMakeData *data, - std::map *changed_blocks) + std::map *changed_blocks, u32 now) { + assert(data); + assert(changed_blocks); v3s16 bpmin = data->blockpos_min; v3s16 bpmax = data->blockpos_max; @@ -283,7 +286,7 @@ void ServerMap::finishBlockMake(BlockMakeData *data, /* Copy transforming liquid information */ - while (data->transforming_liquid.size()) { + while (!data->transforming_liquid.empty()) { m_transforming_liquid.push_back(data->transforming_liquid.front()); data->transforming_liquid.pop_front(); } @@ -297,15 +300,13 @@ void ServerMap::finishBlockMake(BlockMakeData *data, */ block->expireIsAirCache(); /* - Set block as modified + Set block as modified (if it isn't already) */ block->raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_EXPIRE_IS_AIR); } - /* - Set central blocks as generated - */ + // Note: this does not apply to the extra border area for (s16 x = bpmin.X; x <= bpmax.X; x++) for (s16 z = bpmin.Z; z <= bpmax.Z; z++) for (s16 y = bpmin.Y; y <= bpmax.Y; y++) { @@ -314,13 +315,10 @@ void ServerMap::finishBlockMake(BlockMakeData *data, continue; block->setGenerated(true); + // Set timestamp to ensure correct application of LBMs and other stuff + block->setTimestampNoChangedFlag(now); } - /* - Save changed parts of map - NOTE: Will be saved later. - */ - //save(MOD_STATE_WRITE_AT_UNLOAD); m_chunks_in_progress.erase(bpmin); } diff --git a/src/servermap.h b/src/servermap.h index 73b35e38f..7b33a66a9 100644 --- a/src/servermap.h +++ b/src/servermap.h @@ -61,9 +61,13 @@ public: Blocks are generated by using these and makeBlock(). */ bool blockpos_over_mapgen_limit(v3s16 p); + /// @brief copy data from map to prepare for mapgen + /// @return true if mapgen should actually happen bool initBlockMake(v3s16 blockpos, BlockMakeData *data); + /// @brief write data back to map after mapgen + /// @param now current game time void finishBlockMake(BlockMakeData *data, - std::map *changed_blocks); + std::map *changed_blocks, u32 now); /* Get a block from somewhere. diff --git a/src/unittest/test_sao.cpp b/src/unittest/test_sao.cpp index e424fb7a4..5184d529d 100644 --- a/src/unittest/test_sao.cpp +++ b/src/unittest/test_sao.cpp @@ -197,8 +197,8 @@ void TestSAO::testActivate(ServerEnvironment *env) UASSERT(block); block->m_static_objects.insert(0, s_obj); - // Activating the block will convert it to active. - env->activateBlock(block); + // this will convert it to an active object + env->forceActivateBlock(block); const u16 obj_id = assert_active_in_block(block); auto *obj = env->getActiveObject(obj_id); @@ -239,7 +239,7 @@ void TestSAO::testStaticToFalse(ServerEnvironment *env) UASSERT(block); block->m_static_objects.insert(0, s_obj); - env->activateBlock(block); + env->forceActivateBlock(block); const u16 obj_id = assert_active_in_block(block); auto *obj = env->getActiveObject(obj_id); diff --git a/src/util/container.h b/src/util/container.h index 2ab573c19..19af68497 100644 --- a/src/util/container.h +++ b/src/util/container.h @@ -53,11 +53,16 @@ public: return m_queue.front(); } - u32 size() const + size_t size() const { return m_queue.size(); } + bool empty() const + { + return m_queue.empty(); + } + private: std::set m_set; std::queue m_queue;