diff --git a/manager/operations.lua b/manager/operations.lua index 3c81ed3..0b2f8c1 100644 --- a/manager/operations.lua +++ b/manager/operations.lua @@ -148,13 +148,39 @@ function O.refreshCache(onProgress) local totalSlots = 0 local usedSlots = 0 - for ci, chest in ipairs(chests) do - if onProgress then onProgress(ci, #chests, chest) end - local inv = O.wrapCached(chest) - if inv then - totalSlots = totalSlots + inv.size() - local contents = inv.list() - for slot, item in pairs(contents) do + -- Parallel inventory scanning (chunked) + local CHUNK = cfg.PARALLEL_SCAN_CHUNKS or 8 + local scanData = {} + local scanFns = {} + for _, chest in ipairs(chests) do + table.insert(scanFns, function() + local inv = O.wrapCached(chest) + if inv then + scanData[chest] = { + size = inv.size(), + contents = inv.list(), + } + end + end) + end + + for i = 1, #scanFns, CHUNK do + local chunk = {} + local chunkEnd = math.min(i + CHUNK - 1, #scanFns) + for j = i, chunkEnd do + chunk[#chunk + 1] = scanFns[j] + end + if onProgress then + onProgress(chunkEnd, #chests, chests[chunkEnd] or "scanning...") + end + parallel.waitForAll(table.unpack(chunk)) + end + + for _, chest in ipairs(chests) do + local data = scanData[chest] + if data then + totalSlots = totalSlots + data.size + for slot, item in pairs(data.contents) do usedSlots = usedSlots + 1 if not catalogue[item.name] then catalogue[item.name] = {} @@ -293,6 +319,22 @@ end -- Barrel auto-sort ------------------------------------------------- +--- Get chests sorted by priority (highest first). +-- Falls back to name order if no priority is configured. +function O.getChestsByPriority() + local chests = O.getChests() + local priority = cfg.CHEST_PRIORITY + if not priority or not next(priority) then return chests end + local sorted = { table.unpack(chests) } + table.sort(sorted, function(a, b) + local pa = priority[a] or 0 + local pb = priority[b] or 0 + if pa ~= pb then return pa > pb end + return a < b + end) + return sorted +end + function O.sortBarrel(barrelOverride) local barrelTarget = (barrelOverride and barrelOverride ~= "") and barrelOverride or cfg.BARREL_NAME local barrel = O.wrapCached(barrelTarget) @@ -305,7 +347,7 @@ function O.sortBarrel(barrelOverride) state.needsRedraw = true local catalogue = cache.catalogue - local chests = O.getChests() + local chests = O.getChestsByPriority() for slot, item in pairs(contents) do local moved = 0 @@ -751,6 +793,63 @@ function O.checkAlerts() end end +------------------------------------------------- +-- Supply chest (builder / manifest-based stocking) +------------------------------------------------- + +function O.supplyChest() + if cfg.SUPPLY_CHEST == "" or #cfg.SUPPLY_MANIFEST == 0 then return false end + + local supply = O.wrapCached(cfg.SUPPLY_CHEST) + if not supply then return false end + + -- Scan what's already in the supply chest + local existing = {} + local contents = supply.list() + if contents then + for _, item in pairs(contents) do + existing[item.name] = (existing[item.name] or 0) + item.count + end + end + + local catalogue = cache.catalogue + local didWork = false + + for _, manifest in ipairs(cfg.SUPPLY_MANIFEST) do + local have = existing[manifest.name] or 0 + local want = manifest.count or 0 + local deficit = want - have + + if deficit > 0 and catalogue[manifest.name] then + local remaining = deficit + local srcSnapshot = { table.unpack(catalogue[manifest.name]) } + for _, source in ipairs(srcSnapshot) do + if source.total > 0 then + local chest = O.wrapCached(source.chest) + if chest then + for slot, slotItem in pairs(chest.list()) do + if slotItem.name == manifest.name then + local toMove = math.min(slotItem.count, remaining) + local n = chest.pushItems(cfg.SUPPLY_CHEST, slot, toMove) + if n and n > 0 then + state.adjustCache(manifest.name, source.chest, -n) + remaining = remaining - n + didWork = true + log.info("SUPPLY", "%s x%d -> %s", manifest.name, n, cfg.SUPPLY_CHEST) + if remaining <= 0 then break end + end + end + end + end + end + if remaining <= 0 then break end + end + end + end + + return didWork +end + ------------------------------------------------- -- Order / Dispense ------------------------------------------------- @@ -1012,6 +1111,24 @@ function O.craftItem(recipeIdx) end end +------------------------------------------------- +-- Recursive crafting (multi-step chains) +------------------------------------------------- + +function O.recursiveCraft(outputName, count) + if not ctx.craftEngine then + return false, "Craft engine not initialized" + end + activity.crafting = true + state.needsRedraw = true + state.smelterNeedsRedraw = true + local ok, err = ctx.craftEngine.executeChain(outputName, count, ctx) + activity.crafting = false + state.needsRedraw = true + state.smelterNeedsRedraw = true + return ok, err +end + ------------------------------------------------- -- Smelting recipe persistence -------------------------------------------------