diff --git a/internal.lua b/internal.lua index d012ee2..96f84e7 100644 --- a/internal.lua +++ b/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 @@ -86,11 +86,12 @@ local function queue_dequeue(queue) end 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 = minetest.get_node(curPos) + local node = digiline:get_node_force(curPos) local spec = digiline:getspec(node) if spec then -- Effector actions --> Receive @@ -114,4 +115,5 @@ function digiline:transmit(pos, channel, msg, checked) end end end + digiline:vm_end() end diff --git a/util.lua b/util.lua index d138d63..cec75be 100644 --- a/util.lua +++ b/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