b6232f4f99
A certain arrangement of door-gears with a couple of pieces of sand next to it can get in a situation that causes an infinite loop, e.g. as if the sand nodes are trying to switch places with one another and form a circular dependency. The simplest patch for it is just to limit recursion for now. Randomness in the depth reduces the risk that machines themselves can get stuck in some pathological state.
81 lines
2.0 KiB
Lua
81 lines
2.0 KiB
Lua
-- LUALOCALS < ---------------------------------------------------------
|
|
local ipairs, math, minetest, nodecore, vector
|
|
= ipairs, math, minetest, nodecore, vector
|
|
local math_random
|
|
= math.random
|
|
-- LUALOCALS > ---------------------------------------------------------
|
|
|
|
local hashpos = minetest.pos_to_string
|
|
local is_falling = {groups = {falling_node = true}}
|
|
|
|
local queue = {}
|
|
local queued = {}
|
|
|
|
function nodecore.door_push(pos, ...)
|
|
local key = hashpos(pos)
|
|
if queued[key] then return end
|
|
local node = minetest.get_node(pos)
|
|
if not nodecore.match(node, is_falling) then return end
|
|
node.param = nil
|
|
local try = {}
|
|
for i, dir in ipairs({...}) do
|
|
try[i] = vector.add(pos, dir)
|
|
end
|
|
queue[#queue + 1] = {
|
|
key = key,
|
|
from = pos,
|
|
try = try,
|
|
attempts = math_random(10, 20)
|
|
}
|
|
queued[key] = true
|
|
end
|
|
|
|
local function tryprocess(item, retry)
|
|
local node = minetest.get_node(item.from)
|
|
if not nodecore.match(node, is_falling) then return end
|
|
for _, t in ipairs(item.try) do
|
|
if nodecore.buildable_to(t) then
|
|
local meta = minetest.get_meta(item.from):to_table()
|
|
minetest.remove_node(item.from)
|
|
nodecore.fallcheck({x = item.from.x, y = item.from.y + 1, z = item.from.z})
|
|
nodecore.set_loud(t, node)
|
|
meta.fields = meta.fields or {}
|
|
meta.fields.tweenfrom = minetest.serialize(item.from)
|
|
minetest.get_meta(t):from_table(meta)
|
|
nodecore.visinv_update_ents(t)
|
|
nodecore.fallcheck(t)
|
|
local re = retry[hashpos(item.from)]
|
|
if not re then return end
|
|
for _, r in ipairs(re) do
|
|
if r.attempts and r.attempts > 0 and not queued[r.key] then
|
|
r.attempts = r.attempts - 1
|
|
queue[#queue + 1] = r
|
|
queued[r.key] = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
for _, t in ipairs(item.try) do
|
|
local key = hashpos(t)
|
|
local r = retry[key]
|
|
if not r then
|
|
r = {}
|
|
retry[key] = r
|
|
end
|
|
r[#r + 1] = item
|
|
end
|
|
end
|
|
|
|
minetest.register_globalstep(function()
|
|
local retry = {}
|
|
local i = 1
|
|
while i <= #queue do
|
|
local item = queue[i]
|
|
queued[hashpos(item.from)] = nil
|
|
tryprocess(item, retry)
|
|
i = i + 1
|
|
end
|
|
queue = {}
|
|
queued = {}
|
|
end)
|