Aaron Suen a4c5eb3b1b Sledgehammer solution to visinvs not loading
Apparently LBMs are just not reliable anymore as
of 5.4, so we'll have to fall back to the at least
eventually-reliable ABM again.
2021-03-09 00:09:05 -05:00

220 lines
5.6 KiB
Lua

-- LUALOCALS < ---------------------------------------------------------
local math, minetest, nodecore, pairs, vector
= math, minetest, nodecore, pairs, vector
local math_random
= math.random
-- LUALOCALS > ---------------------------------------------------------
nodecore.amcoremod()
--[[
Helpers for visible inventory. Use "visinv" node group.
Sets up on_construct, after_destruct and an ABM to manage
the visual entities.
--]]
local modname = minetest.get_current_modname()
------------------------------------------------------------------------
-- VISIBLE STACK ENTITY
local function getlightcheck(rp, obj, src)
return function()
local stack = nodecore.stack_get(rp)
if not stack or stack:is_empty() then return end
local def = minetest.registered_items[stack:get_name()] or {}
if (def.light_source or 0) ~= src then return end
for _, v in pairs(nodecore.get_objects_at_pos(rp)) do
if v == obj then return true end
end
end
end
local check_queue = {}
local check_queue_dirty
local function visinv_update_ents(pos)
pos = vector.round(pos)
check_queue[minetest.hash_node_position(pos)] = pos
check_queue_dirty = true
end
nodecore.visinv_update_ents = visinv_update_ents
local visinv_ents = {}
local function objremove(ent, obj)
visinv_ents[ent] = nil
return (obj or ent.object):remove()
end
local function itemcheck(self)
local obj = self.object
local pos = obj:get_pos()
if not pos then visinv_ents[self] = nil return end
local stack = nodecore.stack_get(pos)
local sstr = stack:to_string()
if self.stackstring == sstr then return end
self.stackstring = sstr
if stack:is_empty() then return objremove(self, obj) end
local rp = vector.round(pos)
local def = minetest.registered_items[stack:get_name()] or {}
local src = def.light_source or 0
if src > 0 then
self.light_source = src
nodecore.dynamic_light_add(rp, src, getlightcheck(rp, obj, src))
end
local props, scale, yaw = nodecore.stackentprops(stack,
rp.x * 3 + rp.y * 5 + rp.z * 7)
rp.y = rp.y + scale - 31/64
if obj:get_yaw() ~= yaw then
obj:set_yaw(yaw)
end
if not vector.equals(obj:get_pos(), rp) then
obj:set_pos(rp)
end
return obj:set_properties(props)
end
local entname = modname .. ":stackent"
minetest.register_entity(entname, {
initial_properties = nodecore.stackentprops(),
is_stack = true,
itemcheck = itemcheck
})
local check_retry
local function check_retry_add(key, val)
if not check_retry then
check_retry = {}
minetest.after(1 + math_random(), function()
local total = 0
for k, v in pairs(check_retry) do
total = total + 1
check_queue[k] = v
end
check_queue_dirty = true
check_retry = nil
nodecore.log("warning", "visinv entity retry: " .. total)
end)
end
check_retry[key] = val
end
nodecore.register_globalstep("visinv check", function()
if not check_queue_dirty then return end
local batch = check_queue
check_queue = {}
check_queue_dirty = nil
for ent in pairs(visinv_ents) do
if (ent.name == entname) and (not ent.gone) then
local key = ent.poskey
if key then
local data = batch[key]
if data then
if data.n then
objremove(ent)
else
itemcheck(ent)
data.n = true
end
end
end
end
end
for poskey, data in pairs(batch) do
if (not data.n) and (not nodecore.stack_get(data):is_empty()) then
local obj = minetest.add_entity(data, entname)
local ent = obj and obj:get_luaentity()
if ent then
visinv_ents[ent] = true
ent.is_stack = true
ent.poskey = poskey
itemcheck(ent)
else
check_retry_add(poskey, data)
end
end
end
end)
------------------------------------------------------------------------
-- NODE REGISTRATION HELPERS
function nodecore.visinv_on_construct(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
inv:set_size("solo", 1)
nodecore.visinv_update_ents(pos)
end
function nodecore.visinv_after_destruct(pos)
nodecore.visinv_update_ents(pos)
nodecore.fallcheck(pos)
end
nodecore.register_on_register_item(function(_, def)
if def.type ~= "node" then return end
def.groups = def.groups or {}
if def.groups.visinv then
def.on_construct = def.on_construct or nodecore.visinv_on_construct
def.after_destruct = def.after_destruct or nodecore.visinv_after_destruct
end
end)
nodecore.register_lbm({
name = modname .. ":init",
run_at_every_load = true,
nodenames = {"group:visinv"},
action = function(...) return nodecore.visinv_update_ents(...) end
})
nodecore.register_abm({
label = "visinv check",
interval = 2,
chance = 1,
nodenames = {"group:visinv"},
action = function(...) return nodecore.visinv_update_ents(...) end
})
------------------------------------------------------------------------
-- DIG INVENTORY
local dug
local old_node_dig = minetest.node_dig
minetest.node_dig = function(pos, node, digger, ...)
nodecore.stack_sounds(pos, "dug")
local function helper(...)
dug = nil
return ...
end
dug = {pos = pos, who = digger}
return helper(old_node_dig(pos, node, digger, ...))
end
local old_get_node_drops = minetest.get_node_drops
minetest.get_node_drops = function(...)
local drops = old_get_node_drops(...)
if not dug then return drops end
drops = drops or {}
local stack = nodecore.stack_get(dug.pos)
if stack and not stack:is_empty() then
local def = stack:get_definition()
local dmg = def and def.groups and def.groups.damage_touch
if dug.who and dmg and dmg > 0 then
nodecore.addphealth(dug.who, -dmg, "hot pickup")
nodecore.item_eject(dug.pos, stack, 0.001)
else
drops[#drops + 1] = stack
end
end
return drops
end