From: Auke Kok Date: Wed, 30 Mar 2016 14:50:10 +0000 (-0700) Subject: Convert nodeupdate to non-recursive X-Git-Tag: 0.4.14~105 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=d7908ee49480caaab63d05c8a53d93103579d7a9;p=oweals%2Fminetest.git Convert nodeupdate to non-recursive This took me a while to figure out. We no longer visit all 9 block around and with the touched node, but instead visit adjacent plus self. We then walk -non- recursively through all neigbors and if they cause a nodeupdate, we just keep walking until it ends. On the way back we prune the tail. I've tested this with 8000+ sand nodes. Video result is here: https://youtu.be/liKKgLefhFQ Took ~ 10 seconds to process and return to normal. --- diff --git a/builtin/game/falling.lua b/builtin/game/falling.lua index 3ab64f67e..cce8d4bf4 100644 --- a/builtin/game/falling.lua +++ b/builtin/game/falling.lua @@ -147,7 +147,7 @@ end -- Some common functions -- -function nodeupdate_single(p, delay) +function nodeupdate_single(p) local n = core.get_node(p) if core.get_item_group(n.name, "falling_node") ~= 0 then local p_bottom = {x = p.x, y = p.y - 1, z = p.z} @@ -160,36 +160,84 @@ function nodeupdate_single(p, delay) core.get_node_level(p_bottom) < core.get_node_max_level(p_bottom))) and (not core.registered_nodes[n_bottom.name].walkable or core.registered_nodes[n_bottom.name].buildable_to) then - if delay then - core.after(0.1, nodeupdate_single, p, false) - else - n.level = core.get_node_level(p) - core.remove_node(p) - spawn_falling_node(p, n) - nodeupdate(p) - end + n.level = core.get_node_level(p) + core.remove_node(p) + spawn_falling_node(p, n) + return true end end if core.get_item_group(n.name, "attached_node") ~= 0 then if not check_attached_node(p, n) then drop_attached_node(p) - nodeupdate(p) + return true end end + + return false end -function nodeupdate(p, delay) - -- Round p to prevent falling entities to get stuck +-- This table is specifically ordered. +-- We don't walk diagonals, only our direct neighbors, and self. +-- Down first as likely case, but always before self. The same with sides. +-- Up must come last, so that things above self will also fall all at once. +local nodeupdate_neighbors = { + {x = 0, y = -1, z = 0}, + {x = -1, y = 0, z = 0}, + {x = 1, y = 0, z = 0}, + {x = 0, y = 0, z = 1}, + {x = 0, y = 0, z = -1}, + {x = 0, y = 0, z = 0}, + {x = 0, y = 1, z = 0}, +} + +function nodeupdate(p) + -- Round p to prevent falling entities to get stuck. p = vector.round(p) - for x = -1, 1 do - for y = -1, 1 do - for z = -1, 1 do - local d = vector.new(x, y, z) - nodeupdate_single(vector.add(p, d), delay or not (x == 0 and y == 0 and z == 0)) - end - end + -- We make a stack, and manually maintain size for performance. + -- Stored in the stack, we will maintain tables with pos, and + -- last neighbor visited. This way, when we get back to each + -- node, we know which directions we have already walked, and + -- which direction is the next to walk. + local s = {} + local n = 0 + -- The neighbor order we will visit from our table. + local v = 1 + + while true do + -- Push current pos onto the stack. + n = n + 1 + s[n] = {p = p, v = v} + -- Select next node from neighbor list. + p = vector.add(p, nodeupdate_neighbors[v]) + -- Now we check out the node. If it is in need of an update, + -- it will let us know in the return value (true = updated). + if not nodeupdate_single(p) then + -- If we don't need to "recurse" (walk) to it then pop + -- our previous pos off the stack and continue from there, + -- with the v value we were at when we last were at that + -- node + repeat + local pop = s[n] + p = pop.p + v = pop.v + s[n] = nil + n = n - 1 + -- If there's nothing left on the stack, and no + -- more sides to walk to, we're done and can exit + if n == 0 and v == 7 then + return + end + until v < 7 + -- The next round walk the next neighbor in list. + v = v + 1 + else + -- If we did need to walk the neighbor, then + -- start walking it from the walk order start (1), + -- and not the order we just pushed up the stack. + v = 1 + end end end