Files
Inventory-Manager-CC/inventoryManager.lua
2026-03-15 16:15:28 -04:00

295 lines
9.3 KiB
Lua

-- Inventory Manager: Auto-sort barrel, order & dispense via networked dropper
-- Main computer (networked). Computer 1 sits next to dropper_0 and runs dropperController.lua.
local DROPPER_NAME = "minecraft:dropper_9"
local BARREL_NAME = "minecraft:barrel_0"
local MODEM_SIDE = nil -- auto-detected
local CONTROLLER_ID = 1 -- computer ID of the dropper controller
local POLL_INTERVAL = 2 -- seconds between barrel checks
local PROTOCOL = "inventory"
-------------------------------------------------
-- Networking
-------------------------------------------------
-- Find and open the modem
local function setupModem()
for _, side in ipairs({"top","bottom","left","right","front","back"}) do
if peripheral.getType(side) == "modem" then
rednet.open(side)
MODEM_SIDE = side
return true
end
end
-- Also check for wired modems wrapped as peripherals
for _, name in ipairs(peripheral.getNames()) do
if peripheral.getType(name) == "modem" then
rednet.open(name)
MODEM_SIDE = name
return true
end
end
return false
end
-------------------------------------------------
-- Inventory helpers
-------------------------------------------------
-- Get all chest peripheral names on the network
local function getChests()
local chests = {}
for _, name in ipairs(peripheral.getNames()) do
if peripheral.getType(name) == "minecraft:chest" then
table.insert(chests, name)
end
end
return chests
end
-- Scan a single inventory: returns { [itemName] = { total=N, slots={ [slot]={name,count} } } }
local function scanInventory(deviceName)
local inv = peripheral.wrap(deviceName)
if not inv then return {} end
local result = {}
for slot, item in pairs(inv.list()) do
if not result[item.name] then
result[item.name] = { total = 0, slots = {} }
end
result[item.name].total = result[item.name].total + item.count
result[item.name].slots[slot] = { name = item.name, count = item.count }
end
return result
end
-- Build a full catalogue: itemName -> { {chest=name, total=N}, ... }
local function buildCatalogue()
local catalogue = {} -- itemName -> list of {chest, total}
for _, chest in ipairs(getChests()) do
local contents = scanInventory(chest)
for itemName, info in pairs(contents) do
if not catalogue[itemName] then
catalogue[itemName] = {}
end
table.insert(catalogue[itemName], { chest = chest, total = info.total })
end
end
return catalogue
end
-------------------------------------------------
-- Barrel auto-sort
-------------------------------------------------
-- Move all items from the barrel into matching chests (or first chest with space)
local function sortBarrel()
local barrel = peripheral.wrap(BARREL_NAME)
if not barrel then return end
local contents = barrel.list()
if not contents or not next(contents) then return end
local catalogue = buildCatalogue()
local chests = getChests()
for slot, item in pairs(contents) do
local moved = 0
-- Try chests that already hold this item first
if catalogue[item.name] then
for _, entry in ipairs(catalogue[item.name]) do
local n = barrel.pushItems(entry.chest, slot)
if n and n > 0 then
moved = moved + n
print(string.format("[SORT] %s x%d -> %s", item.name, n, entry.chest))
end
if moved >= item.count then break end
end
end
-- If some remain, push to any chest with space
if moved < item.count then
for _, chest in ipairs(chests) do
local n = barrel.pushItems(chest, slot)
if n and n > 0 then
moved = moved + n
print(string.format("[SORT] %s x%d -> %s", item.name, n, chest))
end
if moved >= item.count then break end
end
end
if moved < item.count then
print(string.format("[WARN] Could not sort %d remaining %s", item.count - moved, item.name))
end
end
end
-------------------------------------------------
-- Order & dispense
-------------------------------------------------
-- Send redstone trigger to computer 1 via rednet
local function triggerDropper()
rednet.send(CONTROLLER_ID, "dispense", PROTOCOL)
print("[NET] Sent dispense signal to computer " .. CONTROLLER_ID)
end
-- Order items: move from chest(s) to dropper, then trigger dispense
local function orderItem(itemName, amount)
local catalogue = buildCatalogue()
if not catalogue[itemName] then
print("[ERR] Item not found: " .. itemName)
return false
end
local dropper = peripheral.wrap(DROPPER_NAME)
if not dropper then
print("[ERR] Dropper not found: " .. DROPPER_NAME)
return false
end
local remaining = amount
for _, entry in ipairs(catalogue[itemName]) do
local chest = peripheral.wrap(entry.chest)
if chest then
-- Find slots in this chest with the target item
for slot, slotItem in pairs(chest.list()) do
if slotItem.name == itemName then
local toMove = math.min(remaining, slotItem.count)
local moved = chest.pushItems(DROPPER_NAME, slot, toMove)
if moved and moved > 0 then
remaining = remaining - moved
print(string.format("[ORDER] %s x%d from %s -> dropper", itemName, moved, entry.chest))
end
if remaining <= 0 then break end
end
end
end
if remaining <= 0 then break end
end
if remaining > 0 then
print(string.format("[WARN] Only moved %d/%d of %s", amount - remaining, amount, itemName))
end
-- Trigger computer 1 to fire the dropper
triggerDropper()
return true
end
-------------------------------------------------
-- Display
-------------------------------------------------
local function showCatalogue()
local catalogue = buildCatalogue()
print("")
print("=== Item Catalogue ===")
print("")
local index = 1
local items = {}
for itemName, sources in pairs(catalogue) do
local total = 0
for _, s in ipairs(sources) do total = total + s.total end
-- Strip "minecraft:" prefix for cleaner display
local short = itemName:gsub("^minecraft:", "")
print(string.format(" %2d. %-30s x%d", index, short, total))
items[index] = itemName
index = index + 1
end
print("")
return items
end
-------------------------------------------------
-- Main loop
-------------------------------------------------
local function main()
print("=================================")
print(" Inventory Manager v2")
print("=================================")
print("")
-- Setup networking
if not setupModem() then
print("[WARN] No modem found. Dispense signals won't work.")
else
print("[OK] Modem opened on: " .. tostring(MODEM_SIDE))
end
-- Check dropper
if peripheral.wrap(DROPPER_NAME) then
print("[OK] Dropper found: " .. DROPPER_NAME)
else
print("[WARN] Dropper not found: " .. DROPPER_NAME)
end
-- Check barrel
if peripheral.wrap(BARREL_NAME) then
print("[OK] Barrel found: " .. BARREL_NAME)
else
print("[WARN] Barrel not found: " .. BARREL_NAME)
end
print("")
-- Run barrel watcher and command prompt in parallel
parallel.waitForAny(
-- Task 1: Watch barrel for new items
function()
while true do
sortBarrel()
sleep(POLL_INTERVAL)
end
end,
-- Task 2: Interactive command prompt
function()
while true do
local items = showCatalogue()
print("Commands:")
print(" order <number> [amount] - Dispense an item")
print(" scan - Refresh catalogue")
print(" quit - Exit")
print("")
write("> ")
local input = read()
local parts = {}
for word in input:gmatch("%S+") do
table.insert(parts, word)
end
local cmd = parts[1]
if cmd == "order" or cmd == "o" then
local idx = tonumber(parts[2])
local amt = tonumber(parts[3]) or 1
if idx and items[idx] then
orderItem(items[idx], amt)
else
print("[ERR] Invalid item number.")
end
elseif cmd == "scan" or cmd == "s" then
print("Rescanning...")
elseif cmd == "quit" or cmd == "q" then
print("Shutting down.")
return
else
print("[ERR] Unknown command: " .. tostring(cmd))
end
print("")
end
end
)
end
main()