diff options
Diffstat (limited to 'digilines')
-rw-r--r-- | digilines/init.lua | 2 | ||||
-rw-r--r-- | digilines/internal.lua | 67 | ||||
-rw-r--r-- | digilines/util.lua | 89 |
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 |