From a325c2ccd8028b8660e50b099b7f7d044f72eec3 Mon Sep 17 00:00:00 2001 From: Vanessa Dannenberg Date: Tue, 6 Apr 2021 01:07:29 -0400 Subject: [PATCH] rewrote block queue handlers to reduce code duplication and improve lag management minetest.conf settings added: biome_lib_dtime_limit: Maximum lag allowed, in seconds. Default 0.5s. Larger values allow for more lag, but make map updates run a bit faster. biome_lib_queue_run_ratio: If positive, this is the approximate number of globalstep ticks to skip between map updates. If negative, it becomes the positive number of map updates to run per globalstep tick before lag is checked. No minimum value, maximum +100. Default: -100 (that is, 100 updates per globalstep tick, with none intentionally skipped). Use positive numbers for slow machines or biome_lib-using mods that tend to cause lag, and negative values for fast machines and mods. --- init.lua | 198 ++++++++++++++++--------------------------- search_functions.lua | 4 +- 2 files changed, 73 insertions(+), 129 deletions(-) diff --git a/init.lua b/init.lua index 961a600..74e0eb7 100644 --- a/init.lua +++ b/init.lua @@ -9,27 +9,16 @@ biome_lib = {} biome_lib.air = {name = "air"} -biome_lib.blocklist_aircheck = {} -biome_lib.blocklist_no_aircheck = {} - -biome_lib.surface_nodes_aircheck = {} -biome_lib.surface_nodes_no_aircheck = {} - -biome_lib.surfaceslist_aircheck = {} -biome_lib.surfaceslist_no_aircheck = {} - -biome_lib.actioncount_aircheck = {} -biome_lib.actioncount_no_aircheck = {} +biome_lib.block_log = {} biome_lib.actionslist_aircheck = {} biome_lib.actionslist_no_aircheck = {} +biome_lib.surfaceslist_aircheck = {} +biome_lib.surfaceslist_no_aircheck = {} + biome_lib.modpath = minetest.get_modpath("biome_lib") -biome_lib.total_no_aircheck_calls = 0 - -biome_lib.queue_run_ratio = tonumber(minetest.settings:get("biome_lib_queue_run_ratio")) or 100 - local function tableize(s) return string.split(string.trim(string.gsub(s, " ", ""))) end @@ -328,7 +317,7 @@ local function populate_single_surface(biome, pos, perlin_fertile_area, checkair return true end -function biome_lib:populate_surfaces(biome, nodes_or_function_or_model, snodes, checkair) +function biome_lib.populate_surfaces(biome, nodes_or_function_or_model, snodes, checkair) local items_added = 0 biome_lib:set_defaults(biome) @@ -427,104 +416,78 @@ function biome_lib:populate_surfaces(biome, nodes_or_function_or_model, snodes, return items_added end --- Primary mapgen spawner, for mods that can work with air checking enabled on --- a surface during the initial map read stage. +-- Primary log read-out/mapgen spawner -function biome_lib:generate_block_with_air_checking() - if not biome_lib.blocklist_aircheck[1] then - return - end +function biome_lib.generate_block() + if not biome_lib.block_log[1] then return end -- the block log is empty - local minp = biome_lib.blocklist_aircheck[1][1] - local maxp = biome_lib.blocklist_aircheck[1][2] + local minp = biome_lib.block_log[1][1] + local maxp = biome_lib.block_log[1][2] + local airflag = biome_lib.block_log[1][3] + local pos_hash = minetest.hash_node_position(minp) - -- use the block hash as a unique key into the surface nodes - -- tables, so that we can write the tables thread-safely. - - local blockhash = minetest.hash_node_position(minp) - - if not biome_lib.surface_nodes_aircheck.blockhash then -- read it into the block cache - biome_lib.surface_nodes_aircheck.blockhash = - minetest.find_nodes_in_area_under_air(minp, maxp, biome_lib.surfaceslist_aircheck) - biome_lib.actioncount_aircheck.blockhash = 1 - if #biome_lib.surface_nodes_aircheck.blockhash > 0 then - biome_lib:dbg("Mapblock at "..minetest.pos_to_string(minp).." added, with "..#biome_lib.surface_nodes_aircheck.blockhash.." surface nodes detected.") + if not biome_lib.pos_hash then -- we need to read the maplock and get the surfaces list + biome_lib.pos_hash = {} + biome_lib.pos_hash.surface_node_list = airflag + and minetest.find_nodes_in_area_under_air(minp, maxp, biome_lib.surfaceslist_aircheck) + or minetest.find_nodes_in_area(minp, maxp, biome_lib.surfaceslist_no_aircheck) + biome_lib.pos_hash.action_index = 1 + if #biome_lib.pos_hash.surface_node_list > 0 then + biome_lib:dbg("Mapblock at "..minetest.pos_to_string(minp).. + " has "..#biome_lib.pos_hash.surface_node_list.. + " surface nodes to work on (airflag="..dump(airflag)..")") end - + elseif not (airflag and biome_lib.actionslist_aircheck[biome_lib.pos_hash.action_index]) + and not (not airflag and biome_lib.actionslist_no_aircheck[biome_lib.pos_hash.action_index]) then + -- the block is finished, remove it + if #biome_lib.pos_hash.surface_node_list > 0 then + biome_lib:dbg("Deleted mapblock "..minetest.pos_to_string(minp).." from the block log") + end + table.remove(biome_lib.block_log, 1) + biome_lib.pos_hash = nil else - if biome_lib.actionslist_aircheck[biome_lib.actioncount_aircheck.blockhash] then - -- [1] is biome, [2] is node/function/model - local added = biome_lib:populate_surfaces( - biome_lib.actionslist_aircheck[biome_lib.actioncount_aircheck.blockhash][1], - biome_lib.actionslist_aircheck[biome_lib.actioncount_aircheck.blockhash][2], - biome_lib.surface_nodes_aircheck.blockhash, true) - if added > 0 then - biome_lib:dbg("Ran biome_lib:populate_surfaces for block at "..minetest.pos_to_string(minp).. - ". Entry #"..biome_lib.actioncount_aircheck.blockhash.." added "..added.." items.") + -- below, [1] is biome, [2] is the thing to be added + local added = 0 + if airflag then + if biome_lib.actionslist_aircheck[biome_lib.pos_hash.action_index] then + added = biome_lib.populate_surfaces( + biome_lib.actionslist_aircheck[biome_lib.pos_hash.action_index][1], + biome_lib.actionslist_aircheck[biome_lib.pos_hash.action_index][2], + biome_lib.pos_hash.surface_node_list, true) + biome_lib.pos_hash.action_index = biome_lib.pos_hash.action_index + 1 end - biome_lib.actioncount_aircheck.blockhash = biome_lib.actioncount_aircheck.blockhash + 1 else - table.remove(biome_lib.blocklist_aircheck, 1) - biome_lib.surface_nodes_aircheck.blockhash = nil - biome_lib.actioncount_aircheck.blockhash = nil + if biome_lib.actionslist_no_aircheck[biome_lib.pos_hash.action_index] then + added = biome_lib.populate_surfaces( + biome_lib.actionslist_no_aircheck[biome_lib.pos_hash.action_index][1], + biome_lib.actionslist_no_aircheck[biome_lib.pos_hash.action_index][2], + biome_lib.pos_hash.surface_node_list, false) + biome_lib.pos_hash.action_index = biome_lib.pos_hash.action_index + 1 + end end - end -end - --- Secondary mapgen spawner, for mods that require disabling of --- checking for air during the initial map read stage. - -function biome_lib:generate_block_no_aircheck() - if not biome_lib.blocklist_no_aircheck[1] then - return - end - - local minp = biome_lib.blocklist_no_aircheck[1][1] - local maxp = biome_lib.blocklist_no_aircheck[1][2] - - local blockhash = minetest.hash_node_position(minp) - - if not biome_lib.surface_nodes_no_aircheck.blockhash then - biome_lib.surface_nodes_no_aircheck.blockhash = - minetest.find_nodes_in_area(minp, maxp, biome_lib.surfaceslist_no_aircheck) - biome_lib.actioncount_no_aircheck.blockhash = 1 - - else - if biome_lib.actionslist_no_aircheck[biome_lib.actioncount_no_aircheck.blockhash] then - biome_lib:populate_surfaces( - biome_lib.actionslist_no_aircheck[biome_lib.actioncount_no_aircheck.blockhash][1], - biome_lib.actionslist_no_aircheck[biome_lib.actioncount_no_aircheck.blockhash][2], - biome_lib.surface_nodes_no_aircheck.blockhash, false) - biome_lib.actioncount_no_aircheck.blockhash = biome_lib.actioncount_no_aircheck.blockhash + 1 - else - table.remove(biome_lib.blocklist_no_aircheck, 1) - biome_lib.surface_nodes_no_aircheck.blockhash = nil - biome_lib.actioncount_no_aircheck.blockhash = nil + if added > 0 then + biome_lib:dbg("biome_lib.populate_surfaces ran on mapblock at ".. + minetest.pos_to_string(minp)..". Entry #".. + (biome_lib.pos_hash.action_index-1).." added "..added.." items.") end end end -- "Play" them back, populating them with new stuff in the process -local step_duration = tonumber(minetest.settings:get("dedicated_server_step")) +biome_lib.dtime_limit = tonumber(minetest.settings:get("biome_lib_dtime_limit")) or 0.5 +local rr = tonumber(minetest.settings:get("biome_lib_queue_run_ratio")) or -100 + +biome_lib.queue_run_ratio = 100 - rr +biome_lib.entries_per_step = math.max(-rr, 1) + minetest.register_globalstep(function(dtime) - if dtime >= step_duration + 0.1 -- don't attempt to populate if lag is already too high - or math.random(100) > biome_lib.queue_run_ratio - or (#biome_lib.blocklist_aircheck == 0 and #biome_lib.blocklist_no_aircheck == 0) then + if math.random(100) > biome_lib.queue_run_ratio + or dtime > biome_lib.dtime_limit then return end - - biome_lib.globalstep_start_time = minetest.get_us_time() - biome_lib.globalstep_runtime = 0 - while (#biome_lib.blocklist_aircheck > 0 or #biome_lib.blocklist_no_aircheck > 0) - and biome_lib.globalstep_runtime < 200000 do -- 0.2 seconds, in uS. - if #biome_lib.blocklist_aircheck > 0 then - biome_lib:generate_block_with_air_checking() - end - if #biome_lib.blocklist_no_aircheck > 0 then - biome_lib:generate_block_no_aircheck() - end - biome_lib.globalstep_runtime = minetest.get_us_time() - biome_lib.globalstep_start_time + for s = 1, biome_lib.entries_per_step do + biome_lib.generate_block() end end) @@ -532,26 +495,14 @@ end) -- to prevent unpopulated map areas minetest.register_on_shutdown(function() - if #biome_lib.blocklist_aircheck == 0 then + if #biome_lib.block_log == 0 then return end - print("[biome_lib] Stand by, playing out the rest of the aircheck mapblock log") - print("(there are "..#biome_lib.blocklist_aircheck.." entries)...") - while #biome_lib.blocklist_aircheck > 0 do - biome_lib:generate_block_with_air_checking(0.1) - end -end) - -minetest.register_on_shutdown(function() - if #biome_lib.blocklist_aircheck == 0 then - return - end - - print("[biome_lib] Stand by, playing out the rest of the no-aircheck mapblock log") - print("(there are "..#biome_lib.blocklist_no_aircheck.." entries)...") - while #biome_lib.blocklist_no_aircheck > 0 do - biome_lib:generate_block_no_aircheck(0.1) + print("[biome_lib] Stand by, playing out the rest of the mapblock log") + print("(there are "..#biome_lib.block_log.." entries)...") + while #biome_lib.block_log > 0 do + biome_lib.generate_block() end end) @@ -746,22 +697,17 @@ function biome_lib:get_nodedef_field(nodename, fieldname) end if DEBUG then - biome_lib.last_count_air = 0 - biome_lib.last_count_no_air = 0 + biome_lib.last_count = 0 - function biome_lib.show_pending_block_counts() - if biome_lib.last_count_air ~= #biome_lib.blocklist_aircheck - or biome_lib.last_count_no_air ~= #biome_lib.blocklist_no_aircheck then - biome_lib:dbg(string.format("Pending block counts, air: %-7i no-air: %i", - #biome_lib.blocklist_aircheck, #biome_lib.blocklist_no_aircheck)) - - biome_lib.last_count_air = #biome_lib.blocklist_aircheck - biome_lib.last_count_no_air = #biome_lib.blocklist_no_aircheck + function biome_lib.show_pending_block_count() + if biome_lib.last_count ~= #biome_lib.block_log then + biome_lib:dbg("Pending block count: "..#biome_lib.block_log) + biome_lib.last_count = #biome_lib.block_log end - minetest.after(1, biome_lib.show_pending_block_counts) + minetest.after(1, biome_lib.show_pending_block_count) end - biome_lib.show_pending_block_counts() + biome_lib.show_pending_block_count() minetest.after(0, function() print("Registered a total of "..(#biome_lib.surfaceslist_aircheck)+(#biome_lib.surfaceslist_no_aircheck).." surface types to be evaluated, spread") diff --git a/search_functions.lua b/search_functions.lua index 11f379a..2527dcf 100644 --- a/search_functions.lua +++ b/search_functions.lua @@ -65,9 +65,7 @@ minetest.register_on_generated(function(minp, maxp, blockseed) local bmin = {x=minx, y=miny, z=minz} local bmax = {x=minx + 15, y=miny + 15, z=minz + 15} - - biome_lib.blocklist_aircheck[#biome_lib.blocklist_aircheck + 1] = { bmin, bmax } - biome_lib.blocklist_no_aircheck[#biome_lib.blocklist_no_aircheck + 1] = { bmin, bmax } + biome_lib.block_log[#biome_lib.block_log + 1] = { bmin, bmax, true } end end end