2019-05-01 13:20:09 +02:00
--- 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 = {
2023-12-31 00:52:43 +01:00
power_load = 0 ,
power_storage = 0
2019-05-01 13:20:09 +02:00
}
2020-03-03 17:16:35 +01:00
local me = microexpansion
me.network = network
2019-05-01 13:20:09 +02:00
--- construct a new network
-- @function [parent=#network] new
2023-12-31 00:52:43 +01:00
-- @param #table or the object to become a network or nil
2019-05-07 17:49:26 +02:00
-- @return #table the new network object
2020-03-01 08:23:39 +01:00
function network . new ( o )
2023-12-31 00:52:43 +01:00
local ret = setmetatable ( o or { } , { __index = network } )
ret.counts = { }
return ret
2019-05-01 13:20:09 +02:00
end
--- check if a node can be connected
-- @function [parent=#network] can_connect
2020-03-01 08:23:39 +01:00
-- @param #table np the position of the node to be checked
-- the node itself or the name of the node
2019-05-01 13:20:09 +02:00
-- @return #boolean whether this node has the group me_connect
2020-03-01 08:23:39 +01:00
function network . can_connect ( np )
local nn
if type ( np ) == " string " then
nn = np
else
if np.name then
nn = np.name
else
2020-03-03 17:16:35 +01:00
local node = me.get_node ( np )
2020-03-01 08:23:39 +01:00
nn = node.name
end
end
2023-12-31 00:52:43 +01:00
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
2019-05-01 13:20:09 +02:00
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 )
2023-12-31 00:52:43 +01:00
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 } )
2019-05-01 13:20:09 +02:00
end
2023-12-31 00:52:43 +01:00
else
table.insert ( nodes , { pos = apos , name = nn } )
end
end
end
2019-05-01 13:20:09 +02:00
2023-12-31 00:52:43 +01:00
return nodes
2019-05-01 13:20:09 +02:00
end
--- provide power to the network
-- @function [parent=#network] provide
-- @param #number power the amount of power provided
function network : provide ( power )
2023-12-31 00:52:43 +01:00
self.power_load = self.power_load + power
2019-05-01 13:20:09 +02:00
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 )
2023-12-31 00:52:43 +01:00
if self.power_load - power < 0 then
return false
end
self.power_load = self.power_load - power
return true
2019-05-01 13:20:09 +02:00
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 )
2023-12-31 00:52:43 +01:00
self.power_storage = self.power_storage + power
2019-05-01 13:20:09 +02:00
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 )
2023-12-31 00:52:43 +01:00
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
2019-05-01 13:20:09 +02:00
end
--- remove overload
-- to be called by the controller every turn
-- @function [parent=#network] remove_overload
function network : remove_overload ( )
2023-12-31 00:52:43 +01:00
self.power_load = math.min ( self.power_load , self.power_storage )
2019-05-01 13:20:09 +02:00
end
2019-05-07 17:49:26 +02:00
--- 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 )
2023-12-31 00:52:43 +01:00
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
2019-05-07 17:49:26 +02:00
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 ( )
2023-12-31 00:52:43 +01:00
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
2019-05-07 17:49:26 +02:00
end
2020-03-01 08:23:39 +01:00
2023-12-31 00:52:43 +01:00
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
2020-03-03 17:16:35 +01:00
end
end
--perhaps allow list removal
if target < 0 then
2023-12-31 00:52:43 +01:00
-- TODO: audit, this sould be made impossible if it is, or removed.
2020-03-03 17:16:35 +01:00
target = 1
end
2023-12-31 00:52:43 +01:00
inv : set_size ( listname , target )
2020-03-03 17:16:35 +01:00
end
2023-12-31 00:52:43 +01:00
function network : set_storage_space ( count , listname )
2020-03-01 08:23:39 +01:00
local c = count or 1
2023-12-31 00:52:43 +01:00
listname = listname or " main "
2020-03-01 08:23:39 +01:00
local inv = self : get_inventory ( )
2023-12-31 00:52:43 +01:00
local csize = inv : get_size ( listname )
2020-03-01 08:23:39 +01:00
local space = 0
local inside = 0
2023-12-31 00:52:43 +01:00
local contents = inv : get_list ( listname ) or { }
for i , stack in pairs ( contents ) do
if stack : is_empty ( ) then
2020-03-01 08:23:39 +01:00
space = space + 1
2023-12-31 00:52:43 +01:00
-- me.log("STORAGE: found space at "..i.." now "..space, "error")
2020-03-01 08:23:39 +01:00
else
2023-12-31 00:52:43 +01:00
inside = inside + stack : get_count ( )
2020-03-01 08:23:39 +01:00
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
2023-12-31 00:52:43 +01:00
me.log ( " STORAGE: current: " .. inside .. " capacity: " .. cap , " error " )
2020-03-01 08:23:39 +01:00
if inside < cap then
c = 1
else
c = 0
end
end
local needed = c - space
2023-12-31 00:52:43 +01:00
-- me.log("STORAGE: needed: "..needed, "error")
2020-03-03 17:16:35 +01:00
if needed > 0 then
needed = needed + csize
2023-12-31 00:52:43 +01:00
inv : set_size ( listname , needed )
2020-03-03 17:16:35 +01:00
elseif needed < 0 then
needed = needed + csize
2023-12-31 00:52:43 +01:00
self : remove_slots ( inv , listname , needed , csize )
2020-03-01 08:23:39 +01:00
end
end
function network : update ( )
2020-03-03 17:16:35 +01:00
self : set_storage_space ( true )
2020-03-01 08:23:39 +01:00
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
2023-12-31 00:52:43 +01:00
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
2020-03-01 08:23:39 +01:00
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 ( )
2024-01-02 04:20:18 +01:00
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
2023-12-31 00:52:43 +01:00
return 0
2023-09-02 13:29:10 +02:00
end
2020-03-01 08:23:39 +01:00
-- improve performance by skipping unnessecary calls
2020-09-29 14:38:42 +02:00
if inside_stack : get_name ( ) ~= stack_name or inside_stack : get_count ( ) >= inside_stack : get_stack_max ( ) then
2023-12-31 00:52:43 +01:00
if inv : get_stack ( listname , index + 1 ) : get_name ( ) ~= " " then
return stack : get_count ( )
end
2020-03-01 08:23:39 +01:00
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
2023-12-31 00:52:43 +01:00
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
2020-03-01 08:23:39 +01:00
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 )
2023-12-31 00:52:43 +01:00
me.insert_item ( stack , net , inv , listname )
2020-03-03 17:16:35 +01:00
net : set_storage_space ( true )
2020-03-01 08:23:39 +01:00
end ,
2023-12-31 00:52:43 +01:00
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
2020-03-01 08:23:39 +01:00
end ,
2023-12-31 00:52:43 +01:00
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
2023-09-02 13:29:10 +02:00
--update the inventory size in the next step as it is not allowed in on_take
2023-12-31 00:52:43 +01:00
minetest.after ( 0 , func )
2020-03-01 08:23:39 +01:00
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
2023-12-31 00:52:43 +01:00
local empties = 0
2020-03-01 08:23:39 +01:00
inv : set_size ( listname , # c )
2023-12-31 00:52:43 +01:00
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
2020-03-01 08:23:39 +01:00
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
2023-12-31 00:52:43 +01:00
-- me.log("LOADING: "..minetest.serialize(self.strinv), "error")
2020-03-01 08:23:39 +01:00
self : load_inventory ( self.strinv )
end
end
2023-12-31 00:52:43 +01:00
-- 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
2020-03-01 08:23:39 +01:00
function network : serialize ( )
local sert = { }
for i , v in pairs ( self ) do
if i == " inv " then
sert.strinv = self : save_inventory ( )
2020-10-04 12:12:19 +02:00
elseif i == " strinv " then
if not sert.strinv then
2023-12-31 00:52:43 +01:00
sert [ i ] = v
2020-10-04 12:12:19 +02:00
end
2020-03-01 08:23:39 +01:00
else
sert [ i ] = v
end
end
return sert
end
function network : destruct ( )
2020-03-07 18:02:14 +01:00
minetest.remove_detached_inventory ( self : get_inventory_name ( ) )
2020-03-01 08:23:39 +01:00
self.controller_pos = nil
self.inv = nil
end
2023-12-31 00:52:43 +01:00
-- 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