From f9ae197cfce9f18496bef7289d3b79ab0198e78b Mon Sep 17 00:00:00 2001 From: kepler155c Date: Fri, 26 Oct 2018 16:08:07 -0400 Subject: [PATCH] storage refactor - void option --- milo/Milo.lua | 89 ++-------- milo/apis/milo.lua | 10 +- milo/apis/networkedAdapter18.lua | 213 ----------------------- milo/apis/storage.lua | 273 ++++++++++++++++++++++++++++++ milo/plugins/craftTask.lua | 4 +- milo/plugins/exportTask.lua | 8 +- milo/plugins/importTask.lua | 12 +- milo/plugins/inputChestTask.lua | 8 +- milo/plugins/limitTask.lua | 8 +- milo/plugins/potionImportTask.lua | 6 +- milo/plugins/remote.lua | 8 +- 11 files changed, 321 insertions(+), 318 deletions(-) delete mode 100644 milo/apis/networkedAdapter18.lua create mode 100644 milo/apis/storage.lua diff --git a/milo/Milo.lua b/milo/Milo.lua index 88c06f9..c088274 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -54,25 +54,17 @@ ]]-- ---[[ -limit -organize -replenish -autocraft -]] - _G.requireInjector(_ENV) -local Config = require('config') -local Event = require('event') -local itemDB = require('itemDB') -local Milo = require('milo') -local NetworkAdapter = require('networkedAdapter18') -local Peripheral = require('peripheral') -local UI = require('ui') -local Util = require('util') +local Config = require('config') +local Event = require('event') +local itemDB = require('itemDB') +local Milo = require('milo') +local Peripheral = require('peripheral') +local Storage = require('storage') +local UI = require('ui') +local Util = require('util') -local device = _G.device local fs = _G.fs local multishell = _ENV.multishell local shell = _ENV.shell @@ -110,67 +102,18 @@ local context = { localName = modem.getNameLocal(), tasks = { }, craftingQueue = { }, + storage = Storage(config), } -_G._p = context--debug +_G._p = context --debug -local function initStorage(detachedDevice) - debug('Initializing storage') - local storage = { } - local storageOffline - - -- check to see if any of the storage chests are disconnected - for k,v in pairs(config.remoteDefaults) do - if v.mtype == 'storage' then - if not device[v.name] or v.name == detachedDevice then - storageOffline = true - else - storage[k] = v - end - end - end -debug(storage) - - if storageOffline then - Milo:pauseCrafting() - debug('Crafting paused') - Milo:showError('A storage chest has gone offline, ctrl-l to continue') - --- todo: just can't resume crafting - need to use offline flag instead --- in the case where crafting was paused already when storage went offline --- ie. in crafting process - elseif Milo:isCraftingPaused() then - debug('resuming') - Milo:resumeCrafting() - end ---TODO: cannot do this, must be able to add and mark inactive --- due to activity table --- add an networkAdapter:scan() - context.inventoryAdapter = NetworkAdapter({ remoteDefaults = storage }) - - if not context.inventoryAdapter then - error('Invalid inventory configuration') - end -end - -Event.on({ 'device_attach' }, function(_, dev) - --debug('attach: ' .. dev) - if config.remoteDefaults[dev] and - config.remoteDefaults[dev].mtype == 'storage' then - initStorage() - end +Event.on('storage_offline', function() + Milo:showError('A storage chest has gone offline, ctrl-l to continue') end) -Event.on({ 'device_detach' }, function(_, dev) - --debug('detach: ' .. dev) - if config.remoteDefaults[dev] and - config.remoteDefaults[dev].mtype == 'storage' then - initStorage(dev) - end -end) - -initStorage() Milo:init(context) +context.storage:initStorage() +context.storage:initTrashcan() local function loadDirectory(dir) for _, file in pairs(fs.list(dir)) do @@ -200,14 +143,14 @@ local page = UI:getPage('listing') UI:setPage(page) Event.onInterval(5, function() - if not Milo:isCraftingPaused() then + if not Milo:isCraftingPaused() and context.storage:isOnline() then Milo:resetCraftingStatus() Milo:refreshItems() for _, task in ipairs(context.tasks) do local s, m = pcall(function() task:cycle(context) end) if not s and m then - Util.print(task) + Util.print(task.name) error(m) end end diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index f4b8439..e139353 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -65,7 +65,7 @@ function Milo:uniqueKey(item) end function Milo:resetCraftingStatus() - self.context.inventoryAdapter.activity = { } + self.context.storage.activity = { } for _,key in pairs(Util.keys(self.context.craftingQueue)) do local item = self.context.craftingQueue[key] @@ -116,7 +116,7 @@ end function Milo:clearGrid() local function clear() turtle.eachFilledSlot(function(slot) - self.context.inventoryAdapter:insert(slot.index, slot.count, nil, slot) + self.context.storage:insert(slot.index, slot.count, nil, slot) end) for i = 1, 16 do @@ -131,7 +131,7 @@ end function Milo:eject(item, qty) local s, m = pcall(function() - self.context.inventoryAdapter:provide(item, qty) + self.context.storage:provide(item, qty) turtle.emptyInventory() end) if not s and m then @@ -194,12 +194,12 @@ end -- Return a list of everything in the system function Milo:listItems() - return self.context.inventoryAdapter:listItems() + return self.context.storage:listItems() end -- force a full rescan of chests function Milo:refreshItems() - return self.context.inventoryAdapter:refresh() + return self.context.storage:refresh() end return Milo diff --git a/milo/apis/networkedAdapter18.lua b/milo/apis/networkedAdapter18.lua deleted file mode 100644 index 5c81d90..0000000 --- a/milo/apis/networkedAdapter18.lua +++ /dev/null @@ -1,213 +0,0 @@ -local class = require('class') -local Util = require('util') -local InventoryAdapter = require('inventoryAdapter') -local Peripheral = require('peripheral') - -local NetworkedAdapter = class() - -function NetworkedAdapter:init(args) - local defaults = { - name = 'Networked Adapter', - remotes = { }, - remoteDefaults = { }, - dirty = true, -listCount = 0, - activity = { }, - } - Util.merge(self, defaults) - Util.merge(self, args) - - if not self.side or self.side == 'network' then - self.modem = Peripheral.get('wired_modem') - - if self.modem and self.modem.getNameLocal then - self.localName = self.modem.getNameLocal() - - for k in pairs(self.remoteDefaults) do - local remote = Peripheral.get({ name = k }) - if remote and remote.size and remote.list then - local adapter = InventoryAdapter.wrap({ side = k, direction = self.localName }) - if adapter then - table.insert(self.remotes, adapter) - end - end - end - end - - for _, remote in pairs(self.remotes) do - Util.merge(remote, self.remoteDefaults[remote.side]) - end - - table.sort(self.remotes, function(a, b) - if not a.priority then - return false - elseif not b.priority then - return true - end - return a.priority > b.priority - end) - end -end - -function NetworkedAdapter:isValid() - return #self.remotes > 0 -end - -function NetworkedAdapter:refresh(throttle) - self.dirty = true - return self:listItems(throttle) -end - --- provide a consolidated list of items -function NetworkedAdapter:listItems(throttle) - if not self.dirty then - return self.items - end -self.listCount = self.listCount + 1 ---debug(self.listCount) - - -- todo: only listItems from dirty remotes - - local cache = { } - local items = { } - throttle = throttle or Util.throttle() - - for _, remote in pairs(self.remotes) do - remote:listItems(throttle) - local rcache = remote.cache or { } - --- TODO: add a method in each adapter that only updates a passed cache - for key,v in pairs(rcache) do - if v.count > 0 then - local entry = cache[key] - if not entry then - entry = Util.shallowCopy(v) - entry.count = v.count - cache[key] = entry - table.insert(items, entry) - else - entry.count = entry.count + v.count - end - - throttle() - end - end - end - - self.dirty = false - self.cache = cache - self.items = items - return items -end - -function NetworkedAdapter:getItemInfo(item) - if not self.cache then - self:listItems() - end - local key = table.concat({ item.name, item.damage, item.nbtHash }, ':') - local items = self.cache or { } - return items[key] -end - -function NetworkedAdapter:provide(item, qty, slot, direction) - local total = 0 - - for _, remote in ipairs(self.remotes) do - local amount = remote:provide(item, qty, slot, direction) - if amount > 0 then ---debug('EXT: %s(%d): %s -> %s%s', --- item.name, amount, remote.side, direction or self.localName, --- slot and string.format('[%d]', slot) or '') - self.dirty = true - remote.dirty = true - end - qty = qty - amount - total = total + amount - if qty <= 0 then - break - end - end - - return total -end - -function NetworkedAdapter:extract(slot, qty, toSlot) - - error('extract not supported') - local total = 0 - for _, remote in pairs(self.remotes) do -debug('extract %d slot:%d', qty, slot) - local amount = remote:extract(slot, qty, toSlot) - qty = qty - amount - total = total + amount - if qty <= 0 then - break - end - end - - return total -end - -function NetworkedAdapter:insert(slot, qty, toSlot, item, source) - local total = 0 - - -- toSlot is not really valid with this adapter - if toSlot then - error('NetworkedAdapter: toSlot is not valid') - end - - local key = table.concat({ item.name, item.damage, item.nbtHash }, ':') - - if not self.cache then - self:listItems() - end - - local function insert(remote) - local amount = remote:insert(slot, qty, toSlot, source or self.direction) - if amount > 0 then ---debug('INS: %s(%d): %s[%d] -> %s', --- item.name, amount, --- source or self.localName, slot, remote.side) - self.dirty = true - remote.dirty = true - local entry = self.activity[key] or 0 - self.activity[key] = entry + amount - end - qty = qty - amount - total = total + amount - end - - -- found a chest locked with this item - for _, remote in pairs(self.remotes) do - if remote.lockWith == key or remote.lockWith == item.name then - insert(remote) - return total - end - end - - if self.cache[key] then -- is this item in some chest - -- low to high priority if the chest already contains that item - for _, remote in Util.rpairs(self.remotes) do - if qty <= 0 then - break - end - if remote.cache and remote.cache[key] and not remote.lockWith then - insert(remote) - end - end - end - - -- high to low priority - for _, remote in ipairs(self.remotes) do - if qty <= 0 then - break - end - if not remote.lockWith then - insert(remote) - end - end - - return total -end - -return NetworkedAdapter diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua new file mode 100644 index 0000000..66f45d9 --- /dev/null +++ b/milo/apis/storage.lua @@ -0,0 +1,273 @@ +local class = require('class') +local Event = require('event') +local InventoryAdapter = require('inventoryAdapter') +local Peripheral = require('peripheral') +local Util = require('util') + +local device = _G.device +local os = _G.os + +local NetworkedAdapter = class() + +function NetworkedAdapter:init(args) + local defaults = { + remotes = { }, + remoteDefaults = { }, + dirty = true, +listCount = 0, + activity = { }, + storageOnline = true, + } + Util.merge(self, defaults) + Util.merge(self, args) + + local modem = Peripheral.get('wired_modem') or error('Wired modem not attached') + self.localName = modem.getNameLocal() + + Event.on({ 'device_attach' }, function(_, dev) + --debug('attach: ' .. dev) + if self.remoteDefaults[dev] then + if self.remoteDefaults[dev].mtype == 'storage' then + self:initStorage() + end + if self.remoteDefaults[dev].mtype == 'trashcan' then + self:initTrashcan() + end + end + end) + + Event.on({ 'device_detach' }, function(_, dev) + --debug('detach: ' .. dev) + if self.remoteDefaults[dev] then + if self.remoteDefaults[dev].mtype == 'storage' then + self:initStorage(dev) + end + if self.remoteDefaults[dev].mtype == 'trashcan' then + self:initTrashcan(dev) + end + end + end) +end + +function NetworkedAdapter:setOnline(online) + if online ~= self.storageOnline then + self.storageOnline = online + os.queueEvent(self.storageOnline and 'storage_online' or 'storage_offline') + debug('Storage: %s', self.storageOnline and 'online' or 'offline') + end +end + +function NetworkedAdapter:isOnline() + return self.storageOnline +end + +function NetworkedAdapter:initTrashcan(detachedDevice) + local trashcan = Util.find(self.remoteDefaults, 'mtype', 'trashcan') + + if (detachedDevice and self.trashcan and self.trashcan.name == detachedDevice) or + (trashcan and not device[trashcan.name]) then + self.trashcan = nil +debug('Trashcan: none') + + elseif trashcan and device[trashcan.name] then + if not self.trashcan or (self.trashcan and self.trashcan.name ~= trashcan.name) then +debug('Trashcan: ' .. trashcan.name) + self.trashcan = device[trashcan.name] + end + end +end + +function NetworkedAdapter:initStorage(detachedDevice) + local storage = { } + local online = true + + -- check to see if any of the storage chests are disconnected + for k,v in pairs(self.remoteDefaults) do + if v.mtype == 'storage' then + if not device[v.name] or v.name == detachedDevice then + online = false + else + storage[k] = v + end + end + end + + debug('Initializing storage') + debug(storage) + + self.remotes = { } + for k in pairs(storage) do + local remote = Peripheral.get({ name = k }) + if remote and remote.size and remote.list then + local adapter = InventoryAdapter.wrap({ side = k, direction = self.localName }) + if adapter then + table.insert(self.remotes, adapter) + Util.merge(remote, self.remoteDefaults[remote.side]) + end + end + end + + table.sort(self.remotes, function(a, b) + if not a.priority then + return false + elseif not b.priority then + return true + end + return a.priority > b.priority + end) + + self:setOnline(online) +end + +function NetworkedAdapter:refresh(throttle) + self.dirty = true + return self:listItems(throttle) +end + +-- provide a consolidated list of items +function NetworkedAdapter:listItems(throttle) + if not self.dirty then + return self.items + end +self.listCount = self.listCount + 1 +--debug(self.listCount) + + -- todo: only listItems from dirty remotes + + local cache = { } + local items = { } + throttle = throttle or Util.throttle() + + for _, remote in pairs(self.remotes) do + remote:listItems(throttle) + local rcache = remote.cache or { } + +-- TODO: add a method in each adapter that only updates a passed cache + for key,v in pairs(rcache) do + if v.count > 0 then + local entry = cache[key] + if not entry then + entry = Util.shallowCopy(v) + entry.count = v.count + cache[key] = entry + table.insert(items, entry) + else + entry.count = entry.count + v.count + end + + throttle() + end + end + end + + self.dirty = false + self.cache = cache + self.items = items + return items +end + +function NetworkedAdapter:export(target, slot, count, item) + return self:provide(item, count, slot, target) +end + +function NetworkedAdapter:provide(item, qty, slot, direction) + local total = 0 + + for _, remote in ipairs(self.remotes) do + local amount = remote:provide(item, qty, slot, direction) + if amount > 0 then +--debug('EXT: %s(%d): %s -> %s%s', +-- item.name, amount, remote.side, direction or self.localName, +-- slot and string.format('[%d]', slot) or '') + self.dirty = true + remote.dirty = true + end + qty = qty - amount + total = total + amount + if qty <= 0 then + break + end + end + + return total +end + +function NetworkedAdapter:trash(source, slot, count) + if not self.trashcan then + return + end +debug('TRA: %s[%d] (%d)', source, slot, count) + return self.trashcan.pullItems(source, slot, count) +end + +function NetworkedAdapter:import(source, slot, count, item) + return self:insert(slot, count, nil, item, source) +end + +function NetworkedAdapter:insert(slot, qty, toSlot, item, source) + local total = 0 + + -- toSlot is not really valid with this adapter + if toSlot then + error('NetworkedAdapter: toSlot is not valid') + end + + local key = table.concat({ item.name, item.damage, item.nbtHash }, ':') + + if not self.cache then + self:listItems() + end + + local function insert(remote) + local amount = remote:insert(slot, qty, toSlot, source or self.localName) + if amount > 0 then +debug('INS: %s(%d): %s[%d] -> %s', + item.name, amount, + source or self.localName, slot, remote.side) + self.dirty = true + remote.dirty = true + local entry = self.activity[key] or 0 + self.activity[key] = entry + amount + end + qty = qty - amount + total = total + amount + end + + -- found a chest locked with this item + for _, remote in pairs(self.remotes) do + -- TODO: proper checking using ignore dmg/nbt + if remote.lock == key or remote.lock == item.name then + insert(remote) + if qty > 0 then -- TODO: only if void flag set + total = total + self:trash(source, slot, qty) + end + return total + end + end + + if self.cache[key] then -- is this item in some chest + -- low to high priority if the chest already contains that item + for _, remote in Util.rpairs(self.remotes) do + if qty <= 0 then + break + end + if remote.cache and remote.cache[key] and not remote.lockWith then + insert(remote) + end + end + end + + -- high to low priority + for _, remote in ipairs(self.remotes) do + if qty <= 0 then + break + end + if not remote.lockWith then + insert(remote) + end + end + + return total +end + +return NetworkedAdapter diff --git a/milo/plugins/craftTask.lua b/milo/plugins/craftTask.lua index 0ff8a84..d360810 100644 --- a/milo/plugins/craftTask.lua +++ b/milo/plugins/craftTask.lua @@ -11,7 +11,7 @@ local craftTask = { } function craftTask:craftItem(recipe, item, count) - Craft.craftRecipe(recipe, count, context.inventoryAdapter, item) + Craft.craftRecipe(recipe, count, context.storage, item) Milo:clearGrid() end @@ -73,7 +73,7 @@ function craftTask:forceCraftItem(inRecipe, originalItem, inCount) if craftable > 0 then craftable = Craft.craftRecipe(recipe, craftable * recipe.count, - context.inventoryAdapter, originalItem) / recipe.count + context.storage, originalItem) / recipe.count Milo:clearGrid() end diff --git a/milo/plugins/exportTask.lua b/milo/plugins/exportTask.lua index a655ad6..b8314e6 100644 --- a/milo/plugins/exportTask.lua +++ b/milo/plugins/exportTask.lua @@ -25,11 +25,11 @@ function ExportTask:cycle(context) if count > 0 then item = Milo:getItemWithQty(item) if item and count > 0 then - context.inventoryAdapter:provide( - item, - math.min(count, item.count), + context.storage:export( + target, entry.slot, - target) + math.min(count, item.count), + item) end end end diff --git a/milo/plugins/importTask.lua b/milo/plugins/importTask.lua index 0bcccf2..6020a7e 100644 --- a/milo/plugins/importTask.lua +++ b/milo/plugins/importTask.lua @@ -10,12 +10,12 @@ local ImportTask = { function ImportTask:cycle(context) for source, v in pairs(context.config.remoteDefaults) do if v.imports then - local machine = device[source] - if machine and machine.getItemMeta then - for slotNo in pairs(v.imports) do - local slot = machine.getItemMeta(slotNo) - if slot then - context.inventoryAdapter:insert(slotNo, slot.count, nil, slot, source) + local inventory = device[source] + if inventory and inventory.getItemMeta then + for slot in pairs(v.imports) do + local item = inventory.getItemMeta(slot) + if item then + context.storage:import(source, slot, item.count, item) end end else diff --git a/milo/plugins/inputChestTask.lua b/milo/plugins/inputChestTask.lua index 529f6f9..ae8de01 100644 --- a/milo/plugins/inputChestTask.lua +++ b/milo/plugins/inputChestTask.lua @@ -8,14 +8,14 @@ local InputChest = { } function InputChest:cycle(context) - for name,v in pairs(context.config.remoteDefaults) do + for source,v in pairs(context.config.remoteDefaults) do if v.mtype == 'input' then - local inventory = device[name] + local inventory = device[source] local list = inventory and inventory.list and inventory.list() if list then - for slotNo, slot in pairs(list) do - context.inventoryAdapter:insert(slotNo, slot.count, nil, slot, name) + for slot, item in pairs(list) do + context.storage:import(source, slot, item.count, item) end end end diff --git a/milo/plugins/limitTask.lua b/milo/plugins/limitTask.lua index f86c317..ba639ce 100644 --- a/milo/plugins/limitTask.lua +++ b/milo/plugins/limitTask.lua @@ -23,11 +23,11 @@ function LimitTask:cycle(context) if res.limit then local item = Milo:getItemWithQty(res, res.ignoreDamage, res.ignoreNbtHash) if item and item.count > res.limit then - context.inventoryAdapter:provide( - { name = item.name, damage = item.damage, nbtHash = item.nbtHash }, - item.count - res.limit, + context.storage:export( + trashcan, nil, - trashcan) + item.count - res.limit, + item) end end end diff --git a/milo/plugins/potionImportTask.lua b/milo/plugins/potionImportTask.lua index ba42cae..e3fd9f7 100644 --- a/milo/plugins/potionImportTask.lua +++ b/milo/plugins/potionImportTask.lua @@ -12,9 +12,9 @@ function PotionImportTask:cycle(context) if v.type == 'minecraft:brewing_stand' and v.getBrewTime() == 0 then local list = v.list() if not list[4] then - for i = 1, 3 do - if list[i] then - context.inventoryAdapter:insert(i, 1, nil, list[i], v.name) + for slot = 1, 3 do + if list[slot] then + context.storage:import(v.name, slot, 1, list[slot]) end end end diff --git a/milo/plugins/remote.lua b/milo/plugins/remote.lua index b517dcc..28d00e2 100644 --- a/milo/plugins/remote.lua +++ b/milo/plugins/remote.lua @@ -47,11 +47,11 @@ local function client(socket) Milo:clearGrid() elseif data.request == 'transfer' then - local count = context.inventoryAdapter:provide( - data.item, - data.count, + local count = context.storage:export( + context.localName, nil, - context.localName) + data.count, + data.item) turtle.eachFilledSlot(function(slot) manipulator.getInventory().pullItems(