summaryrefslogtreecommitdiff
path: root/digilines
diff options
context:
space:
mode:
Diffstat (limited to 'digilines')
-rw-r--r--digilines/init.lua2
-rw-r--r--digilines/internal.lua67
-rw-r--r--digilines/util.lua89
3 files changed, 138 insertions, 20 deletions
diff --git a/digilines/init.lua b/digilines/init.lua
index 2f48a73..bffd4e7 100644
--- a/digilines/init.lua
+++ b/digilines/init.lua
@@ -9,7 +9,7 @@ dofile(modpath .. "/wire_std.lua")
function digiline:receptor_send(pos, rules, channel, msg)
local checked = {}
- checked[tostring(pos.x).."_"..tostring(pos.y).."_"..tostring(pos.z)] = true -- exclude itself
+ checked[minetest.hash_node_position(pos)] = true -- exclude itself
for _,rule in ipairs(rules) do
if digiline:rules_link(pos, digiline:addPosRule(pos, rule)) then
digiline:transmit(digiline:addPosRule(pos, rule), channel, msg, checked)
diff --git a/digilines/internal.lua b/digilines/internal.lua
index 2319c16..45cd5d7 100644
--- a/digilines/internal.lua
+++ b/digilines/internal.lua
@@ -14,7 +14,7 @@ function digiline:importrules(spec, node)
end
function digiline:getAnyInputRules(pos)
- local node = minetest.get_node(pos)
+ local node = digiline:get_node_force(pos)
local spec = digiline:getspec(node)
if not spec then return end
@@ -27,7 +27,7 @@ function digiline:getAnyInputRules(pos)
end
function digiline:getAnyOutputRules(pos)
- local node = minetest.get_node(pos)
+ local node = digiline:get_node_force(pos)
local spec = digiline:getspec(node)
if not spec then return end
@@ -63,28 +63,57 @@ function digiline:rules_link_anydir(output, input)
or digiline:rules_link(input, output)
end
-function digiline:transmit(pos, channel, msg, checked)
- local checkedid = tostring(pos.x).."_"..tostring(pos.y).."_"..tostring(pos.z)
- if checked[checkedid] then return end
- checked[checkedid] = true
+local function queue_new()
+ return {nextRead = 1, nextWrite = 1}
+end
- local node = minetest.get_node(pos)
- local spec = digiline:getspec(node)
- if not spec then return end
+local function queue_empty(queue)
+ return queue.nextRead == queue.nextWrite
+end
+local function queue_enqueue(queue, object)
+ local nextWrite = queue.nextWrite
+ queue[nextWrite] = object
+ queue.nextWrite = nextWrite + 1
+end
- -- Effector actions --> Receive
- if spec.effector then
- spec.effector.action(pos, node, channel, msg)
- end
+local function queue_dequeue(queue)
+ local nextRead = queue.nextRead
+ local object = queue[nextRead]
+ queue[nextRead] = nil
+ queue.nextRead = nextRead + 1
+ return object
+end
- -- Cable actions --> Transmit
- if spec.wire then
- local rules = digiline:importrules(spec.wire.rules, node)
- for _,rule in ipairs(rules) do
- if digiline:rules_link(pos, digiline:addPosRule(pos, rule)) then
- digiline:transmit(digiline:addPosRule(pos, rule), channel, msg, checked)
+function digiline:transmit(pos, channel, msg, checked)
+ digiline:vm_begin()
+ local queue = queue_new()
+ queue_enqueue(queue, pos)
+ while not queue_empty(queue) do
+ local curPos = queue_dequeue(queue)
+ local node = digiline:get_node_force(curPos)
+ local spec = digiline:getspec(node)
+ if spec then
+ -- Effector actions --> Receive
+ if spec.effector then
+ spec.effector.action(curPos, node, channel, msg)
+ end
+
+ -- Cable actions --> Transmit
+ if spec.wire then
+ local rules = digiline:importrules(spec.wire.rules, node)
+ for _, rule in ipairs(rules) do
+ local nextPos = digiline:addPosRule(curPos, rule)
+ if digiline:rules_link(curPos, nextPos) then
+ local checkedID = minetest.hash_node_position(nextPos)
+ if not checked[checkedID] then
+ checked[checkedID] = true
+ queue_enqueue(queue, nextPos)
+ end
+ end
+ end
end
end
end
+ digiline:vm_end()
end
diff --git a/digilines/util.lua b/digilines/util.lua
index d138d63..cec75be 100644
--- a/digilines/util.lua
+++ b/digilines/util.lua
@@ -65,3 +65,92 @@ function digiline:tablecopy(table) -- deep table copy
return newtable
end
+
+
+
+-- VoxelManipulator-based node access functions:
+
+-- Maps from a hashed mapblock position (as returned by hash_blockpos) to a
+-- table.
+--
+-- Contents of the table are:
+-- “va” → the VoxelArea
+-- “data” → the data array
+-- “param1” → the param1 array
+-- “param2” → the param2 array
+--
+-- Nil if no bulk-VM operation is in progress.
+local vm_cache = nil
+
+-- Starts a bulk-VoxelManipulator operation.
+--
+-- During a bulk-VoxelManipulator operation, calls to get_node_force operate
+-- directly on VM-loaded arrays, which should be faster for reading many nodes
+-- in rapid succession. However, the cache must be flushed with vm_end once the
+-- scan is finished, to avoid using stale data in future.
+function digiline:vm_begin()
+ vm_cache = {}
+end
+
+-- Ends a bulk-VoxelManipulator operation, freeing the cached data.
+function digiline:vm_end()
+ vm_cache = nil
+end
+
+-- The dimension of a mapblock in nodes.
+local MAPBLOCKSIZE = 16
+
+-- Converts a node position into a hash of a mapblock position.
+local function vm_hash_blockpos(pos)
+ return minetest.hash_node_position({
+ x = math.floor(pos.x / MAPBLOCKSIZE),
+ y = math.floor(pos.y / MAPBLOCKSIZE),
+ z = math.floor(pos.z / MAPBLOCKSIZE)
+ })
+end
+
+-- Gets the cache entry covering a position, populating it if necessary.
+local function vm_get_or_create_entry(pos)
+ local hash = vm_hash_blockpos(pos)
+ local tbl = vm_cache[hash]
+ if not tbl then
+ local vm = minetest.get_voxel_manip(pos, pos)
+ local min_pos, max_pos = vm:get_emerged_area()
+ local va = VoxelArea:new{MinEdge = min_pos, MaxEdge = max_pos}
+ tbl = {va = va, data = vm:get_data(), param1 = vm:get_light_data(), param2 = vm:get_param2_data()}
+ vm_cache[hash] = tbl
+ end
+ return tbl
+end
+
+-- Gets the node at a position during a bulk-VoxelManipulator operation.
+local function vm_get_node(pos)
+ local tbl = vm_get_or_create_entry(pos)
+ local index = tbl.va:indexp(pos)
+ local node_value = tbl.data[index]
+ local node_param1 = tbl.param1[index]
+ local node_param2 = tbl.param2[index]
+ return {name = minetest.get_name_from_content_id(node_value), param1 = node_param1, param2 = node_param2}
+end
+
+-- Gets the node at a given position, regardless of whether it is loaded or
+-- not.
+--
+-- Outside a bulk-VoxelManipulator operation, if the mapblock is not loaded, it
+-- is pulled into the server’s main map data cache and then accessed from
+-- there.
+--
+-- Inside a bulk-VoxelManipulator operation, the operation’s VM cache is used.
+function digiline:get_node_force(pos)
+ if vm_cache then
+ return vm_get_node(pos)
+ end
+ local node = minetest.get_node(pos)
+ if node.name == "ignore" then
+ -- Node is not currently loaded; use a VoxelManipulator to prime
+ -- the mapblock cache and try again.
+ minetest.get_voxel_manip(pos, pos)
+ node = minetest.get_node(pos)
+ end
+ return node
+end