local util = require("util") local stackgroup = {} local prototype = {} function prototype:defragment(chests) table.sort(self, function(a, b) return a.count < b.count end) local top = #self -- Find largest non-full stack for i=top,1,-1 do local entry = self[i] if entry:getStack(chests):availableCount() ~= 0 then top = i break end end while top > 1 do local topEntry = self[top] local bottomEntry = self[1] local txCap = math.min(bottomEntry.count, topEntry:getStack(chests):availableCount()) local tx = bottomEntry:getChest(chests):moveItemsTo(txCap, bottomEntry.slot, topEntry:getChest(chests), topEntry.slot) if tx ~= txCap then error("Safe inventory transfer failed unexpectedly: "..textutils.serialise({ from = { chest = bottomEntry:getChest(chests):getIdentifier(), slot = bottomEntry.slot }, to = { chest = topEntry:getChest(chests):getIdentifier(), slot = topEntry.slot }, txExpected = txCap, txActual = tx })) end bottomEntry.count = bottomEntry.count - tx topEntry.count = topEntry.count + tx if bottomEntry.count == 0 then top = top - 1 table.remove(self, 1) end if topEntry:getStack(chests):availableCount() == 0 then top = top - 1 end end end function prototype:moveItemsToPeripheral(chests, count, per, slot) if count > self.total then error("Insufficient items available to transfer to peripheral: "..textutils.serialise({ available = self.total, count = count })) end table.sort(self, function(a, b) return a.count < b.count end) local totalMoved = 0 while totalMoved < count do local currentSlot = self[1] local txCap = math.min(currentSlot.count, count - totalMoved) local tx = currentSlot:getChest(chests):moveItemsToPeripheral(txCap, currentSlot.slot, per, slot) if tx == 0 then error("Unexpected error when transferring items to peripheral: "..textutils.serialise({ expected = txCap, from = { chest = currentSlot:getChest(chests):getIdentifier(), count = currentSlot.count }, to = { peripheral = peripheral.getName(per) } })) end self.total = self.total - tx totalMoved = totalMoved + tx if tx == currentSlot.count then table.remove(self, 1) end end end local groupEntryPrototype = {} function groupEntryPrototype:getStack(chests) return self:getChest(chests).slots[self.slot] end function groupEntryPrototype:getChest(chests) return chests[self.chestIndex] end local stackgroupSetPrototype = {} function stackgroupSetPrototype:findGroup(item) return util.find(self, function(group) return group.item == item end) end function stackgroup.fromChests(...) local chests = { ... } local groups = {} setmetatable(groups, { __index = stackgroupSetPrototype }) for chestIndex,chest in ipairs(chests) do for slotKey,slot in pairs(chest.slots) do local group = util.find(groups, function(value) return value.item == slot.item end) if group == nil then group = { item = slot.item, total = 0 } setmetatable(group, { __index = prototype }) groups[#groups + 1] = group end local entry = { chestIndex = chestIndex, slot = slotKey, count = slot.count } setmetatable(entry, { __index = groupEntryPrototype }) group[#group + 1] = entry group.total = group.total + slot.count end end return groups end return stackgroup