mirror of
https://github.com/theFox6/microexpansion.git
synced 2024-11-27 09:33:47 +01:00
994 lines
37 KiB
Lua
994 lines
37 KiB
Lua
--- Microexpansion network
|
|
-- @type network
|
|
-- @field #table controller_pos the position of the controller
|
|
-- @field #number power_load the power currently provided to the network
|
|
-- @field #number power_storage the power that can be stored for the next tick
|
|
local network = {
|
|
power_load = 0,
|
|
power_storage = 0
|
|
}
|
|
local me = microexpansion
|
|
me.network = network
|
|
|
|
--- construct a new network
|
|
-- @function [parent=#network] new
|
|
-- @param #table or the object to become a network or nil
|
|
-- @return #table the new network object
|
|
function network.new(o)
|
|
local ret = setmetatable(o or {}, {__index = network})
|
|
ret.counts = {}
|
|
return ret
|
|
end
|
|
|
|
--- check if a node can be connected
|
|
-- @function [parent=#network] can_connect
|
|
-- @param #table np the position of the node to be checked
|
|
-- the node itself or the name of the node
|
|
-- @return #boolean whether this node has the group me_connect
|
|
function network.can_connect(np)
|
|
local nn
|
|
if type(np)=="string" then
|
|
nn = np
|
|
else
|
|
if np.name then
|
|
nn = np.name
|
|
else
|
|
local node = me.get_node(np)
|
|
nn = node.name
|
|
end
|
|
end
|
|
return minetest.get_item_group(nn, "me_connect") > 0
|
|
end
|
|
|
|
function network.get_ref(lstack)
|
|
local tref = lstack:get_meta():get_string("me_store_reference")
|
|
if tref == "" then
|
|
-- me.log("REF: we tried, "..lstack:get_name(), "error")
|
|
-- me.log("REF: fallback", "error")
|
|
local location = lstack:get_location()
|
|
if location.type == "undefined" then
|
|
me.log("REF: we tried", "error")
|
|
end
|
|
local foo = minetest.get_inventory(location)
|
|
local ref = nil
|
|
-- TODO: Do we need this?
|
|
return nil
|
|
end
|
|
local ref = minetest.deserialize(tref)
|
|
return ref
|
|
end
|
|
|
|
--- get all adjacent connected nodes
|
|
-- @function [parent=#network] adjacent_connected_nodes
|
|
-- @param #table pos the position of the base node
|
|
-- @param #boolean include_ctrl whether to check for the controller
|
|
-- @return #table all nodes that have the group me_connect
|
|
function network.adjacent_connected_nodes(pos, include_ctrl)
|
|
local adjacent = {
|
|
{x=pos.x+1, y=pos.y, z=pos.z},
|
|
{x=pos.x-1, y=pos.y, z=pos.z},
|
|
{x=pos.x, y=pos.y+1, z=pos.z},
|
|
{x=pos.x, y=pos.y-1, z=pos.z},
|
|
{x=pos.x, y=pos.y, z=pos.z+1},
|
|
{x=pos.x, y=pos.y, z=pos.z-1},
|
|
}
|
|
|
|
local nodes = {}
|
|
|
|
for _,apos in pairs(adjacent) do
|
|
local napos = me.get_node(apos)
|
|
local nn = napos.name
|
|
if network.can_connect(nn) then
|
|
if include_ctrl == false then
|
|
if nn ~= "microexpansion:ctrl" then
|
|
table.insert(nodes,{pos = apos, name = nn})
|
|
end
|
|
else
|
|
table.insert(nodes,{pos = apos, name = nn})
|
|
end
|
|
end
|
|
end
|
|
|
|
return nodes
|
|
end
|
|
|
|
--- provide power to the network
|
|
-- @function [parent=#network] provide
|
|
-- @param #number power the amount of power provided
|
|
function network:provide(power)
|
|
self.power_load = self.power_load + power
|
|
end
|
|
|
|
--- demand power from the network
|
|
-- @function [parent=#network] demand
|
|
-- @param #number power the amount of power demanded
|
|
-- @return #boolean whether the power was provided
|
|
function network:demand(power)
|
|
if self.power_load - power < 0 then
|
|
return false
|
|
end
|
|
self.power_load = self.power_load - power
|
|
return true
|
|
end
|
|
|
|
--- add power capacity to the network
|
|
-- @function [parent=#network] add_power_capacity
|
|
-- @param #number power the amount of power that can be stored
|
|
function network:add_power_capacity(power)
|
|
self.power_storage = self.power_storage + power
|
|
end
|
|
|
|
--- add power capacity to the network
|
|
-- @function [parent=#network] add_power_capacity
|
|
-- @param #number power the amount of power that can't be stored anymore
|
|
function network:remove_power_capacity(power)
|
|
self.power_storage = self.power_storage - power
|
|
if self.power_storage < 0 then
|
|
me.log("power storage of network "..self.." dropped below zero","warning")
|
|
end
|
|
end
|
|
|
|
--- remove overload
|
|
-- to be called by the controller every turn
|
|
-- @function [parent=#network] remove_overload
|
|
function network:remove_overload()
|
|
self.power_load = math.min(self.power_load, self.power_storage)
|
|
end
|
|
|
|
--- get a drives item capacity
|
|
-- @function get_drive_capacity
|
|
-- @param #table pos the position of the drive
|
|
-- @return #number the number of items that can be stored in the drive
|
|
local function get_drive_capacity(pos)
|
|
local cap = 0
|
|
local meta = minetest.get_meta(pos)
|
|
local inv = meta:get_inventory()
|
|
for i = 1, inv:get_size("main") do
|
|
cap = cap + me.get_cell_size(inv:get_stack("main", i):get_name())
|
|
end
|
|
return cap
|
|
end
|
|
|
|
local function get_interface_capacity(pos)
|
|
local cap = 0
|
|
local meta = minetest.get_meta(pos)
|
|
return meta:get_int("capacity") or 0
|
|
end
|
|
|
|
--- get the item capacity of a network
|
|
-- @function [parent=#network] get_item_capacity
|
|
-- @return #number the total number of items that can be stored in the network
|
|
function network:get_item_capacity()
|
|
local cap = 0
|
|
for npos in me.connected_nodes(self.controller_pos) do
|
|
if me.get_node(npos).name == "microexpansion:drive" then
|
|
cap = cap + get_drive_capacity(npos)
|
|
end
|
|
if me.get_node(npos).name == "microexpansion:interface" then
|
|
cap = cap + get_interface_capacity(npos)
|
|
end
|
|
end
|
|
self.capacity_cache = cap
|
|
me.log("total capacity is "..cap, "error")
|
|
return cap
|
|
end
|
|
|
|
function network:remove_slots(inv, listname, target, csize)
|
|
-- me.log("removing slots on "..listname..", target is "..target.." and csize is "..csize, "error")
|
|
for i = target+1, csize do
|
|
local stack = inv:get_stack(listname, i)
|
|
-- me.log("network remove_slot "..listname..":"..i.." "..stack:get_name(), "error")
|
|
if not stack:is_empty() then
|
|
-- me.log("BROKEN: after remove_slot, still "..stack:get_count().." "..stack:get_name().." left", "error")
|
|
if true then return end
|
|
foobar() -- This can't happen, we can only remove empty slots, and only 1 at that, at the end
|
|
--inv:set_stack(listname, i, "")
|
|
--me.insert_item(stack, self, inv, listname)
|
|
me.remove_item(self, inv, listname, stack)
|
|
if inv:get_stack("main", i):get_count() ~= 0 then
|
|
-- me.log("BROKEN: after remove_slot, still "..inv:get_stack("main", i):get_count().." left", "error")
|
|
end
|
|
end
|
|
end
|
|
--perhaps allow list removal
|
|
if target < 0 then
|
|
-- TODO: audit, this sould be made impossible if it is, or removed.
|
|
target = 1
|
|
end
|
|
inv:set_size(listname, target)
|
|
end
|
|
|
|
function network:set_storage_space(count, listname)
|
|
local c = count or 1
|
|
listname = listname or "main"
|
|
local inv = self:get_inventory()
|
|
local csize = inv:get_size(listname)
|
|
local space = 0
|
|
local inside = 0
|
|
local contents = inv:get_list(listname) or {}
|
|
for i,stack in pairs(contents) do
|
|
if stack:is_empty() then
|
|
space = space + 1
|
|
-- me.log("STORAGE: found space at "..i.." now "..space, "error")
|
|
else
|
|
inside = inside + stack:get_count()
|
|
end
|
|
end
|
|
local cap = self:get_item_capacity()
|
|
--the capacity is allocated outside of the condition because it updates the cached capacity
|
|
if count == true then
|
|
me.log("STORAGE: current: "..inside.." capacity: "..cap, "error")
|
|
if inside < cap then
|
|
c = 1
|
|
else
|
|
c = 0
|
|
end
|
|
end
|
|
local needed = c - space
|
|
-- me.log("STORAGE: needed: "..needed, "error")
|
|
if needed > 0 then
|
|
needed = needed + csize
|
|
inv:set_size(listname, needed)
|
|
elseif needed < 0 then
|
|
needed = needed + csize
|
|
self:remove_slots(inv, listname, needed, csize)
|
|
end
|
|
end
|
|
|
|
function network:update()
|
|
self:set_storage_space(true)
|
|
end
|
|
|
|
function network:get_inventory_name()
|
|
local cp = self.controller_pos
|
|
assert(cp, "trying to get inventory name of a network without controller")
|
|
return "microexpansion_storage_"..cp.x.."_"..cp.y.."_"..cp.z
|
|
end
|
|
|
|
function network:find_loan(inv, stack)
|
|
if not self.byname["loan"] then
|
|
self.byname["loan"] = {}
|
|
end
|
|
if not self.byname["loan"][stack:get_name()] then
|
|
self.byname["loan"][stack:get_name()] = {}
|
|
end
|
|
local loan_slot = self.byname["loan"][stack:get_name()][stack:get_wear()]
|
|
if loan_slot then
|
|
-- me.log("init me.remove_item before update_loan", "error")
|
|
self:update_loan(inv, loan_slot)
|
|
-- me.log("init me.remove_item after update_loan", "error")
|
|
end
|
|
loan_slot = self.byname["loan"][stack:get_name()][stack:get_wear()]
|
|
if loan_slot then
|
|
local lstack = me.loan.get_stack(self, inv, loan_slot)
|
|
if lstack:get_name() == stack:get_name() and lstack:get_wear() == stack:get_wear() then
|
|
return loan_slot
|
|
end
|
|
self.byname["loan"][stack:get_name()][stack:get_wear()] = nil
|
|
end
|
|
-- me.log("init network find_loan searching all loans", "error")
|
|
for loan_slot = 1, me.loan.get_size(self, inv) do
|
|
local lstack = me.loan.get_stack(self, inv, loan_slot)
|
|
if lstack:get_name() == stack:get_name() and lstack:get_wear() == stack:get_wear() then
|
|
-- TODO: Same meta_data...
|
|
self.byname["loan"][stack:get_name()][stack:get_wear()] = loan_slot
|
|
return loan_slot
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function create_inventory(net)
|
|
local invname = net:get_inventory_name()
|
|
net.inv = minetest.create_detached_inventory(invname, {
|
|
allow_put = function(inv, listname, index, stack)
|
|
local inside_stack = inv:get_stack(listname, index)
|
|
local stack_name = stack:get_name()
|
|
if minetest.get_item_group(stack_name, "microexpansion_cell") > 0 and
|
|
stack:get_meta():get_string("items") ~= "" and
|
|
stack:get_meta():get_string("items") ~= "return {}" then
|
|
return 0
|
|
end
|
|
-- improve performance by skipping unnessecary calls
|
|
if inside_stack:get_name() ~= stack_name or inside_stack:get_count() >= inside_stack:get_stack_max() then
|
|
if inv:get_stack(listname, index+1):get_name() ~= "" then
|
|
return stack:get_count()
|
|
end
|
|
end
|
|
local max_slots = inv:get_size(listname)
|
|
local max_items = net.capacity_cache
|
|
|
|
local slots, items = 0, 0
|
|
-- Get amount of items in drive
|
|
for i = 1, max_slots do
|
|
local dstack = inv:get_stack(listname, i)
|
|
if dstack:get_name() ~= "" then
|
|
slots = slots + 1
|
|
local num = dstack:get_count()
|
|
if num == 0 then num = 1 end
|
|
items = items + num
|
|
end
|
|
end
|
|
return math.max(math.min(stack:get_count(),max_items-items),0)
|
|
end,
|
|
on_put = function(inv, listname, _, stack)
|
|
inv:remove_item(listname, stack)
|
|
me.insert_item(stack, net, inv, listname)
|
|
net:set_storage_space(true)
|
|
end,
|
|
allow_take = function(inv, listname, slot, stack, player)
|
|
-- This is not remove_item, we only remove the loan. This real
|
|
-- item will be removed.
|
|
me.log("REMOVE: network allow taking of "..stack:get_name().." from "..listname, "error")
|
|
--me.log("COUNTS: allow_take "..minetest.serialize(net.counts), "error")
|
|
--print(dump2(inv:get_list("loan")))
|
|
-- me.log("LOANS: allow_take "..minetest.serialize(inv:get_list("loan")), "error")
|
|
-- net:dump_loans(inv)
|
|
local count = math.min(stack:get_count(),stack:get_stack_max())
|
|
local orig_count = count
|
|
if listname ~= "main" then
|
|
return count
|
|
end
|
|
local on_loan = net.counts[stack:get_name()]
|
|
local main_slot = nil
|
|
local main_count = 0
|
|
if on_loan then
|
|
local found = false
|
|
local mstack = inv:get_stack("main", slot)
|
|
main_slot = slot
|
|
local mbias = 0
|
|
if net.bias and net.bias["main"] and net.bias["main"][stack:get_name()] then
|
|
mbias = net.bias["main"][stack:get_name()]
|
|
end
|
|
main_count = mstack:get_count() + mbias
|
|
if main_count - on_loan >= 1 then
|
|
return math.min(mstack:get_count() + mbias - on_loan, count)
|
|
end
|
|
local update = false
|
|
--me.log("COUNTS: allow_take2 "..minetest.serialize(net.counts), "error")
|
|
--print(dump2(inv:get_list("loan")))
|
|
-- net:dump_loans(inv)
|
|
local loan_slot = net:find_loan(inv, stack)
|
|
if loan_slot then
|
|
-- me.log("found loan", "error")
|
|
local lstack = me.loan.get_stack(net, inv, loan_slot)
|
|
local loan_count = lstack:get_count()
|
|
local ref = network.get_ref(lstack)
|
|
local rinv = minetest.get_meta(ref.pos):get_inventory()
|
|
local real_stack = nil
|
|
if ref.drawer then
|
|
local c = drawers.drawer_get_content(ref.pos, ref.slot)
|
|
real_stack = ItemStack(c.name)
|
|
real_stack:set_count(math.max(c.count-1,0))
|
|
else
|
|
real_stack = rinv:get_stack(ref.invname, ref.slot)
|
|
end
|
|
local same_name = real_stack:get_name() == stack:get_name()
|
|
local same_wear = real_stack:get_wear() == stack:get_wear()
|
|
-- TODO: Same meta_data...
|
|
if same_name and same_wear and real_stack:get_count() >= 1 then
|
|
found = true
|
|
count = math.min(real_stack:get_count(), count)
|
|
if real_stack:get_count() ~= loan_count then
|
|
update = true
|
|
-- The partial update won't be able to update the count of
|
|
-- this slot as we are taking the whole thing, updating now
|
|
-- before the contents are removed, so the loan remains
|
|
if count == main_count then
|
|
local mstack = inv:get_stack("main", main_slot)
|
|
-- adding 1 is enough to keep it from disappearing
|
|
mstack:set_count(main_count + 1)
|
|
inv:set_stack("main", main_slot, mstack)
|
|
loan_count = loan_count + 1
|
|
lstack:set_count(loan_count)
|
|
me.log("LOAN: allow_take to "..lstack:get_count(), "error")
|
|
me.loan.set_stack(net, inv, loan_slot, lstack)
|
|
end
|
|
end
|
|
if ref.drawer then
|
|
local take = real_stack
|
|
take:set_count(count)
|
|
local c = drawers.drawer_get_content(ref.pos, "")
|
|
drawers.drawer_take_item(ref.pos, take) -- TODO: unify with me.remove_item loan refilling code
|
|
local c = drawers.drawer_get_content(ref.pos, "")
|
|
else
|
|
real_stack:set_count(real_stack:get_count() - count)
|
|
rinv:set_stack(ref.invname, ref.slot, real_stack)
|
|
end
|
|
if loan_count - count == 0 then
|
|
net:maybemoveloan(inv, loan_slot)
|
|
-- me.log("LOAN: removed "..minetest.serialize(net.counts), "error")
|
|
else
|
|
lstack:set_count(loan_count - count)
|
|
me.log("LOAN: allow_take loan to "..lstack:get_count(), "error")
|
|
me.loan.set_stack(net, inv, loan_slot, lstack)
|
|
end
|
|
net.counts[stack:get_name()] = net.counts[stack:get_name()] - count
|
|
me.add_capacity(ref.ipos, -count)
|
|
else
|
|
update = true
|
|
end
|
|
if not found then
|
|
update = true
|
|
-- me.log("Ouch, lost track, someone almost got free shit from us: "..stack:get_name().." "..tostring(count), "error")
|
|
count = 0
|
|
end
|
|
if update then
|
|
-- a little weighty, but someone touched the count and it
|
|
-- wasn't us, make em pay for it.
|
|
net:update_counts()
|
|
end
|
|
end
|
|
end
|
|
return count
|
|
end,
|
|
on_take = function(inv, listname, index, stack, player)
|
|
me.log("REMOVE: net taking of "..stack:get_count().." "..stack:get_name().." from "..listname, "error")
|
|
local func = function()
|
|
if inv:get_stack(listname, index):get_count() == 0 then
|
|
me.maybemove(net, inv, listname, index, stack)
|
|
end
|
|
net:set_storage_space(true) -- TODO: remove, should not be necessary anymore, maybemove should do it
|
|
end
|
|
--update the inventory size in the next step as it is not allowed in on_take
|
|
minetest.after(0, func)
|
|
end
|
|
})
|
|
end
|
|
|
|
function network:get_inventory()
|
|
if not self.inv then
|
|
create_inventory(self)
|
|
assert(self.inv,"no inventory created")
|
|
end
|
|
return self.inv
|
|
end
|
|
|
|
function network:load_inventory(lists)
|
|
local inv = self:get_inventory()
|
|
for listname,c in pairs(lists) do
|
|
local empties = 0
|
|
inv:set_size(listname, #c)
|
|
for i,stack in pairs(c) do
|
|
stack = ItemStack(stack) -- and now we try it here
|
|
inv:set_stack(listname, i-empties, stack)
|
|
-- me.log("LOAD META0: "..stack:get_count().." "..stack:get_name(), "error")
|
|
|
|
if listname == "loan" then
|
|
-- local tref = stack:get_meta():get_string("me_store_reference")
|
|
-- me.log("LOAD META1: tref was "..tref, "error")
|
|
|
|
-- inv:get_stack(listname, i-empties):get_meta():set_string("me_store_reference", tref)
|
|
|
|
-- local inv_stack = inv:get_stack(listname, i-empties)
|
|
-- local inv_meta = inv_stack:get_meta()
|
|
-- inv_meta:set_string("me_store_reference", tref)
|
|
-- inv:set_stack(listname, i-empties, inv_stack)
|
|
-- inv_stack = inv:get_stack(listname, i-empties)
|
|
|
|
-- me.log("LOAD META1.1: tref was "..inv_meta:get_string("me_store_reference"), "error")
|
|
-- me.log("LOAD META1.2: tref was "..inv_stack:get_meta():get_string("me_store_reference"), "error")
|
|
-- me.log("LOAD META1.3: tref was "..inv:get_stack(listname, i-empties):get_meta():get_string("me_store_reference"), "error")
|
|
-- inv:set_stack(listname, i-empties, ItemStack(inv_stack))
|
|
--me.log("LOAD META1.4: tref was "..inv:get_stack(listname, i-empties):get_meta():get_string("me_store_reference"), "error")
|
|
--me.log("LOAD META1.5: tref was "..inv_stack:get_meta():get_string("me_store_reference"), "error")
|
|
|
|
--local meta = inv:get_stack(listname, i-empties)
|
|
--meta = meta:get_meta():get_string("me_store_reference")
|
|
--me.log("LOAD META2: ref was "..minetest.serialize(meta), "error")
|
|
--stack:get_meta():set_string("me_store_reference", tref)
|
|
--meta = inv:get_stack(listname, i-empties)
|
|
--meta = meta:get_meta():get_string("me_store_reference")
|
|
--me.log("LOAD META3: ref was "..minetest.serialize(meta), "error")
|
|
--me.log("loading network1: "..minetest.serialize(stack:get_meta():get_string("me_store_reference")), "error")
|
|
-- stack = ItemStack(stack) -- tried moving this, was here
|
|
-- me.log("loading network2: "..minetest.serialize(stack), "error")
|
|
end
|
|
-- me.log("loading network: "..minetest.serialize(stack), "error")
|
|
if stack and not stack:is_empty() then
|
|
-- me.log("loading network "..listname.." "..stack:get_name().." slot "..i, "error")
|
|
-- TODO: missing bias
|
|
-- does this load loans? Yes.
|
|
if not self.byname then
|
|
self.byname = {}
|
|
end
|
|
if not self.byname[listname] then
|
|
self.byname[listname] = {}
|
|
end
|
|
if not self.byname[listname][stack:get_name()] then
|
|
self.byname[listname][stack:get_name()] = {}
|
|
end
|
|
self.byname[listname][stack:get_name()][stack:get_wear()] = i-empties
|
|
else
|
|
empties = empties + 1
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function network:save_inventory()
|
|
local contents = {}
|
|
local lists = self.inv:get_lists()
|
|
for listname,c in pairs(lists or {}) do
|
|
local ct = {}
|
|
contents[listname] = ct
|
|
for i,stack in pairs(c) do
|
|
ct[i] = stack:to_string()
|
|
end
|
|
end
|
|
return contents
|
|
end
|
|
|
|
function network:load()
|
|
if self.strinv then
|
|
-- me.log("LOADING: "..minetest.serialize(self.strinv), "error")
|
|
self:load_inventory(self.strinv)
|
|
end
|
|
end
|
|
|
|
-- We don't save this data, rather we rewalk upon first use. If 1% of
|
|
-- the people play per reboot, then this saves 99% of the work.
|
|
-- Also, we don't actually read or write any of this data normally,
|
|
-- only for active users, using 1% of the memory.
|
|
-- TODO: I think all the storage for me should be handled the same way.
|
|
-- As it is, we needlessly read and write all the networks for all the users and
|
|
-- writing isn't crash friendly, whereas rewalking is crash friendly.
|
|
-- We don't reload the loans, that is saved and restored already.
|
|
function network:reload_network()
|
|
self.autocrafters = {}
|
|
self.autocrafters_by_pos = {}
|
|
self.process = {}
|
|
for ipos in me.connected_nodes(self.controller_pos) do
|
|
if me.get_node(ipos).name == "microexpansion:interface" then
|
|
me.reload_interface(self, ipos, nil)
|
|
end
|
|
end
|
|
end
|
|
|
|
function network:serialize()
|
|
local sert = {}
|
|
for i,v in pairs(self) do
|
|
if i == "inv" then
|
|
sert.strinv = self:save_inventory()
|
|
elseif i == "strinv" then
|
|
if not sert.strinv then
|
|
sert[i] = v
|
|
end
|
|
else
|
|
sert[i] = v
|
|
end
|
|
end
|
|
return sert
|
|
end
|
|
|
|
function network:destruct()
|
|
minetest.remove_detached_inventory(self:get_inventory_name())
|
|
self.controller_pos = nil
|
|
self.inv = nil
|
|
end
|
|
|
|
-- This ensure main_slot is right, and if not, update it
|
|
function network:find_main(inv, ref, stack)
|
|
local main_slot = ref.main_slot
|
|
local mstack = inv:get_stack("main", ref.main_slot)
|
|
if mstack:get_name() == stack:get_name() and mstack:get_wear() == stack:get_wear() then
|
|
return mstack
|
|
end
|
|
for i = 1, inv:get_size("main") do
|
|
mstack = inv:get_stack("main", i)
|
|
if mstack:get_name() == stack:get_name() and mstack:get_wear() == stack:get_wear() then
|
|
ref.main_slot = i
|
|
return mstack
|
|
end
|
|
end
|
|
end
|
|
|
|
-- we can't update a loan with a bias, cause we don't know the old bias for this specific loan,
|
|
-- therefore we have no clue about changes to the item count, therefore we can't update the
|
|
-- item count.
|
|
function network:update_loan(inv, loan_slot)
|
|
me.log("LOAN: updating some 4", "error")
|
|
local lstack = me.loan.get_stack(self, inv, loan_slot)
|
|
local tref = lstack:get_meta():get_string("me_store_reference")
|
|
me.log("LOAN: update_loan "..lstack:get_count().." "..lstack:get_name().." with ref "..tref, "error")
|
|
local rinv = nil
|
|
local real_stack = nil
|
|
local same_name = nil
|
|
local same_wear = nil
|
|
local excess = nil
|
|
if tref == nil then me.log("LOAN: bad tref in loan", "error") end
|
|
-- if tref == nil then return end
|
|
local ref = minetest.deserialize(tref)
|
|
if ref == nil then me.log("LOAN: bad ref in loan", "error") end
|
|
if ref == nil then return end
|
|
rinv = minetest.get_meta(ref.pos):get_inventory()
|
|
local rbias = 0
|
|
local lbias = (self.bias and self.bias["loan"] and self.bias["loan"][loan_slot]) or 0
|
|
if ref.drawer then
|
|
local c = drawers.drawer_get_content(ref.pos, ref.slot)
|
|
if c.name ~= lstack:get_name() then me.log("LOAN: bad drawer item in loan "..c.name.." "..lstack:get_name(), "error") end
|
|
c.count = math.max(c.count-1,0) -- Poor man's locking
|
|
real_stack = ItemStack(c.name)
|
|
if c.count > math.pow(2,15) then
|
|
rbias = rbias + c.count - math.pow(2,15)
|
|
c.count = math.pow(2,15)
|
|
end
|
|
real_stack:set_count(c.count)
|
|
else
|
|
real_stack = rinv:get_stack(ref.invname, ref.slot)
|
|
if real_stack:get_name() ~= lstack:get_name() then me.log("LOAN: bad chest item in loan "..real_stack:get_name().." "..lstack:get_name(), "error") end
|
|
end
|
|
-- local excess = (real_stack:get_count() + rbias) - (lstack:get_count() + lbias)
|
|
-- TODO: This assumes meta is the same save "me_store_reference".
|
|
same_name = real_stack:get_name() == lstack:get_name()
|
|
same_wear = real_stack:get_wear() == lstack:get_wear()
|
|
-- If someone updates the chest, update loan and counts
|
|
if not same_name or not same_wear then
|
|
me.log("inventory out of sync", "error")
|
|
-- Not at all the same, remove the loan entirely
|
|
if not inv:contains_item("main", lstack) then
|
|
-- TODO: Update anything that can remove to have allow_metadata check
|
|
-- remote inventories first so this cannot happen
|
|
me.log("missing items on loan #1", "error")
|
|
end
|
|
me.remove_item(self, inv, "main", lstack) -- FIXME remove_loan or change_loan? lstack, remove tref and remove that from main.
|
|
lstack:set_count(0)
|
|
if not real_stack:is_empty() then
|
|
local llstack = ItemStack(real_stack)
|
|
llstack:get_meta():set_string("me_store_reference", tref)
|
|
me.log("LOAN: update_loan to "..llstack:get_count()..llstack:get_name(), "error")
|
|
me.loan.set_stack(self, inv, loan_slot, llstack) -- FIXME change loan?
|
|
if rbias > 0 then
|
|
if not self.bias then
|
|
self.bias = {}
|
|
end
|
|
if not self.bias["loan"] then
|
|
self.bias["loan"] = {}
|
|
end
|
|
self.bias["loan"][loan_slot] = rbias -- FIXME, lstack or llstack? was lstack, wrong
|
|
end
|
|
end
|
|
return
|
|
end
|
|
excess = real_stack:get_count() - lstack:get_count()
|
|
me.log("update_loan exess is "..excess..", lc is "..lstack:get_count()..", rc is "..real_stack:get_count(), "error")
|
|
local prev = self.counts[lstack:get_name()] or 0
|
|
self.counts[lstack:get_name()] = prev + excess
|
|
me.log("COUNT: update_loan loan now to "..self.counts[lstack:get_name()].." "..lstack:get_name()..", "..excess.." more", "error")
|
|
if not real_stack:is_empty() then
|
|
me.log("LOAN: updating some", "error")
|
|
real_stack:get_meta():set_string("me_store_reference", tref)
|
|
local llstack = ItemStack(real_stack)
|
|
me.log("LOAN: update_loan to "..llstack:get_count().." "..llstack:get_name(), "error")
|
|
me.loan.set_stack(self, inv, loan_slot, llstack)
|
|
else
|
|
me.log("LOAN: updating some 2", "error")
|
|
remove_loan(ref.pos, inv, lstack, loan_slot, ref)
|
|
end
|
|
if excess > 0 then
|
|
local extra = lstack
|
|
extra:set_count(excess)
|
|
extra:get_meta():set_string("me_store_reference", "")
|
|
me.log("update_loan adding "..excess.." "..lstack:get_name().." to main", "error")
|
|
local mstack = self:find_main(inv, ref, extra)
|
|
if mstack and not mstack:is_empty() then
|
|
mstack:set_count(mstack:get_count()+excess)
|
|
else
|
|
me.log("INV: went missing, readding", "error")
|
|
me.insert_item(extra, self, inv, "main")
|
|
end
|
|
inv:set_stack("main", ref.main_slot, mstack)
|
|
me.add_capacity(ref.ipos, excess)
|
|
elseif excess < 0 then
|
|
local deficit = lstack
|
|
deficit:set_count(-excess)
|
|
me.log("update_loan removing "..-excess.." "..lstack:get_name().." from main", "error")
|
|
-- We should fix this so that this cannot happen. See above.
|
|
-- For now, let's see if we can fix it up.
|
|
if not inv:contains_item("main", deficit) then
|
|
me.log("network no extra items to meet deficit, free items", "error")
|
|
end
|
|
-- TODO: This isn't a copy of the original meta, it is the original meta, and screws other loans, maybe?
|
|
deficit:get_meta():set_string("me_store_reference", "")
|
|
-- was: local mstack = inv:get_stack("main", ref.main_slot)
|
|
local mstack = self:find_main(inv, ref, deficit)
|
|
if mstack then
|
|
mstack:set_count(mstack:get_count()+excess)
|
|
inv:set_stack("main", ref.main_slot, mstack)
|
|
if false and mstack:get_count() == 0 then -- should be impossible, we have an outstanding loan still?
|
|
foobar()
|
|
end
|
|
me.add_capacity(ref.ipos, excess)
|
|
end
|
|
end
|
|
end
|
|
|
|
function network:update_loan_count(inv, loan_slot)
|
|
me.log("COUNT: loan_slot "..loan_slot, "error")
|
|
local lstack = me.loan.get_stack(self, inv, loan_slot)
|
|
local lbias = (self.bias and self.bias["loan"] and self.bias["loan"][loan_slot]) or 0
|
|
local prev = self.counts[lstack:get_name()] or 0
|
|
self.counts[lstack:get_name()] = prev + lstack:get_count() + lbias
|
|
me.log("COUNT: update_loan_count loan now to "..self.counts[lstack:get_name()].." "..lstack:get_name()..", "..(lstack:get_count() + lbias).." more, bias is "..lbias, "error")
|
|
end
|
|
|
|
-- Check everything on loan. This is the cheapest possible version.
|
|
-- We don't rewalk, we don't pick up new slots that now have contents.
|
|
-- We don't even verify the type of node is the same type of node.
|
|
function network:update_counts()
|
|
local inv = self:get_inventory()
|
|
for loan_slot = 1, me.loan.get_size(self, inv) do
|
|
self:update_loan(inv, loan_slot)
|
|
end
|
|
-- Since we are rescanning 100%, we start with no old counts and we
|
|
-- rebuild the counts
|
|
self.counts = {}
|
|
-- no, update_loan_count doesn't read from the inventory, if this is done, it has to be done
|
|
-- by a inventory reader that will re-create it.
|
|
--if self.bias and self.bias["loan"] then
|
|
-- self.bias["loan"] = nil
|
|
--end
|
|
for loan_slot = 1, me.loan.get_size(self, inv) do
|
|
self:update_loan_count(inv, loan_slot)
|
|
end
|
|
end
|
|
|
|
function network:sync_main(inv)
|
|
local listname = "main"
|
|
for i = 1, inv:get_size(listname) do
|
|
local mstack = inv:get_stack(listname, i)
|
|
if mstack:is_empty() and i < inv:get_size("main") then
|
|
me.log("network sync_main, empty stack at pos "..i, "error")
|
|
me.maybemove(self, inv, "main", i, mstack)
|
|
else
|
|
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 slot = self.byname[listname][mstack:get_name()][mstack:get_wear()]
|
|
if not slot then
|
|
me.log("network sync_main, missing "..mstack:get_name().." at pos "..i, "error")
|
|
self.byname[listname][mstack:get_name()][mstack:get_wear()] = i
|
|
elseif slot ~= i then
|
|
me.log("network sync_main, wrong pos for "..mstack:get_name().." at pos "..i", found "..slot, "error")
|
|
self.byname[listname][mstack:get_name()][mstack:get_wear()] = i
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function network:remove_real(ref, inv, stack, count)
|
|
if ref.drawer then
|
|
while count > math.pow(2,16)-1 do
|
|
stack:set_count(math.pow(2,16)-1)
|
|
drawers.drawer_take_large_item(ref.pos, stack)
|
|
count = count - math.pow(2,16)-1
|
|
end
|
|
stack:set_count(count)
|
|
drawers.drawer_take_large_item(ref.pos, stack)
|
|
else
|
|
local rinv = minetest.get_meta(ref.pos):get_inventory()
|
|
stack:set_count(count)
|
|
local rstack = rinv:get_stack(ref.invname, ref.slot)
|
|
rstack:take_item(count)
|
|
rinv:set_stack(ref.invname, ref.slot, rstack)
|
|
end
|
|
end
|
|
|
|
-- Callers have to ensure net.counts exists before calling.
|
|
-- Loans with bias only work for no wear and no metadata items. Only
|
|
-- drawers can create bias loans and they don't support wear or
|
|
-- metadata items. One should add chests first, then drawers, this will top off
|
|
-- items in the drawers. If one wants to empty the chests, do the chests after the
|
|
-- drawer.
|
|
function network:create_loan(stack, ref, inv, int_meta, bias)
|
|
local listname = "loan"
|
|
local on_loan = self.counts[stack:get_name()] or 0
|
|
local lbias = bias or 0
|
|
inv = inv or get_inventory()
|
|
local mstack = ItemStack(stack)
|
|
local prev = int_meta:get_int("capacity") or 0
|
|
local count = stack:get_count() + (bias or 0)
|
|
int_meta:set_int("capacity", prev + count)
|
|
-- me.log("total loaned items: "..tostring(prev + count))
|
|
self:set_storage_space(true)
|
|
local _, main_slot = me.insert_item(mstack, self, inv, "main", bias)
|
|
local now_on_loan = self.counts[stack:get_name()] or 0
|
|
local items_taken = now_on_loan - on_loan
|
|
if items_taken > 0 then
|
|
local loan_slot = self:find_loan(inv, stack)
|
|
self:remove_real(ref, inv, stack, items_taken)
|
|
prev = int_meta:get_int("capacity") or 0
|
|
int_meta:set_int("capacity", prev - items_taken)
|
|
count = count - items_taken
|
|
if count == 0 then
|
|
-- no actual loan in this case.
|
|
return
|
|
end
|
|
if count > math.pow(2,15) then
|
|
bias = count - math.pow(2,15)
|
|
lbias = bias
|
|
count = math.pow(2,15)
|
|
else
|
|
bias = nil
|
|
lbias = 0
|
|
stack:set_count(count)
|
|
end
|
|
end
|
|
if main_slot == 1 then
|
|
me.log("LARGE: creating loan for "..mstack:get_name()..", mc "..mstack:get_count()..", lc "..stack:get_count()..", lbias "..(bias or 0), "error")
|
|
end
|
|
self:set_storage_space(true)
|
|
ref.main_slot = main_slot
|
|
stack:get_meta():set_string("me_store_reference", minetest.serialize(ref))
|
|
local loan_slot = me.loan.get_size(self, inv)+1
|
|
me.loan.set_size(self, inv, loan_slot)
|
|
-- me.log("loan size is now "..me.loan.get_size(self, inv))
|
|
me.log("LOAN: create_loan to "..stack:get_count().." "..stack:get_name(), "error")
|
|
me.loan.set_stack(self, inv, loan_slot, stack)
|
|
me.log("INV: slot "..loan_slot.." now has "..stack:get_count().." "..stack:get_name().." in it", "error")
|
|
if bias then
|
|
if not self.bias then
|
|
self.bias = {}
|
|
end
|
|
if not self.bias[listname] then
|
|
self.bias[listname] = {}
|
|
end
|
|
self.bias[listname][loan_slot] = bias
|
|
me.log("LARGE: "..self.bias[listname][loan_slot].." "..stack:get_name()..", added "..bias, "error")
|
|
end
|
|
prev = self.counts[stack:get_name()] or 0 -- TODO: Contemplate wear and meta for counts. Add wear, meta; no limit large counts to no wear, no meta
|
|
self.counts[stack:get_name()] = prev + count
|
|
me.log("COUNT: create_loan loan now to "..self.counts[stack:get_name()].." "..stack:get_name()..", "..count.." more", "error")
|
|
end
|
|
|
|
-- This removes the entire loan slot, always
|
|
function network:remove_loan(pos, inv, lstack, loan_slot, ref)
|
|
me.log("LOAN: updating some 3", "error")
|
|
lstack:get_meta():set_string("me_store_reference", "")
|
|
local mstack = self:find_main(inv, ref, lstack)
|
|
local main_slot = ref.main_slot
|
|
if mstack == nil then mstack = ItemStack() end
|
|
local omstack = ItemStack(mstack)
|
|
me.log("network remove_loan of "..omstack:get_name()..", at "..main_slot, "error")
|
|
local lbias = (self.bias and self.bias["loan"] and self.bias["loan"][loan_slot]) or 0
|
|
local excess = 0
|
|
local mbias = (self.bias and self.bias["main"] and self.bias["main"][mstack:get_name()]) or 0
|
|
excess = (mstack:get_count() + mbias) - (lstack:get_count() + lbias)
|
|
me.log("LOAN: remove_loan "..(mstack:get_count() + mbias).." "..mstack:get_name().." "..(lstack:get_count() + lbias).." on loan, lbias is "..lbias, "error")
|
|
if lstack:get_name() == "default:steel_ingot" then
|
|
me.log("LARGE: remove_loan excess "..excess.." for "..(lstack:get_name())..", mc "..mstack:get_count()..", mbias "..mbias..", lc "..lstack:get_count()..", lbias "..lbias..", slot "..loan_slot, "error")
|
|
end
|
|
if excess < 0 then
|
|
me.log("network missing "..tostring(-excess).." "..lstack:get_name().." from loan, free items", "error")
|
|
mstack:set_count(0)
|
|
mbias = 0
|
|
if self.bias and self.bias["main"] then
|
|
self.bias["main"][mstack:get_name()] = nil
|
|
end
|
|
lbias = 0
|
|
if self.bias and self.bias["loan"] then
|
|
self.bias["loan"][loan_slot] = nil
|
|
end
|
|
excess = 0
|
|
end
|
|
if excess > math.pow(2,15) then
|
|
me.log("LARGE: remove_loan remaining "..excess.." for "..lstack:get_name(), "error")
|
|
mstack:set_count(math.pow(2,15))
|
|
self.bias["main"][mstack:get_name()] = excess - math.pow(2,15)
|
|
if self.bias and self.bias["loan"] then
|
|
lbias = 0
|
|
self.bias["loan"][loan_slot] = nil
|
|
end
|
|
else
|
|
mstack:set_count(excess)
|
|
if self.bias and self.bias["main"] then
|
|
self.bias["main"][omstack:get_name()] = nil
|
|
end
|
|
if self.bias and self.bias["loan"] then
|
|
lbias = 0
|
|
self.bias["loan"][loan_slot] = nil
|
|
end
|
|
end
|
|
inv:set_stack("main", main_slot, mstack)
|
|
if mstack:get_count() == 0 then
|
|
me.log("network cleaning up empty main slot now", "error")
|
|
me.maybemove(self, inv, "main", main_slot, omstack)
|
|
me.log("network cleaning up empty main slot now, done", "error")
|
|
end
|
|
-- me.loan.set_stack(self, inv, loan_slot, ItemStack())
|
|
if self.bias and self.bias["loan"] then
|
|
self.bias["loan"][loan_slot] = nil
|
|
end
|
|
local on_loan = self.counts[lstack:get_name()]
|
|
if on_loan and on_loan >= lstack:get_count() + lbias then
|
|
self.counts[lstack:get_name()] = on_loan - lstack:get_count() - lbias
|
|
me.log("LOAN: remove_loan down to count "..self.counts[lstack:get_name()], "error")
|
|
else
|
|
me.log("wow, free items, network remove_loan fails to find previous loan counts, "..self.counts[lstack:get_name()].." "..lstack:get_name(), "error")
|
|
self.counts[lstack:get_name()] = 0
|
|
end
|
|
|
|
self:maybemoveloan(inv, loan_slot)
|
|
end
|
|
|
|
-- like me.maybemove
|
|
function network:maybemoveloan(inv, loan_slot)
|
|
local loan_size = me.loan.get_size(self, inv)
|
|
local stack = me.loan.get_stack(self, inv, loan_size)
|
|
if stack:is_empty() then
|
|
me.loan.set_size(self, inv, loan_size-1)
|
|
return self:maybemoveloan(inv, loan_slot)
|
|
end
|
|
local do_update = false
|
|
if loan_size > 1 and loan_slot < loan_size then
|
|
local stack = me.loan.get_stack(self, inv, loan_size)
|
|
if stack:is_empty() then -- should not be necessary
|
|
me.log("network BAD loan", "error")
|
|
-- This trips with interface place remove on main drawers
|
|
foobar()
|
|
end
|
|
local prev = me.loan.get_stack(self, inv, loan_slot)
|
|
if prev:get_count() ~= 0 then
|
|
-- TODO: Should not be necessary, find real problem, full remove, refile check, replace interface
|
|
if self.byname["loan"][prev:get_name()] then
|
|
self.byname["loan"][prev:get_name()][prev:get_wear()] = nil
|
|
end
|
|
else
|
|
-- don't remove the loan before, we need the name
|
|
foobar()
|
|
end
|
|
me.log("LOAN: maybemoveloan to "..stack:get_count(), "error")
|
|
me.loan.set_stack(self, inv, loan_slot, stack)
|
|
-- me.log("maybemoveloan "..stack:get_name(), "error")
|
|
-- me.log("maybemoveloan "..stack:get_count(), "error")
|
|
-- me.log("maybemoveloan "..stack:get_wear(), "error")
|
|
-- me.log("maybemoveloan "..loan_slot, "error")
|
|
-- me.log("maybemoveloan "..minetest.serialize(self.byname), "error")
|
|
-- me.log("maybemoveloan "..minetest.serialize(self.byname["loan"]), "error")
|
|
-- TODO: Should not be necessary, find real problem, full remove, refile check, replace interface
|
|
if self.byname["loan"][stack:get_name()] then
|
|
self.byname["loan"][stack:get_name()][stack:get_wear()] = loan_slot
|
|
end
|
|
do_update = true
|
|
else
|
|
local stack = me.loan.get_stack(self, inv, loan_size)
|
|
if stack:get_count() ~= 0 then
|
|
-- me.log("maybemoveloan "..stack:get_name(), "error")
|
|
-- me.log("maybemoveloan "..stack:get_count(), "error")
|
|
-- me.log("maybemoveloan "..stack:get_wear(), "error")
|
|
-- me.log("maybemoveloan "..loan_slot, "error")
|
|
-- me.log("maybemoveloan "..minetest.serialize(self.byname), "error")
|
|
-- me.log("maybemoveloan "..minetest.serialize(self.byname["loan"]), "error")
|
|
-- TODO: Should not be necessary, find real problem, full remove, refile check, replace interface
|
|
if self.byname["loan"] and self.byname["loan"][stack:get_name()] then
|
|
self.byname["loan"][stack:get_name()][stack:get_wear()] = nil
|
|
end
|
|
else
|
|
-- don't remove the loan before, we need the name
|
|
foobar()
|
|
end
|
|
end
|
|
me.loan.set_size(self, inv, loan_size-1)
|
|
-- me.log("loan size is now "..me.loan.get_size(self, inv))
|
|
if do_update then
|
|
-- update_loan calls us and needs this updated by the time we return
|
|
self:update_loan(inv, loan_slot)
|
|
end
|
|
end
|
|
|
|
function network:dump_loans(inv)
|
|
me.log("COUNTS: dump_loans "..minetest.serialize(self.counts), "error")
|
|
for i, j in pairs(inv:get_list("loan") or {}) do
|
|
print(" slot "..dump(i).." "..j:to_string().." "..j:get_meta():get_string("me_store_reference"))
|
|
end
|
|
end
|