microexpansion/modules/network/init.lua
2023-10-15 17:18:59 +02:00

258 lines
6.8 KiB
Lua

local me = microexpansion
me.networks = {}
local networks = me.networks
local path = microexpansion.get_module_path("network")
local storage = minetest.get_mod_storage()
dofile(path.."/constants.lua")
--deprecated: use ItemStack(x) instead
--[[
local function split_stack_values(stack)
if type(stack) == "string" then
local split_string = stack:split(" ")
if (#split_string < 1) then
return "",0,0,nil
end
local stack_name = split_string[1]
if (#split_string < 2) then
return stack_name,1,0,nil
end
local stack_count = tonumber(split_string[2])
if (#split_string < 3) then
return stack_name,stack_count,0,nil
end
local stack_wear = tonumber(split_string[3])
if (#split_string < 4) then
return stack_name,stack_count,stack_wear,nil
end
return stack_name,stack_count,stack_wear,true
else
return stack:get_name(), stack:get_count(), stack:get_wear(), stack:get_meta()
end
end
--]]
function me.insert_item(stack, inv, listname)
if me.settings.huge_stacks == false then
return inv:add_item(listname, stack)
end
local to_insert = type(stack) == "userdata" and stack or ItemStack(stack)
local found = false
for i = 0, inv:get_size(listname) do
local inside = inv:get_stack(listname, i)
if inside:get_name() == to_insert:get_name() and inside:get_wear() == to_insert:get_wear() then
if inside:get_meta():equals(to_insert:get_meta()) then
local total_count = inside:get_count() + to_insert:get_count()
-- bigger item count is not possible, we only have unsigned 16 bit
if total_count <= math.pow(2,16) then
if not inside:set_count(total_count) then
microexpansion.log("adding items to stack in microexpansion network failed","error")
print("stack is now " .. inside:to_string())
end
inv:set_stack(listname, i, inside)
found = true
break;
end
end
end
end
if not found then
return inv:add_item(listname, stack)
end
end
dofile(path.."/network.lua") -- Network Management
-- generate iterator to find all connected nodes
function me.connected_nodes(start_pos,include_ctrl)
-- nodes to be checked
local open_list = {{pos = start_pos}}
-- nodes that were checked
local closed_set = {}
-- local connected nodes function to reduce table lookups
local adjacent_connected_nodes = me.network.adjacent_connected_nodes
-- return the generated iterator
return function ()
-- start looking for next pos
local open = false
-- pos to be checked
local current
-- find next unclosed
while not open do
-- get unchecked pos
current = table.remove(open_list)
-- none are left
if current == nil then return end
-- assume it's open
open = true
-- check the closed positions
for _,closed in pairs(closed_set) do
-- if current is unclosed
if vector.equals(closed,current.pos) then
--found one was closed
open = false
end
end
end
-- get all connected nodes
local nodes = adjacent_connected_nodes(current.pos,include_ctrl)
-- iterate through them
for _,n in pairs(nodes) do
-- mark position to be checked
table.insert(open_list,n)
end
-- add this one to the closed set
table.insert(closed_set,current.pos)
-- return the one to be checked
return current.pos,current.name
end
end
-- get network connected to position
function me.get_connected_network(start_pos)
for npos,nn in me.connected_nodes(start_pos,true) do
if nn == "microexpansion:ctrl" then
local source = minetest.get_meta(npos):get_string("source")
local network
if source == "" then
network = me.get_network(npos)
else
network = me.get_network(vector.from_string(source))
end
if network then
return network,npos
end
end
end
end
function me.promote_controller(start_pos,net)
local promoted = false
for npos,nn in me.connected_nodes(start_pos,true) do
if nn == "microexpansion:ctrl" and npos ~= start_pos then
if promoted then
minetest.get_meta(npos):set_string("source", promoted)
else
promoted = vector.to_string(npos)
minetest.get_meta(npos):set_string("source", "")
net.controller_pos = npos
end
end
end
return promoted and true or false
end
function me.update_connected_machines(start_pos,event,include_start)
microexpansion.log("updating connected machines","action")
local ev = event or {type = "n/a"}
local sn = microexpansion.get_node(start_pos)
local sd = minetest.registered_nodes[sn.name]
local sm = sd.machine or {}
ev.origin = {
pos = start_pos,
name = sn.name,
type = sm.type
}
--print(dump2(ev,"event"))
for npos in me.connected_nodes(start_pos) do
if include_start or not vector.equals(npos,start_pos) then
me.update_node(npos,ev)
end
end
end
function me.send_event(spos,type,data)
local d = data or {}
local event = {
type = type,
net = d.net,
payload = d.payload
}
me.update_connected_machines(spos,event,false)
end
function me.get_network(pos)
for i,net in pairs(networks) do
if net.controller_pos then
if vector.equals(pos, net.controller_pos) then
return net,i
end
end
end
end
dofile(path.."/ctrl.lua") -- Controller/wires
dofile(path.."/security.lua") --Security Terminal
-- load networks
function me.load()
local res = storage:get_string("networks")
if res == "" then
local f = io.open(me.worldpath.."/microexpansion_networks", "r")
if f then
me.log("loading network data from file","action")
res = minetest.deserialize(f:read("*all"))
f:close()
else
me.log("no network data loaded","action")
return
end
else
me.log("loading network data from mod storage","action")
res = minetest.deserialize(res)
end
if type(res) == "table" then
for _,n in pairs(res) do
local net = me.network.new(n)
net:load()
table.insert(me.networks,net)
end
else
me.log("network data in unexpected format","error")
end
end
-- load now
me.load()
-- save networks
function me.save()
local data = {}
for _,v in pairs(me.networks) do
table.insert(data,v:serialize())
end
if storage then
me.log("saving network data to mod storage","info")
storage:set_string("networks", minetest.serialize(data))
else
me.log("saving network data to file","info")
local f = io.open(me.worldpath.."/microexpansion_networks", "w")
f:write(minetest.serialize(data))
f:close()
end
end
function me.do_autosave()
me.last_autosave = -1
minetest.after(1, function()
--print("autosaving ME Networks")
me.save()
me.last_autosave = minetest.get_server_uptime()
end)
end
function me.autosave()
--TODO: make max autosave interval settable
if not me.last_autosave then
me.do_autosave()
elseif me.last_autosave == -1 then
return
elseif minetest.get_server_uptime() - me.last_autosave >= 600 then
me.do_autosave()
end
end
-- save on server shutdown
minetest.register_on_shutdown(me.save)