storage backend prepared for sqlite

This commit is contained in:
Joachim Stolberg 2020-05-19 18:43:24 +02:00
parent daa84ed931
commit bb6f85c9c7
6 changed files with 296 additions and 152 deletions

View File

@ -53,6 +53,12 @@ local function chat(player, text)
minetest.chat_send_player(player:get_player_name(), "[Techage] "..text)
end
local function postload_area(pos)
if not minetest.forceload_block(pos, true) then
minetest.after(60, postload_area, pos)
end
end
local function add_pos(pos, player)
local lPos = minetest.deserialize(player:get_attribute("techage_forceload_blocks")) or {}
if not in_list(lPos, pos) and (#lPos < techage.max_num_forceload_blocks or
@ -259,7 +265,9 @@ minetest.register_on_joinplayer(function(player)
for _,pos in ipairs(get_pos_list(player)) do
local node = techage.get_node_lvm(pos)
if node.name == "techage:forceload" or node.name == "techage:forceloadtile" then
minetest.forceload_block(pos, true)
if not minetest.forceload_block(pos, true) then
minetest.after(60, postload_area, pos)
end
lPos[#lPos+1] = pos
end
end

107
basis/backend_sqlite.lua Normal file
View File

@ -0,0 +1,107 @@
--[[
TechAge
=======
Copyright (C) 2020 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
Node number management/storage based on SQLite
]]--
local MN = minetest.get_current_modname()
local WP = minetest.get_worldpath()
local IE = minetest.request_insecure_environment()
if not IE then
error("Please add 'secure.trusted_mods = techage' to minetest.conf!")
end
local sqlite3 = IE.require("lsqlite3")
local marshal = IE.require("marshal")
if not sqlite3 then
error("Please install sqlite3 via 'luarocks install lsqlite3'")
end
if not marshal then
error("Please install marshal via 'luarocks install lua-marshal'")
end
local db = sqlite3.open(WP.."/techage_numbers.sqlite3")
-- Prevent use of this db instance.
if sqlite3 then sqlite3 = nil end
db:exec[[
CREATE TABLE test2(id INTEGER PRIMARY KEY, number INTEGER, x INTEGER, y INTEGER, z INTEGER);
CREATE INDEX idx ON test2(number);
]]
local insert = db:prepare("INSERT INTO test2 VALUES(NULL, ?, ?, ?, ?);")
local query = db:prepare("SELECT * FROM test2 WHERE number=?")
local backend = {}
local storage = minetest.get_mod_storage()
local function set(number, pos)
insert:reset()
insert:bind(1, number)
insert:bind(2, pos.x)
insert:bind(3, pos.y)
insert:bind(4, pos.z)
insert:step()
end
local function get(number)
query:reset()
query:bind(1, number)
query:step()
local _, _, x, y, z, name = unpack(query:get_values())
return {pos = {x = x, y = y, z = z}, name = name}
end
-------------------------------------------------------------------
-- API functions
-------------------------------------------------------------------
function backend.get_nodepos(number)
return minetest.string_to_pos(storage:get_string(number))
end
function backend.set_nodepos(number, pos)
storage:get_string(number, minetest.pos_to_string(pos))
end
function backend.add_nodepos(pos)
local num = tostring(NextNumber)
NextNumber = NextNumber + 1
storage:set_int("NextNumber", NextNumber)
storage:get_string(num, minetest.pos_to_string(pos))
return num
end
function backend.del_nodepos(number)
storage:get_string(number, "")
end
-- delete invalid entries
function backend.delete_invalid_entries(node_def)
minetest.log("info", "[TechAge] Data maintenance started")
for i = 1, NextNumber do
local number = tostring(i)
if storage:contains(number) then
local pos = backend.get_nodepos(number)
local name = techage.get_node_lvm(pos).name
if not node_def[name] then
backend.del_nodepos(number)
end
end
end
minetest.log("info", "[TechAge] Data maintenance finished")
end
return backend

96
basis/backend_storage.lua Normal file
View File

@ -0,0 +1,96 @@
--[[
TechAge
=======
Copyright (C) 2020 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
Mod storage based solution
]]--
local backend = {}
local storage = minetest.get_mod_storage()
-- legacy method
local function deserialize(s)
local tbl = {}
for line in s:gmatch("[^;]+") do
local num, spos = unpack(string.split(line, "="))
tbl[num] = minetest.string_to_pos(spos)
end
return tbl
end
local Version = minetest.deserialize(storage:get_string("Version")) or 3
local NextNumber = 0
if Version == 1 then
Version = 3
local tbl = minetest.deserialize(storage:get_string("Number2Pos")) or {}
NextNumber = minetest.deserialize(storage:get_string("NextNumber")) or 1
for num, pos in pairs(tbl) do
storage:set_string(num, minetest.pos_to_string(pos))
end
storage:set_string("Number2Pos", "")
elseif Version == 2 then
Version = 3
NextNumber = minetest.deserialize(storage:get_string("NextNumber")) or 1
local tbl = deserialize(storage:get_string("Number2Pos"))
for num, pos in pairs(tbl) do
storage:set_string(num, minetest.pos_to_string(pos))
end
storage:set_string("Number2Pos", "")
else
Version = 3
NextNumber = storage:get_int("NextNumber")
end
storage:set_int("NextNumber", NextNumber)
storage:set_int("Version", Version)
-------------------------------------------------------------------
-- API functions
-------------------------------------------------------------------
function backend.get_nodepos(number)
return minetest.string_to_pos(storage:get_string(number))
end
function backend.set_nodepos(number, pos)
storage:get_string(number, minetest.pos_to_string(pos))
end
function backend.add_nodepos(pos)
local num = tostring(NextNumber)
NextNumber = NextNumber + 1
storage:set_int("NextNumber", NextNumber)
storage:get_string(num, minetest.pos_to_string(pos))
return num
end
function backend.del_nodepos(number)
storage:get_string(number, "")
end
-- delete invalid entries
function backend.delete_invalid_entries(node_def)
minetest.log("info", "[TechAge] Data maintenance started")
for i = 1, NextNumber do
local number = tostring(i)
if storage:contains(number) then
local pos = backend.get_nodepos(number)
local name = techage.get_node_lvm(pos).name
if not node_def[name] then
backend.del_nodepos(number)
end
end
end
minetest.log("info", "[TechAge] Data maintenance finished")
end
return backend

View File

@ -3,7 +3,7 @@
TechAge
=======
Copyright (C) 2019 Joachim Stolberg
Copyright (C) 2019-2020 Joachim Stolberg
GPL v3
See LICENSE.txt for more information
@ -14,68 +14,62 @@
--- for lazy programmers
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
local P = minetest.string_to_pos
local M = minetest.get_meta
--local P = minetest.string_to_pos
--local M = minetest.get_meta
local NodeInfoCache = {}
local MP = minetest.get_modpath("techage")
local use_database = minetest.settings:get_bool('techage.use_database', false)
-- Localize functions to avoid table lookups (better performance)
local string_split = string.split
local NodeDef = techage.NodeDef
local Tube = techage.Tube
local check_cart_for_loading = minecart.check_cart_for_loading
local function deserialize(s)
local tbl = {}
for line in s:gmatch("[^;]+") do
local num, spos = unpack(string.split(line, "="))
tbl[num] = {pos = minetest.string_to_pos(spos)}
end
return tbl
end
local function serialize(data)
local tbl = {}
for k,v in pairs(data) do
tbl[#tbl+1] = k.."="..minetest.pos_to_string(v.pos)
end
return table.concat(tbl, ";")
end
------------------------------------------------------------------
-- Data base storage
-------------------------------------------------------------------
local storage = minetest.get_mod_storage()
local NextNumber = minetest.deserialize(storage:get_string("NextNumber")) or 1
local Version = minetest.deserialize(storage:get_string("Version")) or 2
local Number2Pos
if Version == 1 then
Version = 2
Number2Pos = minetest.deserialize(storage:get_string("Number2Pos")) or {}
-- Database
-------------------------------------------------------------------
local backend
if use_database then
backend = dofile(MP .. "/basis/backend_sqlite.lua")
else
Number2Pos = deserialize(storage:get_string("Number2Pos"))
backend = dofile(MP .. "/basis/backend_storage.lua")
end
local function update_mod_storage()
local t = minetest.get_us_time()
minetest.log("action", "[TechAge] Store data...")
storage:set_string("NextNumber", minetest.serialize(NextNumber))
storage:set_string("Version", minetest.serialize(Version))
storage:set_string("Number2Pos", serialize(Number2Pos))
-- store data each hour
minetest.after(60*59, update_mod_storage)
t = minetest.get_us_time() - t
minetest.log("action", "[TechAge] Data stored. t="..t.."us")
local function update_nodeinfo(number)
local pos = backend.get_nodepos(number)
if pos then
NodeInfoCache[number] = {pos = pos, name = techage.get_node_lvm(pos).name}
return NodeInfoCache[number]
end
end
minetest.register_on_shutdown(function()
update_mod_storage()
end)
local function delete_nodeinfo_entry(number)
if number and NodeInfoCache[number] then
number, _ = next(NodeInfoCache, number)
if number then
NodeInfoCache[number] = nil
end
else
number, _ = next(NodeInfoCache, nil)
end
return number
end
-- store data after one hour
minetest.after(60*59, update_mod_storage)
-- Keep the cache size small by deleting entries randomly
local function keep_small(number)
number = delete_nodeinfo_entry(number)
minetest.after(2, keep_small, number)
end
-- Key2Number will be generated at runtine
local Key2Number = {}
keep_small()
minetest.after(2, backend.delete_invalid_entries, NodeDef)
-------------------------------------------------------------------
-- Local helper functions
-------------------------------------------------------------------
local function in_list(list, x)
for _, v in ipairs(list) do
if v == x then return true end
@ -83,26 +77,17 @@ local function in_list(list, x)
return false
end
-- Localize functions to avoid table lookups (better performance).
local string_split = string.split
local NodeDef = techage.NodeDef
local Tube = techage.Tube
-- Determine position related node number for addressing purposes
local function get_number(pos)
local key = minetest.hash_node_position(pos)
if not Key2Number[key] then
Key2Number[key] = NextNumber
NextNumber = NextNumber + 1
local function get_number(pos, new)
local meta = minetest.get_meta(pos)
if meta:contains("node_number") then
return meta:get_string("node_number")
end
return string.format("%u", Key2Number[key])
end
local function generate_Key2Number()
local key
for num,item in pairs(Number2Pos) do
key = minetest.hash_node_position(item.pos)
Key2Number[key] = num
-- generate new number
if new then
local num = backend.add_nodepos(pos)
meta:set_string("node_number", num)
return num
end
end
@ -135,16 +120,6 @@ local function register_lbm(name, nodenames)
})
end
local DirToSide = {"B", "R", "F", "L", "D", "U"}
local function dir_to_side(dir, param2)
if dir < 5 then
dir = (((dir - 1) - (param2 % 4)) % 4) + 1
end
return DirToSide[dir]
end
local SideToDir = {B=1, R=2, F=3, L=4, D=5, U=6}
local function side_to_dir(side, param2)
@ -192,26 +167,15 @@ end
-- API helper functions
-------------------------------------------------------------------
-- Function returns { pos, name } for the node on the given position number.
-- Function returns { pos, name } for the node referenced by number
function techage.get_node_info(dest_num)
if Number2Pos[dest_num] then
return Number2Pos[dest_num]
end
return nil
return NodeInfoCache[dest_num] or update_nodeinfo(dest_num)
end
-- Function returns the node number from the given position or
-- nil, if no node number for this position is assigned.
function techage.get_node_number(pos)
local key = minetest.hash_node_position(pos)
local num = Key2Number[key]
if num then
num = string.format("%u", num)
if Number2Pos[num] and Number2Pos[num].name then
return num
end
end
return nil
return get_number(pos)
end
function techage.get_pos(pos, side)
@ -226,12 +190,7 @@ end
-- Function is used for available nodes with lost numbers, only.
function techage.get_new_number(pos, name)
-- store position
local number = get_number(pos)
Number2Pos[number] = {
pos = pos,
name = name,
}
return number
return get_number(pos, true)
end
-- extract ident and value from strings like "ident=value"
@ -252,25 +211,22 @@ function techage.add_node(pos, name)
Tube:after_place_node(pos)
end
-- store position
local number = get_number(pos)
Number2Pos[number] = {
pos = pos,
name = name,
}
return number
return get_number(pos, true)
end
-- Function removes the node from the techage lists.
function techage.remove_node(pos)
local number = get_number(pos)
local name
if Number2Pos[number] then
name = Number2Pos[number].name
Number2Pos[number].name = nil
end
if item_handling_node(name) then
if number then
local ninfo = NodeInfoCache[number] or update_nodeinfo(number)
if ninfo then
backend.del_nodepos(number)
NodeInfoCache[number] = nil
if item_handling_node(ninfo.name) then
Tube:after_dig_node(pos)
end
end
end
end
@ -314,11 +270,9 @@ end
-------------------------------------------------------------------
function techage.not_protected(number, placer_name, clicker_name)
if Number2Pos[number] and Number2Pos[number].name then
local data = Number2Pos[number]
if data.pos then
return not_protected(data.pos, placer_name, clicker_name)
end
local ninfo = NodeInfoCache[number] or update_nodeinfo(number)
if ninfo and ninfo.pos then
return not_protected(ninfo.pos, placer_name, clicker_name)
end
return false
end
@ -341,10 +295,11 @@ end
function techage.send_multi(src, numbers, topic, payload)
--print("send_multi", src, numbers, topic)
for _,num in ipairs(string_split(numbers, " ")) do
if Number2Pos[num] and Number2Pos[num].name then
local data = Number2Pos[num]
if data.pos and NodeDef[data.name] and NodeDef[data.name].on_recv_message then
NodeDef[data.name].on_recv_message(data.pos, src, topic, payload)
local ninfo = NodeInfoCache[num] or update_nodeinfo(num)
if ninfo and ninfo.name and ninfo.pos then
local ndef = NodeDef[ninfo.name]
if ndef and ndef.on_recv_message then
ndef.on_recv_message(ninfo.pos, src, topic, payload)
end
end
end
@ -352,10 +307,11 @@ end
function techage.send_single(src, number, topic, payload)
--print("send_single", src, number, topic)
if Number2Pos[number] and Number2Pos[number].name then
local data = Number2Pos[number]
if data.pos and NodeDef[data.name] and NodeDef[data.name].on_recv_message then
return NodeDef[data.name].on_recv_message(data.pos, src, topic, payload)
local ninfo = NodeInfoCache[number] or update_nodeinfo(number)
if ninfo and ninfo.name and ninfo.pos then
local ndef = NodeDef[ninfo.name]
if ndef and ndef.on_recv_message then
return ndef.on_recv_message(ninfo.pos, src, topic, payload)
end
end
return false
@ -502,7 +458,6 @@ function techage.put_items(inv, listname, item, idx)
return false
end
-- Return "full", "loaded", or "empty" depending
-- on the inventory load.
-- Full is returned, when no empty stack is available.
@ -521,29 +476,3 @@ function techage.get_inv_state(inv, listname)
end
return state
end
-------------------------------------------------------------------------------
-- Data Maintenance
-------------------------------------------------------------------------------
local function data_maintenance()
minetest.log("info", "[TechAge] Data maintenance started")
-- Remove unused positions
local tbl = table.copy(Number2Pos)
Number2Pos = {}
for num,item in pairs(tbl) do
local name = techage.get_node_lvm(item.pos).name
if NodeDef[name] then
Number2Pos[num] = item
-- add node names which are not stored as file
Number2Pos[num].name = name
end
end
minetest.log("info", "[TechAge] Data maintenance finished")
end
generate_Key2Number()
minetest.after(2, data_maintenance)

View File

@ -14,7 +14,9 @@ Suche und fördere Öl, baute Schienenwege zur Ölbeförderung. Ein Kraftwerk li
TA4: Zukunft (Future Age)
Regenerative Energiequellen wie Wind, Sonne und Biokraft helfen dir, das Ölzeitalter zu verlassen. Mit modernen Technologien und intelligenten Maschinen machst du dich auf in die Zukunft.
[wlanchip|image]
Hinweis: Mit Klicken auf die Pluszeichen kommst du in die Unterkapitel dieser Anleitung.
[techage_ta4|image]

View File

@ -14,7 +14,9 @@ Find and extract oil, built railways for oil transportation. A power plant provi
TA4: Future Age
Renewable energy sources such as wind, sun and biofuels help you to leave the oil age. With modern technologies and intelligent machines you set out into the future.
[wlanchip|image]
Note: With a click on the plus sign you get into the subchapters of this manual.
[techage_ta4|image]