summaryrefslogtreecommitdiff
path: root/builtin/game/falling.lua
diff options
context:
space:
mode:
authorAuke Kok <sofar@foo-projects.org>2016-03-30 07:50:10 -0700
committerkwolekr <kwolekr@minetest.net>2016-04-11 00:01:28 -0400
commitd7908ee49480caaab63d05c8a53d93103579d7a9 (patch)
tree0dbea671ae937fa4558d2bf09355da014f21bf6a /builtin/game/falling.lua
parent2eeb62057a9e08def6a0f013e3ca5d84768d1566 (diff)
downloadminetest-d7908ee49480caaab63d05c8a53d93103579d7a9.tar.gz
minetest-d7908ee49480caaab63d05c8a53d93103579d7a9.tar.bz2
minetest-d7908ee49480caaab63d05c8a53d93103579d7a9.zip
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.
Diffstat (limited to 'builtin/game/falling.lua')
-rw-r--r--builtin/game/falling.lua86
1 files changed, 67 insertions, 19 deletions
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