Aaron Suen 9c9eb4b85a Guard against dummy objects in 5.3+
Some time in the 5.3 dev stream (docs updated at
217f3a42), object refs started being invalidated
immediately on calling obj:remove(), such that
obj:get_pos() starts to return nil instead of the object's
last known position.

This can cause some crashes in NodeCore, where we
assume that our object is still valid (or usable as if it
were still valid) even though we're looping through
handlers and any one of them may have remove()d the
object before other handlers get a chance to fire.

Instead, just watch for unexpected nil returns from
functions we expect would never return nil (e.g.
get_pos or get_properties) and return if we hit one.
We can assume all other calls will be non-nil after that
one, as long as we stay in the same function flow.
2020-05-18 18:36:06 -04:00

134 lines
3.9 KiB
Lua

-- LUALOCALS < ---------------------------------------------------------
local ItemStack, minetest, nodecore, pairs, setmetatable, vector
= ItemStack, minetest, nodecore, pairs, setmetatable, vector
-- LUALOCALS > ---------------------------------------------------------
local modname = minetest.get_current_modname()
-- Register nodes that can be replaced by dynamic lights
local canreplace = {air = true}
local true_airlike = {
drawtype = "airlike",
pointable = false,
walkable = false,
climbable = false,
buildable_to = true,
floodable = true,
air_equivalent = true,
paramtype = "light",
light_source = 0,
sunlight_propagates = true,
}
minetest.after(0, function()
for k, v in pairs(minetest.registered_nodes) do
local ok = not canreplace[k]
for dk, dv in pairs(true_airlike) do
ok = ok and v[dk] == dv
end
if ok then canreplace[k] = true end
end
end)
-- Register dynamic light nodes
local nodes = {}
local function dynamic_light_node(level) return modname .. ":light" .. level end
nodecore.dynamic_light_node = dynamic_light_node
for level = 1, nodecore.light_sun - 1 do
if nodes[level] then return nodes[level] end
local name = dynamic_light_node(level)
local def = {
light_source = level,
on_timer = minetest.remove_node,
groups = {dynamic_light = level}
}
for k, v in pairs(true_airlike) do def[k] = def[k] or v end
minetest.register_node(":" .. name, def)
nodes[level] = name
canreplace[name] = true
end
minetest.register_alias("nc_torch:wield_light", dynamic_light_node(8))
-- API for adding dynamic lights to world
local active_lights = {}
nodecore.register_limited_abm({
label = "dynamic light cleanup",
interval = 1,
chance = 1,
nodenames = {"group:dynamic_light"},
action = function(pos)
local time = active_lights[minetest.hash_node_position(pos)] or 0
if time >= nodecore.gametime - 2 then return end
minetest.log("dynalight cleaned up at " .. minetest.pos_to_string(pos))
return minetest.remove_node(pos)
end
})
local function dynamic_light_add(pos, level, ttl)
if not pos then return end
local name = minetest.get_node(pos).name
if not canreplace[name] then return end
if level < 1 then return end
if level > nodecore.light_sun - 1 then level = nodecore.light_sun - 1 end
local setname = dynamic_light_node(level)
pos = vector.round(pos)
local ll = nodecore.get_node_light(pos)
if ll and ll > level then return end
if name ~= setname then minetest.set_node(pos, {name = setname}) end
active_lights[minetest.hash_node_position(pos)] = nodecore.gametime
return minetest.get_node_timer(pos):start(ttl)
end
nodecore.dynamic_light_add = dynamic_light_add
-- Automatic player wield lights
local function lightsrc(stack)
local def = minetest.registered_items[stack:get_name()] or {}
return def.light_source or 0
end
local function player_wield_light(player)
local glow = nodecore.scaling_light_level or 0
for _, stack in pairs(player:get_inventory():get_list("main")) do
local src = lightsrc(stack)
if src > glow then glow = src end
end
if glow < 1 then return end
local pos = player:get_pos()
pos.y = pos.y + player:get_properties().eye_height
return dynamic_light_add(pos, glow, 0.5)
end
minetest.register_globalstep(function()
for _, player in pairs(minetest.get_connected_players()) do
player_wield_light(player)
end
end)
-- Automatic entity light sources
local function entlight(self, ...)
local stack = ItemStack(self.node and self.node.name or self.itemstring or "")
local src = lightsrc(stack)
if src > 0 then nodecore.dynamic_light_add(self.object:get_pos(), src, 0.5) end
return ...
end
for _, name in pairs({"item", "falling_node"}) do
local def = minetest.registered_entities["__builtin:" .. name]
local ndef = {
on_step = function(self, ...)
return entlight(self, def.on_step(self, ...))
end
}
setmetatable(ndef, def)
minetest.register_entity(":__builtin:" .. name, ndef)
end