mirror of
https://github.com/theFox6/microexpansion.git
synced 2024-11-27 01:23:44 +01:00
433 lines
13 KiB
Lua
433 lines
13 KiB
Lua
-- microexpansion/machines.lua
|
|
|
|
local me = microexpansion
|
|
|
|
local netdrives
|
|
|
|
-- load drives
|
|
local function load_drives()
|
|
local f = io.open(me.worldpath.."/microexpansion_drives", "r")
|
|
if f then
|
|
netdrives = minetest.deserialize(f:read("*all")) or {}
|
|
f:close()
|
|
--[[
|
|
if type(res) == "table" then
|
|
for _,d in pairs(res) do
|
|
table.insert(netdrives,d)
|
|
end
|
|
end
|
|
]]
|
|
else
|
|
netdrives = {}
|
|
end
|
|
end
|
|
|
|
-- load now
|
|
load_drives()
|
|
|
|
-- save drives
|
|
local function save_drives()
|
|
local f = io.open(me.worldpath.."/microexpansion_drives", "w")
|
|
f:write(minetest.serialize(netdrives))
|
|
f:close()
|
|
end
|
|
|
|
-- save on server shutdown
|
|
minetest.register_on_shutdown(save_drives)
|
|
|
|
local function get_drive_controller(pos)
|
|
for i,d in pairs(netdrives) do
|
|
if d.dpos then
|
|
if vector.equals(pos, d.dpos) then
|
|
return d,i
|
|
end
|
|
end
|
|
end
|
|
return --false,#netdrives+1
|
|
end
|
|
|
|
local function set_drive_controller(dpos,setd,cpos,i)
|
|
if i then
|
|
local dt = netdrives[i]
|
|
if dt then
|
|
if setd then
|
|
dt.dpos = dpos
|
|
end
|
|
if cpos ~= nil then
|
|
dt.cpos = cpos
|
|
end
|
|
else
|
|
netdrives[i] = {dpos = dpos, cpos = cpos}
|
|
end
|
|
else
|
|
local dt = get_drive_controller(dpos)
|
|
if dt then
|
|
if setd then
|
|
dt.dpos = dpos
|
|
end
|
|
if cpos ~= nil then
|
|
dt.cpos = cpos
|
|
end
|
|
else
|
|
table.insert(netdrives,{dpos = dpos, cpos = cpos})
|
|
end
|
|
end
|
|
end
|
|
|
|
local function write_to_cell(cell, items, item_count)
|
|
local size = microexpansion.get_cell_size(cell:get_name())
|
|
local item_meta = cell:get_meta()
|
|
--print(dump2(items,"cell_items"))
|
|
item_meta:set_string("items", minetest.serialize(items))
|
|
local base_desc = minetest.registered_craftitems[cell:get_name()].microexpansion.base_desc
|
|
-- Calculate Percentage
|
|
local percent = math.floor(item_count / size * 100)
|
|
-- Update description
|
|
item_meta:set_string("description", base_desc.."\n"..
|
|
minetest.colorize("grey", tostring(item_count).."/"..tostring(size).." Items ("..tostring(percent).."%)"))
|
|
return cell
|
|
end
|
|
|
|
local function write_drive_cells(pos, net)
|
|
local meta = minetest.get_meta(pos)
|
|
local own_inv = meta:get_inventory()
|
|
if net == nil then
|
|
return false
|
|
end
|
|
local ctrl_inv = net:get_inventory()
|
|
local cells = {}
|
|
for i = 1, own_inv:get_size("main") do
|
|
local cell = own_inv:get_stack("main", i)
|
|
local name = cell:get_name()
|
|
if name ~= "" then
|
|
cells[i] = cell
|
|
end
|
|
end
|
|
local cell_idx = next(cells)
|
|
if cell_idx == nil then
|
|
return
|
|
end
|
|
local size = microexpansion.get_cell_size(cells[cell_idx]:get_name())
|
|
local items_in_cell_count = 0
|
|
local cell_items = {}
|
|
|
|
net:update_counts()
|
|
if not net.counts then
|
|
net.counts = {}
|
|
end
|
|
for i = 1, ctrl_inv:get_size("main") do
|
|
local stack = ctrl_inv:get_stack("main", i)
|
|
local item_string = stack:to_string()
|
|
if item_string ~= "" then
|
|
item_string = item_string:split(" ")
|
|
lbias = (net.counts and net.counts[stack:get_name()]) or 0
|
|
local mbias = (net.bias and net.bias["main"] and net.bias["main"][stack:get_name()]) or 0
|
|
if mbias > lbias and lbias > 0 then
|
|
mbias = mbias - lbias
|
|
lbias = 0
|
|
net.bias["main"][stack:get_name()] = mbias
|
|
net.counts[stack:get_name()] = nil
|
|
elseif lbias > mbias and mbias > 0 then
|
|
lbias = lbias - mbias
|
|
mbias = 0
|
|
net.counts[stack:get_name()] = lbias
|
|
net.bias["main"][stack:get_name()] = nil
|
|
elseif mbias == lbias and mbias > 0 then
|
|
net.bias["main"][stack:get_name()] = nil
|
|
net.counts[stack:get_name()] = nil
|
|
end
|
|
local item_count = stack:get_count() + mbias
|
|
-- TODO: on_loan includes the >32k bias in it, so, therefore the
|
|
-- stack:get_count() + mbias should have it in it.
|
|
local on_loan = net.counts[stack:get_name()] or 0
|
|
item_count = item_count - on_loan
|
|
if item_count < 0 then
|
|
me.log("LOAN: drive "..item_count.." "..stack:get_name().." "..on_loan.." on loan", "error")
|
|
-- TODO: we need to update the count faster and we need to update counts from actual inventories
|
|
-- and not allow taking unless there is an actual item there, mostly done now
|
|
-- TODO: In theory this should be impossible now, but we have
|
|
-- bugs with cell removal and insert, see wow
|
|
me.log("wow, free items "..stack:get_name().." during drive write, "..tostring(-item_count).." extra, with a loan of "..on_loan, "error")
|
|
item_count = 0
|
|
if mbias > 0 then
|
|
net.bias["main"][stack:get_name()] = nil
|
|
end
|
|
end
|
|
if item_count > 1 and item_string[2] ~= tostring(item_count) then
|
|
me.log("stack count differs from second field of the item string","warning")
|
|
end
|
|
while item_count ~= 0 and cell_idx ~= nil do
|
|
--print(("stack to store: %q"):format(table.concat(item_string," ")))
|
|
|
|
-- TODO: This should fail if we write 64k items onto a 64k
|
|
-- drive (or larger). Fix by writting the number first, that's
|
|
-- the bias and then the name. This requires that no node in
|
|
-- the storage system starts with a number. Then in read,
|
|
-- support that.
|
|
|
|
if size < items_in_cell_count + item_count then
|
|
local space = size - items_in_cell_count
|
|
item_string[2] = tostring(space)
|
|
table.insert(cell_items,table.concat(item_string," "))
|
|
items_in_cell_count = items_in_cell_count + space
|
|
|
|
own_inv:set_stack("main", cell_idx, write_to_cell(cells[cell_idx],cell_items,items_in_cell_count))
|
|
cell_idx = next(cells, cell_idx)
|
|
if cell_idx == nil then
|
|
--there may be other drives within the network
|
|
me.log("too many items to store in drive","info")
|
|
break
|
|
end
|
|
size = microexpansion.get_cell_size(cells[cell_idx]:get_name())
|
|
items_in_cell_count = 0
|
|
cell_items = {}
|
|
item_count = item_count - space
|
|
else
|
|
items_in_cell_count = items_in_cell_count + item_count
|
|
item_string[2] = tostring(item_count)
|
|
table.insert(cell_items,table.concat(item_string," "))
|
|
item_count = 0
|
|
end
|
|
end
|
|
end
|
|
if cell_idx == nil then
|
|
break
|
|
end
|
|
end
|
|
while cell_idx ~= nil do
|
|
own_inv:set_stack("main", cell_idx, write_to_cell(cells[cell_idx],cell_items,items_in_cell_count))
|
|
items_in_cell_count = 0
|
|
cell_items = {}
|
|
cell_idx = next(cells, cell_idx)
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
local function take_all(pos,net)
|
|
-- me.log("take_all", "error")
|
|
local meta = minetest.get_meta(pos)
|
|
local own_inv = meta:get_inventory()
|
|
local ctrl_inv = net:get_inventory()
|
|
local items = {}
|
|
for i = 1, own_inv:get_size("main") do
|
|
local stack = own_inv:get_stack("main", i)
|
|
local name = stack:get_name()
|
|
if name ~= "" then
|
|
local its = minetest.deserialize(stack:get_meta():get_string("items"))
|
|
for _,s in pairs(its) do
|
|
table.insert(items,s)
|
|
end
|
|
end
|
|
end
|
|
for _,ostack in pairs(items) do
|
|
--this returns 99 (max count) even if it removes more
|
|
--ctrl_inv:remove_item("main", ostack)
|
|
local postack = ItemStack(ostack)
|
|
-- me.log("drive take_all remove_item "..minetest.serialize(ostack), "error")
|
|
me.log("DRIVE: take_all remove_item "..tostring(postack:get_count()).." "..postack:get_name(), "error")
|
|
me.remove_item(net, ctrl_inv, "main", postack)
|
|
end
|
|
|
|
net:update()
|
|
me.send_event(pos,"items")
|
|
end
|
|
|
|
local function add_all(pos,net)
|
|
-- me.log("add_all", "error")
|
|
local meta = minetest.get_meta(pos)
|
|
local own_inv = meta:get_inventory()
|
|
local ctrl_inv = net:get_inventory()
|
|
local items = {}
|
|
for i = 1, own_inv:get_size("main") do
|
|
local stack = own_inv:get_stack("main", i)
|
|
local name = stack:get_name()
|
|
if name ~= "" then
|
|
local its = minetest.deserialize(stack:get_meta():get_string("items"))
|
|
if its then
|
|
for _,s in pairs(its) do
|
|
table.insert(items,s)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
for _,ostack in pairs(items) do
|
|
me.insert_item(ostack, net, ctrl_inv, "main")
|
|
end
|
|
|
|
net:update()
|
|
me.send_event(pos,"items",{net = net})
|
|
end
|
|
|
|
function me.disconnect_drive(pos,ncpos)
|
|
me.log("disconnecting drive at "..minetest.pos_to_string(pos),"action")
|
|
local fc,i = get_drive_controller(pos)
|
|
if not fc.cpos then
|
|
return
|
|
end
|
|
local fnet = me.get_network(fc.cpos)
|
|
write_drive_cells(pos,fnet)
|
|
if ncpos then
|
|
set_drive_controller(pos,false,ncpos,i)
|
|
else
|
|
set_drive_controller(pos,false,false,i)
|
|
end
|
|
if fnet then
|
|
take_all(pos,fnet)
|
|
else
|
|
me.log("drive couldn't take items from its former network","warning")
|
|
end
|
|
end
|
|
|
|
local function update_drive(pos,_,ev)
|
|
if ev.type~="connect" and ev.type~="disconnect" then
|
|
return
|
|
end
|
|
local fc,i = get_drive_controller(pos)
|
|
local cnet = ev.net or me.get_connected_network(pos)
|
|
if cnet then
|
|
if not fc then
|
|
me.log("connecting drive at "..minetest.pos_to_string(pos), "action")
|
|
set_drive_controller(pos,true,cnet.controller_pos,i)
|
|
add_all(pos,cnet)
|
|
elseif not fc.cpos then
|
|
me.log("connecting drive at "..minetest.pos_to_string(pos), "action")
|
|
set_drive_controller(pos,false,cnet.controller_pos,i)
|
|
add_all(pos,cnet)
|
|
elseif not vector.equals(fc.cpos,cnet.controller_pos) then
|
|
me.log("reconnecting drive at "..minetest.pos_to_string(pos), "action")
|
|
write_drive_cells(pos,me.get_network(fc.cpos))
|
|
set_drive_controller(pos,false,cnet.controller_pos,i)
|
|
add_all(pos,cnet)
|
|
me.disconnect_drive(pos,cnet.controller_pos)
|
|
else
|
|
if ev.origin.name == "microexpansion:ctrl" then
|
|
me.disconnect_drive(pos,false)
|
|
end
|
|
end
|
|
elseif fc then
|
|
if fc.cpos then
|
|
me.disconnect_drive(pos,false)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- [me chest] Register node
|
|
microexpansion.register_node("drive", {
|
|
description = "ME Drive",
|
|
usedfor = "Stores items into ME storage cells",
|
|
tiles = {
|
|
"chest_top",
|
|
"chest_top",
|
|
"chest_side",
|
|
"chest_side",
|
|
"chest_side",
|
|
"drive_full",
|
|
},
|
|
recipe = {
|
|
{ 1, {
|
|
{"default:steel_ingot", "default:chest", "default:steel_ingot" },
|
|
{"default:steel_ingot", "microexpansion:machine_casing", "default:steel_ingot" },
|
|
{"default:steel_ingot", "default:chest", "default:steel_ingot" },
|
|
},
|
|
}
|
|
},
|
|
is_ground_content = false,
|
|
groups = { cracky = 1, me_connect = 1 },
|
|
paramtype = "light",
|
|
paramtype2 = "facedir",
|
|
me_update = update_drive,
|
|
on_construct = function(pos)
|
|
local meta = minetest.get_meta(pos)
|
|
meta:set_string("formspec",
|
|
"size[9,7.5]"..
|
|
microexpansion.gui_bg ..
|
|
microexpansion.gui_slots ..
|
|
[[
|
|
label[0,-0.23;ME Drive]
|
|
list[context;main;0,0.3;5,2]
|
|
list[current_player;main;0,3.5;8,1;]
|
|
list[current_player;main;0,4.73;8,3;8]
|
|
listring[current_name;main]
|
|
listring[current_player;main]
|
|
field_close_on_enter[filter;false]
|
|
]])
|
|
local inv = meta:get_inventory()
|
|
inv:set_size("main", 10)
|
|
me.send_event(pos,"connect")
|
|
end,
|
|
can_dig = function(pos, player)
|
|
if minetest.is_protected(pos, player) then
|
|
return false
|
|
end
|
|
local meta = minetest.get_meta(pos)
|
|
local inv = meta:get_inventory()
|
|
return inv:is_empty("main")
|
|
end,
|
|
after_destruct = function(pos)
|
|
me.send_event(pos,"disconnect")
|
|
end,
|
|
allow_metadata_inventory_put = function(pos, _, _, stack, player)
|
|
if minetest.is_protected(pos, player)
|
|
or minetest.get_item_group(stack:get_name(), "microexpansion_cell") == 0 then
|
|
return 0
|
|
end
|
|
return 1
|
|
end,
|
|
on_metadata_inventory_put = function(pos, _, _, stack)
|
|
me.send_event(pos,"item_cap")
|
|
local network = me.get_connected_network(pos)
|
|
if network == nil then
|
|
return
|
|
end
|
|
local ctrl_inv = network:get_inventory()
|
|
local items = minetest.deserialize(stack:get_meta():get_string("items"))
|
|
if items == nil then
|
|
print("no items")
|
|
me.send_event(pos,"items",{net=network})
|
|
return
|
|
end
|
|
-- network:set_storage_space(#items)
|
|
for _,stack in pairs(items) do
|
|
network:set_storage_space(true)
|
|
me.insert_item(stack, network, ctrl_inv, "main")
|
|
end
|
|
network:set_storage_space(true)
|
|
me.send_event(pos,"items",{net=network})
|
|
end,
|
|
allow_metadata_inventory_take = function(pos,_,_,stack, player) --args: pos, listname, index, stack, player
|
|
if minetest.is_protected(pos, player) then
|
|
return 0
|
|
end
|
|
local network = me.get_connected_network(pos)
|
|
write_drive_cells(pos,network)
|
|
return stack:get_count()
|
|
end,
|
|
on_metadata_inventory_take = function(pos, _, _, stack)
|
|
local network = me.get_connected_network(pos)
|
|
if network == nil then
|
|
return
|
|
end
|
|
me.send_event(pos,"item_cap",{net=network})
|
|
local ctrl_inv = network:get_inventory()
|
|
local items = minetest.deserialize(stack:get_meta():get_string("items"))
|
|
if items == nil then
|
|
network:update()
|
|
return
|
|
end
|
|
for _,ostack in pairs(items) do
|
|
local postack = ItemStack(ostack)
|
|
-- me.log("drive meta_inv_take remove_item "..tostring(postack:get_count()).." "..postack:get_name(), "error")
|
|
-- TODO: this was here, but did nothing? me.remove_item(network, ctrl_inv, "main", postack)
|
|
-- No, this is the main item removal on cell removal from drive
|
|
me.remove_item(network, ctrl_inv, "main", postack)
|
|
--this returns 99 (max count) even if it removes more
|
|
--ctrl_inv:remove_item("main", ostack)
|
|
end
|
|
--print(stack:to_string())
|
|
|
|
network:update()
|
|
me.send_event(pos,"items",{net=network})
|
|
end,
|
|
})
|