logistica-cd2025/logic/network_cache.lua

181 lines
5.9 KiB
Lua

local META_KEY_PREV = "logprev_"
--local META_KEY_CURR = "logcurr_"
local SEP = ";"
LOG_CACHE_MASS_STORAGE = {
listName = "filter",
clear = function (network) network.storage_cache = {} end,
cache = function (network) return network.storage_cache end,
nodes = function (network) return network.mass_storage end,
}
LOG_CACHE_SUPPLIER = {
listName = "main",
clear = function (network) network.supplier_cache = {} end,
cache = function (network) return network.supplier_cache end,
nodes = function (network) return network.suppliers end,
}
LOG_CACHE_REQUESTER = {
listName = "filter",
clear = function (network) network.requester_cache = {} end,
cache = function (network) return network.requester_cache end,
nodes = function (network) return network.requesters end,
}
-- returns {{b is missing these from a}, {b has these new to a}}
local function diff(seta, setb)
local d = {}
local e = {}
for k,v in pairs(seta) do d[k]=true end
for k,v in pairs(setb) do if not d[k] then e[k] = true else d[k]=nil end end
return {d, e}
end
local function get_meta(pos)
logistica.load_position(pos)
return minetest.get_meta(pos)
end
local function list_to_cache_nameset(list)
local ret = {}
for i, item in ipairs(list) do
if item:get_name() ~= "" then
ret[item:get_name()] = true
end
end
return ret
end
local function nameset_to_cache_str(nameset)
local str = ""
local first = true
for k, _ in pairs(nameset) do
if first then str = k ; first = false
else str = str..SEP..k end
end
return str
end
local function cache_str_to_nameset(cache_str)
local vals = string.split(cache_str, SEP, false)
local ret = {}
for _,v in ipairs(vals) do ret[v] = true end
return ret
end
local function save_prev_cache(nodeMeta, listName, nameset)
nodeMeta:set_string(META_KEY_PREV..listName, nameset_to_cache_str(nameset))
end
local function clear_prev_cache(nodeMeta, listName)
nodeMeta:set_string(META_KEY_PREV..listName, "")
end
local function get_prev_cache_as_nameset(nodeMeta, listName)
return cache_str_to_nameset(nodeMeta:get_string(META_KEY_PREV..listName))
end
-- local function save_curr_cache(nodeMeta, listName, cacheStr)
-- nodeMeta:set_string(META_KEY_CURR..listName, cacheStr)
-- end
-- local function get_curr_cache_as_namelist(nodeMeta, listName)
-- return nodeMeta:get_string(META_KEY_CURR..listName)
-- end
-- clears all cache for the given ops, and re-caches them
local function update_network_cache(network, cacheOps)
cacheOps.clear(network)
local nodes = cacheOps.nodes(network)
local cache = cacheOps.cache(network)
local listName = cacheOps.listName
for hash, _ in pairs(nodes) do
local storagePos = minetest.get_position_from_hash(hash)
logistica.load_position(storagePos)
local nodeMeta = get_meta(storagePos)
local list = logistica.get_cache_list_for(storagePos, nodeMeta, listName)
for _, itemStack in ipairs(list) do
local name = itemStack:get_name()
if name ~= "" then
if not cache[name] then cache[name] = {} end
cache[name][hash] = true
end
end
save_prev_cache(nodeMeta, listName, list_to_cache_nameset(list))
end
end
-- smartly tries to update the the cache for the given position
local function update_network_cache_for_pos(pos, cacheOps, optNetwork)
local network = optNetwork or logistica.get_network_or_nil(pos)
if not network then return end
local meta = get_meta(pos)
local hash = minetest.hash_node_position(pos)
local listName = cacheOps.listName
local prevCacheItems = get_prev_cache_as_nameset(meta, listName)
local currCacheItems = list_to_cache_nameset(logistica.get_cache_list_for(pos, meta, listName))
local cache = cacheOps.cache(network)
local remAndAdd = diff(prevCacheItems, currCacheItems)
for name, _ in pairs(remAndAdd[1]) do
if cache[name] then cache[name][hash] = nil end
if logistica.table_is_empty(cache[name]) then cache[name] = nil end
end
for name, _ in pairs(remAndAdd[2]) do
if not cache[name] then cache[name] = {} end
if cache[name] then cache[name][hash] = true end
end
save_prev_cache(meta, listName, currCacheItems)
end
local function remove_network_cache_for_pos(pos, cacheOps, optNetwork)
local network = optNetwork or logistica.get_network_or_nil(pos)
if not network then return end
local meta = get_meta(pos)
local hash = minetest.hash_node_position(pos)
local listName = cacheOps.listName
local cache = cacheOps.cache(network)
local prevCacheItems = get_prev_cache_as_nameset(meta, listName)
for name, _ in pairs(prevCacheItems) do
local posCache = cache[name]
if posCache then posCache[hash] = nil end
end
--clear_prev_cache(meta, listName)
end
--------------------------------
-- public functions
--------------------------------
-- clears previous specified cache and updates entirely
-- `type` is one of LOG_CACHE_MASS_STORAGE, LOG_CACHE_SUPPLIER, LOG_CACHE_REQUESTER
function logistica.update_cache_network(network, type)
update_network_cache(network, type)
end
-- updates the storage cache for the specific position
-- `type` is one of LOG_CACHE_MASS_STORAGE, LOG_CACHE_SUPPLIER, LOG_CACHE_REQUESTER
-- optNetwork is optional, the network it belongs to
function logistica.update_cache_at_pos(pos, type, optNetwork)
update_network_cache_for_pos(pos, type, optNetwork)
end
-- removes the given pos's cache from the network
-- `type` is one of LOG_CACHE_MASS_STORAGE, LOG_CACHE_SUPPLIER, LOG_CACHE_REQUESTER
function logistica.update_cache_node_removed_at_pos(pos, type, optNetwork)
remove_network_cache_for_pos(pos, type, optNetwork)
end
-- helper
function logistica.get_cache_list_for(position, meta, listName)
local nodeName = minetest.get_node(position).name
local nodeDef = minetest.registered_nodes[nodeName]
if nodeDef and nodeDef.logistica and nodeDef.logistica.get_cache_list then
return nodeDef.logistica.get_cache_list(position) or {}
else
return logistica.get_list(meta:get_inventory(), listName)
end
end