Aaron Suen 4b82eb6d56 Small DNT API cleanups
- Auto-remove deprecated DNTs
- Auto clear meta if no DNTs pending
- Remove redundant code
2021-03-24 06:51:07 -04:00

148 lines
3.7 KiB
Lua

-- LUALOCALS < ---------------------------------------------------------
local error, math, minetest, nodecore, pairs, string
= error, math, minetest, nodecore, pairs, string
local math_random, string_format
= math.random, string.format
-- LUALOCALS > ---------------------------------------------------------
-- Active Block Modifiers, meet Delayed Node Triggers.
-- Definition:
--- name: "modname:technicalname"
--- nodenames: {"mod:itemname", "group:name"}
--- time: float (optional),
--- loop: boolean,
--- action: function(pos, node) end
nodecore.registered_dnts = {}
local grouppref = "group:"
local function buildidx(list)
if not list then return end
local n = {}
local g
for _, v in pairs(list) do
if v:sub(1, #grouppref) == grouppref then
g = g or {}
g[v:sub(#grouppref + 1)] = true
else
n[v] = true
end
end
if g then
minetest.after(0, function()
for k in pairs(minetest.registered_nodes) do
for x in pairs(g) do
if minetest.get_item_group(k, x) > 0 then
n[k] = true
end
end
end
end)
end
return n
end
function nodecore.register_dnt(def)
if not def.name then return error("dnt name required") end
if not def.action then return error("dnt action required") end
if nodecore.registered_dnts[def.name] then
return error(string_format("dnt %q already registered", def.name))
end
def.nodeidx = buildidx(def.nodenames)
nodecore.registered_dnts[def.name] = def
end
local dntkey = "dnt"
local function dntsave(pos, meta, data)
local now = nodecore.gametime
local prev = data[false]
local el = prev and (now - prev) or 0
local min
local run = {}
local reg = nodecore.registered_dnts
local any
for k, v in pairs(data) do
if k then
v = v - el
local def = reg[k]
if not def then
-- clear deprecated dnts
data[k] = nil
else
if v < 0 then
if def.ignore_stasis or not nodecore.stasis then
run[def] = true
v = def.loop and def.time or nil
else
-- auto-defer while on stasis
v = def.time and (def.time < 1) and def.time or 1
end
end
data[k] = v
any = any or v
if v and ((not min) or (min < v)) then min = v end
end
end
end
data[false] = now
meta:set_string(dntkey, any and minetest.serialize(data) or "")
if min then minetest.get_node_timer(pos):start(min) end
local node = minetest.get_node(pos)
local nn = node.name
for k in pairs(run) do
local idx = k.nodeidx
if (not idx) or idx[nn] then k.action(pos, node) end
end
end
local function dntload(pos)
local meta = minetest.get_meta(pos)
local s = meta:get_string(dntkey)
s = s and s ~= "" and minetest.deserialize(s) or {}
return s, function() return dntsave(pos, meta, s) end
end
local squelched = {}
local function maybecheck(pos, save)
local hash = minetest.hash_node_position(pos)
local s = squelched[hash]
if s and s > nodecore.gametime then return end
squelched[hash] = nodecore.gametime + 5 + math_random() * 10
return save()
end
function nodecore.dnt_set(pos, name, time)
local data, save = dntload(pos)
local prev = data[name]
time = time or nodecore.registered_dnts[name].time or 1
if prev and prev < time then return maybecheck(pos, save) end
data[name] = time
return save()
end
function nodecore.dnt_reset(pos, name, time)
local data, save = dntload(pos)
local prev = data[name]
time = time or nodecore.registered_dnts[name].time or 1
if prev and prev == time then return maybecheck(pos, save) end
data[name] = time
return save()
end
minetest.nodedef_default.on_timer = function(pos)
local _, save = dntload(pos)
return save()
end
nodecore.register_on_register_item(function(_, def)
if def.on_timer then
return error("on_timer hook is disallowed in "
.. nodecore.product .. "; use DNT instead")
end
end)