Begin standardization of unload bounds check

- New near_unloaded API that supports custom
  distance parameter, map bounds check, and
  some optimizations.
- Add area automatic unload check support to ABM
  API and add to applicable ABMs, replacing many
  old manual checks.

Note that the explicit unload check is only strictly
necessary when something will happen due to
a node NOT being present in the area, e.g. something
igniting due to absent coolant, or soaking quantity
being reset due to missing sources.  In simple cases
where the absence of a thing causes the ABM to do
nothing, then the standard check can work because
the default action is to do nothing anyway.
This commit is contained in:
Aaron Suen 2021-12-18 11:28:14 -05:00
parent 646711fe8e
commit a1b2df3941
26 changed files with 112 additions and 18 deletions

View File

@ -1,6 +1,6 @@
-- LUALOCALS < ---------------------------------------------------------
local math, minetest, nodecore, string, tonumber
= math, minetest, nodecore, string, tonumber
local math, minetest, nodecore, string, tonumber, vector
= math, minetest, nodecore, string, tonumber, vector
local math_floor, string_format
= math.floor, string.format
-- LUALOCALS > ---------------------------------------------------------
@ -11,17 +11,74 @@ local chunksize = tonumber(minetest.get_mapgen_setting("chunksize")) or 5
chunksize = chunksize * 16
local limitchunks = math_floor(limit / chunksize)
nodecore.map_limit_min = (-limitchunks + 0.5) * chunksize + 7.5
nodecore.map_limit_max = (limitchunks - 0.5) * chunksize + 7.5
local min = (-limitchunks + 0.5) * chunksize + 7.5
nodecore.map_limit_min = min
local max = (limitchunks - 0.5) * chunksize + 7.5
nodecore.map_limit_max = max
nodecore.log("info", string_format("mapgen limit: %d, chunk: %d, bounds: %0.1f to %0.1f",
limit, chunksize, nodecore.map_limit_min, nodecore.map_limit_max))
function nodecore.within_map_limits(pos)
return pos.x >= nodecore.map_limit_min
and pos.y >= nodecore.map_limit_min
and pos.z >= nodecore.map_limit_min
and pos.x <= nodecore.map_limit_max
and pos.y <= nodecore.map_limit_max
and pos.z <= nodecore.map_limit_max
return pos.x >= min
and pos.y >= min
and pos.z >= min
and pos.x <= max
and pos.y <= max
and pos.z <= max
end
function nodecore.near_unloaded(pos, node, dist)
pos = vector.floor(pos)
if not (node or minetest.get_node_or_nil(pos)) then return true end
if (not dist) or (dist < 1) then return end
dist = math_floor(dist)
-- Clamp area to check to map bounds; areas outside the map
-- are always considered "loaded" and their ignores are
-- treated as canonical.
local zmin = pos.z - dist
if zmin < min then zmin = min end
local zmax = pos.z + dist
if zmax > max then zmax = max end
local ymin = pos.y - dist
if ymin < min then ymin = min end
local ymax = pos.y + dist
if ymax > max then ymax = max end
local xmin = pos.x - dist
if xmin < min then xmin = min end
local xmax = pos.x + dist
if xmax > max then xmax = max end
-- Check corners of area
local step = dist * 2
for z = zmin, zmax, step do
pos.z = z
for y = ymin, ymax, step do
pos.y = y
for x = xmin, xmax, step do
pos.x = x
if not minetest.get_node_or_nil(pos) then return true end
end
end
end
-- If area is wide enough to have entire mapblocks not
-- covered by the center and corners, then check each
-- mapblock in case internal ones are unloaded.
if dist <= 16 then return end
for z = zmin, zmax, 16 do
pos.z = z
for y = ymin, ymax, 16 do
pos.y = y
for x = xmin, xmax, 16 do
pos.x = x
if not minetest.get_node_or_nil(pos) then return true end
end
end
end
end

View File

@ -404,10 +404,6 @@ function nodecore.grav_air_accel_ent(obj)
return obj:set_acceleration(new)
end
function nodecore.near_unloaded(pos, radius)
return minetest.find_node_near(pos, radius or 1, {"ignore"}, true)
end
function nodecore.get_objects_at_pos(pos)
pos = vector.round(pos)
local t = {}

View File

@ -0,0 +1,17 @@
-- LUALOCALS < ---------------------------------------------------------
local minetest, nodecore
= minetest, nodecore
-- LUALOCALS > ---------------------------------------------------------
local oldreg = minetest.register_abm
function minetest.register_abm(def)
if def.arealoaded then
local dist = def.arealoaded
local oldact = def.action
def.action = function(pos, node, ...)
if nodecore.near_unloaded(pos, node, dist) then return end
return oldact(pos, node, ...)
end
end
return oldreg(def)
end

