microexpansion/modules/storage/interface.lua
2024-01-09 09:51:17 -08:00

333 lines
10 KiB
Lua

-- interface
-- microexpansion/interface.lua
local me = microexpansion
local pipeworks_enabled = minetest.get_modpath("pipeworks") and true or false
-- Interfaces work by walking all connected blocks. We walk machines and inventories.
-- This explains which nodes are connected.
-- Missing from technic_plus, the generators, might be nice to be able
-- to feed them wood when the power is low.
-- Odd machines like the reactor or the force field aren't supported.
-- We'd have to figure out what we'd want to do with them.
local function can_connect(name)
if me.registered_inventory[name] then
return true
end
return false
end
function me.walk_connected(pos)
local nodes = {}
local visited = {}
visited[pos.x] = {}
visited[pos.x][pos.y] = {}
visited[pos.x][pos.y][pos.z] = true
local to_visit = {pos}
while #to_visit > 0 do
local pos = table.remove(to_visit)
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},
}
for _,apos in pairs(adjacent) do
if not visited[apos.x] then
visited[apos.x] = {}
end
if not visited[apos.x][apos.y] then
visited[apos.x][apos.y] = {}
end
if visited[apos.x][apos.y][apos.z] ~= true then
visited[apos.x][apos.y][apos.z] = true
local napos = minetest.get_node(apos)
local nn = napos.name
if can_connect(nn) then
table.insert(nodes, {pos=apos, name=nn})
table.insert(to_visit, apos)
end
end
end
end
return nodes
end
local function update(pos,_,ev)
if ev.type == "connect" then
-- net.update_counts()
elseif ev.type == "disconnect" then
--
end
end
function me.reload_inventory(name, net, ctrl_inv, int_meta, n, pos, doinventories)
local func = me.registered_inventory and me.registered_inventory[name]
if func then
func(net, ctrl_inv, int_meta, n, pos, doinventories)
end
end
function me.chest_reload(net, ctrl_inv, int_meta, n, pos, doinventories)
if not doinventories then return end
local meta = minetest.get_meta(n.pos)
local inv = meta:get_inventory()
for i = 1, inv:get_size("main") do
local stack = inv:get_stack("main", i)
if not stack:is_empty() then
net:create_loan(stack, {pos=n.pos, invname="main", slot=i, ipos=pos}, ctrl_inv, int_meta)
end
end
end
me.register_inventory("default:chest", me.chest_reload)
-- This never rewalks connected machines. To do that add a gui
-- rewalk and/or remove, replace.
function me.reload_interface(net, pos, doinventories)
if not net then return end
local ctrl_inv = net:get_inventory()
local int_meta = minetest.get_meta(pos)
local inv = int_meta:get_inventory()
local inventories = minetest.deserialize(int_meta:get_string("connected"))
-- not appropriate
-- me.send_event(pos,"connect")
int_meta:set_string("infotext", "chests: "..#inventories)
if not net.counts then
net.counts = {}
end
if not net.autocrafters then
net.autocrafters = {}
end
if not net.autocrafters_by_pos then
net.autocrafters_by_pos = {}
end
if not net.process then
net.process = {}
end
for _, n in pairs(inventories) do
local node = minetest.get_node(n.pos)
local name = node.name
-- me.log("INT: found a "..name, "error")
local outputs = me.output_by_typename[me.block_to_typename_map[name]]
if outputs then
for _, name in pairs(outputs) do
if not net.process[name] then
net.process[name] = {}
end
-- me.log("INT: registering "..name.." for the "..node.name, "error")
net.process[name][n.pos] = pos
end
else
me.reload_inventory(name, net, ctrl_inv, int_meta, n, pos, doinventories)
end
end
-- me.send_event(pos,...
end
-- [me chest] Register node
me.register_node("interface", {
description = "ME Interface",
usedfor = "Interface for ME system",
tiles = {
"chest_top",
"chest_top",
"chest_side",
"chest_side",
"chest_side",
"chest_side", -- TODO: Maybe customize it?
},
recipe = {
{ 1, {
{"default:steel_ingot", "microexpansion:machine_casing", "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, tubedevice = 1, tubedevice_receiver = 1 },
paramtype = "light",
paramtype2 = "facedir",
me_update = update,
on_construct = function(pos)
local int_meta = minetest.get_meta(pos)
int_meta:set_string("formspec",
"size[9,7.5]"..
microexpansion.gui_bg ..
microexpansion.gui_slots ..
[[
label[0,-0.23;ME Interface]
list[context;import;0,0.3;9,1]
list[context;export;0,0.3;9,1]
list[current_player;main;0,3.5;8,1;]
list[current_player;main;0,4.73;8,3;8]
listring[current_name;import]
listring[current_player;main]
field_close_on_enter[filter;false]
]])
local inv = int_meta:get_inventory()
inv:set_size("export", 3)
inv:set_size("import", 3)
local inventories = me.walk_connected(pos)
int_meta:set_string("connected", minetest.serialize(inventories))
local net = me.get_connected_network(pos)
if net == nil then
int_meta:set_string("infotext", "No Network")
return
end
me.reload_interface(net, pos, true)
end,
can_dig = function(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return inv:is_empty("import")
end,
on_destruct = function(pos)
local net = me.get_connected_network(pos)
if net == nil then return end
local inv = net:get_inventory()
local int_meta = minetest.get_meta(pos)
local inventories = minetest.deserialize(int_meta:get_string("connected"))
int_meta:set_string("infotext", "")
local to_remove = {}
for _, n in pairs(inventories) do
local pos = n.pos
if not to_remove[pos.x] then
to_remove[pos.x] = {}
end
if not to_remove[pos.x][pos.y] then
to_remove[pos.x][pos.y] = {}
end
if not to_remove[pos.x][pos.y][pos.z] then
to_remove[pos.x][pos.y][pos.z] = true
end
end
net:update_counts()
local loan_slot = me.loan.get_size(net, inv)
local ref
while loan_slot > 0 do
local lstack = me.loan.get_stack(net, inv, loan_slot)
if lstack:is_empty() then
-- TODO: Don't think this can happen now, update_counts
-- me.log("interface empty loan at "..loan_slot, "error")
goto continue
foobar()
end
-- me.log("interface removing loan at "..loan_slot, "error")
ref = me.network.get_ref(lstack)
if ref and to_remove[ref.pos.x] and to_remove[ref.pos.x][ref.pos.y] and to_remove[ref.pos.x][ref.pos.y][ref.pos.z] then
net:remove_loan(ref.pos, inv, lstack, loan_slot, ref)
end
::continue::
loan_slot = loan_slot - 1
end
net:update_counts()
-- pos is a table and does not have value semantics.
if net.autocrafters_by_pos then
for k, v in pairs(net.autocrafters_by_pos) do
if k.x == pos.x and k.y == pos.y and k.z == pos.z then
pos = k
break
end
end
if net.autocrafters_by_pos[pos] then
for name, apos in pairs(net.autocrafters_by_pos[pos]) do
-- deindex these upon removal of the interface controlling them
net.autocrafters_by_pos[pos][name] = nil
net.autocrafters[name][apos] = nil
end
end
end
if net.process then
-- todo: This is a little slow, speed it up? Interface removal is infrequent.
for _, v in pairs(net.process) do
for k, ipos in pairs(v) do
if ipos.x == pos.x and ipos.y == pos.y and ipos.z == pos.z then
pos = ipos
break
end
end
end
for name, v in pairs(net.process) do
for apos, ipos in pairs(v) do
if ipos == pos then
me.log("INTERFACE: killing a mchine for "..name, "error")
net.process[name][apos] = nil
end
end
end
end
end,
after_destruct = function(pos)
me.send_event(pos,"disconnect")
end,
allow_metadata_inventory_put = function(pos, listname, index, stack)
return stack:get_count()
end,
on_metadata_inventory_put = function(pos, _, _, stack)
local net = me.get_connected_network(pos)
if net == nil then
return
end
local ctrl_inv = net:get_inventory()
end,
allow_metadata_inventory_take = function(pos,_,_,stack) --args: pos, listname, index, stack, player
local net = me.get_connected_network(pos)
return stack:get_count()
end,
on_metadata_inventory_take = function(pos, _, _, stack)
local net = me.get_connected_network(pos)
if net == nil then
return
end
local ctrl_inv = net:get_inventory()
end,
-- tube connection
tube = {
can_insert = function(pos, _, stack) --pos, node, stack, direction
local net = me.get_connected_network(pos)
local inv = net:get_inventory()
local max_slots = inv:get_size("main")
local max_items = net.capacity_cache
local slots, items = 0, 0
-- Get amount of items in drive
for i = 1, max_slots do
local mstack = inv:get_stack("main", i)
if mstack:get_name() ~= "" then
slots = slots + 1
local num = mstack:get_count()
if num == 0 then num = 1 end
items = items + num
end
end
items = items + stack:get_count()
return max_items > items
end,
insert_object = function(pos, _, stack)
local net = me.get_connected_network(pos)
local leftovers = stack
if net then
local inv = net:get_inventory()
leftovers = me.insert_item(stack, net, inv, "main")
net:set_storage_space(true)
end
return leftovers
end,
connect_sides = {left=1, right=1, front=1, back=1, top=1, bottom=1},
},
after_place_node = pipeworks_enabled and pipeworks.after_place,
after_dig_node = pipeworks_enabled and pipeworks.after_dig,
})