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

63 lines
1.7 KiB
Lua

-- LUALOCALS < ---------------------------------------------------------
local ItemStack, math, minetest, nodecore, pairs, vector
= ItemStack, math, minetest, nodecore, pairs, vector
local math_floor, math_random
= math.floor, math.random
-- LUALOCALS > ---------------------------------------------------------
local cache = {}
function nodecore.item_ent_merge(pos)
if not pos then return end
pos = vector.round(pos)
local hash = minetest.hash_node_position(pos)
local t = cache[hash]
if t and t > nodecore.gametime then return end
cache[hash] = (t or nodecore.gametime) + 0.75 + 0.5 * math_random()
local db = {}
for _, obj in pairs(nodecore.get_objects_at_pos(pos, 1)) do
local lua = obj.get_luaentity and obj:get_luaentity()
if lua and lua.name == "__builtin:item" then
local stack = ItemStack(lua.itemstring or "")
if not stack:is_empty() then
local qty = stack:get_count()
stack:set_count(1)
local key = stack:to_string()
local entry = db[key]
if entry then
entry.qty = entry.qty + qty
entry.objs[#entry.objs + 1] = {obj = obj, lua = lua}
else
db[key] = {
stack = stack,
qty = qty,
objs = {{obj = obj, lua = lua}}
}
end
end
end
end
for _, entry in pairs(db) do
if #entry.objs > 1 then
local stack = entry.stack
local qty = entry.qty
local max = stack:get_stack_max()
local stax = math_floor((qty + max - 1) / max)
if stax < #entry.objs then
for _, item in pairs(entry.objs) do
if qty > 0 then
local num = qty > max and max or qty
stack:set_count(num)
item.lua:set_item(stack)
qty = qty - num
else
item.obj:remove()
end
end
end
end
end
end