694 lines
20 KiB
Lua
694 lines
20 KiB
Lua
|
--[[
|
||
|
|
||
|
Tube Library
|
||
|
============
|
||
|
|
||
|
Copyright (C) 2017-2019 Joachim Stolberg
|
||
|
|
||
|
LGPLv2.1+
|
||
|
See LICENSE.txt for more information
|
||
|
|
||
|
command.lua:
|
||
|
|
||
|
See [api.md] for the interface documentation
|
||
|
|
||
|
]]--
|
||
|
|
||
|
--- 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 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
|
||
|
|
||
|
-- to convert the data base from v2 to v3
|
||
|
local function extract_data(data)
|
||
|
local tbl = {}
|
||
|
for num,item in pairs(data) do
|
||
|
local new_item = {}
|
||
|
local data_present = false
|
||
|
for k,v in pairs(item) do
|
||
|
if k ~= "name" and k ~= "pos" then
|
||
|
new_item[k] = v
|
||
|
data_present = true
|
||
|
end
|
||
|
end
|
||
|
if data_present then
|
||
|
tbl[num] = new_item
|
||
|
end
|
||
|
end
|
||
|
return 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 1
|
||
|
|
||
|
local Number2Pos
|
||
|
local Number2Data
|
||
|
local TemporaryRemovedNodes
|
||
|
if Version == 2 then -- legacy data base
|
||
|
Number2Pos = minetest.deserialize(storage:get_string("Number2Pos")) or {}
|
||
|
Number2Data = extract_data(Number2Pos)
|
||
|
TemporaryRemovedNodes = {}
|
||
|
Version = 3
|
||
|
else
|
||
|
Number2Pos = deserialize(storage:get_string("Number2Pos"))
|
||
|
Number2Data = minetest.deserialize(storage:get_string("Number2Data")) or {}
|
||
|
TemporaryRemovedNodes = minetest.deserialize(storage:get_string("TemporaryRemovedNodes")) or {}
|
||
|
end
|
||
|
|
||
|
|
||
|
|
||
|
local function update_mod_storage()
|
||
|
local t = minetest.get_us_time()
|
||
|
minetest.log("action", "[Tubelib] Store data...")
|
||
|
storage:set_string("NextNumber", minetest.serialize(NextNumber))
|
||
|
storage:set_string("Version", minetest.serialize(Version))
|
||
|
storage:set_string("Number2Pos", serialize(Number2Pos))
|
||
|
storage:set_string("Number2Data", minetest.serialize(Number2Data))
|
||
|
storage:set_string("TemporaryRemovedNodes", minetest.serialize(TemporaryRemovedNodes))
|
||
|
-- store data each hour
|
||
|
minetest.after(60*59, update_mod_storage)
|
||
|
t = minetest.get_us_time() - t
|
||
|
minetest.log("action", "[Tubelib] Data stored. t="..t.."us")
|
||
|
end
|
||
|
|
||
|
minetest.register_on_shutdown(function()
|
||
|
update_mod_storage()
|
||
|
end)
|
||
|
|
||
|
-- store data after one hour
|
||
|
minetest.after(60*59, update_mod_storage)
|
||
|
|
||
|
-- Key2Number will be generated at runtine
|
||
|
local Key2Number = {}
|
||
|
|
||
|
local Name2Name = {} -- translation table
|
||
|
|
||
|
-------------------------------------------------------------------
|
||
|
-- Local helper functions
|
||
|
-------------------------------------------------------------------
|
||
|
|
||
|
-- Localize functions to avoid table lookups (better performance).
|
||
|
local string_find = string.find
|
||
|
local string_split = string.split
|
||
|
local tubelib_NodeDef = tubelib.NodeDef
|
||
|
local Tube = tubelib.Tube
|
||
|
|
||
|
-- Generate a key string based on the given pos table,
|
||
|
-- Used internaly as table key,
|
||
|
local function get_key_str(pos)
|
||
|
pos = minetest.pos_to_string(pos)
|
||
|
return string.sub(pos, 2, -2)
|
||
|
end
|
||
|
|
||
|
-- Determine position related node number for addressing purposes
|
||
|
local function get_number(pos)
|
||
|
local key = get_key_str(pos)
|
||
|
if not Key2Number[key] then
|
||
|
Key2Number[key] = NextNumber
|
||
|
NextNumber = NextNumber + 1
|
||
|
end
|
||
|
return string.format("%.04u", Key2Number[key])
|
||
|
end
|
||
|
|
||
|
local function get_node_lvm(pos)
|
||
|
local node = minetest.get_node_or_nil(pos)
|
||
|
if node then
|
||
|
return node
|
||
|
end
|
||
|
local vm = minetest.get_voxel_manip()
|
||
|
local MinEdge, MaxEdge = vm:read_from_map(pos, pos)
|
||
|
local data = vm:get_data()
|
||
|
local param2_data = vm:get_param2_data()
|
||
|
local area = VoxelArea:new({MinEdge = MinEdge, MaxEdge = MaxEdge})
|
||
|
local idx = area:index(pos.x, pos.y, pos.z)
|
||
|
node = {
|
||
|
name = minetest.get_name_from_content_id(data[idx]),
|
||
|
param2 = param2_data[idx]
|
||
|
}
|
||
|
return node
|
||
|
end
|
||
|
|
||
|
local function generate_Key2Number()
|
||
|
local key
|
||
|
for num,item in pairs(Number2Pos) do
|
||
|
key = get_key_str(item.pos)
|
||
|
Key2Number[key] = num
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local function not_protected(pos, placer_name, clicker_name)
|
||
|
local meta = minetest.get_meta(pos)
|
||
|
if meta then
|
||
|
local cached_name = meta:get_string("tubelib_cached_name")
|
||
|
if placer_name and (placer_name == cached_name or not minetest.is_protected(pos, placer_name)) then
|
||
|
meta:set_string("tubelib_cached_name", placer_name)
|
||
|
if clicker_name == nil or not minetest.is_protected(pos, clicker_name) then
|
||
|
return true
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
local function register_lbm(name, nodenames)
|
||
|
minetest.register_lbm({
|
||
|
label = "[Tubelib] Node update",
|
||
|
name = name.."update",
|
||
|
nodenames = nodenames,
|
||
|
run_at_every_load = true,
|
||
|
action = function(pos, node)
|
||
|
local name = Name2Name[node.name]
|
||
|
if tubelib_NodeDef[name] and tubelib_NodeDef[name].on_node_load then
|
||
|
tubelib_NodeDef[name].on_node_load(pos)
|
||
|
end
|
||
|
end
|
||
|
})
|
||
|
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)
|
||
|
local dir = SideToDir[side]
|
||
|
if dir < 5 then
|
||
|
dir = (((dir - 1) + (param2 % 4)) % 4) + 1
|
||
|
end
|
||
|
return dir
|
||
|
end
|
||
|
|
||
|
local function get_dest_node(pos, side)
|
||
|
local _,node = Tube:get_node(pos)
|
||
|
local dir = side_to_dir(side, node.param2)
|
||
|
local spos, sdir = Tube:get_connected_node_pos(pos, dir)
|
||
|
if not (spos and sdir) then return end
|
||
|
_,node = Tube:get_node(spos)
|
||
|
local out_side = dir_to_side(tubelib2.Turn180Deg[sdir], node.param2)
|
||
|
return spos, out_side, Name2Name[node.name] or node.name
|
||
|
end
|
||
|
|
||
|
local function item_handling_node(name)
|
||
|
local node_def = name and tubelib_NodeDef[name]
|
||
|
if node_def then
|
||
|
return node_def.on_pull_item or node_def.on_push_item or node_def.is_pusher
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-------------------------------------------------------------------
|
||
|
-- API helper functions
|
||
|
-------------------------------------------------------------------
|
||
|
|
||
|
-- Check the given list of numbers.
|
||
|
-- Returns true if number(s) is/are valid and point to real nodes.
|
||
|
function tubelib.check_numbers(numbers)
|
||
|
if numbers then
|
||
|
for _,num in ipairs(string_split(numbers, " ")) do
|
||
|
if Number2Pos[num] == nil then
|
||
|
return false
|
||
|
end
|
||
|
end
|
||
|
return true
|
||
|
end
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
-- Function returns { pos, name } for the node on the given position number.
|
||
|
function tubelib.get_node_info(dest_num)
|
||
|
if Number2Pos[dest_num] then
|
||
|
return Number2Pos[dest_num]
|
||
|
end
|
||
|
return nil
|
||
|
end
|
||
|
|
||
|
-- Function returns the node number from the given position or
|
||
|
-- nil, if no node number for this position is assigned.
|
||
|
function tubelib.get_node_number(pos)
|
||
|
local key = get_key_str(pos)
|
||
|
local num = Key2Number[key]
|
||
|
if num then
|
||
|
num = string.format("%.04u", num)
|
||
|
if Number2Pos[num] and Number2Pos[num].name then
|
||
|
return num
|
||
|
end
|
||
|
end
|
||
|
return nil
|
||
|
end
|
||
|
|
||
|
-- Function is used for available nodes with lost numbers, only.
|
||
|
function tubelib.get_new_number(pos, name)
|
||
|
-- store position
|
||
|
local number = get_number(pos)
|
||
|
Number2Pos[number] = {
|
||
|
pos = pos,
|
||
|
name = name,
|
||
|
}
|
||
|
return number
|
||
|
end
|
||
|
|
||
|
|
||
|
-- Store any node number related, additional data
|
||
|
-- param number: node number, returned by tubelib.add_node
|
||
|
-- param name: name of the data (string)
|
||
|
-- param data: any data (number, string, table)
|
||
|
function tubelib.set_data(number, name, data)
|
||
|
Number2Data[number] = Number2Data[number] or {}
|
||
|
if type(name) == "string" then
|
||
|
Number2Data[number]["u_"..name] = data
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- Read node number related data
|
||
|
-- param number: node number, returned by tubelib.add_node
|
||
|
-- param name: name of the data (string)
|
||
|
function tubelib.get_data(number, name)
|
||
|
if Number2Data[number] and type(name) == "string" then
|
||
|
return Number2Data[number]["u_"..name]
|
||
|
end
|
||
|
return nil
|
||
|
end
|
||
|
|
||
|
-------------------------------------------------------------------
|
||
|
-- Node construction/destruction functions
|
||
|
-------------------------------------------------------------------
|
||
|
|
||
|
-- Add node to the tubelib lists.
|
||
|
-- Function determines and returns the node position number,
|
||
|
-- needed for message communication.
|
||
|
function tubelib.add_node(pos, name)
|
||
|
if item_handling_node(name) then
|
||
|
Tube:after_place_node(pos)
|
||
|
end
|
||
|
-- store position
|
||
|
local number = get_number(pos)
|
||
|
Number2Pos[number] = {
|
||
|
pos = pos,
|
||
|
name = name,
|
||
|
}
|
||
|
return number
|
||
|
end
|
||
|
|
||
|
-- Function removes the node from the tubelib lists.
|
||
|
function tubelib.remove_node(pos)
|
||
|
local number = get_number(pos)
|
||
|
local name
|
||
|
if Number2Pos[number] then
|
||
|
name = Number2Pos[number].name
|
||
|
Number2Pos[number] = {
|
||
|
pos = pos,
|
||
|
name = nil,
|
||
|
time = minetest.get_day_count() -- used for reservation timeout
|
||
|
}
|
||
|
end
|
||
|
if item_handling_node(name) then
|
||
|
Tube:after_dig_node(pos)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
|
||
|
-------------------------------------------------------------------
|
||
|
-- Node register function
|
||
|
-------------------------------------------------------------------
|
||
|
|
||
|
-- Register node for tubelib communication
|
||
|
-- Call this function only at load time!
|
||
|
-- Param name: The node name like "tubelib:pusher"
|
||
|
-- Param add_names: Alternativ node names if needded, e.g.: "tubelib:pusher_active"
|
||
|
-- Param node_definition: A table according to:
|
||
|
-- {
|
||
|
-- on_pull_item = func(pos, side, player_name),
|
||
|
-- on_push_item = func(pos, side, item, player_name),
|
||
|
-- on_unpull_item = func(pos, side, item, player_name),
|
||
|
-- on_recv_message = func(pos, topic, payload),
|
||
|
-- on_node_load = func(pos), -- LBM function
|
||
|
-- on_node_repair = func(pos), -- repair defect (feature!) nodes
|
||
|
-- }
|
||
|
function tubelib.register_node(name, add_names, node_definition)
|
||
|
tubelib_NodeDef[name] = node_definition
|
||
|
-- store facedir table for all known node names
|
||
|
Name2Name[name] = name
|
||
|
for _,n in ipairs(add_names) do
|
||
|
Name2Name[n] = name
|
||
|
end
|
||
|
if node_definition.on_pull_item or node_definition.on_push_item or
|
||
|
node_definition.is_pusher then
|
||
|
Tube:add_secondary_node_names({name})
|
||
|
Tube:add_secondary_node_names(add_names)
|
||
|
|
||
|
tubelib.KnownNodes[name] = true
|
||
|
for _,n in ipairs(add_names) do
|
||
|
tubelib.KnownNodes[n] = true
|
||
|
end
|
||
|
end
|
||
|
-- register LBM
|
||
|
if node_definition.on_node_load then
|
||
|
local nodenames = {name}
|
||
|
for _,n in ipairs(add_names) do
|
||
|
nodenames[#nodenames + 1] = n
|
||
|
end
|
||
|
register_lbm(name, nodenames)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-------------------------------------------------------------------
|
||
|
-- Send message functions
|
||
|
-------------------------------------------------------------------
|
||
|
|
||
|
function tubelib.send_message(numbers, placer_name, clicker_name, topic, payload)
|
||
|
for _,num in ipairs(string_split(numbers, " ")) do
|
||
|
if Number2Pos[num] and Number2Pos[num].name then
|
||
|
local data = Number2Pos[num]
|
||
|
if not_protected(data.pos, placer_name, clicker_name) then
|
||
|
if tubelib_NodeDef[data.name] and tubelib_NodeDef[data.name].on_recv_message then
|
||
|
tubelib_NodeDef[data.name].on_recv_message(data.pos, topic, payload)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function tubelib.send_request(number, topic, payload)
|
||
|
if Number2Pos[number] and Number2Pos[number].name then
|
||
|
local data = Number2Pos[number]
|
||
|
if tubelib_NodeDef[data.name] and tubelib_NodeDef[data.name].on_recv_message then
|
||
|
return tubelib_NodeDef[data.name].on_recv_message(data.pos, topic, payload)
|
||
|
end
|
||
|
end
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
-- for defect nodes
|
||
|
function tubelib.repair_node(pos)
|
||
|
local node = minetest.get_node(pos)
|
||
|
local name = Name2Name[node.name]
|
||
|
if tubelib_NodeDef[name] and tubelib_NodeDef[name].on_node_repair then
|
||
|
return tubelib_NodeDef[name].on_node_repair(pos)
|
||
|
end
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
-------------------------------------------------------------------
|
||
|
-- Client side Push/Pull item functions
|
||
|
-------------------------------------------------------------------
|
||
|
|
||
|
function tubelib.pull_items(pos, side, player_name)
|
||
|
local npos, nside, name = get_dest_node(pos, side)
|
||
|
if npos == nil then return end
|
||
|
if tubelib_NodeDef[name] and tubelib_NodeDef[name].on_pull_item then
|
||
|
return tubelib_NodeDef[name].on_pull_item(npos, nside, player_name)
|
||
|
end
|
||
|
return nil
|
||
|
end
|
||
|
|
||
|
function tubelib.push_items(pos, side, items, player_name)
|
||
|
local npos, nside, name = get_dest_node(pos, side)
|
||
|
if npos == nil then return end
|
||
|
if tubelib_NodeDef[name] and tubelib_NodeDef[name].on_push_item then
|
||
|
return tubelib_NodeDef[name].on_push_item(npos, nside, items, player_name)
|
||
|
elseif name == "air" then
|
||
|
minetest.add_item(npos, items)
|
||
|
return true
|
||
|
end
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
function tubelib.unpull_items(pos, side, items, player_name)
|
||
|
local npos, nside, name = get_dest_node(pos, side)
|
||
|
if npos == nil then return end
|
||
|
if tubelib_NodeDef[name] and tubelib_NodeDef[name].on_unpull_item then
|
||
|
return tubelib_NodeDef[name].on_unpull_item(npos, nside, items, player_name)
|
||
|
end
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
function tubelib.pull_stack(pos, side, player_name)
|
||
|
local npos, nside, name = get_dest_node(pos, side)
|
||
|
if npos == nil then return end
|
||
|
if tubelib_NodeDef[name] then
|
||
|
if tubelib_NodeDef[name].on_pull_stack then
|
||
|
return tubelib_NodeDef[name].on_pull_stack(npos, nside, player_name)
|
||
|
elseif tubelib_NodeDef[name].on_pull_item then
|
||
|
return tubelib_NodeDef[name].on_pull_item(npos, nside, player_name)
|
||
|
end
|
||
|
end
|
||
|
return nil
|
||
|
end
|
||
|
|
||
|
-------------------------------------------------------------------
|
||
|
-- Server side helper functions
|
||
|
-------------------------------------------------------------------
|
||
|
|
||
|
-- Get one item from the given ItemList. The position within the list
|
||
|
-- is incremented each time so that different item stacks will be considered.
|
||
|
-- Returns nil if ItemList is empty.
|
||
|
function tubelib.get_item(meta, listname)
|
||
|
if meta == nil or meta.get_inventory == nil then return nil end
|
||
|
local inv = meta:get_inventory()
|
||
|
if inv:is_empty(listname) then
|
||
|
return nil
|
||
|
end
|
||
|
local size = inv:get_size(listname)
|
||
|
local startpos = meta:get_int("tubelib_startpos") or 0
|
||
|
for idx = startpos, startpos+size do
|
||
|
idx = (idx % size) + 1
|
||
|
local items = inv:get_stack(listname, idx)
|
||
|
if items:get_count() > 0 then
|
||
|
local taken = items:take_item(1)
|
||
|
inv:set_stack(listname, idx, items)
|
||
|
meta:set_int("tubelib_startpos", idx)
|
||
|
return taken
|
||
|
end
|
||
|
end
|
||
|
meta:set_int("tubelib_startpos", 0)
|
||
|
return nil
|
||
|
end
|
||
|
|
||
|
-- Get one (or more) item(s) from the given ItemList, specified by stack number (1..n).
|
||
|
-- Returns nil if ItemList is empty.
|
||
|
function tubelib.get_this_item(meta, listname, list_number, num_items)
|
||
|
if meta == nil or meta.get_inventory == nil then return nil end
|
||
|
local inv = meta:get_inventory()
|
||
|
if inv:is_empty(listname) then
|
||
|
return nil
|
||
|
end
|
||
|
|
||
|
if num_items == nil then num_items = 1 end
|
||
|
local items = inv:get_stack(listname, list_number)
|
||
|
if items:get_count() > 0 then
|
||
|
local taken = items:take_item(num_items)
|
||
|
inv:set_stack(listname, list_number, items)
|
||
|
return taken
|
||
|
end
|
||
|
return nil
|
||
|
end
|
||
|
|
||
|
|
||
|
-- Put the given item into the given ItemList.
|
||
|
-- Function returns false if ItemList is full.
|
||
|
function tubelib.put_item(meta, listname, item)
|
||
|
if meta == nil or meta.get_inventory == nil then return false end
|
||
|
local inv = meta:get_inventory()
|
||
|
if inv:room_for_item(listname, item) then
|
||
|
inv:add_item(listname, item)
|
||
|
return true
|
||
|
end
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
-- Take the number of items from the given ItemList.
|
||
|
-- Returns nil if the requested number is not available.
|
||
|
function tubelib.get_num_items(meta, listname, num)
|
||
|
if meta == nil or meta.get_inventory == nil then return nil end
|
||
|
local inv = meta:get_inventory()
|
||
|
if inv:is_empty(listname) then
|
||
|
return nil
|
||
|
end
|
||
|
local size = inv:get_size(listname)
|
||
|
for idx = 1, size do
|
||
|
local items = inv:get_stack(listname, idx)
|
||
|
if items:get_count() >= num then
|
||
|
local taken = items:take_item(num)
|
||
|
inv:set_stack(listname, idx, items)
|
||
|
return taken
|
||
|
end
|
||
|
end
|
||
|
return nil
|
||
|
end
|
||
|
|
||
|
function tubelib.get_stack(meta, listname)
|
||
|
local inv = meta:get_inventory()
|
||
|
local item = tubelib.get_item(meta, listname)
|
||
|
if item and item:get_stack_max() > 1 and inv:contains_item(listname, item) then
|
||
|
-- try to remove a complete stack
|
||
|
item:set_count(math.min(98, item:get_stack_max() - 1))
|
||
|
local taken = inv:remove_item(listname, item)
|
||
|
-- add the already removed
|
||
|
taken:set_count(taken:get_count() + 1)
|
||
|
return taken
|
||
|
end
|
||
|
return item
|
||
|
end
|
||
|
|
||
|
-- Return "full", "loaded", or "empty" depending
|
||
|
-- on the number of fuel stack items.
|
||
|
-- Function only works on fuel inventories with one stacks/99 items
|
||
|
function tubelib.fuelstate(meta, listname, item)
|
||
|
if meta == nil or meta.get_inventory == nil then return nil end
|
||
|
local inv = meta:get_inventory()
|
||
|
if inv:is_empty(listname) then
|
||
|
return "empty"
|
||
|
end
|
||
|
local list = inv:get_list(listname)
|
||
|
if #list == 1 and list[1]:get_count() == 99 then
|
||
|
return "full"
|
||
|
else
|
||
|
return "loaded"
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- Return "full", "loaded", or "empty" depending
|
||
|
-- on the inventory load.
|
||
|
-- Full is returned, when no empty stack is available.
|
||
|
function tubelib.get_inv_state(meta, listname)
|
||
|
if meta == nil or meta.get_inventory == nil then return nil end
|
||
|
local inv = meta:get_inventory()
|
||
|
local state
|
||
|
if inv:is_empty(listname) then
|
||
|
state = "empty"
|
||
|
else
|
||
|
local list = inv:get_list(listname)
|
||
|
state = "full"
|
||
|
local num = 0
|
||
|
for i, item in ipairs(list) do
|
||
|
if item:is_empty() then
|
||
|
return "loaded"
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
return state
|
||
|
end
|
||
|
|
||
|
-- Mainly used for door/gate nodes
|
||
|
-- To delete an entry, provide nil as number. The stored data will be returned.
|
||
|
function tubelib.temporary_remove_node(pos, number, name, add_data)
|
||
|
local key = get_key_str(pos)
|
||
|
if number then
|
||
|
add_data = add_data or {}
|
||
|
add_data.pos = pos
|
||
|
add_data.number = number
|
||
|
add_data.name = name
|
||
|
TemporaryRemovedNodes[key] = add_data
|
||
|
else
|
||
|
local data = table.copy(TemporaryRemovedNodes[key])
|
||
|
TemporaryRemovedNodes[key] = nil
|
||
|
return data
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-------------------------------------------------------------------------------
|
||
|
-- Data Maintenance
|
||
|
-------------------------------------------------------------------------------
|
||
|
|
||
|
local function parse_number(s)
|
||
|
for _,word in ipairs(s:split(" ")) do
|
||
|
local n = tonumber(word)
|
||
|
if n and n > 0 then
|
||
|
return word
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local function get_node_number(pos)
|
||
|
local meta = M(pos)
|
||
|
local num = meta:get_string("tubelib_number")
|
||
|
if num and num ~= "" then return num end
|
||
|
|
||
|
num = meta:get_string("number")
|
||
|
if num and num ~= "" then return num end
|
||
|
|
||
|
num = meta:get_string("own_number")
|
||
|
if num and num ~= "" then return num end
|
||
|
|
||
|
num = parse_number(meta:get_string("infotext"))
|
||
|
if num and num ~= "" then return num end
|
||
|
|
||
|
return 0
|
||
|
end
|
||
|
|
||
|
local function data_maintenance()
|
||
|
minetest.log("info", "[Tubelib] Data maintenance started")
|
||
|
|
||
|
-- Remove unused positions
|
||
|
local tbl = table.copy(Number2Pos)
|
||
|
Number2Pos = {}
|
||
|
local cnt1 = 0
|
||
|
local cnt2 = 0
|
||
|
for num,item in pairs(tbl) do
|
||
|
local name = Name2Name[get_node_lvm(item.pos).name]
|
||
|
cnt1 = cnt1 + 1
|
||
|
-- Is there a tubelib node?
|
||
|
if tubelib_NodeDef[name] then
|
||
|
-- Does the number match?
|
||
|
local nnum = get_node_number(item.pos)
|
||
|
if nnum == num then
|
||
|
cnt2 = cnt2 + 1
|
||
|
-- Store again
|
||
|
Number2Pos[num] = item
|
||
|
-- Add node names which are not stored as file
|
||
|
Number2Pos[num].name = name
|
||
|
--print("added", num, name)
|
||
|
else
|
||
|
--print("wrong number", num, name)
|
||
|
end
|
||
|
else
|
||
|
local key = get_key_str(item.pos)
|
||
|
local data = TemporaryRemovedNodes[key]
|
||
|
if data then
|
||
|
cnt2 = cnt2 + 1
|
||
|
-- Store again
|
||
|
Number2Pos[data.number] = data
|
||
|
--print("restored", data.number, data.name)
|
||
|
else
|
||
|
--print("no data", num)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
minetest.log("info", "[Tubelib] Data base shrank from "..cnt1.." to "..cnt2.." nodes")
|
||
|
minetest.log("info", "[Tubelib] Data maintenance finished")
|
||
|
end
|
||
|
|
||
|
generate_Key2Number()
|
||
|
|
||
|
-- maintain data after 2 seconds
|
||
|
minetest.after(2, data_maintenance)
|
||
|
|
||
|
|