mirror of
https://github.com/theFox6/microexpansion.git
synced 2024-11-09 00:43:49 +01:00
498 lines
18 KiB
Lua
498 lines
18 KiB
Lua
local me = microexpansion
|
|
me.networks = {}
|
|
local networks = me.networks
|
|
local path = me.get_module_path("network")
|
|
|
|
--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
|
|
--]]
|
|
|
|
local annotate_large_stack = function(stack, count)
|
|
local description = minetest.registered_items[stack:get_name()]
|
|
if description then
|
|
-- steel is an alias and won't be found in here, skip it
|
|
description = description.description
|
|
--stack:set_count(1)
|
|
--This screw up everything, autocrafting, item removal and more
|
|
--stack:get_meta():set_string("description", description.." "..count)
|
|
stack:get_meta():set_string("description", "")
|
|
end
|
|
end
|
|
|
|
function me.insert_item(stack, net, inv, listname, bias)
|
|
if stack == nil then
|
|
foobar()
|
|
return ItemStack(), 0
|
|
end
|
|
stack = type(stack) == "userdata" and stack or ItemStack(stack)
|
|
if stack:get_name() == "" then
|
|
-- foobar() -- TODO: can trip, ignore for now
|
|
return ItemStack(), 0
|
|
end
|
|
local slot
|
|
assert(net, not stack:is_empty())
|
|
local found = false
|
|
if not net.byname then
|
|
net.byname = {}
|
|
end
|
|
if not net.byname[listname] then
|
|
net.byname[listname] = {}
|
|
end
|
|
if not net.byname[listname][stack:get_name()] then
|
|
net.byname[listname][stack:get_name()] = {}
|
|
end
|
|
--if not net.byname[listname][stack:get_name()][stack:get_wear()] then
|
|
-- net.byname[listname][stack:get_name()][stack:get_wear()] = {}
|
|
--end
|
|
local meta = stack:get_meta()
|
|
-- slot = net.byname[listname][stack:get_name()][stack:get_wear()][meta]
|
|
-- assert(net, minetest.serialize(meta))
|
|
::was_empty::
|
|
slot = net.byname[listname][stack:get_name()][stack:get_wear()]
|
|
-- me.log("checking "..listname.." slot "..tostring(slot).." has "..stack:get_name().." in it", "error")
|
|
if not slot then
|
|
local ret = inv:add_item(listname, stack) -- TODO: fix to use set_stack, careful capacity
|
|
slot = inv:get_size(listname)
|
|
-- me.log(listname.." slot "..tostring(slot).." should now have "..stack:get_name().." in it", "error")
|
|
-- me.log(listname.." slot "..tostring(slot).." has "..inv:get_stack(listname, slot):get_name().." in it", "error")
|
|
if inv:get_stack(listname, slot):get_name() ~= stack:get_name() then
|
|
return ret, 0
|
|
--net:sync_main(inv)
|
|
-- TODO: infinite loop on full?
|
|
--goto was_empty
|
|
end
|
|
-- net.byname[listname][stack:get_name()][stack:get_wear()][meta] = slot
|
|
net.byname[listname][stack:get_name()][stack:get_wear()] = slot
|
|
-- me.log("byname is "..minetest.serialize(net.byname), "error")
|
|
if bias then
|
|
if not net.bias then
|
|
net.bias = {}
|
|
end
|
|
if not net.bias[listname] then
|
|
net.bias[listname] = {}
|
|
end
|
|
if not net.bias[listname][stack:get_name()] then
|
|
net.bias[listname][stack:get_name()] = 0
|
|
end
|
|
net.bias[listname][stack:get_name()] = net.bias[listname][stack:get_name()] + bias
|
|
me.log("LARGE: init insert_item bias "..bias.." for "..stack:get_name(), "error")
|
|
local mstack = inv:get_stack(listname, slot)
|
|
annotate_large_stack(mstack, mstack:get_count()+net.bias[listname][stack:get_name()])
|
|
inv:set_stack(listname, slot, mstack)
|
|
end
|
|
return ret, slot
|
|
end
|
|
local mstack = inv:get_stack(listname, slot)
|
|
-- me.log(listname.." slot "..tostring(slot).." has "..mstack:get_name().." in it", "error")
|
|
if mstack:is_empty() then
|
|
-- me.log("adding items to stack in microexpansion network was going to fail, fixing", "error")
|
|
net.byname[listname][stack:get_name()][stack:get_wear()] = nil
|
|
goto was_empty
|
|
end
|
|
-- me.log("init insert_item "..stack:get_name(), "error")
|
|
-- me.log("init insert_item "..mstack:get_name(), "error")
|
|
if mstack:get_name() ~= stack:get_name() then
|
|
-- me.log("adding items to stack in microexpansion network was going to fail, wrong item, fixing", "error")
|
|
net.byname[listname][stack:get_name()][stack:get_wear()] = nil
|
|
goto was_empty
|
|
end
|
|
local mbias = (net.bias and net.bias[listname] and net.bias[listname][stack:get_name()]) or 0
|
|
local total_count = mstack:get_count() + mbias + stack:get_count() + (bias or 0)
|
|
-- me.log("insert_item "..mstack:get_name().." "..tostring(total_count), "error")
|
|
-- bigger item count is not possible, we only have unsigned 16 bit
|
|
if total_count > math.pow(2,15) then
|
|
-- We handle overflows by storing the excess stores into a bias number for the slot.
|
|
-- This give us lua max int (2^48 or so) for the actual count.
|
|
if not net.bias then
|
|
net.bias = {}
|
|
end
|
|
if not net.bias[listname] then
|
|
net.bias[listname] = {}
|
|
end
|
|
if not net.bias[listname][stack:get_name()] then
|
|
net.bias[listname][stack:get_name()] = 0
|
|
end
|
|
net.bias[listname][stack:get_name()] = total_count - math.pow(2,15)
|
|
me.log("LARGE: overflow bias "..stack:get_count().." for "..stack:get_name(), "error")
|
|
total_count = math.pow(2,15)
|
|
end
|
|
local addition_real_loaned = total_count - mstack:get_count()
|
|
mstack:set_count(total_count)
|
|
inv:set_stack(listname, slot, mstack)
|
|
-- if there is one or more loans for the item, add stack:get_count()
|
|
-- + (bias or 0) to them and update those inventories, if they have room
|
|
local remaining = stack:get_count() + (bias or 0)
|
|
local loan_slot = net:find_loan(inv, stack)
|
|
if loan_slot then
|
|
me.loan.bump_loan(net, inv, loan_slot, remaining, addition_real_loaned)
|
|
end
|
|
return ItemStack(), slot
|
|
end
|
|
|
|
function me.add_capacity(ipos, count)
|
|
local int_meta = minetest.get_meta(ipos)
|
|
local prev = int_meta:get_int("capacity") or 0
|
|
int_meta:set_int("capacity", prev + count)
|
|
end
|
|
|
|
function me.remove_item(net, inv, listname, stack)
|
|
if not stack then return ItemStack() end
|
|
if stack == "" then return ItemStack() end
|
|
if type(stack) == string then
|
|
me.log("REMOVE: converting "..stack, "error")
|
|
stack = ItemStack(stack)
|
|
end
|
|
-- stack = ItemStack(stack):set_count(stack:get_count())
|
|
-- this can dump...
|
|
me.log("type is "..type(stack), "error")
|
|
-- me.log("serial is "..minetest.serialize(stack), "error")
|
|
if stack:get_count() == 0 then return ItemStack() end
|
|
me.log("me.remove_item "..listname, "error")
|
|
me.log("me.remove_item "..listname.." "..stack:get_count().." "..stack:get_name(), "error")
|
|
-- me.log("me.remove_item "..listname.." "..stack:get_wear(), "error")
|
|
if listname ~= "main" then
|
|
foobar()
|
|
return inv:remove_item(listname, stack)
|
|
end
|
|
if not net.byname[listname] or not net.byname[listname][stack:get_name()] then
|
|
return ItemStack()
|
|
end
|
|
local slot = net.byname[listname][stack:get_name()][stack:get_wear()]
|
|
if not slot then
|
|
me.log("wanted to remove "..stack:get_name().." from "..listname..", but didn't find anything", "error")
|
|
return ItemStack()
|
|
end
|
|
local mstack = inv:get_stack(listname, slot)
|
|
me.log("init remove item "..tostring(stack:get_count()).." "..stack:get_name(), "error")
|
|
if stack:get_name() ~= mstack:get_name()
|
|
or stack:get_wear() ~= mstack:get_wear()
|
|
or not stack:get_meta():equals(mstack:get_meta()) then
|
|
me.log("wanted to remove "..stack:get_name().." from "..listname..", but had "..mstack:get_name().." in slot "..slot, "error")
|
|
-- foobar()
|
|
net.byname[listname][stack:get_name()][stack:get_wear()] = nil
|
|
return ItemStack()
|
|
end
|
|
local mbias = 0
|
|
if net.bias and net.bias[listname] then
|
|
mbias = net.bias[listname][stack:get_name()] or 0
|
|
end
|
|
local on_loan = net.counts[stack:get_name()]
|
|
local remaining = mstack:get_count() + mbias - stack:get_count()
|
|
me.log("init me.remove_item has "..(on_loan or 0).." on loan and "..remaining.." remaining", "error")
|
|
if on_loan and remaining < on_loan then
|
|
me.log("init me.remove_item has "..on_loan.." on loan and "..remaining.." remaining", "error")
|
|
local loan_slot = net:find_loan(inv, stack)
|
|
-- find_loan can update main, refetch
|
|
mstack = inv:get_stack(listname, slot)
|
|
if not loan_slot then
|
|
-- someone updated the real inventories, there are no more of these
|
|
return ItemStack()
|
|
end
|
|
local lstack = me.loan.get_stack(net, inv, loan_slot)
|
|
if lstack:is_empty() then
|
|
return ItemStack()
|
|
end
|
|
local ref = me.network.get_ref(lstack)
|
|
local real_count = nil
|
|
if ref.drawer then
|
|
local c = drawers.drawer_get_content(ref.pos, ref.slot)
|
|
c.count = math.max(c.count-1,0) -- Poor man's locking
|
|
local count = math.min(stack:get_count(), c.count)
|
|
me.log("init removing "..count.." items from drawer", "error")
|
|
stack:set_count(count)
|
|
local take = stack
|
|
drawers.drawer_take_large_item(ref.pos, take)
|
|
c.count = c.count - count
|
|
real_count = c.count
|
|
if real_count > math.pow(2,15) then
|
|
real_count = math.pow(2,15)
|
|
end
|
|
-- me.log("CAPACITY: drawer "..-count, "error")
|
|
-- me.add_capacity(ref.ipos, -count)
|
|
else
|
|
local rinv = minetest.get_meta(ref.pos):get_inventory()
|
|
local real_stack = rinv:get_stack(ref.invname, ref.slot)
|
|
local count = math.min(stack:get_count(), real_stack:get_count())
|
|
me.log("init removing "..count.." items from chest", "error")
|
|
stack:set_count(count)
|
|
real_count = real_stack:get_count()-count
|
|
real_stack:set_count(real_count)
|
|
rinv:set_stack(ref.invname, ref.slot, real_stack)
|
|
-- me.log("CAPACITY: chest "..-count, "error")
|
|
-- me.add_capacity(ref.ipos, -count)
|
|
end
|
|
me.log("init now down to "..real_count.." items", "error")
|
|
local lcount = lstack:get_count() -- it was mc 1 excess 0 lcount 1 rc 1
|
|
local excess = real_count - lcount -- it was mc 1 excess -1 lcount 2 rc 1
|
|
me.log("it was "..mstack:get_count().." "..excess.." "..lcount.." "..real_count, "error") -- fails with: 1 -1
|
|
if real_count == 0 then
|
|
me.log("init me.remove_item before remove_loan chest", "error")
|
|
net:remove_loan(ref.pos, inv, lstack, loan_slot, ref)
|
|
elseif excess ~= 0 then
|
|
-- update loan and main with new excess
|
|
lstack:set_count(lcount + excess)
|
|
me.log("LOAN: me.remove_item loan to "..lstack:get_count(), "error")
|
|
me.loan.set_stack(net, inv, loan_slot, lstack)
|
|
mstack:set_count(mstack:get_count() + excess)
|
|
inv:set_stack(listname, slot, mstack)
|
|
if mstack:is_empty() then
|
|
me.maybemove(net, inv, listname, slot, stack)
|
|
end
|
|
-- TODO: Think this is wrong, only adjust by excess no stack:get_count()
|
|
--net.counts[lstack:get_name()] = net.counts[lstack:get_name()] - stack:get_count()
|
|
--me.add_capacity(ref.ipos, -stack:get_count())
|
|
me.log("CAPACITY: is "..excess, "error")
|
|
net.counts[lstack:get_name()] = net.counts[lstack:get_name()] + excess
|
|
me.add_capacity(ref.ipos, excess)
|
|
end
|
|
return stack
|
|
end
|
|
if remaining > 0 then
|
|
if remaining > math.pow(2,15) then
|
|
if not net.bias then
|
|
net.bias = {}
|
|
end
|
|
if not net.bias[listname] then
|
|
net.bias[listname] = {}
|
|
end
|
|
me.log("LARGE: total count "..remaining.." for "..stack:get_name(), "error")
|
|
annotate_large_stack(mstack, remaining)
|
|
net.bias[listname][stack:get_name()] = remaining - math.pow(2,15)
|
|
remaining = math.pow(2,15)
|
|
end
|
|
mstack:set_count(remaining)
|
|
inv:set_stack(listname, slot, mstack)
|
|
return stack
|
|
end
|
|
if remaining < 0 then
|
|
me.log("init wow, missing "..tostring(-remaining).." "..stack:get_name().." during removal, taking less", "error")
|
|
-- take fewer
|
|
stack:set_count(mstack:get_count() + mbias)
|
|
remaining = 0
|
|
end
|
|
mstack:set_count(remaining)
|
|
inv:set_stack(listname, slot, mstack)
|
|
me.log("init me.remove_item bottom before", "error")
|
|
me.maybemove(net, inv, listname, slot, stack)
|
|
me.log("init me.remove_item bottom after", "error")
|
|
return stack
|
|
end
|
|
|
|
function me.maybemove(net, inv, listname, slot, stack)
|
|
if stack == nil then
|
|
-- me.log("init nil stack", "error")
|
|
return
|
|
end
|
|
if net == nil then
|
|
-- me.log("init nil net", "error")
|
|
return
|
|
end
|
|
if inv == nil then
|
|
-- me.log("init nil inv", "error")
|
|
return
|
|
end
|
|
-- me.log("init maybemove "..stack:get_name(), "error")
|
|
if not net.byname or not net.byname[listname] or not net.byname[listname][stack:get_name()] then
|
|
me.log("byname on "..listname.." is already nil", "error")
|
|
else
|
|
-- slot has been completely removed, deindex it
|
|
net.byname[listname][stack:get_name()][stack:get_wear()] = nil
|
|
end
|
|
if net.bias and net.bias[listname] then
|
|
net.bias[listname][stack:get_name()] = nil
|
|
end
|
|
-- Move the last or the second to the last if the last is empty,
|
|
-- back to the hole left by the removed items
|
|
local main_size = inv:get_size(listname)
|
|
-- me.log("CALC main_size"..main_size.." slot "..slot, "error")
|
|
if slot < main_size and main_size > 1 then
|
|
local orig_slot = main_size
|
|
local mstack = inv:get_stack(listname, orig_slot)
|
|
-- me.log("CALC count "..mstack:get_count().." orig_slot-1 "..orig_slot-1, "error")
|
|
if mstack:is_empty() and slot < orig_slot-1 and orig_slot-1 > 1 then
|
|
orig_slot = orig_slot-1
|
|
mstack = inv:get_stack(listname, orig_slot)
|
|
end
|
|
if not mstack:is_empty() then
|
|
inv:set_stack(listname, orig_slot, ItemStack())
|
|
inv:set_stack(listname, slot, mstack)
|
|
-- [meta]
|
|
-- me.log("CALC not empty, old "..stack:get_name().." new "..mstack:get_name(), "error")
|
|
if net.byname and net.byname[listname] and net.byname[listname][stack:get_name()] then
|
|
net.byname[listname][stack:get_name()][stack:get_wear()] = nil
|
|
end
|
|
net.byname[listname][mstack:get_name()][mstack:get_wear()] = slot
|
|
else
|
|
inv:set_stack(listname, slot, mstack)
|
|
-- me.log("CALC empty, old "..stack:get_name(), "error")
|
|
if net.byname and net.byname[listname] and net.byname[listname][stack:get_name()] then
|
|
net.byname[listname][stack:get_name()][stack:get_wear()] = nil
|
|
end
|
|
end
|
|
end
|
|
inv:set_size(listname, main_size-1)
|
|
end
|
|
|
|
dofile(path.."/loan.lua") -- Loan Management
|
|
dofile(path.."/network.lua") -- Network Management
|
|
dofile(path.."/autocraft.lua") -- Autocrafting
|
|
|
|
-- 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 net = me.get_network(npos)
|
|
if net then
|
|
return net,npos
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function me.update_connected_machines(start_pos,event,include_start)
|
|
me.log("updating connected machines", "action")
|
|
local ev = event or {type = "n/a"}
|
|
local sn = me.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
|
|
|
|
-- load networks
|
|
function me.load()
|
|
local f = io.open(me.worldpath.."/microexpansion_networks", "r")
|
|
if f then
|
|
local res = minetest.deserialize(f:read("*all"))
|
|
f:close()
|
|
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
|
|
end
|
|
end
|
|
end
|
|
|
|
-- load now
|
|
me.load()
|
|
|
|
-- save networks
|
|
function me.save()
|
|
local data = {}
|
|
for _,net in pairs(me.networks) do
|
|
-- We rebuild this data by walking as they contain non-serializable content.
|
|
-- All other data is serialized and survives.
|
|
net.autocrafters = nil
|
|
net.autocrafters_by_pos = nil
|
|
net.process = nil
|
|
table.insert(data,net:serialize())
|
|
end
|
|
local f = io.open(me.worldpath.."/microexpansion_networks", "w")
|
|
f:write(minetest.serialize(data))
|
|
f:close()
|
|
end
|
|
|
|
-- save on server shutdown
|
|
minetest.register_on_shutdown(me.save)
|