nodecore-cd2025/mods/nc_tree/grow_active.lua
Aaron Suen 166cfe5f14 Mitigate excess server block sends
The problem was pointed out by Josh
(gitlab.com/krazy-j) in merge request !22.
Apparently MT is not very smart about marking
mapblocks dirty to send to clients based on
calling mt.set_node(), i.e. it will mark them
dirty presumptively even if you set the node to
the same value it had already been.

This behavior can be confirmed by registering
an ABM against a common node like grass and
setting action = minetest.set_node.  This
causes every mapblock containing that node
to be invalidated every interval, causing a big
spike in the packets received each interval
that you can clearly see on the F5 graph.

Rather than just fixing it for the most easily
observed case (fire checks), add utlity functions
to check this for ALL node change situations,
and apply it more or less universally anywhere
that we are not certain that the node is being
changed and we don't need to worry about the
extra overhead cost of the check.

Note that we don't need a
nodecore.set_loud_check call, as set_loud was
only ever being used already in cases where
we were pretty sure we were actually changing
a node.
2022-08-24 20:44:14 -04:00

226 lines
6.6 KiB
Lua

-- LUALOCALS < ---------------------------------------------------------
local math, minetest, nodecore, pairs, table
= math, minetest, nodecore, pairs, table
local math_random, table_concat
= math.random, table.concat
-- LUALOCALS > ---------------------------------------------------------
local modname = minetest.get_current_modname()
local function growparticles(pos, rate, width)
nodecore.soaking_particles(pos, rate, 10, width, modname .. ":leaves_bud")
end
local sproutcost = 2000
nodecore.register_soaking_abm({
label = "eggcorn sprout",
fieldname = "eggcorn",
nodenames = {modname .. ":eggcorn_planted"},
interval = 10,
arealoaded = 1,
soakrate = nodecore.tree_growth_rate,
soakcheck = function(data, pos)
if data.total >= sproutcost then
nodecore.node_sound(pos, "dig")
nodecore.set_loud(pos, {name = modname .. ":root"})
local apos = {x = pos.x, y = pos.y + 1, z = pos.z}
nodecore.set_loud(apos,
{name = modname .. ":tree_bud", param2 = 1})
nodecore.witness(apos, "tree growth")
return nodecore.soaking_abm_push(apos,
"treegrow", data.total - sproutcost)
end
return growparticles(pos, data.rate, 0.2)
end
})
local function leafbud(pos, dx, dy, dz, param2, surplus, rate)
local npos = {x = pos.x + dx, y = pos.y + dy, z = pos.z + dz}
if not nodecore.buildable_to(npos) then
local node = minetest.get_node(npos)
if minetest.get_item_group(node.name, "canopy") == 0 or param2 < node.param2
then return end
end
if param2 <= 1 then
if 240 < math_random(0, 255) then return end
return nodecore.set_loud(npos, nodecore.calc_leaves(npos))
end
nodecore.set_loud(npos, {name = modname .. ":leaves_bud", param2 = param2})
minetest.get_meta(npos):set_float("growrate", rate)
return nodecore.soaking_abm_push(npos, "leafgrow", surplus)
end
local trunkcost = 1000
nodecore.register_soaking_abm({
label = "tree trunk grow",
fieldname = "treegrow",
nodenames = {modname .. ":tree_bud"},
interval = 10,
arealoaded = 1,
soakrate = function(pos, ...)
local v = nodecore.tree_trunk_growth_rate(pos, ...)
if v == false then
minetest.set_node(pos, {name = modname .. ":tree"})
end
return v
end,
soakcheck = function(data, pos, node)
if data.total < trunkcost then
return growparticles(pos, data.rate, 0.45)
end
local tp = nodecore.tree_params[node.param2]
if not tp then return minetest.remove_node(pos) end
minetest.set_node(pos, {name = modname .. ":tree"})
local apos = {x = pos.x, y = pos.y + 1, z = pos.z}
if not nodecore.buildable_to(apos)
and minetest.get_item_group(minetest.get_node(apos).name, "canopy") == 0
then return end
local param2 = node.param2 + 1
tp = nodecore.tree_params[param2]
if not tp then return minetest.remove_node(pos) end
while tp.prob and (tp.prob < math_random(0, 255)) do
param2 = param2 + 1
tp = nodecore.tree_params[param2]
if not tp then return minetest.remove_node(pos) end
end
local surplus = data.total - trunkcost
if tp.leaves then
leafbud(apos, 1, 0, 0, tp.leaves + 1, surplus, data.rate)
leafbud(apos, -1, 0, 0, tp.leaves + 1, surplus, data.rate)
leafbud(apos, 0, 0, 1, tp.leaves, surplus, data.rate)
leafbud(apos, 0, 0, -1, tp.leaves, surplus, data.rate)
end
if tp.notrunk then
leafbud(apos, 0, 0, 0, tp.leaves, surplus, data.rate)
else
nodecore.set_loud(apos, {
name = modname .. ":tree_bud",
param2 = param2
})
nodecore.witness(apos, "tree growth")
nodecore.soaking_abm_push(apos,
"treegrow", surplus)
end
end
})
local leafcost = trunkcost
nodecore.register_soaking_abm({
label = "tree leaves grow",
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 data.total < leafcost then return end
nodecore.set_loud(pos, nodecore.calc_leaves(pos))
local surplus = data.total - leafcost
if node.param2 <= 1 then
return
elseif node.param2 == 2 then
leafbud(pos, 1, 0, 0, 1, surplus, data.rate)
leafbud(pos, -1, 0, 0, 1, surplus, data.rate)
elseif node.param2 == 3 then
leafbud(pos, 0, 0, 1, 1, surplus, data.rate)
leafbud(pos, 0, 0, -1, 1, surplus, data.rate)
else
leafbud(pos, 1, 0, 0, 3, surplus, data.rate)
leafbud(pos, -1, 0, 0, 3, surplus, data.rate)
leafbud(pos, 0, 0, 1, 2, surplus, data.rate)
leafbud(pos, 0, 0, -1, 2, surplus, data.rate)
if node.param2 >= 6 then
leafbud(pos, 0, 1, 0, node.param2 - 4, surplus, data.rate)
end
end
end
})
local growtreedata = {
[modname .. ":eggcorn_planted"] = {
r = nodecore.tree_growth_rate,
f = "eggcorn"
},
[modname .. ":tree_bud"] = {
r = nodecore.tree_trunk_growth_rate,
f = "treegrow"
},
[modname .. ":leaves_bud"] = {
r = function() return 1 end,
f = "leafgrow"
}
}
minetest.register_chatcommand("growtrees", {
description = "Accelerate growth of nearby trees",
privs = {["debug"] = true},
func = function(pname)
local player = minetest.get_player_by_name(pname)
if not player then return end
local pos = player:get_pos()
local spec = {}
for k in pairs(growtreedata) do spec[#spec + 1] = k end
local grew = {}
for _, p in pairs(nodecore.find_nodes_around(pos, spec, 5)) do
local nn = minetest.get_node(p).name
local data = growtreedata[nn]
local r = data.r(p)
if r and r > 0 then
nodecore.soaking_abm_push(p, data.f, 100000)
grew[#grew + 1] = nn .. " at " .. minetest.pos_to_string(p)
end
end
return true, table_concat(grew, "\n")
end
})
nodecore.register_craft({
label = "tickle eggcorn",
action = "pummel",
toolgroups = {crumbly = 1},
normal = {y = 1},
nodes = {
{match = {name = modname .. ":eggcorn_planted", stacked = false}}
},
after = function(pos)
nodecore.soaking_abm_tickle(pos, "eggcorn")
nodecore.soaking_particles(pos, 25, 0.5, .45, modname .. ":leaves_bud")
end
})
nodecore.register_craft({
label = "tickle tree trunk",
action = "pummel",
toolgroups = {snappy = 1},
normal = {y = 1},
nodes = {
{match = {name = modname .. ":tree_bud", stacked = false}}
},
after = function(pos)
nodecore.soaking_abm_tickle(pos, "treegrow")
nodecore.soaking_particles(pos, 25, 0.5, .45, modname .. ":leaves_bud")
end
})
nodecore.register_craft({
label = "tickle tree leaves",
action = "pummel",
toolgroups = {snappy = 1},
nodes = {
{match = {name = modname .. ":leaves_bud", stacked = false}}
},
after = function(pos)
nodecore.soaking_abm_tickle(pos, "leafgrow")
end
})