From 3dabc5582588217127c2e961fb8f0e44190da8ea Mon Sep 17 00:00:00 2001 From: Gabriel Tofvesson Date: Thu, 15 May 2025 17:14:39 +0200 Subject: [PATCH] Implement storage slot defragmentation --- item.lua | 1 - itemstack.lua | 1 - localchest.lua | 5 ++- stackgroup.lua | 117 +++++++++++++++++++++++++++++++++++++++++++++++++ util.lua | 9 ++++ 5 files changed, 130 insertions(+), 3 deletions(-) create mode 100644 stackgroup.lua diff --git a/item.lua b/item.lua index dec3830..4362b6e 100644 --- a/item.lua +++ b/item.lua @@ -4,7 +4,6 @@ local item = {} local metatable = { __eq = util.deepEqual, - __metatable = nil, __tostring = function(ser) return textutils.serialise(ser, { compact = true }) end diff --git a/itemstack.lua b/itemstack.lua index e18c716..28bd783 100644 --- a/itemstack.lua +++ b/itemstack.lua @@ -30,7 +30,6 @@ local metatable = { a.count == b.count and a.itemLimit == b.itemLimit end, - __metatable = nil, __index = prototype, __tostring = function(ser) return textutils.serialise({ diff --git a/localchest.lua b/localchest.lua index 397c928..bde5cf0 100644 --- a/localchest.lua +++ b/localchest.lua @@ -87,9 +87,12 @@ function prototype:moveItemsTo(count, fromSlot, otherChest, toSlot) return tx end +function prototype:getIdentifier() + return peripheral.getName(self.peripheral) +end + local metatable = { __index = prototype, - __metatable = nil, __tostring = function(ser) local slots = {} for key,value in pairs(ser.slots) do diff --git a/stackgroup.lua b/stackgroup.lua new file mode 100644 index 0000000..ff3380b --- /dev/null +++ b/stackgroup.lua @@ -0,0 +1,117 @@ +local util = require("util") + +local stackgroup = {} +local prototype = {} +local metatable = {} + +function prototype:defragment(chests) + table.sort(self, function(a, b) return a.count < b.count end) + + local resultGroup = { + item = self.item, + total = self.total + } + + setmetatable(resultGroup, metatable) + + local top = #self + local bottom = 1 + + -- Find largest non-full stack + for i=top,bottom,-1 do + local entry = self[i] + if entry:getStack(chests).availableCount() ~= 0 then + top = i + break + else + resultGroup[#resultGroup + 1] = entry + end + end + + while top > bottom do + local topEntry = self[top] + local bottomEntry = self[bottom] + + 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 + bottom = bottom + 1 + end + + if topEntry:getStack(chests).availableCount() == 0 then + top = top - 1 + resultGroup[#resultGroup + 1] = topEntry + end + end + + if top == bottom and self[top].count > 0 then + resultGroup[#resultGroup+1] = self[top] + end + + return resultGroup +end + +metatable.__index = prototype + +local groupEntryPrototype = {} + +function groupEntryPrototype:getStack(chests) + return self:getChest(chests).slots[self.slot] +end + +function groupEntryPrototype:getChest(chests) + return chests[self.chestIndex] +end + +local groupEntryMetatable = { + __index = groupEntryPrototype +} + +function stackgroup.fromChests(...) + local chests = { ... } + local groups = {} + local outGroups = {} + + 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, metatable) + + groups[#groups + 1] = group + end + + local entry = { + chestIndex = chestIndex, + slot = slotKey, + count = slot.count + } + setmetatable(entry, groupEntryMetatable) + + group[#group + 1] = entry + group.total = group.total + slot.count + end + end + + return outGroups +end + +return stackgroup \ No newline at end of file diff --git a/util.lua b/util.lua index ce7c0f2..df6d7f1 100644 --- a/util.lua +++ b/util.lua @@ -25,4 +25,13 @@ function util.deepEqual(a, b) return a == b end +function util.find(array, f) + for _,v in pairs(array) do + if f(v) then + return v + end + end + return nil +end + return util \ No newline at end of file