View File

@ -13,6 +13,7 @@ end
include("abmmux")
include("abminvert")
include("abmarea")
include("stasis")
include("dnts")
include("aism")

View File

@ -96,6 +96,7 @@ function nodecore.register_cook_abm(def)
def.label = def.label or "cook " .. minetest.write_json(def.nodenames)
def.interval = def.interval or 1
def.chance = def.chance or 1
def.arealoaded = def.arealoaded or 1
def.action = cookcheck
nodecore.group_expand(def.nodenames, function(k) cooknames[k] = true end)
minetest.register_abm(def)

View File

@ -83,6 +83,7 @@ nodecore.register_soaking_abm({
fieldname = "repack",
nodenames = {"group:loose_repack"},
interval = 10,
arealoaded = 1,
soakrate = function(pos, node)
local def = minetest.registered_items[node.name] or {}
if def.no_repack or def.no_self_repack then return end

View File

@ -81,6 +81,7 @@ minetest.register_abm({
neighbors = {"air"},
interval = 2,
chance = 5,
arealoaded = 1,
action = function(pos)
if not reposeq then
reposeq = {}

View File

@ -89,6 +89,7 @@ nodecore.register_soaking_abm({
interval = 1,
nodenames = {"group:concrete_etchable"},
fieldname = "plycuring",
arealoaded = 1,
soakrate = function(pos)
if minetest.find_node_near(pos,
1, {"group:concrete_flow", "group:water"}) then

View File

@ -103,6 +103,7 @@ nodecore.register_soaking_abm({
interval = 5,
nodenames = {"group:concrete_source"},
fieldname = "curing",
arealoaded = 1,
soakrate = function(pos)
if minetest.find_node_near(pos,
1, {"group:concrete_flow", "group:water"}) then

View File

@ -48,6 +48,7 @@ do
label = "fire consume",
interval = 1,
chance = 1,
arealoaded = 1,
nodenames = {modname .. ":fire"},
action = function(pos)
sparks_add(pos)
@ -75,6 +76,7 @@ minetest.register_abm({
nodenames = {"group:flammable"},
neighbors = {"group:igniter"},
neighbors_invert = true,
arealoaded = 1,
action = function(pos)
nodecore.fire_check_ignite(pos)
end
@ -85,6 +87,7 @@ minetest.register_abm({
interval = 1,
chance = 1,
nodenames = {"group:ember"},
arealoaded = 1,
action = function(pos, node)
local snuff, vents = nodecore.fire_check_snuff(pos, node)
if snuff or not vents then return end

View File

@ -184,6 +184,7 @@ minetest.register_abm({
interval = 1,
chance = 100,
nodenames = {"group:flower_living"},
arealoaded = 2,
action = function(pos, node)
local function die()
local wilt = minetest.registered_items[node.name].flower_wilts_to

View File

@ -99,6 +99,7 @@ minetest.register_abm({
label = "rush drying/spreading",
interval = 1,
chance = 50,
arealoaded = 2,
nodenames = {modname .. ":rush"},
action = function(pos)
local subst, below = rushcheck(pos)

View File

@ -113,6 +113,7 @@ minetest.register_abm({
label = "sedge growth/death",
interval = 2,
chance = 250,
arealoaded = 1,
nodenames = {"group:flora_sedges"},
action = function(pos, node)
local below = {x = pos.x, y = pos.y - 1, z = pos.z}

View File

@ -72,6 +72,7 @@ minetest.register_abm({
interval = 1,
chance = 2,
nodenames = {"group:amalgam"},
arealoaded = 1,
action = function(pos)
if nodecore.quenched(pos) then return end
return nodecore.set_loud(pos, {name = lavasrc})

View File

@ -52,6 +52,7 @@ minetest.register_abm({
chance = 2,
nodenames = {pumname},
neighbors = {"group:lava"},
arealoaded = 1,
action = function(pos)
if math_random() < 0.95 and nodecore.quenched(pos) then return end
nodecore.set_loud(pos, {name = "nc_terrain:lava_flowing", param2 = 7})

View File

@ -51,6 +51,7 @@ minetest.register_abm({
nodenames = {"group:visinv"},
interval = 2,
chance = 1,
arealoaded = 1,
action = function(pos)
if nevermatch[nodecore.stack_get(pos):get_name()] then return end
return nodecore.dnt_set(pos, dntname)

View File

@ -29,6 +29,7 @@ minetest.register_abm({
nodenames = {cracked},
interval = 1,
chance = 1,
arealoaded = 1,
action = function(pos)
if nodecore.quenched(pos) or #nodecore.find_nodes_around(
pos, "group:flame", 1) < 1 then

View File

@ -72,6 +72,7 @@ minetest.register_abm({
interval = 1,
chance = 2,
nodenames = {modname .. ":flux_source"},
arealoaded = 1,
action = function(pos)
for _, v in pairs(indirs) do
local p = vector.add(pos, v)

View File

@ -8,6 +8,7 @@ minetest.register_abm({
interval = 1,
chance = 2,
nodenames = {"group:lux_cobble"},
arealoaded = 1,
action = function(pos, node)
local qty = nodecore.lux_react_qty(pos)
local name = node.name:gsub("cobble%d", "cobble" .. qty)

View File

@ -10,6 +10,7 @@ nodecore.register_soaking_abm({
fieldname = "lavalux",
interval = 10,
nodenames = {"group:amalgam"},
arealoaded = 14,
soakrate = nodecore.lux_soak_rate,
soakcheck = function(data, pos)
if data.total < 12500 then return end

View File

@ -11,6 +11,7 @@ minetest.register_abm({
chance = 1,
nodenames = {"group:" .. modname},
ignore_stasis = true,
arealoaded = 1,
action = function(pos)
local data = minetest.get_meta(pos):get_string("data")
if (not data) or (data == "") then

View File

@ -54,6 +54,7 @@ minetest.register_abm({
interval = 1,
chance = 100,
nodenames = {modname .. ":sponge_wet"},
arealoaded = 1,
action = function(pos)
local above = {x = pos.x, y = pos.y + 1, z = pos.z}
if nodecore.is_full_sun(above) and #findwater(pos) < 1 then

View File

@ -76,6 +76,7 @@ minetest.register_abm({
interval = 1,
chance = 10,
nodenames = {living},
arealoaded = 1,
action = function(pos, node)
if not spongesurvive({pos = pos, node = node}) then
nodecore.set_loud(pos, {name = wet})
@ -109,9 +110,9 @@ nodecore.register_soaking_abm({
nodenames = {living},
interval = 5,
chance = 2,
arealoaded = 6,
soakrate = function() return 2 end,
soakcheck = function(data, pos)
if nodecore.near_unloaded(pos) then return end
if data.total < basecost then return end
local count = 0

View File

@ -72,6 +72,7 @@ minetest.register_abm({
label = "artificial water check",
interval = 1,
chance = 1,
arealoaded = 1,
nodenames = {graywatersrc},
action = function(pos) return nodecore.artificial_water_check(pos) end
})

View File

@ -38,9 +38,9 @@ nodecore.register_soaking_abm({
fieldname = "eggcorn",
nodenames = {modname .. ":eggcorn_planted"},
interval = 10,
arealoaded = 1,
soakrate = nodecore.tree_growth_rate,
soakcheck = function(data, pos)
if nodecore.near_unloaded(pos) then return end
if data.total >= sproutcost then
nodecore.node_sound(pos, "dig")
nodecore.set_loud(pos, {name = modname .. ":root"})
@ -77,9 +77,9 @@ nodecore.register_soaking_abm({
fieldname = "treegrow",
nodenames = {modname .. ":tree_bud"},
interval = 10,
arealoaded = 1,
soakrate = nodecore.tree_trunk_growth_rate,
soakcheck = function(data, pos, node)
if nodecore.near_unloaded(pos) then return end
if data.total < trunkcost then
return growparticles(pos, data.rate, 0.45)
end
@ -131,12 +131,12 @@ nodecore.register_soaking_abm({
nodenames = {modname .. ":leaves_bud"},
fieldname = "leafgrow",
interval = 10,
arealoaded = 1,
soakrate = function(pos)
local rate = minetest.get_meta(pos):get_float("growrate") or 0
return rate and rate ~= 0 and rate or 10
end,
soakcheck = function(data, pos, node)
if nodecore.near_unloaded(pos) then return end
if data.total < leafcost then return end
nodecore.set_loud(pos, nodecore.calc_leaves(pos))

View File

@ -22,6 +22,7 @@ function nodecore.register_dirt_leaching(fromnode, tonode, rate)
fieldname = "leach",
nodenames = {fromnode},
interval = 5,
arealoaded = 1,
quickcheck = function(pos)
return waterat(pos, 0, 1, 0)
end,