diff --git a/inventoryManager.lua b/inventoryManager.lua index b1e5c7a..0c74b9c 100644 --- a/inventoryManager.lua +++ b/inventoryManager.lua @@ -749,6 +749,28 @@ local function invalidatePeripheralCaches() cachedFurnaces = nil end +-- Peripheral handle cache (avoids re-creating proxy tables on every call) +-- Handles are cleared on peripheral_detach events and during full scans. +local wrapCache = {} + +local function wrapCached(name) + local handle = wrapCache[name] + if handle then return handle end + handle = peripheral.wrap(name) + if handle then + wrapCache[name] = handle + end + return handle +end + +local function invalidateWrapCache(name) + if name then + wrapCache[name] = nil + else + wrapCache = {} + end +end + local function getChests() local now = os.clock() if cachedChests and (now - cachedChestsTime) < PERIPHERAL_CACHE_TTL then @@ -787,7 +809,7 @@ local function refreshFurnaceStatus() local furnaces = getFurnaces() local status = {} for _, fname in ipairs(furnaces) do - local furnace = peripheral.wrap(fname) + local furnace = wrapCached(fname) if furnace then local contents = furnace.list() local entry = { @@ -805,7 +827,7 @@ local function refreshFurnaceStatus() end local function scanInventory(deviceName) - local inv = peripheral.wrap(deviceName) + local inv = wrapCached(deviceName) if not inv then return {} end local result = {} for slot, item in pairs(inv.list()) do @@ -823,6 +845,9 @@ end local function refreshCache(onProgress) activity.scanning = true + -- Clear handle cache so we pick up any changes + invalidateWrapCache() + -- Single pass over peripherals: classify into chests and furnaces local chests = {} local furnaces = {} @@ -851,7 +876,7 @@ local function refreshCache(onProgress) for ci, chest in ipairs(chests) do if onProgress then onProgress(ci, #chests, chest) end - local inv = peripheral.wrap(chest) + local inv = wrapCached(chest) if inv then totalSlots = totalSlots + inv.size() local contents = inv.list() @@ -896,8 +921,8 @@ local function refreshCache(onProgress) cache.usedSlots = usedSlots cache.freeSlots = totalSlots - usedSlots cache.usedRatio = totalSlots > 0 and (usedSlots / totalSlots) or 0 - cache.dropperOk = peripheral.wrap(DROPPER_NAME) ~= nil - cache.barrelOk = peripheral.wrap(BARREL_NAME) ~= nil + cache.dropperOk = peripheral.isPresent(DROPPER_NAME) + cache.barrelOk = peripheral.isPresent(BARREL_NAME) -- Discover all droppers on the network (for location-based dispensing) -- Use name-based matching as peripheral.getType() may return "inventory" @@ -1641,7 +1666,7 @@ local function craftItem(recipeIdx) if cache.catalogue[itemName] then for _, source in ipairs(cache.catalogue[itemName]) do - local chest = peripheral.wrap(source.chest) + local chest = wrapCached(source.chest) if chest then for slot, slotItem in pairs(chest.list()) do local key = source.chest .. ":" .. slot @@ -1737,7 +1762,7 @@ local function craftItem(recipeIdx) for _, r in ipairs(result.results) do -- Find which chest likely received it for _, ch in ipairs(chests) do - local chest = peripheral.wrap(ch) + local chest = wrapCached(ch) if chest then for slot, slotItem in pairs(chest.list()) do if slotItem.name == r.name then @@ -2060,7 +2085,7 @@ local function drawSmelterDashboard() -- ===== Available Crafting Recipes ===== -- Turtle status on tab row - local turtleOk = craftTurtleName and peripheral.wrap(craftTurtleName) ~= nil + local turtleOk = craftTurtleName and peripheral.isPresent(craftTurtleName) local tLabel = turtleOk and " Turtle OK " or " No Turtle " local tBg = turtleOk and colors.lime or colors.red local tFg = turtleOk and colors.black or colors.white @@ -2272,7 +2297,7 @@ end local function sortBarrel(barrelOverride) local barrelTarget = (barrelOverride and barrelOverride ~= "") and barrelOverride or BARREL_NAME - local barrel = peripheral.wrap(barrelTarget) + local barrel = wrapCached(barrelTarget) if not barrel then return end local contents = barrel.list() @@ -2344,7 +2369,7 @@ local function autoSmelt() local emptyInputFurnaces = {} -- collected during steps 1-3, filled in step 5 for _, fname in ipairs(furnaces) do - local furnace = peripheral.wrap(fname) + local furnace = wrapCached(fname) if furnace then local contents = furnace.list() @@ -2420,7 +2445,7 @@ local function autoSmelt() for _, fuel in ipairs(FUEL_LIST) do if catalogue[fuel.name] then for _, source in ipairs(catalogue[fuel.name]) do - local chest = peripheral.wrap(source.chest) + local chest = wrapCached(source.chest) if chest then for slot, slotItem in pairs(chest.list()) do if slotItem.name == fuel.name then @@ -2505,7 +2530,7 @@ local function autoSmelt() local loaded = false for _, source in ipairs(catalogue[itemName]) do - local chest = peripheral.wrap(source.chest) + local chest = wrapCached(source.chest) if chest then for slot, slotItem in pairs(chest.list()) do if slotItem.name == itemName then @@ -2556,7 +2581,7 @@ local function defragInventory() -- Build a map: itemName -> list of { chest, slot, count, maxCount } local itemSlots = {} for _, chestName in ipairs(chests) do - local inv = peripheral.wrap(chestName) + local inv = wrapCached(chestName) if inv then local contents = inv.list() local detail_cache = {} @@ -2603,7 +2628,7 @@ local function defragInventory() else local space = recv.max - recv.count local toMove = math.min(donor.count, space) - local donorInv = peripheral.wrap(donor.chest) + local donorInv = wrapCached(donor.chest) if donorInv then local n = donorInv.pushItems(recv.chest, donor.slot, toMove, recv.slot) if n and n > 0 then @@ -2636,7 +2661,7 @@ local function autoCompost() local didWork = false -- 1) Pull bone meal from hopper back to chests - local hopper = peripheral.wrap(COMPOST_HOPPER) + local hopper = wrapCached(COMPOST_HOPPER) if hopper then local contents = hopper.list() if contents then @@ -2655,7 +2680,7 @@ local function autoCompost() end -- 2) Feed compostable items into dropper - local dropper = peripheral.wrap(COMPOST_DROPPER) + local dropper = wrapCached(COMPOST_DROPPER) if not dropper then return didWork end -- Check how much item capacity the dropper has (slots * 64 - current items) @@ -2689,7 +2714,7 @@ local function autoCompost() local toFeed = math.min(available, dropperFreeItems) local fed = 0 for _, source in ipairs(catalogue[itemName]) do - local chest = peripheral.wrap(source.chest) + local chest = wrapCached(source.chest) if chest then for slot, slotItem in pairs(chest.list()) do if slotItem.name == itemName then @@ -2766,7 +2791,7 @@ local function orderItem(itemName, amount, dropperOverride) return false end - local dropper = peripheral.wrap(dropperTarget) + local dropper = wrapCached(dropperTarget) if not dropper then statusMessage = "Dropper offline: " .. dropperTarget statusColor = colors.red @@ -2778,7 +2803,7 @@ local function orderItem(itemName, amount, dropperOverride) local remaining = amount for _, entry in ipairs(catalogue[itemName]) do - local chest = peripheral.wrap(entry.chest) + local chest = wrapCached(entry.chest) if chest then for slot, slotItem in pairs(chest.list()) do if slotItem.name == itemName then @@ -3011,7 +3036,7 @@ local function broadcastState() disabledRecipes = disabledRecipes, smeltable = SMELTABLE, craftable = CRAFTABLE, - craftTurtleOk = craftTurtleName and peripheral.wrap(craftTurtleName) ~= nil, + craftTurtleOk = craftTurtleName and peripheral.isPresent(craftTurtleName), } networkModem.transmit(BROADCAST_CHANNEL, ORDER_CHANNEL, state) end @@ -3026,13 +3051,13 @@ local function main() print("=================================") print("") - if peripheral.wrap(DROPPER_NAME) then + if peripheral.isPresent(DROPPER_NAME) then print("[OK] Dropper: " .. DROPPER_NAME) else print("[WARN] Dropper not found: " .. DROPPER_NAME) end - if peripheral.wrap(BARREL_NAME) then + if peripheral.isPresent(BARREL_NAME) then print("[OK] Barrel: " .. BARREL_NAME) else print("[WARN] Barrel not found: " .. BARREL_NAME) @@ -3093,12 +3118,12 @@ local function main() print(string.format("[INIT] %d/%d recipes enabled", enabledCount, totalRecipeCount)) -- Detect compost peripherals - if peripheral.wrap(COMPOST_DROPPER) then + if peripheral.isPresent(COMPOST_DROPPER) then print("[OK] Compost dropper: " .. COMPOST_DROPPER) else print("[WARN] Compost dropper not found: " .. COMPOST_DROPPER) end - if peripheral.wrap(COMPOST_HOPPER) then + if peripheral.isPresent(COMPOST_HOPPER) then print("[OK] Compost hopper: " .. COMPOST_HOPPER) else print("[WARN] Compost hopper not found: " .. COMPOST_HOPPER)