diff --git a/minetest.conf.example b/minetest.conf.example index 7b8ab3692..7e5228bec 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -100,6 +100,9 @@ #new_style_water = false # Max liquids processed per step #liquid_loop_max = 10000 +# The time (in seconds) that the liquids queue may grow beyond processing +# capacity until an attempt is made to decrease its size by dumping old queue items +#liquid_queue_purge_time = 30 # Update liquids every .. recommend for finite: 0.2 #liquid_update = 1.0 # Enable nice leaves; disable for speed diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 1ebf5dc49..5b83c2694 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -277,6 +277,7 @@ void set_default_settings(Settings *settings) //liquid stuff settings->setDefault("liquid_loop_max", "10000"); + settings->setDefault("liquid_queue_purge_time", "30"); settings->setDefault("liquid_update", "1.0"); //mapgen stuff diff --git a/src/map.cpp b/src/map.cpp index 009bc3d72..10a6f6283 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -76,7 +76,11 @@ with this program; if not, write to the Free Software Foundation, Inc., Map::Map(std::ostream &dout, IGameDef *gamedef): m_dout(dout), m_gamedef(gamedef), - m_sector_cache(NULL) + m_sector_cache(NULL), + m_transforming_liquid_loop_count_multiplier(1.0f), + m_unprocessed_count(0), + m_inc_trending_up_start_time(0), + m_queue_size_timer_started(false) { } @@ -1611,6 +1615,7 @@ s32 Map::transforming_liquid_size() { void Map::transformLiquids(std::map & modified_blocks) { + INodeDefManager *nodemgr = m_gamedef->ndef(); DSTACK(__FUNCTION_NAME); @@ -1619,6 +1624,8 @@ void Map::transformLiquids(std::map & modified_blocks) u32 loopcount = 0; u32 initial_size = m_transforming_liquid.size(); + u32 curr_time = getTime(PRECISION_MILLI); + /*if(initial_size != 0) infostream<<"transformLiquids(): initial_size="< & modified_blocks) // List of MapBlocks that will require a lighting update (due to lava) std::map lighting_modified_blocks; - u16 loop_max = g_settings->getU16("liquid_loop_max"); + u32 liquid_loop_max = g_settings->getS32("liquid_loop_max"); + u32 loop_max = liquid_loop_max; + +// std::cout << "transformLiquids(): loopmax initial=" +// << loop_max * m_transforming_liquid_loop_count_multiplier; + + // If liquid_loop_max is not keeping up with the queue size increase + // loop_max up to a maximum of liquid_loop_max * dedicated_server_step. + if (m_transforming_liquid.size() > loop_max * 2) { + // "Burst" mode + float server_step = g_settings->getFloat("dedicated_server_step"); + if (m_transforming_liquid_loop_count_multiplier - 1.0 < server_step) + m_transforming_liquid_loop_count_multiplier *= 1.0 + server_step / 10; + } else { + m_transforming_liquid_loop_count_multiplier = 1.0; + } + + loop_max *= m_transforming_liquid_loop_count_multiplier; + +// std::cout << " queue sz=" << m_transforming_liquid.size() +// << " loop_max=" << loop_max; while(m_transforming_liquid.size() != 0) { @@ -1884,6 +1911,54 @@ void Map::transformLiquids(std::map & modified_blocks) while (must_reflow.size() > 0) m_transforming_liquid.push_back(must_reflow.pop_front()); updateLighting(lighting_modified_blocks, modified_blocks); + + + /* + * Queue size limiting + */ + u32 prev_unprocessed = m_unprocessed_count; + m_unprocessed_count = m_transforming_liquid.size(); + + // if unprocessed block count is decreasing or stable + if (m_unprocessed_count <= prev_unprocessed) { + m_queue_size_timer_started = false; + } else { + if (!m_queue_size_timer_started) + m_inc_trending_up_start_time = curr_time; + m_queue_size_timer_started = true; + } + + u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time"); + time_until_purge *= 1000; // seconds -> milliseconds + +// std::cout << " growing for: " +// << (m_queue_size_timer_started ? curr_time - m_inc_trend_up_start_time : 0) +// << "ms" << std::endl; + + // Account for curr_time overflowing + if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time) + m_queue_size_timer_started = false; + + /* If the queue has been growing for more than liquid_queue_purge_time seconds + * and the number of unprocessed blocks is still > liquid_loop_max then we + * cannot keep up; dump the oldest blocks from the queue so that the queue + * has liquid_loop_max items in it + */ + if (m_queue_size_timer_started + && curr_time - m_inc_trending_up_start_time > time_until_purge + && m_unprocessed_count > liquid_loop_max) { + + size_t dump_qty = m_unprocessed_count - liquid_loop_max; + + infostream << "transformLiquids(): DUMPING " << dump_qty + << " blocks from the queue" << std::endl; + + while (dump_qty--) + m_transforming_liquid.pop_front(); + + m_queue_size_timer_started = false; // optimistically assume we can keep up now + m_unprocessed_count = m_transforming_liquid.size(); + } } NodeMetadata *Map::getNodeMetadata(v3s16 p) diff --git a/src/map.h b/src/map.h index c650e51b6..70082d664 100644 --- a/src/map.h +++ b/src/map.h @@ -366,6 +366,12 @@ protected: // Queued transforming water nodes UniqueQueue m_transforming_liquid; + +private: + f32 m_transforming_liquid_loop_count_multiplier; + u32 m_unprocessed_count; + u32 m_inc_trending_up_start_time; // milliseconds + bool m_queue_size_timer_started; }; /*