From 8883c4cb23ff9790061b8169e0326f1082be3d99 Mon Sep 17 00:00:00 2001 From: lhofhansl Date: Wed, 24 Dec 2025 10:02:26 +0100 Subject: [PATCH] Process (a chunk local) liquid queue early when blocks are generated (#16771) When generating a new chunk, the chunk's liquid queue is processed immediately, instead of adding entries to the global liquid queue. Since the generated have not been sent to the client, this avoids sending duplicate blocks (that interfere with map sending) later when the global liquid queue is processed. --- src/emerge.cpp | 3 +-- src/servermap.cpp | 46 +++++++++++++++++++++++++++++----------------- src/servermap.h | 5 +++-- 3 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/emerge.cpp b/src/emerge.cpp index e98ede145c..825b50d5d1 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -596,8 +596,7 @@ 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_server->m_env->getGameTime()); + m_map->finishBlockMake(bmdata, modified_blocks, m_server->m_env); MapBlock *block = m_map->getBlockNoCreateNoEx(pos); if (!block) { diff --git a/src/servermap.cpp b/src/servermap.cpp index 0c04cad39f..01fe04a3a9 100644 --- a/src/servermap.cpp +++ b/src/servermap.cpp @@ -279,10 +279,11 @@ void ServerMap::cancelBlockMake(BlockMakeData *data) } void ServerMap::finishBlockMake(BlockMakeData *data, - std::map *changed_blocks, u32 now) + std::map *changed_blocks, ServerEnvironment *env) { assert(data); assert(changed_blocks); + u32 now = env->getGameTime(); const v3s16 bpmin = data->blockpos_min; const v3s16 bpmax = data->blockpos_max; @@ -299,7 +300,13 @@ void ServerMap::finishBlockMake(BlockMakeData *data, << changed_blocks->size()); /* - Copy transforming liquid information + Process the chunk's liquid queue now. + This avoids sending many duplicate block updates. + */ + transformLiquidsLocal(*changed_blocks, data->transforming_liquid, env, g_settings->getS32("liquid_loop_max")); + + /* + Copy remaining (if any) transforming liquid information */ while (!data->transforming_liquid.empty()) { m_transforming_liquid.push_back(data->transforming_liquid.front()); @@ -919,11 +926,10 @@ void ServerMap::transforming_liquid_add(v3s16 p) m_transforming_liquid.push_back(p); } -void ServerMap::transformLiquids(std::map &modified_blocks, - ServerEnvironment *env) +void ServerMap::transformLiquidsLocal(std::map &modified_blocks, UniqueQueue &liquid_queue, + ServerEnvironment *env, u32 liquid_loop_max) { u32 loopcount = 0; - u32 initial_size = m_transforming_liquid.size(); /*if(initial_size != 0) infostream<<"transformLiquids(): initial_size="< &modified_blocks, std::vector check_for_falling; - u32 liquid_loop_max = g_settings->getS32("liquid_loop_max"); - u32 loop_max = liquid_loop_max; - - while (m_transforming_liquid.size() != 0) + while (!liquid_queue.empty()) { // This should be done here so that it is done when continue is used - if (loopcount >= initial_size || loopcount >= loop_max) + if (loopcount >= liquid_loop_max) break; loopcount++; /* Get a queued transforming liquid node */ - v3s16 p0 = m_transforming_liquid.front(); - m_transforming_liquid.pop_front(); + v3s16 p0 = liquid_queue.front(); + liquid_queue.pop_front(); MapNode n0 = getNode(p0); @@ -1025,7 +1028,7 @@ void ServerMap::transformLiquids(std::map &modified_blocks, // should be enqueded for transformation regardless of whether the // current node changes or not. if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE) - m_transforming_liquid.push_back(npos); + liquid_queue.push_back(npos); // if the current node happens to be a flowing node, it will start to flow down here. if (nb.t == NEIGHBOR_LOWER) flowing_down = true; @@ -1220,15 +1223,15 @@ void ServerMap::transformLiquids(std::map &modified_blocks, // make sure source flows into all neighboring nodes for (u16 i = 0; i < num_flows; i++) if (flows[i].t != NEIGHBOR_UPPER) - m_transforming_liquid.push_back(flows[i].p); + liquid_queue.push_back(flows[i].p); for (u16 i = 0; i < num_airs; i++) if (airs[i].t != NEIGHBOR_UPPER) - m_transforming_liquid.push_back(airs[i].p); + liquid_queue.push_back(airs[i].p); break; case LIQUID_NONE: // this flow has turned to air; neighboring flows might need to do the same for (u16 i = 0; i < num_flows; i++) - m_transforming_liquid.push_back(flows[i].p); + liquid_queue.push_back(flows[i].p); break; case LiquidType_END: break; @@ -1237,7 +1240,7 @@ void ServerMap::transformLiquids(std::map &modified_blocks, //infostream<<"Map::transformLiquids(): loopcount="< &modified_blocks, } env->getScriptIface()->on_liquid_transformed(changed_nodes); +} + +void ServerMap::transformLiquids(std::map &modified_blocks, + ServerEnvironment *env) +{ + // process the whole queue at most once, to rate-limit + u32 liquid_loop_max = std::min(m_transforming_liquid.size(), g_settings->getS32("liquid_loop_max")); + + transformLiquidsLocal(modified_blocks, m_transforming_liquid, env, liquid_loop_max); /* ---------------------------------------------------------------------- * Manage the queue so that it does not grow indefinitely diff --git a/src/servermap.h b/src/servermap.h index d44823de2a..558a86bf9c 100644 --- a/src/servermap.h +++ b/src/servermap.h @@ -66,7 +66,7 @@ public: /// @brief write data back to map after mapgen /// @param now current game time void finishBlockMake(BlockMakeData *data, - std::map *changed_blocks, u32 now); + std::map *changed_blocks, ServerEnvironment *env); void cancelBlockMake(BlockMakeData *data); /* @@ -158,7 +158,8 @@ public: void transformLiquids(std::map & modified_blocks, ServerEnvironment *env); - + void transformLiquidsLocal(std::map &modified_blocks, UniqueQueue &liquid_queue, + ServerEnvironment *env, u32 liquid_loop_max); void transforming_liquid_add(v3s16 p); MapSettingsManager settings_mgr;