summaryrefslogtreecommitdiff
path: root/mesecons/util.lua
diff options
context:
space:
mode:
Diffstat (limited to 'mesecons/util.lua')
-rw-r--r--mesecons/util.lua145
1 files changed, 117 insertions, 28 deletions
diff --git a/mesecons/util.lua b/mesecons/util.lua
index d95f216..c9ba7fc 100644
--- a/mesecons/util.lua
+++ b/mesecons/util.lua
@@ -219,7 +219,7 @@ function mesecon.table2file(filename, table)
f:close()
end
--- Forceloading: Force server to load area if node is nil
+-- Block position "hashing" (convert to integer) functions for voxelmanip cache
local BLOCKSIZE = 16
-- convert node position --> block hash
@@ -236,43 +236,132 @@ local function unhash_blockpos(hash)
return vector.multiply(minetest.get_position_from_hash(hash), BLOCKSIZE)
end
-mesecon.forceloaded_blocks = {}
+-- Maps from a hashed mapblock position (as returned by hash_blockpos) to a
+-- table.
+--
+-- Contents of the table are:
+-- “vm” → the VoxelManipulator
+-- “va” → the VoxelArea
+-- “data” → the data array
+-- “param1” → the param1 array
+-- “param2” → the param2 array
+-- “dirty” → true if data has been modified
+--
+-- Nil if no VM-based transaction is in progress.
+local vm_cache = nil
+
+-- Starts a VoxelManipulator-based transaction.
+--
+-- During a VM transaction, calls to vm_get_node and vm_swap_node operate on a
+-- cached copy of the world loaded via VoxelManipulators. That cache can later
+-- be committed to the real map by means of vm_commit or discarded by means of
+-- vm_abort.
+function mesecon.vm_begin()
+ vm_cache = {}
+end
--- get node and force-load area
-function mesecon.get_node_force(pos)
+-- Finishes a VoxelManipulator-based transaction, freeing the VMs and map data
+-- and writing back any modified areas.
+function mesecon.vm_commit()
+ for hash, tbl in pairs(vm_cache) do
+ if tbl.dirty then
+ local vm = tbl.vm
+ vm:set_data(tbl.data)
+ vm:write_to_map()
+ vm:update_map()
+ end
+ end
+ vm_cache = nil
+end
+
+-- Finishes a VoxelManipulator-based transaction, freeing the VMs and throwing
+-- away any modified areas.
+function mesecon.vm_abort()
+ vm_cache = nil
+end
+
+-- Gets the cache entry covering a position, populating it if necessary.
+local function vm_get_or_create_entry(pos)
local hash = 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 = {vm = vm, va = va, data = vm:get_data(), param1 = vm:get_light_data(), param2 = vm:get_param2_data(), dirty = false}
+ vm_cache[hash] = tbl
+ end
+ return tbl
+end
- if mesecon.forceloaded_blocks[hash] == nil then
- -- if no more forceload spaces are available, try again next time
- if minetest.forceload_block(pos) then
- mesecon.forceloaded_blocks[hash] = 0
- end
+-- Gets the node at a given position during a VoxelManipulator-based
+-- transaction.
+function mesecon.vm_get_node(pos)
+ local tbl = vm_get_or_create_entry(pos)
+ local index = tbl.va:indexp(pos)
+ local node_value = tbl.data[index]
+ if node_value == core.CONTENT_IGNORE then
+ return nil
else
- mesecon.forceloaded_blocks[hash] = 0
+ 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
+end
- return minetest.get_node_or_nil(pos)
+-- Sets a node’s name during a VoxelManipulator-based transaction.
+--
+-- Existing param1, param2, and metadata are left alone.
+function mesecon.vm_swap_node(pos, name)
+ local tbl = vm_get_or_create_entry(pos)
+ local index = tbl.va:indexp(pos)
+ tbl.data[index] = minetest.get_content_id(name)
+ tbl.dirty = true
end
-minetest.register_globalstep(function (dtime)
- for hash, time in pairs(mesecon.forceloaded_blocks) do
- -- unload forceloaded blocks after 10 minutes without usage
- if (time > mesecon.setting("forceload_timeout", 600)) then
- minetest.forceload_free_block(unhash_blockpos(hash))
- mesecon.forceloaded_blocks[hash] = nil
- else
- mesecon.forceloaded_blocks[hash] = time + dtime
+-- Gets the node at a given position, regardless of whether it is loaded or
+-- not, respecting a transaction if one is in progress.
+--
+-- Outside a VM transaction, if the mapblock is not loaded, it is pulled into
+-- the server’s main map data cache and then accessed from there.
+--
+-- Inside a VM transaction, the transaction’s VM cache is used.
+function mesecon.get_node_force(pos)
+ if vm_cache then
+ return mesecon.vm_get_node(pos)
+ else
+ local node = minetest.get_node_or_nil(pos)
+ if node == nil 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_or_nil(pos)
end
+ return node
end
-end)
-
--- Store and read the forceloaded blocks to / from a file
--- so that those blocks are remembered when the game
--- is restarted
-mesecon.forceloaded_blocks = mesecon.file2table("mesecon_forceloaded")
-minetest.register_on_shutdown(function()
- mesecon.table2file("mesecon_forceloaded", mesecon.forceloaded_blocks)
-end)
+end
+
+-- Swaps the node at a given position, regardless of whether it is loaded or
+-- not, respecting a transaction if one is in progress.
+--
+-- Outside a VM transaction, if the mapblock is not loaded, it is pulled into
+-- the server’s main map data cache and then accessed from there.
+--
+-- Inside a VM transaction, the transaction’s VM cache is used.
+--
+-- This function can only be used to change the node’s name, not its parameters
+-- or metadata.
+function mesecon.swap_node_force(pos, name)
+ if vm_cache then
+ return mesecon.vm_swap_node(pos, name)
+ else
+ -- This serves to both ensure the mapblock is loaded and also hand us
+ -- the old node table so we can preserve param2.
+ local node = mesecon.get_node_force(pos)
+ node.name = name
+ minetest.swap_node(pos, node)
+ end
+end
-- Autoconnect Hooks
-- Nodes like conductors may change their appearance and their connection rules