184 lines
5.0 KiB
Lua
184 lines
5.0 KiB
Lua
--[[
|
|
|
|
TechAge
|
|
=======
|
|
|
|
Copyright (C) 2019-2020 Joachim Stolberg
|
|
|
|
AGPL v3
|
|
See LICENSE.txt for more information
|
|
|
|
Data storage system for node related volatile and non-volatile data.
|
|
Non-volatile data is stored from time to time and at shutdown.
|
|
Volatile data is lost at every shutdown.
|
|
|
|
]]--
|
|
|
|
local NvmStore = {} -- non-volatile data cache
|
|
local MemStore = {} -- volatile data cache
|
|
|
|
local N = function(pos) print(minetest.pos_to_string(pos), minetest.get_node(pos).name) end
|
|
|
|
-------------------------------------------------------------------
|
|
-- Backend
|
|
-------------------------------------------------------------------
|
|
local MP = minetest.get_modpath("techage")
|
|
local techage_use_sqlite = minetest.settings:get_bool('techage_use_sqlite', false)
|
|
local backend
|
|
|
|
if techage_use_sqlite then
|
|
backend = dofile(MP .. "/basis/nodedata_sqlite.lua")
|
|
else
|
|
backend = dofile(MP .. "/basis/nodedata_meta.lua")
|
|
end
|
|
|
|
-- return keys for mapblock and inner-mapblock addressing based on the node position
|
|
local function get_keys(pos)
|
|
local kx1, kx2 = math.floor(pos.x / 16) + 2048, pos.x % 16
|
|
local ky1, ky2 = math.floor(pos.y / 16) + 2048, pos.y % 16
|
|
local kz1, kz2 = math.floor(pos.z / 16) + 2048, pos.z % 16
|
|
return kx1 * 4096 * 4096 + ky1 * 4096 + kz1, kx2 * 16 * 16 + ky2 * 16 + kz2
|
|
end
|
|
|
|
local function pos_from_key(key1, key2)
|
|
|
|
local x1 = (math.floor(key1 / (4096 * 4096)) - 2048) * 16
|
|
local y1 = ((math.floor(key1 / 4096) % 4096) - 2048) * 16
|
|
local z1 = ((key1 % 4096) - 2048) * 16
|
|
local x2 = math.floor(key2 / (16 * 16))
|
|
local y2 = math.floor(key2 / 16) % 16
|
|
local z2 = key2 % 16
|
|
|
|
return {x = x1 + x2, y = y1 + y2, z = z1 + z2}
|
|
end
|
|
|
|
local function debug(key1, item)
|
|
--local pos1 = pos_from_key(key1, 0)
|
|
--local pos2 = {x = pos1.x + 15, y = pos1.y + 15, z = pos1.z + 15}
|
|
--techage.mark_region("mapblock", pos1, pos2, "singleplayer", 5)
|
|
|
|
local cnt = 0
|
|
for key2, tbl in pairs(item) do
|
|
if key2 ~= "in_use" then
|
|
cnt = cnt + 1
|
|
--N(pos_from_key(key1, key2))
|
|
end
|
|
end
|
|
print("mapblock", string.format("%09X", key1), cnt.." nodes")
|
|
end
|
|
|
|
|
|
-------------------------------------------------------------------
|
|
-- Storage scheduler
|
|
-------------------------------------------------------------------
|
|
local CYCLE_TIME = 600 -- store data every 10 min
|
|
local JobQueue = {}
|
|
local first = 0
|
|
local last = -1
|
|
local SystemTime = 0
|
|
|
|
local function push(key)
|
|
last = last + 1
|
|
JobQueue[last] = {key = key, time = SystemTime + CYCLE_TIME}
|
|
end
|
|
|
|
local function pop()
|
|
if first > last then return end
|
|
local item = JobQueue[first]
|
|
if item.time <= SystemTime then
|
|
JobQueue[first] = nil -- to allow garbage collection
|
|
first = first + 1
|
|
return item.key
|
|
end
|
|
end
|
|
|
|
-- check every 100 msec if any data has to be stored
|
|
minetest.register_globalstep(function(dtime)
|
|
SystemTime = SystemTime + dtime
|
|
local key = pop()
|
|
if key and NvmStore[key] then
|
|
-- minetest.log("warning",
|
|
-- string.format("[TA Storage] SystemTime = %.3f, #JobQueue = %d, in_use = %s",
|
|
-- SystemTime, last - first, NvmStore[key].in_use))
|
|
local t = minetest.get_us_time()
|
|
if NvmStore[key].in_use then
|
|
NvmStore[key].in_use = nil
|
|
backend.store_mapblock_data(key, NvmStore[key])
|
|
push(key)
|
|
else
|
|
NvmStore[key] = nil -- remove unused data from cache
|
|
end
|
|
t = minetest.get_us_time() - t
|
|
if t > 20000 then
|
|
minetest.log("warning", "[TA Storage] duration = "..(t/1000.0).." ms")
|
|
end
|
|
end
|
|
end)
|
|
|
|
-------------------------------------------------------------------
|
|
-- Store/Restore NVM data
|
|
-------------------------------------------------------------------
|
|
NvmStore = backend.restore_at_startup()
|
|
|
|
minetest.register_on_shutdown(function()
|
|
backend.freeze_at_shutdown(NvmStore)
|
|
end)
|
|
|
|
-------------------------------------------------------------------
|
|
-- API functions
|
|
-------------------------------------------------------------------
|
|
-- Returns volatile node data as table
|
|
function techage.get_mem(pos)
|
|
local hash = minetest.hash_node_position(pos)
|
|
if not MemStore[hash] then
|
|
MemStore[hash] = {}
|
|
end
|
|
return MemStore[hash]
|
|
end
|
|
|
|
-- Returns non-volatile node data as table
|
|
function techage.get_nvm(pos)
|
|
local key1, key2 = get_keys(pos)
|
|
|
|
if not NvmStore[key1] then
|
|
NvmStore[key1] = backend.get_mapblock_data(key1)
|
|
push(key1)
|
|
end
|
|
|
|
local block = NvmStore[key1]
|
|
block.in_use = true
|
|
if not block[key2] then
|
|
block[key2] = backend.get_node_data(pos)
|
|
end
|
|
return block[key2]
|
|
end
|
|
|
|
-- Returns true/false
|
|
function techage.has_nvm(pos)
|
|
local key1, key2 = get_keys(pos)
|
|
|
|
if not NvmStore[key1] then
|
|
NvmStore[key1] = backend.get_mapblock_data(key1)
|
|
push(key1)
|
|
end
|
|
|
|
return NvmStore[key1][key2] ~= nil
|
|
end
|
|
|
|
function techage.peek_nvm(pos)
|
|
local key1, key2 = get_keys(pos)
|
|
local block = NvmStore[key1] or {}
|
|
return block[key2] or {}
|
|
end
|
|
|
|
-- To be called when a node is removed
|
|
function techage.del_mem(pos)
|
|
local hash = minetest.hash_node_position(pos)
|
|
MemStore[hash] = nil
|
|
|
|
local key1, key2 = get_keys(pos)
|
|
NvmStore[key1] = NvmStore[key1] or backend.get_mapblock_data(key1)
|
|
NvmStore[key1][key2] = nil
|
|
backend.store_mapblock_data(key1, NvmStore[key1])
|
|
end
|