From b0f262dcea75a93c9991f2584308162bf2e920ed Mon Sep 17 00:00:00 2001 From: MayaTheShy Date: Sun, 15 Mar 2026 16:08:10 -0400 Subject: [PATCH] Refactor inventory management to enhance device scanning, sorting, and dispensing functionality --- inventoryManager.lua | 356 +++++++++++++++++++++++++++++++++---------- 1 file changed, 275 insertions(+), 81 deletions(-) diff --git a/inventoryManager.lua b/inventoryManager.lua index 258aa76..c8af8e1 100644 --- a/inventoryManager.lua +++ b/inventoryManager.lua @@ -1,100 +1,294 @@ --- Inventory Manager: Scan & Group by shared items +-- Inventory Manager: Auto-sort barrel, order & dispense via networked dropper +-- Main computer (networked). Computer 1 sits next to dropper_0 and runs dropperController.lua. --- Scan all peripherals and categorize them -local function getInventoryDevices() - local devices = {} - for _, name in ipairs(peripheral.getNames()) do - local pType = peripheral.getType(name) - if pType == "minecraft:chest" or pType == "minecraft:dropper" then - table.insert(devices, { - name = name, - type = pType, - }) +local DROPPER_NAME = "minecraft:dropper_0" +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 - return devices + -- 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 --- Scan the contents of a single inventory peripheral -local function scanInventory(deviceName) - local items = {} - local inventory = peripheral.wrap(deviceName) - if not inventory then return items end +------------------------------------------------- +-- Inventory helpers +------------------------------------------------- - local contents = inventory.list() - for slot, item in pairs(contents) do - if items[item.name] then - items[item.name] = items[item.name] + item.count - else - items[item.name] = item.count +-- 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 --- Build a map of item -> list of devices that contain it -local function buildItemMap(devices) - local itemMap = {} +------------------------------------------------- +-- Main loop +------------------------------------------------- - for _, device in ipairs(devices) do - device.items = scanInventory(device.name) - - for itemName, count in pairs(device.items) do - if not itemMap[itemName] then - itemMap[itemName] = {} - end - table.insert(itemMap[itemName], { - name = device.name, - type = device.type, - count = count, - }) - end - end - - return itemMap -end - --- Display grouped results -local function displayGroups(itemMap) - print("=== Inventory Groups ===") +local function main() + print("=================================") + print(" Inventory Manager v2") + print("=================================") print("") - for itemName, devices in pairs(itemMap) do - local chests = {} - local droppers = {} + -- 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 - for _, device in ipairs(devices) do - if device.type == "minecraft:chest" then - table.insert(chests, device) - else - table.insert(droppers, device) + -- 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 [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 - - -- Mark as group when at least one chest AND one dropper share the item - local isGroup = #chests > 0 and #droppers > 0 - local tag = isGroup and "[GROUP]" or "[-----]" - - print(tag .. " " .. itemName) - - for _, device in ipairs(devices) do - local label = device.type == "minecraft:chest" and "CHEST " or "DROPPER" - print(string.format(" %s %-35s x%d", label, device.name, device.count)) - end - - print("") - end + ) end --- Main -local devices = getInventoryDevices() -print("Scanning " .. #devices .. " inventories...") -print("") - -if #devices == 0 then - print("No chests or droppers found on the network.") - return -end - -local itemMap = buildItemMap(devices) -displayGroups(itemMap) +main()