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.
This commit is contained in:
Vanessa Dannenberg 2021-04-06 01:07:29 -04:00
parent 8ecb401309
commit a325c2ccd8
2 changed files with 73 additions and 129 deletions

198
init.lua
View File

@ -9,27 +9,16 @@
biome_lib = {} biome_lib = {}
biome_lib.air = {name = "air"} biome_lib.air = {name = "air"}
biome_lib.blocklist_aircheck = {} biome_lib.block_log = {}
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.actionslist_aircheck = {} biome_lib.actionslist_aircheck = {}
biome_lib.actionslist_no_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.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) local function tableize(s)
return string.split(string.trim(string.gsub(s, " ", ""))) return string.split(string.trim(string.gsub(s, " ", "")))
end end
@ -328,7 +317,7 @@ local function populate_single_surface(biome, pos, perlin_fertile_area, checkair
return true return true
end 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 local items_added = 0
biome_lib:set_defaults(biome) biome_lib:set_defaults(biome)
@ -427,104 +416,78 @@ function biome_lib:populate_surfaces(biome, nodes_or_function_or_model, snodes,
return items_added return items_added
end end
-- Primary mapgen spawner, for mods that can work with air checking enabled on -- Primary log read-out/mapgen spawner
-- a surface during the initial map read stage.
function biome_lib:generate_block_with_air_checking() function biome_lib.generate_block()
if not biome_lib.blocklist_aircheck[1] then if not biome_lib.block_log[1] then return end -- the block log is empty
return
end
local minp = biome_lib.blocklist_aircheck[1][1] local minp = biome_lib.block_log[1][1]
local maxp = biome_lib.blocklist_aircheck[1][2] 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 if not biome_lib.pos_hash then -- we need to read the maplock and get the surfaces list
-- tables, so that we can write the tables thread-safely. biome_lib.pos_hash = {}
biome_lib.pos_hash.surface_node_list = airflag
local blockhash = minetest.hash_node_position(minp) 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)
if not biome_lib.surface_nodes_aircheck.blockhash then -- read it into the block cache biome_lib.pos_hash.action_index = 1
biome_lib.surface_nodes_aircheck.blockhash = if #biome_lib.pos_hash.surface_node_list > 0 then
minetest.find_nodes_in_area_under_air(minp, maxp, biome_lib.surfaceslist_aircheck) biome_lib:dbg("Mapblock at "..minetest.pos_to_string(minp)..
biome_lib.actioncount_aircheck.blockhash = 1 " has "..#biome_lib.pos_hash.surface_node_list..
if #biome_lib.surface_nodes_aircheck.blockhash > 0 then " surface nodes to work on (airflag="..dump(airflag)..")")
biome_lib:dbg("Mapblock at "..minetest.pos_to_string(minp).." added, with "..#biome_lib.surface_nodes_aircheck.blockhash.." surface nodes detected.")
end 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 else
if biome_lib.actionslist_aircheck[biome_lib.actioncount_aircheck.blockhash] then -- below, [1] is biome, [2] is the thing to be added
-- [1] is biome, [2] is node/function/model local added = 0
local added = biome_lib:populate_surfaces( if airflag then
biome_lib.actionslist_aircheck[biome_lib.actioncount_aircheck.blockhash][1], if biome_lib.actionslist_aircheck[biome_lib.pos_hash.action_index] then
biome_lib.actionslist_aircheck[biome_lib.actioncount_aircheck.blockhash][2], added = biome_lib.populate_surfaces(
biome_lib.surface_nodes_aircheck.blockhash, true) biome_lib.actionslist_aircheck[biome_lib.pos_hash.action_index][1],
if added > 0 then biome_lib.actionslist_aircheck[biome_lib.pos_hash.action_index][2],
biome_lib:dbg("Ran biome_lib:populate_surfaces for block at "..minetest.pos_to_string(minp).. biome_lib.pos_hash.surface_node_list, true)
". Entry #"..biome_lib.actioncount_aircheck.blockhash.." added "..added.." items.") biome_lib.pos_hash.action_index = biome_lib.pos_hash.action_index + 1
end end
biome_lib.actioncount_aircheck.blockhash = biome_lib.actioncount_aircheck.blockhash + 1
else else
table.remove(biome_lib.blocklist_aircheck, 1) if biome_lib.actionslist_no_aircheck[biome_lib.pos_hash.action_index] then
biome_lib.surface_nodes_aircheck.blockhash = nil added = biome_lib.populate_surfaces(
biome_lib.actioncount_aircheck.blockhash = nil 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 if added > 0 then
end biome_lib:dbg("biome_lib.populate_surfaces ran on mapblock at "..
minetest.pos_to_string(minp)..". Entry #"..
-- Secondary mapgen spawner, for mods that require disabling of (biome_lib.pos_hash.action_index-1).." added "..added.." items.")
-- 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
end end
end end
end end
-- "Play" them back, populating them with new stuff in the process -- "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) minetest.register_globalstep(function(dtime)
if dtime >= step_duration + 0.1 -- don't attempt to populate if lag is already too high if math.random(100) > biome_lib.queue_run_ratio
or math.random(100) > biome_lib.queue_run_ratio or dtime > biome_lib.dtime_limit then
or (#biome_lib.blocklist_aircheck == 0 and #biome_lib.blocklist_no_aircheck == 0) then
return return
end end
for s = 1, biome_lib.entries_per_step do
biome_lib.globalstep_start_time = minetest.get_us_time() biome_lib.generate_block()
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
end end
end) end)
@ -532,26 +495,14 @@ end)
-- to prevent unpopulated map areas -- to prevent unpopulated map areas
minetest.register_on_shutdown(function() minetest.register_on_shutdown(function()
if #biome_lib.blocklist_aircheck == 0 then if #biome_lib.block_log == 0 then
return return
end end
print("[biome_lib] Stand by, playing out the rest of the aircheck mapblock log") print("[biome_lib] Stand by, playing out the rest of the mapblock log")
print("(there are "..#biome_lib.blocklist_aircheck.." entries)...") print("(there are "..#biome_lib.block_log.." entries)...")
while #biome_lib.blocklist_aircheck > 0 do while #biome_lib.block_log > 0 do
biome_lib:generate_block_with_air_checking(0.1) biome_lib.generate_block()
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)
end end
end) end)
@ -746,22 +697,17 @@ function biome_lib:get_nodedef_field(nodename, fieldname)
end end
if DEBUG then if DEBUG then
biome_lib.last_count_air = 0 biome_lib.last_count = 0
biome_lib.last_count_no_air = 0
function biome_lib.show_pending_block_counts() function biome_lib.show_pending_block_count()
if biome_lib.last_count_air ~= #biome_lib.blocklist_aircheck if biome_lib.last_count ~= #biome_lib.block_log then
or biome_lib.last_count_no_air ~= #biome_lib.blocklist_no_aircheck then biome_lib:dbg("Pending block count: "..#biome_lib.block_log)
biome_lib:dbg(string.format("Pending block counts, air: %-7i no-air: %i", biome_lib.last_count = #biome_lib.block_log
#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
end end
minetest.after(1, biome_lib.show_pending_block_counts) minetest.after(1, biome_lib.show_pending_block_count)
end end
biome_lib.show_pending_block_counts() biome_lib.show_pending_block_count()
minetest.after(0, function() minetest.after(0, function()
print("Registered a total of "..(#biome_lib.surfaceslist_aircheck)+(#biome_lib.surfaceslist_no_aircheck).." surface types to be evaluated, spread") print("Registered a total of "..(#biome_lib.surfaceslist_aircheck)+(#biome_lib.surfaceslist_no_aircheck).." surface types to be evaluated, spread")

View File

@ -65,9 +65,7 @@ minetest.register_on_generated(function(minp, maxp, blockseed)
local bmin = {x=minx, y=miny, z=minz} local bmin = {x=minx, y=miny, z=minz}
local bmax = {x=minx + 15, y=miny + 15, z=minz + 15} local bmax = {x=minx + 15, y=miny + 15, z=minz + 15}
biome_lib.block_log[#biome_lib.block_log + 1] = { bmin, bmax, true }
biome_lib.blocklist_aircheck[#biome_lib.blocklist_aircheck + 1] = { bmin, bmax }
biome_lib.blocklist_no_aircheck[#biome_lib.blocklist_no_aircheck + 1] = { bmin, bmax }
end end
end end
end end