255 lines
8.0 KiB
Lua
255 lines
8.0 KiB
Lua
-- Inventory Manager: Auto-sort barrel & order items to dropper
|
|
-- Main computer (networked). Computer 1 sits next to dropper_0 and auto-dispenses.
|
|
|
|
local DROPPER_NAME = "minecraft:dropper_9"
|
|
local BARREL_NAME = "minecraft:barrel_0"
|
|
local POLL_INTERVAL = 2 -- seconds between barrel checks
|
|
|
|
-------------------------------------------------
|
|
-- 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
|
|
-------------------------------------------------
|
|
|
|
-- Order items: move from chest(s) to dropper
|
|
-- Computer 1 will auto-detect and 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
|
|
|
|
print("[OK] Items in dropper. Computer 1 will dispense.")
|
|
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("")
|
|
|
|
-- 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()
|