Add a Defragment button to Milo #36
@@ -291,6 +291,89 @@ function Storage:listItems(throttle)
|
||||
return self.cache
|
||||
|
|
||||
end
|
||||
|
||||
-- provide a raw list of all the items in all storage chests
|
||||
-- it might be beneficial to move this to the adapter class at some point and cache the raw item list in this class at some point
|
||||
function Storage:listItemsRaw()
|
||||
local res = {}
|
||||
|
||||
for _, v in pairs(self.nodes) do
|
||||
if v.category == "storage" then
|
||||
local chest = device[v.name]
|
||||
local items = chest.list()
|
||||
|
Need to check if a chest is locked to specific items. There's 2 different ways to go here:
My vote is for #2 :) Need to check if a chest is locked to specific items. There's 2 different ways to go here:
1. the easy way - ignore locked chests.
2. process the locked chests first - only pulling items in that match the filter.
My vote is for #2 :)
Just so I get this right, when a chest is locked it would be enough to prioritise that chest when trying to find a target for the item? Just so I get this right, when a chest is locked it would be enough to prioritise that chest when trying to find a target for the item?
|
||||
|
||||
for slot, item in pairs(items) do
|
||||
items[slot] = itemDB:get(item, function() return chest.getItemMeta(slot) end)
|
||||
end
|
||||
|
||||
res[v.name] = items
|
||||
end
|
||||
end
|
||||
|
||||
return res
|
||||
end
|
||||
|
||||
-- provide a list of items and which chests provide them
|
||||
function Storage:listProviders()
|
||||
local res = {}
|
||||
|
||||
local rawItems = self:listItemsRaw()
|
||||
|
||||
for chest, items in pairs(rawItems) do
|
||||
for slot, item in pairs(items) do
|
||||
local key = table.concat({item.name, item.damage, item.nbtHash}, ":")
|
||||
if not res[key] then
|
||||
res[key] = {}
|
||||
end
|
||||
table.insert(res[key], {item = item, device = device[chest], slot = slot})
|
||||
end
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
-- defrags the storage system
|
||||
function Storage:defrag()
|
||||
local items = self:listProviders()
|
||||
|
||||
for _, providers in pairs(items) do
|
||||
table.sort(providers, function(a, b)
|
||||
return a.item.count < b.item.count
|
||||
end)
|
||||
|
||||
-- We're done when we either compressed the stacks so far, that there's only one left (#providers == 1)
|
||||
-- Or when we've compressed so far, that the there's only one stack which has a lower count than the maxCount
|
||||
-- Because of the sorting, we know that this will be the stack in providers[1], so we check if providers[2] is at the maxCount
|
||||
while #providers > 1 and providers[2].item.count ~= providers[2].item.maxCount do
|
||||
local from = providers[1]
|
||||
local to
|
||||
|
||||
-- We're pushing to the highest stack which is still below the maxCount, this way as many slots as possible will be filled
|
||||
-- This loop is guarenteed to assign a value to "to", as the only cases where it wouldn't (#providers == 1 or no provider with less than maxCount)
|
||||
-- are ruled out by the condition of the outer while loop
|
||||
for i = 2, #providers do
|
||||
if providers[i].item.count < providers[i].item.maxCount then
|
||||
to = providers[i]
|
||||
else
|
||||
-- As this slot is already at maxCount, all the remaining ones will also be due to sorting
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
local toMove = math.min(to.item.maxCount - to.item.count, from.item.count)
|
||||
from.device.pushItems(to.device.name, from.slot, toMove, to.slot)
|
||||
to.item.count = to.item.count + toMove
|
||||
from.item.count = from.item.count - toMove
|
||||
|
||||
if from.item.count <= 0 then
|
||||
table.remove(providers, 1)
|
||||
end
|
||||
|
||||
table.sort(providers, function(a, b)
|
||||
return a.item.count < b.item.count
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Storage:updateCache(adapter, item, count)
|
||||
if not adapter.cache then
|
||||
adapter.dirty = true
|
||||
|
||||
Reference in New Issue
Block a user
There might be cases where pushItems will generate an error (ie. if the user's ender chest is set to storage for some reason and they are offline). It would be best to wrap the pushItems in a pcall().