From 755c37bc9bd80de2b2acfa502cff67728470c60d Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sun, 14 Oct 2018 15:37:54 -0400 Subject: [PATCH 001/165] new icon set + scanning miner --- apps/farm.lua | 44 ++- apps/scanningMiner.lua | 596 +++++++++++++++++++++++++++++++++++++++++ etc/apps/opus-apps.db | 52 +++- 3 files changed, 670 insertions(+), 22 deletions(-) create mode 100644 apps/scanningMiner.lua diff --git a/apps/farm.lua b/apps/farm.lua index 088c412..a3b37da 100644 --- a/apps/farm.lua +++ b/apps/farm.lua @@ -1,15 +1,29 @@ _G.requireInjector(_ENV) local Point = require('point') -local Util = require('util') +local Util = require('util') -local scanner = device['plethora:scanner'] +local scanner = device['plethora:scanner'] or error('Plethora scanner required') + +local crops = { + ['minecraft:wheat'] = + { seed = 'minecraft:wheat_seeds', mature = 7 }, + ['minecraft:carrots'] = + { seed = 'minecraft:carrot', mature = 7 }, + ['minecraft:potatoes'] = + { seed = 'minecraft:potato', mature = 7 }, + ['minecraft:beetroots'] = + { seed = 'minecraft:beetroot_seeds', mature = 3 }, +} local function scan() local blocks = scanner.scan() Util.filterInplace(blocks, function(v) - return v.name == 'minecraft:wheat' and - scanner.getBlockMeta(v.x, v.y, v.z).metadata == 7 + if v.name == 'minecraft:reeds' then + return v.y == 0 + end + return crops[v.name] and + scanner.getBlockMeta(v.x, v.y, v.z).metadata == crops[v.name].mature end) return blocks @@ -18,21 +32,31 @@ end local function harvest(blocks) Point.eachClosest(turtle.point, blocks, function(b) Util.print(b) - turtle.goto(Point.above(b)) - turtle.digDown() - turtle.placeDown('minecraft:wheat_seeds') + if b.name == 'minecraft:reeds' then + turtle.goto(b) + else + turtle.goto(Point.above(b)) + turtle.digDown() + turtle.placeDown(crops[b.name].seed) + turtle.select(1) + end end) end turtle.reset() -local directions = { [5] = 2, [3] = 3, [4] = 0, [2] = 1, } -turtle.placeUp('minecraft:chest') +local directions = { [5] = 2, [3] = 3, [4] = 0, [2] = 1, } +turtle.placeUp('minecraft:chest') or error('Missing chest') local _, bi = turtle.inspectUp() turtle.digUp() turtle.point.heading = directions[bi.metadata] +turtle.setPolicy('digOnly') + while true do + print('scanning') local blocks = scan() + turtle.setPoint({ x = 0, y = 0, z = 0, heading = turtle.point.heading }) harvest(blocks) + print('sleeping') os.sleep(10) -end \ No newline at end of file +end diff --git a/apps/scanningMiner.lua b/apps/scanningMiner.lua new file mode 100644 index 0000000..b72d02f --- /dev/null +++ b/apps/scanningMiner.lua @@ -0,0 +1,596 @@ +--[[ + Efficient miner + + GPS is required. + + Miner Requires: + Diamond pick + Ender Modem + Plethora scanner + Bucket +--]] +_G.requireInjector(_ENV) + +local Event = require('event') +local GPS = require('gps') +local Point = require('point') +local UI = require('ui') +local Util = require('util') + +local colors = _G.colors +local fs = _G.fs +local os = _G.os +local peripheral = _G.peripheral +local read = _G.read +local turtle = _G.turtle + +local args = { ... } +local options = { + chunks = { arg = 'c', type = 'number', value = -1, + desc = 'Number of chunks to mine' }, + setTrash = { arg = 's', type = 'flag', value = false, + desc = 'Set trash items' }, + help = { arg = 'h', type = 'flag', value = false, + desc = 'Displays the options' }, +} + +local MIN_FUEL = 7500 +local LOW_FUEL = 1500 +local MAX_FUEL = turtle.getFuelLimit() + +local DICTIONARY_FILE = 'usr/config/mining.dictionary' +local PROGRESS_FILE = 'usr/config/scanning_mining.progress' + +local mining +local ignores = { + ignore = true, + retain = true, +} + +local dictionary = { + data = Util.readTable(DICTIONARY_FILE) or { + [ 'minecraft:chest' ] = 'suck', + [ 'minecraft:lava' ] = 'liquid_fuel', + [ 'minecraft:flowing_lava' ] = 'liquid_fuel', + [ 'minecraft:bedrock' ] = 'ignore', + [ 'minecraft:flowing_water' ] = 'ignore', + [ 'minecraft:water' ] = 'ignore', + [ 'minecraft:air' ] = 'ignore', + [ 'minecraft:bucket' ] = 'retain', + [ 'computercraft:advanced_modem' ] = 'retain', + [ 'minecraft:diamond_pickaxe' ] = 'retain', + [ 'plethora:module' ] = 'retain', + }, +} + +function dictionary:write() + Util.writeTable(DICTIONARY_FILE, self.data) +end +function dictionary:mineable(name, damage) + self.data[name .. ':' .. damage] = nil +end +function dictionary:ignore(name, damage) + if damage then + self.data[name .. ':' .. damage] = 'ignore' + else + self.data[name] = 'ignore' + end +end +function dictionary:get(name, damage) + return self.data[name] or self.data[name .. ':' .. damage] +end +function dictionary:isTrash(name, damage) + return self:get(name, damage) == 'ignore' +end + +local page = UI.Page { + menuBar = UI.MenuBar { + buttons = { + --{ text = 'Mine', event = 'mine' }, + { text = 'Ignore', event = 'ignore' }, + { text = 'Ignore All', event = 'ignore_all' }, + }, + }, + grid = UI.Grid { + y = 2, ey = -2, + sortColumn = 'name', + columns = { + { heading = 'Resource', key = 'displayName' }, + { heading = 'Count', key = 'count', width = 6 }, + }, + }, + statusBar = UI.StatusBar { + columns = { + { key = 'status' }, + { key = 'fuel', width = 6 }, + }, + }, + accelerators = { + q = 'cancel', + } +} + +function page:eventHandler(event) + local t = self.grid:getSelected() + if t then + if event.type == 'mine' then + dictionary:mineable(t.name, t.damage) + dictionary:write() + + elseif event.type == 'ignore' then + dictionary:ignore(t.name, t.damage) + dictionary:write() + self.grid:draw() + + elseif event.type == 'ignore_all' then + dictionary:ignore(t.name) + dictionary:write() + self.grid:draw() + end + end + if event.type == 'quit' then + turtle.abort(true) + end + UI.Page.eventHandler(self, event) +end + +function page.grid:getRowTextColor(row, selected) + if dictionary:get(row.name, row.damage) == 'ignore' then + return colors.lightGray + end + if row.displayName == self.nextBlock then + return colors.yellow + end + return UI.Grid.getRowTextColor(self, row, selected) +end + +local function getChunkCoordinates(diameter, index, x, z) + local dirs = { -- circumference of grid + { xd = 0, zd = 1, heading = 1 }, -- south + { xd = -1, zd = 0, heading = 2 }, + { xd = 0, zd = -1, heading = 3 }, + { xd = 1, zd = 0, heading = 0 } -- east + } + -- always move east when entering the next diameter + if index == 0 then + dirs[4].x = x + 16 + dirs[4].z = z + return dirs[4] + end + local dir = dirs[math.floor(index / (diameter - 1)) + 1] + dir.x = x + dir.xd * 16 + dir.z = z + dir.zd * 16 + return dir +end + +local function getCornerOf(c) + return math.floor(c.x / 16) * 16, math.floor(c.z / 16) * 16 +end + +local function nextChunk() + local x, z = getCornerOf({ x = mining.x, z = mining.z }) + local points = math.pow(mining.diameter, 2) - math.pow(mining.diameter-2, 2) + mining.chunkIndex = mining.chunkIndex + 1 + + if mining.chunkIndex >= points then + mining.diameter = mining.diameter + 2 + mining.chunkIndex = 0 + end + + if mining.chunks ~= -1 then + local chunks = math.pow(mining.diameter-2, 2) + mining.chunkIndex + if chunks >= mining.chunks then + return false + end + end + + local nc = getChunkCoordinates(mining.diameter, mining.chunkIndex, x, z) + + -- enter next chunk + mining.x = nc.x + mining.z = nc.z + + Util.writeTable(PROGRESS_FILE, mining) + + return true +end + +local function status(newStatus) + turtle.setStatus(newStatus) + page.statusBar:setValue('status', newStatus) + page.statusBar:draw() + page:sync() +end + +local function refuel() + if turtle.getFuelLevel() < MIN_FUEL then + local oldStatus = turtle.getStatus() + status('refueling') + + turtle.refuel('minecraft:coal:0', 32) + if turtle.getFuelLevel() < MIN_FUEL then + turtle.eachFilledSlot(function(slot) + if turtle.getFuelLevel() < MIN_FUEL then + turtle.select(slot.index) + turtle.refuel(64) + end + end) + end + status(oldStatus) + end + + turtle.select(1) +end + +local function safeGoto(x, z, y, h) + local oldStatus = turtle.getStatus() + + while not turtle._goto({ x = x, z = z, y = y or turtle.point.y, heading = h }) do + --status('stuck') + if turtle.isAborted() then + return false + end + os.sleep(3) + end + turtle.setStatus(oldStatus) + return true +end + +local function safeGotoY(y) + local oldStatus = turtle.getStatus() + while not turtle.gotoY(y) do + status('stuck') + if turtle.isAborted() then + return false + end + os.sleep(1) + end + turtle.setStatus(oldStatus) + return true +end + +local function unload() + local oldStatus = turtle.getStatus() + status('unloading') + local pt = Util.shallowCopy(turtle.point) + safeGotoY(0) + + safeGoto(0, 0, 0) + if not turtle.detectUp() then + error('no chest') + end + local slots = turtle.getFilledSlots() + for _,slot in pairs(slots) do + local action = dictionary:get(slot.name, slot.damage) + if not ignores[action] then + turtle.select(slot.index) + turtle.dropUp(64) + end + end + turtle.condense() + turtle.select(1) + safeGoto(pt.x, pt.z, 0, pt.heading) + + safeGotoY(pt.y) + status(oldStatus) +end + +local function ejectTrash() + turtle.eachFilledSlot(function(slot) + if dictionary:isTrash(slot.name, slot.damage) then + turtle.select(slot.index) + turtle.dropDown(64) + end + end) +end + +local function checkSpace() + if turtle.getItemCount(15) > 0 then + refuel() + local oldStatus = turtle.getStatus() + status('condensing') + ejectTrash() + turtle.condense() + if turtle.getItemCount(15) > 0 then + unload() + end + status(oldStatus) + turtle.select(1) + end +end + +local function collectDrops(suckAction) + for _ = 1, 50 do + checkSpace() + if not suckAction() then + break + end + end +end + +local function equip(side, item) + if not turtle.equip(side, item) then + turtle.selectSlotWithQuantity(0) + turtle.equip(side) + if not turtle.equip(side, item) then + error('Unable to equip ' .. item) + end + end +end + +local function scan() + equip('left', 'plethora:module') + local blocks = peripheral.call('left', 'scan') + equip('left', 'minecraft:diamond_pickaxe') + + local bedrock = -256 + local counts = { } + + for _, b in pairs(blocks) do + if b.x == 0 and b.y == 0 and b.z == 0 then + b.name = 'minecraft:air' + end + b.x = b.x + turtle.point.x + b.y = b.y + turtle.point.y + b.z = b.z + turtle.point.z + + if b.name == 'minecraft:bedrock' then + if b.y > bedrock then + bedrock = b.y + end + end + end + + Util.filterInplace(blocks, function(b) + if b.y >= 0 or + (b.action == 'liquid_fuel' and b.y <= bedrock) then + return false + + elseif b.y >= bedrock then + b.action = dictionary:get(b.name, b.metadata) or 'mine' + + if ignores[b.action] then + return false + end + + local key = b.name .. ':' .. b.metadata + if not counts[key] then + counts[key] = { + displayName = key, + name = b.name, + damage = b.metadata, + count = 1 + } + else + counts[key].count = counts[key].count + 1 + end + return true + end + end) + + turtle.select(1) + + local dirty = true + + local function display() + if dirty then + page.grid:draw() + page:sync() + end + dirty = false + end + + page.grid:setValues(counts) + page.grid:draw() + display() + + status('mining') + + local i = #blocks + Point.eachClosest(turtle.point, blocks, function(b) + if turtle.isAborted() then + return + end + + page.grid.nextBlock = b.displayName + + -- Get the action again in case the user has ignored via UI + b.action = dictionary:get(b.name, b.metadata) or 'mine' + if b.action == 'suck' or b.action == 'mine' then + if b.action == 'suck' then + local pt = turtle.moveAgainst(b) + collectDrops(turtle.getAction(pt.direction).suck) + end + checkSpace() + local s, m + if b.y == bedrock then + s, m = turtle.digDownAt(b) + else + s, m = turtle.digAt(b) + end + if not s then + page.statusBar:setValue('status', m) + page.statusBar:draw() + page:sync() + os.sleep(5) + end + dirty = true + elseif b.action == 'liquid_fuel' then + if turtle.getFuelLevel() < (MAX_FUEL - 1000) then + if turtle.placeAt(b, 'minecraft:bucket:0') then + turtle.refuel() + turtle.select(1) + dirty = true + end + end + end + local key = b.name .. ':' .. b.metadata + counts[key].count = counts[key].count - 1 + i = i - 1 + display() + end) +end + +local function mineChunk() + local topDown = turtle.point.y > -mining.home.y / 2 + local y = topDown and -8 or -mining.home.y + 8 + local inc = topDown and + function() y = y - 16 end or + function() y = y + 16 end + + while true do + status('scanning') + turtle.select(1) + turtle._goto({ x = mining.x + 8, z = mining.z + 8, y = y }) + scan() + if turtle.isAborted() then + status('aborting') + return false + end + if turtle.getFuelLevel() < LOW_FUEL then + refuel() + local veryMinFuel = Point.turtleDistance(turtle.point, { x = 0, y = 0, z = 0}) + 512 + if turtle.getFuelLevel() < veryMinFuel then + page.statusBar:setValue('Not enough fuel to continue') + page.statusBar:draw() + page:sync() + return false + end + end + inc() + + if y > 0 or y < -mining.home.y then + break + end + end + + return true +end + +local function addTrash() + local slots = turtle.getFilledSlots() + + for _,slot in pairs(slots) do + local e = dictionary:get(slot.name, slot.damage) + if not e or e ~= 'retain' then + dictionary:ignore(slot.name, slot.damage) + end + end + + dictionary:write() +end + +-- Startup logic +if not Util.getOptions(options, args) then + return +end + +-- in plethora code, we can override initialize with a scanner version +turtle.initialize = function() + if turtle.isEquipped('modem') ~= 'right' then + equip('right', 'computercraft:advanced_modem') + end + + equip('left', 'minecraft:diamond_pickaxe') + + local function verify(item) + if not turtle.has(item) then + error('Missing: ' .. item) + end + end + + local items = { 'minecraft:bucket', 'plethora:module' } + for _,v in pairs(items) do + verify(v) + end + + --os.sleep(5) + local pt = GPS.getPoint(2) or error('GPS not found') + equip('left', 'plethora:module') + local facing = peripheral.call('left', 'getBlockMeta', 0, 0, 0).state.facing + pt.heading = Point.facings[facing].heading + turtle.setPoint(pt, true) + equip('left', 'minecraft:diamond_pickaxe') +end + +local function main() + repeat + if not mineChunk() then + return + end + Util.writeTable(PROGRESS_FILE, mining) + until not nextChunk() +end + +Event.addRoutine(function() + turtle.run(function() + turtle.reset() + + if not fs.exists(DICTIONARY_FILE) or options.setTrash.value then + print('Add blocks to ignore, press enter when ready') + read() + addTrash() + end + + ejectTrash() + + turtle.initialize { + right = 'computercraft:advanced_modem', + left = 'minecraft:diamond_pickaxe', + required = { + 'minecraft:bucket', + 'plethora:module', + }, + GPS = true, + minFuel = 100, + -- searchFor = 'ironchest:iron_shulker_box_white' + } + + turtle.setMoveCallback(function() + page.statusBar:setValue('fuel', Util.toBytes(turtle.getFuelLevel())) + page.statusBar:draw() + page:sync() + end) + + mining = Util.readTable(PROGRESS_FILE) or { + diameter = 1, + chunkIndex = 0, + x = 0, z = 0, + chunks = options.chunks.value, + home = Point.copy(turtle.point), + heading = turtle.point.heading, -- always using east for now + } + + -- use coordinates relative to initial starting point + turtle.setPoint({ + x = turtle.point.x - mining.home.x, + y = turtle.point.y - mining.home.y, + z = turtle.point.z - mining.home.z, + }) + + if not fs.exists(PROGRESS_FILE) then + Util.writeTable(PROGRESS_FILE, mining) + end + + turtle.setPolicy(turtle.policies.digAttack) + turtle.setDigPolicy(turtle.digPolicies.turtleSafe) + turtle.setMovementStrategy('goto') + status('mining') + + local s, m = pcall(main) + + status(s and 'finished' or 'error') + turtle._goto({ x = 0, y = 0, z = 0 }) + turtle.reset() + + if not s and m then + _G.printError(m) + end + + Event.exitPullEvents() + end) +end) + +UI:setPage(page) +UI:pullEvents() +UI.term:reset() diff --git a/etc/apps/opus-apps.db b/etc/apps/opus-apps.db index ee5ea7d..8f0ccc4 100644 --- a/etc/apps/opus-apps.db +++ b/etc/apps/opus-apps.db @@ -5,14 +5,20 @@ icon = " \0305 \030c \0305 \030 \ \030d \030c \0305 \030c \0308 \030d\031f\"\ \0308\031f.\030 \031 \0308\031f.\030 \031 ", + iconExt = "\030 \031f\030f\031f\128\0305\135\030c\031c\128\128\0305\031f\139\0307\149\0308\0317\143\0307\128\ +\030 \031f\030c\031f\145\031c\128\030d\132\136\030c\128\0307\149\0318\143\133\ +\030 \031f\030f\031f\128\0317\143\031f\128\128\0317\143\031f\128\128\128", run = "Turtles.lua", }, [ "381e3298b2b8f6caeb2208b57d805ada38402f0b" ] = { + title = "Scripts", category = "Apps", icon = "\0300\0317if\031 \0307 \ \0300\0317turt\ \0300\0317retu", - title = "Scripts", + iconExt = "\0300\0317if\140\140\140\ +\0300\0317\140\031fthen\ +\0300\0317else\140", run = "Script.lua", }, [ "7ef35cac539f84722b0a988caee03b2df734c56a" ] = { @@ -21,6 +27,9 @@ icon = "\030 \0310=\0300 \030 XX\0300\031f \030 \ \030 \031f \0300 \030 \ \030 \031f \0310o \031f \0310o\031f ", + iconExt = "\031e\139\0318\151\151\151\151\151\149\ +\030 \031 \0308\031f\136\136\136\136\030f\0318\135\ +\030 \031 \030f\0317\130\030 \031 \030f\0317\130", run = "Appstore.lua", }, [ "4486006f811b88cacd5f211fd579717e29b600cd" ] = { @@ -67,16 +76,15 @@ \031e\\/\031 \0319c", run = "vnc.lua", }, - [ "8d59207c8a84153b3e9f035cc3b6ec7a23671323" ] = { - title = "Micropaint", - category = "Apps", - icon = "\030 \031f \030f \ -\030 \031f^ \0300 \030f \ -\030 \031fv \0300 \030 ", - run = "http://pastebin.com/raw/tMRzJXx2", - requires = "advancedComputer", - }, --]] + [ "a0365977708b7387ee9ce2c13e5820e6e11732cb" ] = { + title = "Pain", + category = "Apps", + icon = "\030 \031f\0307\031f\159\030 \159\030 \ +\030 \031f\0308\031f\135\0307\0318\144\140\030f\0317\159\143\031c\139\0302\135\030f\0312\157\ +\030 \031f\030f\0318\143\133\0312\136\0302\031f\159\159\143\131\030f\0312\132", + run = "http://pastebin.com/raw/wJQ7jav0", + }, [ "9e092dda4f0e27d0c7686ddd00272079e678b6e6" ] = { title = "Storage", category = "Apps", @@ -92,6 +100,9 @@ icon = "\030 \031f \031b \031foo \ \030 \031f \030e\031b \030 \031f/\ \030 \031b \030e \030 \031f\\", + iconExt = "\030 \031f\030f\031f\128\030e\143\030f\031e\144\031f\128\0304\149\0307\0314\131\131\030f\149\ +\030 \031f\030e\031f\129\031e\128\128\030f\148\0304\031f\149\0307\0318\140\140\030f\0314\149\ +\030 \031f\030f\031e\139\030e\128\030f\159\129\0314\130\131\131\129", run = "recorder.lua", }, [ "131f0b008f44298812221d120d982940609be781" ] = { @@ -131,6 +142,9 @@ icon = "\0301\031f \0309 \030c \030b \030e \030c \0306 \ \030 \031f \ \030 \031f \0300 \0310 ", + iconExt = "\030 \031f\030f\0319\144\030d\031f\159\030b\159\030f\0311\144\031b\144\030c\031f\159\030f\0311\144\ +\030 \031f\030f\0311\130\031b\129\0319\130\031e\130\0310\144\031d\129\0316\129\ +\030 \031f\030f\0310\136\140\140\030 ", run = "https://gist.github.com/LDDestroier/c7528d95bc0103545c2a/raw", }, [ "d78f28759f255a0db76604ee560b87c4715a0da5" ] = { @@ -139,23 +153,37 @@ icon = " \031bskch\ \0303\031f \030d \ \030d\031f ", + iconExt = "\030 \031f\0308\031f\151\0313\140\140\140\030f\0318\148\ +\030 \031f\030b\031f\149\0318\130\131\129\030f\031b\149\ +\030 \031f\030f\0318\138\0308\031b\130\131\129\030f\0318\133", run = "http://pastebin.com/raw/Mm5hd97E", }, [ "58ec8d6e36e346d9f42eb43935652e3e58e2c829" ] = { + title = "Mwm", category = "Apps", icon = "\030f\031f \0304 \ \030f\031dshell]\0304\0314 \ \0304\031f ", - title = "Mwm", + iconExt = "\030 \031f\0305\031f\155\030f\128\031d\152\140\030d\031f\151\030f\128\128\0304\0314\128\ +\030 \031f\030f\0315\152\129\030d\031f\141\030f\031d\153\030d\031f\149\030f\031d\131\148\0304\0314\128\ +\030 \031f\0304\031f\131\131\131\131\131\131\131\030e\0314\131", run = "mwm.lua usr/config/mwm", }, [ "8d1b0a73bedc0dc492377c2f6ab880940b97ec6e" ] = { + title = "Treefarm", icon = "\030 \031f \0305 \030 \030d \030 \ \0305\031f \030d \030 \030d \0305 \030d \ \030 \031f \030c \030 \0304 \030 \030c \030 ", category = "Apps", - title = "Treefarm", run = "treefarm.lua", requires = "turtle", }, + [ "01c933b2a36ad8ed2d54089cb2903039046c1216" ] = { + title = "Enchat", + icon = "\030e\031f\151\030f\031e\156\0311\140\0314\140\0315\140\031d\140\031b\140\031a\132\ +\030f\0314\128\030e\031f\132\030f\031e\132\0318nchat\ +\030f\031e\138\141\0311\140\0314\140\0315\132\0317v\03183\031a\132", + category = "Apps", + run = "https://raw.githubusercontent.com/LDDestroier/enchat/master/enchat3.lua", + }, } From 77aada762faac138276b3ac41849d8ce7a1b759c Mon Sep 17 00:00:00 2001 From: kepler155c Date: Mon, 15 Oct 2018 16:07:03 -0400 Subject: [PATCH 002/165] new icon set + scanning miner --- apps/scanningMiner.lua | 181 +++++++++++++++++++++-------------------- etc/apps/opus-apps.db | 5 ++ 2 files changed, 99 insertions(+), 87 deletions(-) diff --git a/apps/scanningMiner.lua b/apps/scanningMiner.lua index b72d02f..26e6211 100644 --- a/apps/scanningMiner.lua +++ b/apps/scanningMiner.lua @@ -96,7 +96,7 @@ local page = UI.Page { sortColumn = 'name', columns = { { heading = 'Resource', key = 'displayName' }, - { heading = 'Count', key = 'count', width = 6 }, + { heading = 'Count', key = 'count', width = 5 }, }, }, statusBar = UI.StatusBar { @@ -167,6 +167,15 @@ local function getCornerOf(c) return math.floor(c.x / 16) * 16, math.floor(c.z / 16) * 16 end +local function isFinished() + if mining.chunks ~= -1 then + local chunks = math.pow(mining.diameter-2, 2) + mining.chunkIndex + if chunks >= mining.chunks then + return true + end + end +end + local function nextChunk() local x, z = getCornerOf({ x = mining.x, z = mining.z }) local points = math.pow(mining.diameter, 2) - math.pow(mining.diameter-2, 2) @@ -177,13 +186,6 @@ local function nextChunk() mining.chunkIndex = 0 end - if mining.chunks ~= -1 then - local chunks = math.pow(mining.diameter-2, 2) + mining.chunkIndex - if chunks >= mining.chunks then - return false - end - end - local nc = getChunkCoordinates(mining.diameter, mining.chunkIndex, x, z) -- enter next chunk @@ -192,7 +194,7 @@ local function nextChunk() Util.writeTable(PROGRESS_FILE, mining) - return true + return not isFinished() end local function status(newStatus) @@ -389,10 +391,10 @@ local function scan() local i = #blocks Point.eachClosest(turtle.point, blocks, function(b) if turtle.isAborted() then - return + error('aborted') end - page.grid.nextBlock = b.displayName + page.grid.nextBlock = b.name .. ':' .. b.metadata -- Get the action again in case the user has ignored via UI b.action = dictionary:get(b.name, b.metadata) or 'mine' @@ -441,20 +443,13 @@ local function mineChunk() while true do status('scanning') turtle.select(1) - turtle._goto({ x = mining.x + 8, z = mining.z + 8, y = y }) + safeGoto(mining.x + 8, mining.z + 8, y) scan() - if turtle.isAborted() then - status('aborting') - return false - end if turtle.getFuelLevel() < LOW_FUEL then refuel() local veryMinFuel = Point.turtleDistance(turtle.point, { x = 0, y = 0, z = 0}) + 512 if turtle.getFuelLevel() < veryMinFuel then - page.statusBar:setValue('Not enough fuel to continue') - page.statusBar:draw() - page:sync() - return false + error('Not enough fuel to continue') end end inc() @@ -463,8 +458,6 @@ local function mineChunk() break end end - - return true end local function addTrash() @@ -515,82 +508,96 @@ end local function main() repeat - if not mineChunk() then - return - end - Util.writeTable(PROGRESS_FILE, mining) + mineChunk() until not nextChunk() end +local success, msg + Event.addRoutine(function() - turtle.run(function() - turtle.reset() + turtle.reset() - if not fs.exists(DICTIONARY_FILE) or options.setTrash.value then - print('Add blocks to ignore, press enter when ready') - read() - addTrash() - end + if not fs.exists(DICTIONARY_FILE) or options.setTrash.value then + print('Add blocks to ignore, press enter when ready') + read() + addTrash() + end - ejectTrash() + ejectTrash() - turtle.initialize { - right = 'computercraft:advanced_modem', - left = 'minecraft:diamond_pickaxe', - required = { - 'minecraft:bucket', - 'plethora:module', - }, - GPS = true, - minFuel = 100, - -- searchFor = 'ironchest:iron_shulker_box_white' - } + turtle.initialize { + right = 'computercraft:advanced_modem', + left = 'minecraft:diamond_pickaxe', + required = { + 'minecraft:bucket', + 'plethora:module', + }, + GPS = true, + minFuel = 100, + -- searchFor = 'ironchest:iron_shulker_box_white' + } - turtle.setMoveCallback(function() - page.statusBar:setValue('fuel', Util.toBytes(turtle.getFuelLevel())) - page.statusBar:draw() - page:sync() - end) - - mining = Util.readTable(PROGRESS_FILE) or { - diameter = 1, - chunkIndex = 0, - x = 0, z = 0, - chunks = options.chunks.value, - home = Point.copy(turtle.point), - heading = turtle.point.heading, -- always using east for now - } - - -- use coordinates relative to initial starting point - turtle.setPoint({ - x = turtle.point.x - mining.home.x, - y = turtle.point.y - mining.home.y, - z = turtle.point.z - mining.home.z, - }) - - if not fs.exists(PROGRESS_FILE) then - Util.writeTable(PROGRESS_FILE, mining) - end - - turtle.setPolicy(turtle.policies.digAttack) - turtle.setDigPolicy(turtle.digPolicies.turtleSafe) - turtle.setMovementStrategy('goto') - status('mining') - - local s, m = pcall(main) - - status(s and 'finished' or 'error') - turtle._goto({ x = 0, y = 0, z = 0 }) - turtle.reset() - - if not s and m then - _G.printError(m) - end - - Event.exitPullEvents() + turtle.setMoveCallback(function() + page.statusBar:setValue('fuel', Util.toBytes(turtle.getFuelLevel())) + page.statusBar:draw() + page:sync() end) + + mining = Util.readTable(PROGRESS_FILE) or { + diameter = 1, + chunkIndex = 0, + x = 0, z = 0, + chunks = options.chunks.value, + home = Point.copy(turtle.point), + heading = turtle.point.heading, -- always using east for now + } + + if options.chunks.value ~= -1 then + mining.chunks = options.chunks.value + end + + -- use coordinates relative to initial starting point + turtle.setPoint({ + x = turtle.point.x - mining.home.x, + y = turtle.point.y - mining.home.y, + z = turtle.point.z - mining.home.z, + }) + + if not fs.exists(PROGRESS_FILE) then + Util.writeTable(PROGRESS_FILE, mining) + end + + turtle.setPolicy(turtle.policies.digAttack) + turtle.setDigPolicy(turtle.digPolicies.turtleSafe) + turtle.setMovementStrategy('goto') + status('mining') + + if isFinished() then + success = false + msg = 'Mining complete' + else + success, msg = pcall(main) + end + + status(success and 'finished' or turtle.isAborted() and 'aborting' or 'error') + if turtle._goto({ x = 0, y = 0, z = 0 }) then + unload() + end + turtle.reset() + + Event.exitPullEvents() +end) + +Event.onTerminate(function() + turtle.abort(true) end) UI:setPage(page) UI:pullEvents() UI.term:reset() + +turtle.reset() + +if not success and msg then + _G.printError(msg) +end diff --git a/etc/apps/opus-apps.db b/etc/apps/opus-apps.db index 8f0ccc4..e2a8d90 100644 --- a/etc/apps/opus-apps.db +++ b/etc/apps/opus-apps.db @@ -134,6 +134,9 @@ icon = "\0308\031f \0300 \0308 \ \0308\031f \0300 \030f \ \0300\031f \030f ", + iconExt = "\0308\0318\128\0300\159\129\0310\128\0308\159\129\0318\128\ +\0300\0318\135\0310\128\128\030f\135\0300\031f\143\159\030f\0310\144\ +\0300\128\030f\159\129\138\0300\031f\143\149\030f\0310\134", run = "https://pastebin.com/raw/jyDH7mLH", }, [ "48d6857f6b2869d031f463b13aa34df47e18c548" ] = { @@ -147,6 +150,7 @@ \030 \031f\030f\0310\136\140\140\030 ", run = "https://gist.github.com/LDDestroier/c7528d95bc0103545c2a/raw", }, +--[[ [ "d78f28759f255a0db76604ee560b87c4715a0da5" ] = { title = "Sketch", category = "Apps", @@ -158,6 +162,7 @@ \030 \031f\030f\0318\138\0308\031b\130\131\129\030f\0318\133", run = "http://pastebin.com/raw/Mm5hd97E", }, +]] [ "58ec8d6e36e346d9f42eb43935652e3e58e2c829" ] = { title = "Mwm", category = "Apps", From 611e4676a43025ba30e5c86294a39720d1e901a6 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Mon, 15 Oct 2018 21:14:37 -0400 Subject: [PATCH 003/165] use scanner for farm heading + scanning miner fixes --- apps/farm.lua | 7 ++----- apps/scanningMiner.lua | 39 ++++++++++++++++++++++----------------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/apps/farm.lua b/apps/farm.lua index a3b37da..cd656ba 100644 --- a/apps/farm.lua +++ b/apps/farm.lua @@ -44,11 +44,8 @@ local function harvest(blocks) end turtle.reset() -local directions = { [5] = 2, [3] = 3, [4] = 0, [2] = 1, } -turtle.placeUp('minecraft:chest') or error('Missing chest') -local _, bi = turtle.inspectUp() -turtle.digUp() -turtle.point.heading = directions[bi.metadata] +local facing = scanner.getBlockMeta(0, 0, 0).state.facing +pt.heading = Point.facings[facing].heading turtle.setPolicy('digOnly') diff --git a/apps/scanningMiner.lua b/apps/scanningMiner.lua index 26e6211..fdbd670 100644 --- a/apps/scanningMiner.lua +++ b/apps/scanningMiner.lua @@ -95,8 +95,8 @@ local page = UI.Page { y = 2, ey = -2, sortColumn = 'name', columns = { - { heading = 'Resource', key = 'displayName' }, { heading = 'Count', key = 'count', width = 5 }, + { heading = 'Resource', key = 'displayName' }, }, }, statusBar = UI.StatusBar { @@ -228,7 +228,7 @@ local function safeGoto(x, z, y, h) local oldStatus = turtle.getStatus() while not turtle._goto({ x = x, z = z, y = y or turtle.point.y, heading = h }) do - --status('stuck') + status('stuck') if turtle.isAborted() then return false end @@ -434,30 +434,35 @@ local function scan() end local function mineChunk() - local topDown = turtle.point.y > -mining.home.y / 2 - local y = topDown and -8 or -mining.home.y + 8 - local inc = topDown and - function() y = y - 16 end or - function() y = y + 16 end + local pts = { } + + for i = 1, math.ceil(mining.home.y / 16) do + pts[i] = { x = mining.x + 8, z = mining.z + 8, y = (i - 1) * 16 + 8 } + if pts[i].y > mining.home.y - 8 then + pts[i].y = mining.home.y - 8 + end + pts[i].y = pts[i].y - mining.home.y -- abs to rel + debug(pts[i]) + end + + Point.eachClosest(turtle.point, pts, function(pt) + if turtle.isAborted() then + error('aborted') + end + status('scanning ' .. pt.y + mining.home.y - 8 .. ' - ' .. pt.y + mining.home.y + 8) - while true do - status('scanning') turtle.select(1) - safeGoto(mining.x + 8, mining.z + 8, y) + safeGoto(pt.x, pt.z, pt.y) scan() + if turtle.getFuelLevel() < LOW_FUEL then refuel() - local veryMinFuel = Point.turtleDistance(turtle.point, { x = 0, y = 0, z = 0}) + 512 + local veryMinFuel = Point.turtleDistance(turtle.point, { x = 0, y = 0, z = 0 }) + 512 if turtle.getFuelLevel() < veryMinFuel then error('Not enough fuel to continue') end end - inc() - - if y > 0 or y < -mining.home.y then - break - end - end + end) end local function addTrash() From 1da12b834201299355edfe98a39a961ea7e39640 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Tue, 16 Oct 2018 19:10:11 -0400 Subject: [PATCH 004/165] plethora app fixes --- apps/farm.lua | 20 +++++++++++++++----- apps/scanningMiner.lua | 7 ++++--- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/apps/farm.lua b/apps/farm.lua index cd656ba..89dc227 100644 --- a/apps/farm.lua +++ b/apps/farm.lua @@ -3,7 +3,13 @@ _G.requireInjector(_ENV) local Point = require('point') local Util = require('util') -local scanner = device['plethora:scanner'] or error('Plethora scanner required') +local device = _G.device +local os = _G.os +local turtle = _G.turtle + +local scanner = device['plethora:scanner'] or + turtle.equip('right', 'plethora:module:2') or + error('Plethora scanner required') local crops = { ['minecraft:wheat'] = @@ -25,27 +31,31 @@ local function scan() return crops[v.name] and scanner.getBlockMeta(v.x, v.y, v.z).metadata == crops[v.name].mature end) - + return blocks end local function harvest(blocks) + turtle.equip('right', 'minecraft:diamond_pickaxe') + Point.eachClosest(turtle.point, blocks, function(b) Util.print(b) if b.name == 'minecraft:reeds' then - turtle.goto(b) + turtle._goto(b) else - turtle.goto(Point.above(b)) + turtle._goto(Point.above(b)) turtle.digDown() turtle.placeDown(crops[b.name].seed) turtle.select(1) end end) + + turtle.equip('right', 'plethora:module:2') end turtle.reset() local facing = scanner.getBlockMeta(0, 0, 0).state.facing -pt.heading = Point.facings[facing].heading +turtle.point.heading = Point.facings[facing].heading turtle.setPolicy('digOnly') diff --git a/apps/scanningMiner.lua b/apps/scanningMiner.lua index fdbd670..67f0886 100644 --- a/apps/scanningMiner.lua +++ b/apps/scanningMiner.lua @@ -414,7 +414,9 @@ local function scan() page.statusBar:setValue('status', m) page.statusBar:draw() page:sync() - os.sleep(5) + os.sleep(3) + else + page.statusBar:setValue('mining', m) end dirty = true elseif b.action == 'liquid_fuel' then @@ -442,14 +444,13 @@ local function mineChunk() pts[i].y = mining.home.y - 8 end pts[i].y = pts[i].y - mining.home.y -- abs to rel - debug(pts[i]) end Point.eachClosest(turtle.point, pts, function(pt) if turtle.isAborted() then error('aborted') end - status('scanning ' .. pt.y + mining.home.y - 8 .. ' - ' .. pt.y + mining.home.y + 8) + status('scanning ' .. pt.y + mining.home.y - 8 .. '-' .. pt.y + mining.home.y + 8) turtle.select(1) safeGoto(pt.x, pt.z, pt.y) From a4121c50afa5b9b9ad13d31eb2af25a563ae4a76 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Tue, 16 Oct 2018 21:37:48 -0400 Subject: [PATCH 005/165] farmer drop items into chest --- apps/farm.lua | 61 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 12 deletions(-) diff --git a/apps/farm.lua b/apps/farm.lua index 89dc227..2d4fd17 100644 --- a/apps/farm.lua +++ b/apps/farm.lua @@ -4,14 +4,17 @@ local Point = require('point') local Util = require('util') local device = _G.device +local fs = _G.fs local os = _G.os local turtle = _G.turtle +local CONFIG_FILE = 'usr/config/farm' + local scanner = device['plethora:scanner'] or - turtle.equip('right', 'plethora:module:2') or + turtle.equip('right', 'plethora:module:2') and device['plethora:scanner'] or error('Plethora scanner required') -local crops = { +local crops = Util.readFile(CONFIG_FILE) or { ['minecraft:wheat'] = { seed = 'minecraft:wheat_seeds', mature = 7 }, ['minecraft:carrots'] = @@ -22,6 +25,10 @@ local crops = { { seed = 'minecraft:beetroot_seeds', mature = 3 }, } +if not fs.exists(CONFIG_FILE) then + Util.writeTable(CONFIG_FILE, crops) +end + local function scan() local blocks = scanner.scan() Util.filterInplace(blocks, function(v) @@ -37,6 +44,7 @@ end local function harvest(blocks) turtle.equip('right', 'minecraft:diamond_pickaxe') + turtle.setPoint({ x = 0, y = 0, z = 0, heading = turtle.point.heading }) Point.eachClosest(turtle.point, blocks, function(b) Util.print(b) @@ -53,17 +61,46 @@ local function harvest(blocks) turtle.equip('right', 'plethora:module:2') end -turtle.reset() -local facing = scanner.getBlockMeta(0, 0, 0).state.facing -turtle.point.heading = Point.facings[facing].heading +local function dropOff() + local blocks = scanner.scan() + local done -turtle.setPolicy('digOnly') + Util.filterInplace(blocks, function(v) + if v.name == 'minecraft:chest' then + return v.y == -1 + end + end) -while true do - print('scanning') - local blocks = scan() turtle.setPoint({ x = 0, y = 0, z = 0, heading = turtle.point.heading }) - harvest(blocks) - print('sleeping') - os.sleep(10) + Point.eachClosest(turtle.point, blocks, function(b) + if not done then + if turtle._goto(Point.above(b)) then + for k,v in pairs(turtle.getSummedInventory()) do + if v.count > 32 then + turtle.dropDown(k, v.count - 32) + end + done = true + end + end + end + end) end + +turtle.run(function() + local facing = scanner.getBlockMeta(0, 0, 0).state.facing + turtle.point.heading = Point.facings[facing].heading + + turtle.setPolicy('digOnly') + + repeat + local blocks = scan() + if #blocks > 0 then + turtle.setStatus('Harvesting') + harvest(blocks) + turtle.setStatus('Storing') + dropOff() + turtle.setStatus('Sleeping') + end + os.sleep(10) + until turtle.isAborted() +end) From b4f7e3a69c7ec673d153776c5e4450f9d0a6fa2c Mon Sep 17 00:00:00 2001 From: kepler155c Date: Tue, 16 Oct 2018 22:17:08 -0400 Subject: [PATCH 006/165] farmer drop items into chest --- apps/farm.lua | 68 +++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/apps/farm.lua b/apps/farm.lua index 2d4fd17..cbdde54 100644 --- a/apps/farm.lua +++ b/apps/farm.lua @@ -31,76 +31,74 @@ end local function scan() local blocks = scanner.scan() + Util.filterInplace(blocks, function(v) if v.name == 'minecraft:reeds' then return v.y == 0 end + if v.name == 'minecraft:chest' then + return v.y == -1 + end return crops[v.name] and scanner.getBlockMeta(v.x, v.y, v.z).metadata == crops[v.name].mature - end) + end) - return blocks + local harvestCount = 0 + for _,v in pairs(blocks) do + if v.name ~= 'minecraft:chest' then + harvestCount = harvestCount + 1 + end + end + + return blocks, harvestCount end local function harvest(blocks) turtle.equip('right', 'minecraft:diamond_pickaxe') turtle.setPoint({ x = 0, y = 0, z = 0, heading = turtle.point.heading }) + turtle.select(1) Point.eachClosest(turtle.point, blocks, function(b) - Util.print(b) if b.name == 'minecraft:reeds' then turtle._goto(b) + elseif b.name == 'minecraft:chest' then + local summed = turtle.getSummedInventory() + for _,v in pairs(summed) do + if v.count > 32 then + if turtle._goto(Point.above(b)) then + for k,v2 in pairs(summed) do + if v2.count > 32 then + turtle.dropDown(k, v2.count - 32) + end + end + end + break + end + end else turtle._goto(Point.above(b)) turtle.digDown() turtle.placeDown(crops[b.name].seed) - turtle.select(1) end end) - turtle.equip('right', 'plethora:module:2') end -local function dropOff() - local blocks = scanner.scan() - local done - - Util.filterInplace(blocks, function(v) - if v.name == 'minecraft:chest' then - return v.y == -1 - end - end) - - turtle.setPoint({ x = 0, y = 0, z = 0, heading = turtle.point.heading }) - Point.eachClosest(turtle.point, blocks, function(b) - if not done then - if turtle._goto(Point.above(b)) then - for k,v in pairs(turtle.getSummedInventory()) do - if v.count > 32 then - turtle.dropDown(k, v.count - 32) - end - done = true - end - end - end - end) -end - turtle.run(function() local facing = scanner.getBlockMeta(0, 0, 0).state.facing turtle.point.heading = Point.facings[facing].heading turtle.setPolicy('digOnly') - repeat - local blocks = scan() - if #blocks > 0 then + local blocks, harvestCount = scan() + if harvestCount > 0 then turtle.setStatus('Harvesting') harvest(blocks) - turtle.setStatus('Storing') - dropOff() turtle.setStatus('Sleeping') end os.sleep(10) + if turtle.getFuelLevel() < 10 then + error('Out of fuel') + end until turtle.isAborted() end) From d4296d2b8b627db43e600c2acfc4223233bbfa95 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Tue, 16 Oct 2018 22:18:15 -0400 Subject: [PATCH 007/165] farmer drop items into chest --- apps/farm.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/farm.lua b/apps/farm.lua index cbdde54..b6e58bd 100644 --- a/apps/farm.lua +++ b/apps/farm.lua @@ -56,9 +56,9 @@ end local function harvest(blocks) turtle.equip('right', 'minecraft:diamond_pickaxe') turtle.setPoint({ x = 0, y = 0, z = 0, heading = turtle.point.heading }) - turtle.select(1) Point.eachClosest(turtle.point, blocks, function(b) + turtle.select(1) if b.name == 'minecraft:reeds' then turtle._goto(b) elseif b.name == 'minecraft:chest' then From 93b1635de2bdc699d8c259db047e96f238420749 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Tue, 16 Oct 2018 22:26:05 -0400 Subject: [PATCH 008/165] farmer drop items into chest --- apps/farm.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/farm.lua b/apps/farm.lua index b6e58bd..1cebd3f 100644 --- a/apps/farm.lua +++ b/apps/farm.lua @@ -14,7 +14,7 @@ local scanner = device['plethora:scanner'] or turtle.equip('right', 'plethora:module:2') and device['plethora:scanner'] or error('Plethora scanner required') -local crops = Util.readFile(CONFIG_FILE) or { +local crops = Util.readTable(CONFIG_FILE) or { ['minecraft:wheat'] = { seed = 'minecraft:wheat_seeds', mature = 7 }, ['minecraft:carrots'] = From e8b69254fd9ee3e55821abdb0eb1d692364378c6 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Wed, 17 Oct 2018 01:15:04 -0400 Subject: [PATCH 009/165] plethora app fixes --- apps/farm.lua | 6 +++--- apps/scanningMiner.lua | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/farm.lua b/apps/farm.lua index 1cebd3f..d6131f7 100644 --- a/apps/farm.lua +++ b/apps/farm.lua @@ -64,11 +64,11 @@ local function harvest(blocks) elseif b.name == 'minecraft:chest' then local summed = turtle.getSummedInventory() for _,v in pairs(summed) do - if v.count > 32 then + if v.count > 48 then if turtle._goto(Point.above(b)) then for k,v2 in pairs(summed) do - if v2.count > 32 then - turtle.dropDown(k, v2.count - 32) + if v2.count > 16 then + turtle.dropDown(k, v2.count - 16) end end end diff --git a/apps/scanningMiner.lua b/apps/scanningMiner.lua index 67f0886..f24c413 100644 --- a/apps/scanningMiner.lua +++ b/apps/scanningMiner.lua @@ -24,6 +24,8 @@ local peripheral = _G.peripheral local read = _G.read local turtle = _G.turtle +UI:configure('scanningMiner', ...) + local args = { ... } local options = { chunks = { arg = 'c', type = 'number', value = -1, From 21c182768219136ab3d0bec3f3f136288f73f9be Mon Sep 17 00:00:00 2001 From: kepler155c Date: Wed, 17 Oct 2018 19:27:00 -0400 Subject: [PATCH 010/165] new cow breeder app --- apps/cows.lua | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 apps/cows.lua diff --git a/apps/cows.lua b/apps/cows.lua new file mode 100644 index 0000000..393c0f2 --- /dev/null +++ b/apps/cows.lua @@ -0,0 +1,69 @@ +_G.requireInjector(_ENV) + +local Config = require('config') +local Util = require('util') + +local device = _G.device +local os = _G.os +local turtle = _G.turtle + +local config = { + max_cows = 15, +} +Config.load('cows', config) + +local sensor = device['plethora:sensor'] or + turtle.equip('right', 'plethora:module:3') and device['plethora:sensor'] or + error('Plethora sensor required') + +local function getCowCount() + local blocks = sensor.sense() + + Util.filterInplace(blocks, function(v) + if v.name == 'Cow' then + return v.y > -.5 + end + end) + + return #blocks +end + +local function butcher() + turtle.equip('right', 'minecraft:diamond_sword') + turtle.select(1) + + turtle.attack() + for _ = 1, 3 do + turtle.turnRight() + turtle.attack() + end + turtle.equip('right', 'plethora:module:3') + + turtle.dropUp('minecraft:beef') + turtle.dropUp('minecraft:leather') +end + +local function breed() + turtle.select(1) + + turtle.place('minecraft:wheat') + for _ = 1, 3 do + turtle.turnRight() + turtle.place('minecraft:wheat') + end +end + +turtle.run(function() + repeat + if getCowCount() > config.max_cows then + turtle.setStatus('Butchering') + butcher() + elseif turtle.getItemCount('minecraft:wheat') == 0 then + turtle.setStatus('Out of wheat') + else + turtle.setStatus('Breeding') + breed() + end + os.sleep(5) + until turtle.isAborted() +end) From 72bf4d89ba53cb658735dd918d03bbe00cb35289 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Wed, 17 Oct 2018 20:13:32 -0400 Subject: [PATCH 011/165] new cow breeder app --- apps/cows.lua | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/apps/cows.lua b/apps/cows.lua index 393c0f2..03fb319 100644 --- a/apps/cows.lua +++ b/apps/cows.lua @@ -5,6 +5,7 @@ local Util = require('util') local device = _G.device local os = _G.os +local term = _G.term local turtle = _G.turtle local config = { @@ -19,12 +20,20 @@ local sensor = device['plethora:sensor'] or local function getCowCount() local blocks = sensor.sense() + local grown = 0 + local babies = 0 + Util.filterInplace(blocks, function(v) if v.name == 'Cow' then + grown = grown + (v.y > -.5) and 1 or 0 + babies = babies + (v.y < -.5) and 1 or 0 return v.y > -.5 end end) + term.clearLine() + Util.print('%d grown, %d babies', grown, babies) + return #blocks end From 51e73e1c985438bad52aececb283267aa522072f Mon Sep 17 00:00:00 2001 From: kepler155c Date: Wed, 17 Oct 2018 20:16:01 -0400 Subject: [PATCH 012/165] new cow breeder app --- apps/cows.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/cows.lua b/apps/cows.lua index 03fb319..d3798cf 100644 --- a/apps/cows.lua +++ b/apps/cows.lua @@ -25,8 +25,8 @@ local function getCowCount() Util.filterInplace(blocks, function(v) if v.name == 'Cow' then - grown = grown + (v.y > -.5) and 1 or 0 - babies = babies + (v.y < -.5) and 1 or 0 + if v.y > -.5 then grown = grown + 1 end + if v.y < -.5 then babies = babies + 1 end return v.y > -.5 end end) From 1290ca5486b872510f082d92c4c95bdae27445b3 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Fri, 19 Oct 2018 19:34:29 -0400 Subject: [PATCH 013/165] scanner app improvements --- apps/cows.lua | 2 -- apps/farm.lua | 76 +++++++++++++++++++++++++++--------------- apps/scanningMiner.lua | 13 ++++---- 3 files changed, 56 insertions(+), 35 deletions(-) diff --git a/apps/cows.lua b/apps/cows.lua index d3798cf..784e595 100644 --- a/apps/cows.lua +++ b/apps/cows.lua @@ -5,7 +5,6 @@ local Util = require('util') local device = _G.device local os = _G.os -local term = _G.term local turtle = _G.turtle local config = { @@ -31,7 +30,6 @@ local function getCowCount() end end) - term.clearLine() Util.print('%d grown, %d babies', grown, babies) return #blocks diff --git a/apps/farm.lua b/apps/farm.lua index d6131f7..2a1988d 100644 --- a/apps/farm.lua +++ b/apps/farm.lua @@ -16,13 +16,17 @@ local scanner = device['plethora:scanner'] or local crops = Util.readTable(CONFIG_FILE) or { ['minecraft:wheat'] = - { seed = 'minecraft:wheat_seeds', mature = 7 }, + { seed = 'minecraft:wheat_seeds', mature = 7, action = 'plant' }, ['minecraft:carrots'] = - { seed = 'minecraft:carrot', mature = 7 }, + { seed = 'minecraft:carrot', mature = 7, action = 'plant' }, ['minecraft:potatoes'] = - { seed = 'minecraft:potato', mature = 7 }, + { seed = 'minecraft:potato', mature = 7, action = 'plant' }, ['minecraft:beetroots'] = - { seed = 'minecraft:beetroot_seeds', mature = 3 }, + { seed = 'minecraft:beetroot_seeds', mature = 3, 'plant' }, + ['minecraft:reeds'] = { action = 'bash' }, + ['minecraft:melon_block'] = { action = 'smash' }, + ['minecraft:pumpkin'] = { action = 'smash' }, + ['minecraft:chest'] = { action = 'drop' }, } if not fs.exists(CONFIG_FILE) then @@ -31,21 +35,36 @@ end local function scan() local blocks = scanner.scan() + local summed = turtle.getSummedInventory() + local doDropOff + + for _,v in pairs(summed) do + if v.count > 48 then + doDropOff = true + break + end + end Util.filterInplace(blocks, function(v) - if v.name == 'minecraft:reeds' then + v.action = crops[v.name] and crops[v.name].action + + if v.action == 'bash' then return v.y == 0 end - if v.name == 'minecraft:chest' then + if v.action == 'drop' then + return doDropOff and v.y == -1 + end + if v.action == 'smash' then return v.y == -1 end - return crops[v.name] and - scanner.getBlockMeta(v.x, v.y, v.z).metadata == crops[v.name].mature + return v.action == 'plant' and + v.metadata == crops[v.name].mature and + v.y == -1 end) local harvestCount = 0 for _,v in pairs(blocks) do - if v.name ~= 'minecraft:chest' then + if v.action ~= 'drop' then harvestCount = harvestCount + 1 end end @@ -56,29 +75,31 @@ end local function harvest(blocks) turtle.equip('right', 'minecraft:diamond_pickaxe') turtle.setPoint({ x = 0, y = 0, z = 0, heading = turtle.point.heading }) + turtle.select(1) + + local dropped Point.eachClosest(turtle.point, blocks, function(b) - turtle.select(1) - if b.name == 'minecraft:reeds' then - turtle._goto(b) - elseif b.name == 'minecraft:chest' then - local summed = turtle.getSummedInventory() - for _,v in pairs(summed) do - if v.count > 48 then - if turtle._goto(Point.above(b)) then - for k,v2 in pairs(summed) do - if v2.count > 16 then - turtle.dropDown(k, v2.count - 16) - end - end + if b.action == 'bash' then + turtle.digForwardAt(b) + elseif b.action == 'drop' and not dropped then + if turtle._goto(Point.above(b)) then + local summed = turtle.getSummedInventory() + for k,v in pairs(summed) do + if v.count > 16 then + turtle.dropDown(k, v.count - 16) end - break end + dropped = true + turtle.select(1) + end + elseif b.action == 'smash' then + turtle.digDownAt(b) + elseif b.action == 'plant' then + if turtle.digDownAt(b) then + turtle.placeDown(crops[b.name].seed) + turtle.select(1) end - else - turtle._goto(Point.above(b)) - turtle.digDown() - turtle.placeDown(crops[b.name].seed) end end) turtle.equip('right', 'plethora:module:2') @@ -89,6 +110,7 @@ turtle.run(function() turtle.point.heading = Point.facings[facing].heading turtle.setPolicy('digOnly') + turtle.setMovementStrategy('goto') repeat local blocks, harvestCount = scan() if harvestCount > 0 then diff --git a/apps/scanningMiner.lua b/apps/scanningMiner.lua index f24c413..01f894c 100644 --- a/apps/scanningMiner.lua +++ b/apps/scanningMiner.lua @@ -522,15 +522,16 @@ end local success, msg +if not fs.exists(DICTIONARY_FILE) or options.setTrash.value then + print('Place blocks into the turtles inventor to ignore, such as cobble, stone, gravel, etc.') + print('\nPress enter when ready') + read() + addTrash() +end + Event.addRoutine(function() turtle.reset() - if not fs.exists(DICTIONARY_FILE) or options.setTrash.value then - print('Add blocks to ignore, press enter when ready') - read() - addTrash() - end - ejectTrash() turtle.initialize { From 0db3ad47bb59c5b7efa9e304f0ac46e0786cc73a Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sat, 20 Oct 2018 20:36:16 -0400 Subject: [PATCH 014/165] plethora networked autocrafting --- apis/chestAdapter18.lua | 11 +- inventoryManager/.package | 12 + inventoryManager/apis/craft.lua | 319 ++++ inventoryManager/apis/inventoryAdapter.lua | 55 + inventoryManager/apis/networkedAdapter18.lua | 145 ++ inventoryManager/apps/Crafter.lua | 1130 ++++++++++++++ inventoryManager/apps/inventoryManager.lua | 1400 ++++++++++++++++++ 7 files changed, 3068 insertions(+), 4 deletions(-) create mode 100644 inventoryManager/.package create mode 100644 inventoryManager/apis/craft.lua create mode 100644 inventoryManager/apis/inventoryAdapter.lua create mode 100644 inventoryManager/apis/networkedAdapter18.lua create mode 100644 inventoryManager/apps/Crafter.lua create mode 100644 inventoryManager/apps/inventoryManager.lua diff --git a/apis/chestAdapter18.lua b/apis/chestAdapter18.lua index a0829d1..3053aa4 100644 --- a/apis/chestAdapter18.lua +++ b/apis/chestAdapter18.lua @@ -122,6 +122,8 @@ function ChestAdapter:getPercentUsed() end function ChestAdapter:provide(item, qty, slot, direction) + local total = 0 + local s, m = pcall(function() local stacks = self.list() for key,stack in Util.rpairs(stacks) do @@ -130,24 +132,25 @@ function ChestAdapter:provide(item, qty, slot, direction) (not item.nbtHash or stack.nbtHash == item.nbtHash) then local amount = math.min(qty, stack.count) if amount > 0 then - self.pushItems(direction or self.direction, key, amount, slot) + amount = self.pushItems(direction or self.direction, key, amount, slot) end qty = qty - amount + total = total + amount if qty <= 0 then break end end end end) - return s, m + return total, m end function ChestAdapter:extract(slot, qty, toSlot) - self.pushItems(self.direction, slot, qty, toSlot) + return self.pushItems(self.direction, slot, qty, toSlot) end function ChestAdapter:insert(slot, qty, toSlot) - self.pullItems(self.direction, slot, qty, toSlot) + return self.pullItems(self.direction, slot, qty, toSlot) end return ChestAdapter diff --git a/inventoryManager/.package b/inventoryManager/.package new file mode 100644 index 0000000..f1522ad --- /dev/null +++ b/inventoryManager/.package @@ -0,0 +1,12 @@ +{ + required = { + 'opus-inventory-manager', + }, + title = 'Inventory manager for Opus OS', + description = [[ +Unstable Branch +]], + licence = 'MIT', + --location = '', + --mount = 'packages/opus-neural gitfs kepler155c/opus-neural/master', +} diff --git a/inventoryManager/apis/craft.lua b/inventoryManager/apis/craft.lua new file mode 100644 index 0000000..1b7b676 --- /dev/null +++ b/inventoryManager/apis/craft.lua @@ -0,0 +1,319 @@ +local itemDB = require('itemDB') +local Util = require('util') + +local fs = _G.fs +local turtle = _G.turtle + +local RECIPES_DIR = 'usr/etc/recipes' +local USER_RECIPES = 'usr/config/recipes.db' + +local Craft = { } + +local function clearGrid(inventoryAdapter) + for i = 1, 16 do + local count = turtle.getItemCount(i) + if count > 0 then + inventoryAdapter:insert(i, count) + if turtle.getItemCount(i) ~= 0 then + -- inventory is possibly full + return false + end + end + end + return true +end + +local function splitKey(key) + local t = Util.split(key, '(.-):') + local item = { } + if #t[#t] > 8 then + item.nbtHash = table.remove(t) + end + item.damage = tonumber(table.remove(t)) + item.name = table.concat(t, ':') + return item +end + +function Craft.getItemCount(items, item) + if type(item) == 'string' then + item = splitKey(item) + end + + local count = 0 + for _,v in pairs(items) do + if v.name == item.name and + (not item.damage or v.damage == item.damage) and + v.nbtHash == item.nbtHash then + if item.damage then + return v.count + end + count = count + v.count + end + end + return count +end + +local function turtleCraft(recipe, qty, inventoryAdapter) + if not clearGrid(inventoryAdapter) then + return false + end + + for k,v in pairs(recipe.ingredients) do + local item = splitKey(v) + local provideQty = qty + --[[ + Turtles can only craft 1 item at a time when using a tool. + + if recipe.craftingTools and recipe.craftingTools[k] then + provideQty = 1 + end + ]]-- + inventoryAdapter:provide(item, provideQty, k) + if turtle.getItemCount(k) == 0 then -- ~= qty then + -- FIX: ingredients cannot be stacked +--debug('failed ' .. v .. ' - ' .. provideQty) + return false + end + end + + return turtle.craft() +end + +function Craft.loadRecipes() + Craft.recipes = { } + + Util.merge(Craft.recipes, (Util.readTable(fs.combine(RECIPES_DIR, 'minecraft.db')) or { }).recipes) + + local config = Util.readTable('usr/config/recipeBooks.db') or { } + for _, book in pairs(config) do + local recipeFile = Util.readTable(book) + Util.merge(Craft.recipes, recipeFile.recipes) + end + + local recipes = Util.readTable(USER_RECIPES) or { } + Util.merge(Craft.recipes, recipes) +end + +function Craft.sumIngredients(recipe) + -- produces { ['minecraft:planks:0'] = 8 } + local t = { } + for _,item in pairs(recipe.ingredients) do + t[item] = (t[item] or 0) + 1 + end +-- need a check for crafting tool + return t +end + +function Craft.craftRecipe(recipe, count, inventoryAdapter) + if type(recipe) == 'string' then + recipe = Craft.recipes[recipe] + if not recipe then + return 0, 'No recipe' + end + end + + local items = inventoryAdapter:listItems() + if not items then + return 0, 'Inventory changed' + end + + count = math.ceil(count / recipe.count) + local maxCount = recipe.maxCount or math.floor(64 / recipe.count) + + for key,icount in pairs(Craft.sumIngredients(recipe)) do + local itemCount = Craft.getItemCount(items, key) + local need = icount * count + if recipe.craftingTools and recipe.craftingTools[key] then + need = 1 + end + maxCount = math.min(maxCount, itemDB:getMaxCount(key)) + if itemCount < need then + local irecipe = Craft.findRecipe(key) + if irecipe then + local iqty = need - itemCount + local crafted = Craft.craftRecipe(irecipe, iqty, inventoryAdapter) + if crafted ~= iqty then + turtle.select(1) + return 0 + end + end + end + end + + local crafted = 0 + repeat + if not turtleCraft(recipe, math.min(count, maxCount), inventoryAdapter) then + turtle.select(1) + break + end + crafted = crafted + math.min(count, maxCount) + count = count - maxCount + until count <= 0 + + turtle.select(1) + return crafted * recipe.count +end + +local function makeRecipeKey(item) + if type(item) == 'string' then + item = splitKey(item) + end + return table.concat({ item.name, item.damage or 0, item.nbtHash }, ':') +end + +function Craft.findRecipe(key) + if type(key) ~= 'string' then + key = itemDB:makeKey(key) + end + + local item = itemDB:splitKey(key) + if item.damage then + return Craft.recipes[makeRecipeKey(item)] + end + + -- handle cases where the request is like : IC2:reactorVent:* + for rkey,recipe in pairs(Craft.recipes) do + local r = itemDB:splitKey(rkey) + if item.name == r.name and + (not item.nbtHash or r.nbtHash == item.nbtHash) then + return recipe + end + end +end + +-- determine the full list of ingredients needed to craft +-- a quantity of a recipe. +function Craft.getResourceList(inRecipe, items, inCount) + local summed = { } + + local function sumItems(recipe, key, count) + local item = itemDB:splitKey(key) + local summedItem = summed[key] + if not summedItem then + summedItem = Util.shallowCopy(item) + summedItem.recipe = Craft.findRecipe(key) + summedItem.count = Craft.getItemCount(items, item) + summedItem.displayName = itemDB:getName(item) + summedItem.total = 0 + summedItem.need = 0 + summedItem.used = 0 + summed[key] = summedItem + end + local total = count + local used = math.min(summedItem.count, total) + local need = total - used + + if recipe.craftingTools and recipe.craftingTools[key] then + summedItem.total = 1 + if summedItem.count > 0 then + summedItem.used = 1 + summedItem.need = 0 + need = 0 + elseif not summedItem.recipe then + summedItem.need = 1 + need = 1 + else + need = 1 + end + else + summedItem.total = summedItem.total + total + summedItem.count = summedItem.count - used + summedItem.used = summedItem.used + used + if not summedItem.recipe then + summedItem.need = summedItem.need + need + end + end + + if need > 0 and summedItem.recipe then + need = math.ceil(need / summedItem.recipe.count) + for ikey,iqty in pairs(Craft.sumIngredients(summedItem.recipe)) do + sumItems(summedItem.recipe, ikey, math.ceil(need * iqty)) + end + end + end + + inCount = math.ceil(inCount / inRecipe.count) + for ikey,iqty in pairs(Craft.sumIngredients(inRecipe)) do + sumItems(inRecipe, ikey, math.ceil(inCount * iqty)) + end + + return summed +end + +function Craft.getResourceList4(inRecipe, items, count) + local summed = Craft.getResourceList(inRecipe, items, count) +-- filter down to just raw materials + return Util.filter(summed, function(a) return a.used > 0 or a.need > 0 end) +end + +-- given a certain quantity, return how many of those can be crafted +function Craft.getCraftableAmount(inRecipe, count, items, missing) + local function sumItems(recipe, summedItems, count) + local canCraft = 0 + + for _ = 1, count do + for _,item in pairs(recipe.ingredients) do + local summedItem = summedItems[item] or Craft.getItemCount(items, item) + + local irecipe = Craft.findRecipe(item) + if irecipe and summedItem <= 0 then + summedItem = summedItem + sumItems(irecipe, summedItems, 1) + end + if summedItem <= 0 then + if missing and not irecipe then + missing.name = item + end + return canCraft + end + if not recipe.craftingTools or not recipe.craftingTools[item] then + summedItems[item] = summedItem - 1 + end + end + canCraft = canCraft + recipe.count + end + + return canCraft + end + + return sumItems(inRecipe, { }, math.ceil(count / inRecipe.count)) +end + +function Craft.canCraft(item, count, items) + return Craft.getCraftableAmount(Craft.recipes[item], count, items) == count +end + +function Craft.setRecipes(recipes) + Craft.recipes = recipes +end + +function Craft.getCraftableAmountTest() + local results = { } + Craft.setRecipes(Util.readTable('usr/etc/recipes.db')) + + local items = { + { name = 'minecraft:planks', damage = 0, count = 5 }, + { name = 'minecraft:log', damage = 0, count = 2 }, + } + results[1] = { item = 'chest', expected = 1, + got = Craft.getCraftableAmount(Craft.recipes['minecraft:chest:0'], 2, items) } + + items = { + { name = 'minecraft:log', damage = 0, count = 1 }, + { name = 'minecraft:coal', damage = 1, count = 1 }, + } + results[2] = { item = 'torch', expected = 4, + got = Craft.getCraftableAmount(Craft.recipes['minecraft:torch:0'], 4, items) } + + return results +end + +function Craft.craftRecipeTest(name, count) + local ChestAdapter = require('chestAdapter18') + local chestAdapter = ChestAdapter({ wrapSide = 'top', direction = 'down' }) + Craft.setRecipes(Util.readTable('usr/etc/recipes.db')) + return { Craft.craftRecipe(Craft.recipes[name], count, chestAdapter) } +end + +Craft.loadRecipes() + +return Craft diff --git a/inventoryManager/apis/inventoryAdapter.lua b/inventoryManager/apis/inventoryAdapter.lua new file mode 100644 index 0000000..3f16327 --- /dev/null +++ b/inventoryManager/apis/inventoryAdapter.lua @@ -0,0 +1,55 @@ +local Adapter = { } + +function Adapter.wrap(args) + local adapters = { + 'networkedAdapter18', + 'refinedAdapter', + 'meAdapter18', + 'chestAdapter18', + + -- adapters for version 1.7 + 'meAdapter', + 'chestAdapter', + } + + for _,adapterType in ipairs(adapters) do + local adapter = require(adapterType)(args) + + if adapter:isValid() then + + -- figure out which direction to push/pull items from an inventory + -- based on the side the inventory is attached and which way the + -- turtle/computer is facing + if args and args.facing and adapter.side and not adapter.direction then + local horz = { top = 'down', bottom = 'up' } + adapter.direction = horz[adapter.side] + + if not adapter.direction then + local sides = { + front = 0, + right = 1, + back = 2, + left = 3, + } + -- pretty sure computer/turtle have sides reversed + local cards = { + east = 0, + south = 1, + west = 2, + north = 3, + } + local icards = { + [ 0 ] = 'west', + [ 1 ] = 'north', + [ 2 ] = 'east', + [ 3 ] = 'south', + } + adapter.direction = icards[(cards[args.facing] + sides[adapter.side]) % 4] + end + end + return adapter + end + end +end + +return Adapter diff --git a/inventoryManager/apis/networkedAdapter18.lua b/inventoryManager/apis/networkedAdapter18.lua new file mode 100644 index 0000000..2e3305f --- /dev/null +++ b/inventoryManager/apis/networkedAdapter18.lua @@ -0,0 +1,145 @@ +local class = require('class') +local Util = require('util') +local InventoryAdapter = require('inventoryAdapter') +local itemDB = require('itemDB') +local Peripheral = require('peripheral') + +local NetworkedAdapter = class() + +function NetworkedAdapter:init(args) + local defaults = { + name = 'Networked Adapter', + remotes = { }, + } + Util.merge(self, defaults) + Util.merge(self, args) + + if not self.side or self.side == 'network' then + self.chests = { } + self.modem = Peripheral.get('wired_modem') + + if self.modem and self.modem.getNameLocal then + self.localName = self.modem.getNameLocal() + for _, v in pairs(self.modem.getNamesRemote()) do + local remote = Peripheral.get({ name = v }) + if remote and remote.size and remote.size() >= 27 and remote.list then + + local adapter = InventoryAdapter.wrap({ side = v, direction = self.localName }) + if adapter then + table.insert(self.remotes, adapter) + end + end + end + end + end + + _G._p = self +end + +function NetworkedAdapter:isValid() + return #self.remotes > 0 +end + +function NetworkedAdapter:refresh(throttle) + return self:listItems(throttle) +end + +-- provide a consolidated list of items +function NetworkedAdapter:listItems(throttle) + local cache = { } + local items = { } + throttle = throttle or Util.throttle() + + for _, v in pairs(self.remotes) do + v.__cache = v:listItems(throttle) + + for k,v in pairs(v.__cache) do + if v.count > 0 then + local key = table.concat({ v.name, v.damage, v.nbtHash }, ':') + + 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 + + if not Util.empty(items) then + self.cache = cache + return items + end +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:getPercentUsed() + if self.cache and self.getDrawerCount then + return math.floor(Util.size(self.cache) / self.getDrawerCount() * 100) + end + return 0 +end + +function NetworkedAdapter:provide(item, qty, slot, direction) + local total = 0 + + for _, remote in pairs(self.remotes) do +debug('%s -> slot %d: %d %s', remote.side, slot, qty, item.name) + local amount = remote:provide(item, qty, slot) + 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) + local total = 0 + for _, remote in pairs(self.remotes) do +debug('slot %d -> %s: %s', slot, remote.side, qty) + local amount = remote:insert(slot, qty, toSlot) + qty = qty - amount + total = total + amount + if qty <= 0 then + break + end + end + + return total +end + +return NetworkedAdapter diff --git a/inventoryManager/apps/Crafter.lua b/inventoryManager/apps/Crafter.lua new file mode 100644 index 0000000..0589d0e --- /dev/null +++ b/inventoryManager/apps/Crafter.lua @@ -0,0 +1,1130 @@ +--[[ + Turtle/machine crafting. + + Requirements: + Turtle must be restricted forward and back by some obstacle. + The turtle must have access to the main inventory at the most forward location. + Machines must be placed above or below the line along the turtle's backwards travel. + + Optional: + Monitors can be placed touching the turtle at the most forward position to + display crafting status. + + Sample setups: + M = machine, I = inventory, O = obstacle, T = turtle + + Turtle facing <--- + + MMMM + IT O + MMM + + IMMMM + O O + MMMMMM +]]-- + +_G.requireInjector() + +local InventoryAdapter = require('inventoryAdapter') +local Config = require('config') +local Event = require('event') +local itemDB = require('itemDB') +local Peripheral = require('peripheral') +local UI = require('ui') +local Terminal = require('terminal') +local Util = require('util') + +local colors = _G.colors +local os = _G.os +local term = _G.term +local turtle = _G.turtle + +local config = { + computerFacing = 'north', + inventorySide = 'front', + monitor = 'type/monitor', +} +Config.load('crafter', config) + +repeat until not turtle.forward() + +local inventoryAdapter = InventoryAdapter.wrap({ + side = config.inventorySide, + facing = config.computerFacing +}) +if not inventoryAdapter then + error('Invalid inventory configuration') +end + +local RESOURCE_FILE = 'usr/config/resources.db' +local RECIPES_FILE = 'usr/config/recipes2.db' +local MACHINES_FILE = 'usr/config/machines.db' + +local STATUS_ERROR = 'error' +local STATUS_INFO = 'info' +local STATUS_SUCCESS = 'success' +local STATUS_WARNING = 'warning' + +local recipes = Util.readTable(RECIPES_FILE) or { } +local resources +local machines = { } +local jobListGrid +local listing, docked = false, false + +local function getItem(items, inItem, ignoreDamage, ignoreNbtHash) + for _,item in pairs(items) do + if item.name == inItem.name and + (ignoreDamage or item.damage == inItem.damage) and + (ignoreNbtHash or item.nbtHash == inItem.nbtHash) then + return item + end + end +end + +local function uniqueKey(item) + return table.concat({ item.name, item.damage, item.nbtHash }, ':') +end + +local function getItemQuantity(items, res) + local count = 0 + for _,v in pairs(items) do + if res.name == v.name and + ((not res.damage and v.maxDamage > 0) or res.damage == v.damage) and + ((not res.nbtHash and v.nbtHash) or res.nbtHash == v.nbtHash) then + count = count + v.count + end + end + return count +end + +local function mergeResources(t) + for _,v in pairs(resources) do + local item = getItem(t, v) + if item then + Util.merge(item, v) + else + item = Util.shallowCopy(v) + item.count = 0 + table.insert(t, item) + end + end + + for k in pairs(recipes) do + local v = itemDB:splitKey(k) + local item = getItem(t, v) + if not item then + item = Util.shallowCopy(v) + item.count = 0 + table.insert(t, item) + end + item.has_recipe = true + end + + for _,v in pairs(t) do + if not v.displayName then + v.displayName = itemDB:getName(v) + end + v.lname = v.displayName:lower() + end +end + +local function filterItems(t, filter) + if filter then + local r = {} + filter = filter:lower() + for _,v in pairs(t) do + if string.find(v.lname, filter) then + table.insert(r, v) + end + end + return r + end + return t +end + +local function clearGrid() + for i = 1, 16 do + local count = turtle.getItemCount(i) + if count > 0 then + inventoryAdapter:insert(i, count) + if turtle.getItemCount(i) ~= 0 then + return false + end + end + end + return true +end + +local function undock() + while listing do + os.sleep(.5) + end + docked = false +end + +local function gotoMachine(machine) + undock() + for _ = 1, machine.index do + if not turtle.back() then + return + end + end + + return true +end + +local function dock() + if not docked then + repeat until not turtle.forward() + end + docked = true +end + +local function getItems() + while not docked do + os.sleep(.5) + end + + listing = true + + local items + for _ = 1, 5 do + items = inventoryAdapter:listItems() + if items then + break + end + end + if not items then + error('could not check inventory') + end + + listing = false + + return items +end + +local function isMachineEmpty(machine, item) + local list = { true } + + pcall(function() -- fails randomly in 1.7x + local side = turtle.getAction(machine.dir).side + local methods = Util.transpose(Peripheral.getMethods(side)) + + if methods.getAllStacks then -- 1.7x + list = Peripheral.call(side, 'getAllStacks', false) + elseif methods.list then + list = Peripheral.call(side, 'list') + elseif methods.getProgress then + if Peripheral.call(side, 'getProgress') == 0 then + return true + end + else + item.statusCode = STATUS_ERROR + item.status = 'Unable to check empty status' + return + end + + if tonumber(machine.ignoreSlot) then + list[tonumber(machine.ignoreSlot)] = nil + end + end) + + if Util.empty(list) then + return true + end + item.statusCode = STATUS_INFO + item.status = 'machine busy' +end + +local function craftItem(ikey, item, items, machineStatus) + dock() + + local resource = resources[ikey] + if not resource or not resource.machine then + item.statusCode = STATUS_ERROR + item.status = 'machine not defined' + return + end + + local machine = Util.find(machines, 'order', resource.machine) + if not machine then + item.statusCode = STATUS_ERROR + item.status = 'invalid machine' + return + end + + local ms = machineStatus[machine.order] + if not ms then + ms = { count = 0 } + machineStatus[machine.order] = ms + end + + local slot = 1 + local maxCount = math.ceil(item.need / item.recipe.count) + maxCount = math.min(machine.maxCount or 64, maxCount) + + if maxCount > 0 and maxCount - ms.count <= 0 then + item.statusCode = STATUS_INFO + item.status = 'machine busy' + return + end + + maxCount = maxCount - ms.count + + for key,qty in pairs(item.recipe.ingredients) do + local ingredient = itemDB:get(key) + local c = math.min(maxCount * qty, getItemQuantity(items, ingredient)) + c = math.min(c, ingredient.maxCount) + c = math.floor(c / qty) + if c < maxCount then + maxCount = c + end + if maxCount <= 0 then + item.status = 'Missing ' .. ingredient.displayName + item.statusCode = STATUS_WARNING + return + end + end + + if machine.maxCount then + ms.count = ms.count + maxCount + end + + turtle.setStatus('Craft: ' .. itemDB:getName(ikey)) + for key,qty in pairs(item.recipe.ingredients) do + local ingredient = itemDB:get(key) +-- local c = item.craftable * qty +-- while c > 0 do +--debug(key) + inventoryAdapter:provide(ingredient, maxCount * qty, slot) + if turtle.getItemCount(slot) ~= maxCount * qty then + item.status = 'Extract failed: ' .. (ingredient.displayName or itemDB:getName(ingredient)) + item.statusCode = STATUS_ERROR + return + end +-- c = c - maxCount + slot = slot + 1 + --end + end + + if not gotoMachine(machine) then + item.status = 'failed to find machine' + item.statusCode = STATUS_ERROR + else + if machine.empty and not isMachineEmpty(machine, item) then + return + end + + if machine.dir == 'up' then + turtle.emptyInventory(turtle.dropUp) + else + turtle.emptyInventory(turtle.dropDown) + end + if #turtle.getFilledSlots() ~= 0 then + item.statusCode = STATUS_INFO + item.status = 'machine busy' + else + item.statusCode = STATUS_SUCCESS + item.status = 'crafting' + end + end +end + +local function expandList(list, items) + local summed = { } + + local function sumItems(key, count) + local item = itemDB:splitKey(key) + local summedItem = summed[key] + if not summedItem then + summedItem = Util.shallowCopy(item) + summedItem.recipe = recipes[key] + summedItem.count = getItemQuantity(items, item) + summedItem.displayName = itemDB:getName(item) + summedItem.total = 0 + summedItem.need = 0 + summedItem.used = 0 + summedItem.craftable = 0 + summed[key] = summedItem + end + local total = count + local used = math.min(summedItem.count, total) + local need = total - used + + summedItem.total = summedItem.total + total + summedItem.count = summedItem.count - used + summedItem.used = summedItem.used + used + summedItem.need = summedItem.need + need + + if need > 0 and summedItem.recipe then + need = math.ceil(need / summedItem.recipe.count) + for ikey,iqty in pairs(summedItem.recipe.ingredients) do + sumItems(ikey, math.ceil(need * iqty)) + end + end + end + + for key, item in pairs(list) do + sumItems(key, item.count) + end + + return Util.filter(summed, function(a) return a.need > 0 end) +end + +local function watchResources(items) + local craftList = { } + + for _,res in pairs(resources) do + if res.low then + local item = Util.shallowCopy(res) + item.nbtHash = res.nbtHash + item.damage = res.damage + if res.ignoreDamage then + item.damage = nil + end + item.count = getItemQuantity(items, item) + if item.count < res.low then + item.displayName = itemDB:getName(res) + item.count = res.low -- - item.count + craftList[uniqueKey(res)] = item + end + end + end + + return craftList +end + +local function craftItems() + local machineStatus = { } + local items = getItems() + local craftList = watchResources(items) + local list = expandList(craftList, items) + jobListGrid:setValues(list) + jobListGrid:update() + jobListGrid:draw() + jobListGrid:sync() + for key, item in pairs(list) do + if item.need > 0 and item.recipe then + craftItem(key, item, items, machineStatus) + dock() + items = getItems() + clearGrid() + elseif item.need > 0 then + item.status = 'no recipe' + item.statusCode = STATUS_ERROR + end + jobListGrid:update() + jobListGrid:draw() + jobListGrid:sync() + end + turtle.setStatus('idle') +end + +local function loadResources() + resources = Util.readTable(RESOURCE_FILE) or { } + for k,v in pairs(resources) do + Util.merge(v, itemDB:splitKey(k)) + end +end + +local function saveResources() + local t = { } + + for k,v in pairs(resources) do + v = Util.shallowCopy(v) + + v.name = nil + v.damage = nil + v.nbtHash = nil + t[k] = v + end + + Util.writeTable(RESOURCE_FILE, t) +end + +local function findMachines() + turtle.setStatus('Inspecting machines') + + dock() + + local function getName(dir) + local side = turtle.getAction(dir).side + if Peripheral.isPresent(side) then + local methods = Util.transpose(Peripheral.getMethods(side)) + if methods.getMetadata then + local name = Peripheral.call(side, 'getMetadata').displayName + if name and not string.find(name, '.', 1, true) then + return name + end + elseif methods.getInventoryName then -- 1.7x + return Peripheral.call(side, 'getInventoryName') + end + return Peripheral.getType(side) + end + local _, machine = turtle.getAction(dir).inspect() + if not machine or type(machine) ~= 'table' then + return 'Unknown' + end + return machine.name or 'Unknown' + end + + local index = 0 + + local function getMachine(dir) + local name = getName(dir) + table.insert(machines, { + name = name, + rawName = name, + index = index, + dir = dir, + order = #machines + 1 + }) + end + + repeat + getMachine('down') + getMachine('up') + index = index + 1 + undock() + until not turtle.back() + + local mf = Util.readTable(MACHINES_FILE) or { } + for _,m in pairs(machines) do + local m2 = Util.find(mf, 'order', m.order) + if m2 then + if not m2.rawName then + m2.rawName = m.rawName + end + if m.rawName == m2.rawName then + m.name = m2.name or m.name + end + m.empty = m2.empty + m.ignore = m2.ignore + m.ignoreSlot = m2.ignoreSlot + m.maxCount = m2.maxCount + end + end +end + +local function jobMonitor() + local mon = Peripheral.lookup(config.monitor) + + if mon then + mon = UI.Device({ + device = mon, + textScale = .5, + }) + else + mon = UI.Device({ + device = Terminal.getNullTerm(term.current()) + }) + end + + jobListGrid = UI.Grid({ + parent = mon, + sortColumn = 'displayName', + columns = { + { heading = 'Qty', key = 'need', width = 6 }, + { heading = 'Crafting', key = 'displayName', width = (mon.width - 18) / 2 }, + { heading = 'Status', key = 'status', }, + }, + }) + + function jobListGrid:getRowTextColor(row, selected) + if row.statusCode == STATUS_ERROR then + return colors.red + elseif row.statusCode == STATUS_WARNING then + return colors.yellow + elseif row.statusCode == STATUS_SUCCESS then + return colors.lime + end + + return UI.Grid:getRowTextColor(row, selected) + end + + jobListGrid:draw() + jobListGrid:sync() +end + +local itemPage = UI.Page { + titleBar = UI.TitleBar { + title = 'Limit Resource', + previousPage = true, + event = 'form_cancel', + }, + form = UI.Form { + x = 1, y = 2, height = 10, ex = -1, + [1] = UI.TextEntry { + width = 7, + formLabel = 'Min', formKey = 'low', help = 'Craft if below min' + }, + [2] = UI.Chooser { + width = 7, + formLabel = 'Ignore Dmg', formKey = 'ignoreDamage', + nochoice = 'No', + choices = { + { name = 'Yes', value = true }, + { name = 'No', value = false }, + }, + help = 'Ignore damage of item' + }, + [3] = UI.Button { + text = 'Select', event= 'selectMachine', + formLabel = 'Machine' + }, + info = UI.TextArea { + x = 2, ex = -2, y = 6, height = 3, + textColor = colors.gray, + }, + button = UI.Button { + x = 2, y = 9, + text = 'Recipe', event = 'learn', + }, + }, + machines = UI.SlideOut { + backgroundColor = colors.cyan, + titleBar = UI.TitleBar { + title = 'Select Machine', + previousPage = true, + }, + grid = UI.ScrollingGrid { + y = 2, ey = -4, + values = machines, + disableHeader = true, + columns = { + { heading = '', key = 'index', width = 2 }, + { heading = 'Name', key = 'name'}, + }, + sortColumn = 'order', + }, + button1 = UI.Button { + x = -14, y = -2, + text = 'Ok', event = 'setMachine', + }, + button2 = UI.Button { + x = -9, y = -2, + text = 'Cancel', event = 'cancelMachine', + }, + }, + statusBar = UI.StatusBar { } +} + +function itemPage:enable(item) + if item then + self.item = Util.shallowCopy(item) + self.form:setValues(item) + self.titleBar.title = item.displayName or item.name + end + UI.Page.enable(self) + self:focusFirst() +end + +function itemPage.form.info:draw() + local recipe = recipes[uniqueKey(itemPage.item)] + self.value = '' + if recipe and itemPage.item.machine then + self.value = string.format('Crafts %d using the %s machine', + recipe.count, + machines[itemPage.item.machine].name) + end + UI.TextArea.draw(self) +end + +function itemPage.machines.grid:getRowTextColor(row, selected) + if itemPage.item.machine == row.order then + return colors.yellow + end + return UI.Grid:getRowTextColor(row, selected) +end + +--[[ +function itemPage.machines:eventHandler(event) + if event.type == 'grid_focus_row' then + self.statusBar:setStatus(string.format('%d %s', event.selected.index, event.selected.dir)) + else + return UI.SlideOut.eventHandler(self, event) + end + return true +end +]] + +function itemPage:eventHandler(event) + if event.type == 'form_cancel' then + UI:setPreviousPage() + + elseif event.type == 'learn' then + UI:setPage('learn', self.item) + + elseif event.type == 'setMachine' then + self.item.machine = self.machines.grid:getSelected().order + self.machines:hide() + + elseif event.type == 'cancelMachine' then + self.machines:hide() + + elseif event.type == 'selectMachine' then + local machineCopy = Util.shallowCopy(machines) + Util.filterInplace(machineCopy, function(m) return not m.ignore end) + self.machines.grid:setValues(machineCopy) + if self.item.machine then + local _, index = Util.find(machineCopy, 'order', self.item.machine) + if index then + self.machines.grid:setIndex(index) + end + else + self.machines.grid:setIndex(1) + end + self.machines:show() + + elseif event.type == 'focus_change' then + self.statusBar:setStatus(event.focused.help) + self.statusBar:draw() + + elseif event.type == 'form_complete' then + local values = self.form.values + local keys = { 'name', 'low', 'damage', 'nbtHash', 'machine' } + + local filtered = { } + for _,key in pairs(keys) do + filtered[key] = values[key] + end + filtered.low = tonumber(filtered.low) + filtered.machine = self.item.machine + + if values.ignoreDamage == true then + filtered.damage = 0 + filtered.ignoreDamage = true + end + + local key = uniqueKey(filtered) + + resources[key] = filtered + saveResources() + + UI:setPreviousPage() + + else + return UI.Page.eventHandler(self, event) + end + return true +end + +local learnPage = UI.Page { + ingredients = UI.ScrollingGrid { + y = 2, height = 3, + disableHeader = true, + columns = { + { heading = 'Name', key = 'displayName', width = 31 }, + { heading = 'Qty', key = 'count' , width = 5 }, + }, + sortColumn = 'displayName', + }, + grid = UI.ScrollingGrid { + y = 6, height = 5, + disableHeader = true, + columns = { + { heading = 'Name', key = 'displayName', width = 31 }, + { heading = 'Qty', key = 'count' , width = 5 }, + }, + sortColumn = 'displayName', + }, + filter = UI.TextEntry { + x = 20, ex = -2, y = 5, + limit = 50, + shadowText = 'filter', + backgroundColor = colors.lightGray, + backgroundFocusColor = colors.lightGray, + }, + count = UI.TextEntry { + x = 11, y = -1, width = 5, + limit = 50, + }, + button1 = UI.Button { + x = -14, y = -1, + text = 'Ok', event = 'accept', + }, + button2 = UI.Button { + x = -9, y = -1, + text = 'Cancel', event = 'cancel', + }, +} + +function learnPage:enable(target) + self.target = target + self.allItems = getItems() + mergeResources(self.allItems) + + self.filter.value = '' + self.grid.values = self.allItems + self.grid:update() + self.ingredients.values = { } + self.count.value = 1 + + if target.has_recipe then + local recipe = recipes[uniqueKey(target)] + self.count.value = recipe.count + for k,v in pairs(recipe.ingredients) do + self.ingredients.values[k] = + { name = k, count = v, displayName = itemDB:getName(k) } + end + end + self.ingredients:update() + + self:setFocus(self.filter) + UI.Page.enable(self) +end + +function learnPage:draw() + UI.Window.draw(self) + self:write(2, 1, 'Ingredients', nil, colors.yellow) + self:write(2, 5, 'Inventory', nil, colors.yellow) + self:write(2, 12, 'Produces') +end + +function learnPage:eventHandler(event) + + if event.type == 'text_change' and event.element == self.filter then + local t = filterItems(learnPage.allItems, event.text) + self.grid:setValues(t) + self.grid:draw() + + elseif event.type == 'cancel' then + UI:setPreviousPage() + + elseif event.type == 'accept' then + + local recipe = { + count = tonumber(self.count.value) or 1, + ingredients = { }, + } + for key, item in pairs(self.ingredients.values) do + recipe.ingredients[key] = item.count + end + recipes[uniqueKey(self.target)] = recipe + Util.writeTable(RECIPES_FILE, recipes) + + UI:setPreviousPage() + + elseif event.type == 'grid_select' then + if event.element == self.grid then + local key = uniqueKey(event.selected) + if not self.ingredients.values[key] then + self.ingredients.values[key] = Util.shallowCopy(event.selected) + self.ingredients.values[key].count = 0 + end + self.ingredients.values[key].count = self.ingredients.values[key].count + 1 + self.ingredients:update() + self.ingredients:draw() + elseif event.element == self.ingredients then + event.selected.count = event.selected.count - 1 + if event.selected.count == 0 then + self.ingredients.values[uniqueKey(event.selected)] = nil + self.ingredients:update() + end + self.ingredients:draw() + end + + else + return UI.Page.eventHandler(self, event) + end + return true +end + +local machinesPage = UI.Page { + titleBar = UI.TitleBar { + previousPage = true, + title = 'Machines', + }, + grid = UI.ScrollingGrid { + y = 2, ey = -2, + values = machines, + columns = { + { heading = 'Name', key = 'name' }, + { heading = 'Side', key = 'dir', width = 5 }, + { heading = 'Index', key = 'index', width = 5 }, + }, + sortColumn = 'order', + }, + detail = UI.SlideOut { + backgroundColor = colors.cyan, + form = UI.Form { + x = 1, y = 2, ex = -1, ey = -2, + [1] = UI.TextEntry { + formLabel = 'Name', formKey = 'name', help = '...', + limit = 64, + }, + [2] = UI.Chooser { + width = 7, + formLabel = 'Hidden', formKey = 'ignore', + nochoice = 'No', + choices = { + { name = 'Yes', value = true }, + { name = 'No', value = false }, + }, + help = 'Do not show this machine' + }, + [3] = UI.Chooser { + width = 7, + formLabel = 'Empty', formKey = 'empty', + nochoice = 'No', + choices = { + { name = 'Yes', value = true }, + { name = 'No', value = false }, + }, + help = 'Check if machine is empty before crafting' + }, + [4] = UI.TextEntry { + formLabel = 'Ignore Slot', formKey = 'ignoreSlot', help = '...', + limit = 4, + }, + [5] = UI.TextEntry { + formLabel = 'Max Craft', formKey = 'maxCount', help = '...', + limit = 4, + }, + }, + statusBar = UI.StatusBar(), + }, + statusBar = UI.StatusBar { + values = 'Select Machine', + }, + accelerators = { + h = 'toggle_hidden', + } +} + +function machinesPage:enable() + self.grid:update() + UI.Page.enable(self) +end + +function machinesPage.detail:eventHandler(event) + if event.type == 'focus_change' then + self.statusBar:setStatus(event.focused.help) + end + return UI.SlideOut.eventHandler(self, event) +end + +function machinesPage.grid:getRowTextColor(row, selected) + if row.ignore then + return colors.yellow + end + return UI.Grid:getRowTextColor(row, selected) +end + +function machinesPage:eventHandler(event) + if event.type == 'grid_select' then + self.detail.form:setValues(event.selected) + self.detail:show() + + elseif event.type == 'toggle_hidden' then + local selected = self.grid:getSelected() + if selected then + selected.ignore = not selected.ignore + Util.writeTable(MACHINES_FILE, machines) + self:draw() + end + + elseif event.type == 'form_complete' then + self.detail.form.values.empty = self.detail.form.values.empty == true + self.detail.form.values.ignore = self.detail.form.values.ignore == true + self.detail.form.values.ignoreSlot = tonumber(self.detail.form.values.ignoreSlot) + self.detail.form.values.maxCount = tonumber(self.detail.form.values.maxCount) + Util.writeTable(MACHINES_FILE, machines) + self.detail:hide() + + elseif event.type == 'form_cancel' then + self.detail:hide() + + else + UI.Page.eventHandler(self, event) + end + return true +end + +local listingPage = UI.Page { + menuBar = UI.MenuBar { + buttons = { + { text = 'Forget', event = 'forget' }, + { text = 'Machines', event = 'machines' }, + { text = 'Refresh', event = 'refresh', x = -9 }, + }, + }, + grid = UI.Grid { + y = 2, height = UI.term.height - 2, + columns = { + { heading = 'Name', key = 'displayName' }, + { heading = 'Qty', key = 'count' , width = 5 }, + { heading = 'Min', key = 'low' , width = 4 }, + }, + sortColumn = 'displayName', + }, + statusBar = UI.Window { + y = -1, + filter = UI.TextEntry { + limit = 50, + shadowText = 'filter', + shadowTextColor = colors.lightGray, + backgroundColor = colors.gray, + backgroundFocusColor = colors.gray, + }, + }, + accelerators = { + r = 'refresh', + q = 'quit', + } +} + +function listingPage.grid:getRowTextColor(row, selected) + if row.is_craftable then + return colors.yellow + end + if row.has_recipe then + return colors.cyan + end + return UI.Grid:getRowTextColor(row, selected) +end + +function listingPage.grid:getDisplayValues(row) + row = Util.shallowCopy(row) + row.count = Util.toBytes(row.count) + if row.low then + row.low = Util.toBytes(row.low) + end + return row +end + +function listingPage.statusBar.filter:eventHandler(event) + if event.type == 'mouse_rightclick' then + self.value = '' + self:draw() + local page = UI:getCurrentPage() + page.filter = nil + page:applyFilter() + page.grid:draw() + page:setFocus(self) + end + return UI.TextEntry.eventHandler(self, event) +end + +function listingPage:eventHandler(event) + if event.type == 'quit' then + UI:exitPullEvents() + + elseif event.type == 'grid_select' then + local selected = event.selected + UI:setPage('item', selected) + + elseif event.type == 'refresh' then + self:refresh() + self.grid:draw() + self.statusBar.filter:focus() + + elseif event.type == 'machines' then + UI:setPage('machines') + + elseif event.type == 'craft' then + UI:setPage('craft', self.grid:getSelected()) + + elseif event.type == 'forget' then + local item = self.grid:getSelected() + if item then + local key = uniqueKey(item) + + if recipes[key] then + recipes[key] = nil + Util.writeTable(RECIPES_FILE, recipes) + end + + if resources[key] then + resources[key] = nil + Util.writeTable(RESOURCE_FILE, resources) + end + + self.statusBar:timedStatus('Forgot: ' .. item.name, 3) + self:refresh() + self.grid:draw() + end + + elseif event.type == 'text_change' then + self.filter = event.text + if #self.filter == 0 then + self.filter = nil + end + self:applyFilter() + self.grid:draw() + self.statusBar.filter:focus() + + else + UI.Page.eventHandler(self, event) + end + return true +end + +function listingPage:enable() + self:refresh() + self:setFocus(self.statusBar.filter) + UI.Page.enable(self) +end + +function listingPage:refresh() + self.allItems = getItems() + mergeResources(self.allItems) + self:applyFilter() +end + +function listingPage:applyFilter() + local t = filterItems(self.allItems, self.filter) + self.grid:setValues(t) +end + +-- randomly errors in 1.7x with "you are not attached to this computer" +print('Inspecting machines') +local retryCount = 0 +while true do + Util.clear(machines) + local s, m = pcall(findMachines) + if not s and m then + _G.printError(m) + else + break + end + retryCount = retryCount + 1 + if retryCount > 3 then + error(m) + end + print('retrying...') +end + +loadResources() +dock() +clearGrid() +jobMonitor() + +UI:setPages({ + listing = listingPage, + machines = machinesPage, + item = itemPage, + learn = learnPage, +}) + +UI:setPage(listingPage) +listingPage:setFocus(listingPage.statusBar.filter) + +Event.on('turtle_abort', function() + UI:exitPullEvents() +end) + +Event.onInterval(30, function() + dock() + if turtle.getFuelLevel() < 100 then + turtle.select(1) + inventoryAdapter:provide({ name = 'minecraft:coal', damage = 1 }, 16, 1) + turtle.refuel() + end + craftItems() +end) + +turtle.setStatus('idle') +UI:pullEvents() +jobListGrid.parent:reset() diff --git a/inventoryManager/apps/inventoryManager.lua b/inventoryManager/apps/inventoryManager.lua new file mode 100644 index 0000000..a4808eb --- /dev/null +++ b/inventoryManager/apps/inventoryManager.lua @@ -0,0 +1,1400 @@ +--[[ + Provides: autocrafting, resource limits, on-demand crafting, storage stocker. + + Using a turtle allows for crafting of items eliminating the need for AE/RS + molecular assemblers / crafters. + + Inventory setup: + Turtle/computer must be touching at least one type of inventory + + Generic inventory block such as: + Vanilla chest + RFTools modular storage + Storage drawers controller + and many others... + + Applied energistics + AE cable or interface (depending upon AE/MC version) + + Refined storage + TODO: add required block + + Turtle crafting (optional): + 1. The turtle must have a crafting table equipped. + 2. Equip the turtle with an introspection module. + + Controller (optional): + Provides the ability to request crafting from AE / RS + + Applied Energistics + In versions 1.7x, AE can be used for both inventory access and crafting + requests. + + In versions 1.8+, AE can only be used to request crafting. + + Refined Storage + In versions 1.8x, inventory access works depending upon version. + + Turtle/computer must be touching an interface for inventory access. If only + requesting crafting, the controller must be either be touching or connected + via CC cables. + + Configuration: + Configuration file is usr/config/inventoryManager + + valid sides: + top, bottom, left, right, front, back + + valid directions: + up, down, north, south, east, west + + Required: + computerFacing : direction turtle is facing + inventory : side for the main inventory (can be the same as the controller) + + Optional: + craftingChest : side for the chest used for crafting + controller : side for AE cable/interface or RS controller + trashDirection : direction of trash block (trashcan/inventory/etc) in + relationship to the main inventory. This block does not + need to touch the turtle, only the main inventory block. + monitor : valid options include: + type/monitor - will use the first monitor found + side/north - specify a direction (top/bottom/east/etc) + name/monitor_1 - specify the exact name of the peripheral +]]-- + +_G.requireInjector() + +local Ansi = require('ansi') +local Config = require('config') +local Craft = require('turtle.craft') +local Event = require('event') +local itemDB = require('itemDB') +local Peripheral = require('peripheral') +local Terminal = require('terminal') +local UI = require('ui') +local Util = require('util') + +local ControllerAdapter = require('controllerAdapter') +local InventoryAdapter = require('inventoryAdapter') + +local colors = _G.colors +local device = _G.device +local multishell = _ENV.multishell +local os = _G.os +local term = _G.term +local turtle = _G.turtle + +if multishell then + multishell.setTitle(multishell.getCurrent(), 'Resource Manager') +end + +local config = { + computerFacing = 'north', -- direction turtle is facing + + inventory = 'network', -- main inventory + controller = 'none', -- AE / RS controller + + trashDirection = 'up', -- trash/chest in relation to inventory + monitor = 'type/monitor', +} + +Config.loadWithCheck('inventoryManager', config) + +--local controllerAdapter = ControllerAdapter.wrap({ side = config.controller, facing = config.computerFacing }) +local inventoryAdapter = config.inventory == 'network' and + InventoryAdapter.wrap() or + InventoryAdapter.wrap({ side = config.inventory, facing = config.computerFacing }) + +if not inventoryAdapter then + error('Invalid inventory configuration') +end + +local introspectionModule = device['plethora:introspection'] + +local controllerAdapter +if inventoryAdapter.craft then + controllerAdapter = inventoryAdapter +end + +local STATUS_INFO = 'info' +local STATUS_WARNING = 'warning' +local STATUS_ERROR = 'error' + +local RESOURCE_FILE = 'usr/config/resources.db' +local RECIPES_FILE = 'usr/config/recipes.db' + +local craftingPaused = false +local canCraft = not not (turtle and turtle.craft) +local canLearn = not not (canCraft and introspectionModule) +local userRecipes = Util.readTable(RECIPES_FILE) or { } +local jobList +local resources +local demandCrafting = { } + +local function getItem(items, inItem, ignoreDamage, ignoreNbtHash) + for _,item in pairs(items) do + if item.name == inItem.name and + (ignoreDamage or item.damage == inItem.damage) and + (ignoreNbtHash or item.nbtHash == inItem.nbtHash) then + return item + end + end +end + +local function uniqueKey(item) + return table.concat({ item.name, item.damage, item.nbtHash }, ':') +end + +local function mergeResources(t) + for _,v in pairs(resources) do + local item = getItem(t, v) + if item then + Util.merge(item, v) + else + item = Util.shallowCopy(v) + item.count = 0 + table.insert(t, item) + end + end + + for k in pairs(Craft.recipes) do + local v = itemDB:splitKey(k) + local item = getItem(t, v) + if not item then + item = Util.shallowCopy(v) + item.count = 0 + table.insert(t, item) + end + item.has_recipe = true + end + + for _,v in pairs(t) do + if not v.displayName then + v.displayName = itemDB:getName(v) + end + v.lname = v.displayName:lower() + end +end + +local function listItems() + local items + for _ = 1, 5 do + items = inventoryAdapter:listItems() + if items then + break + end + jobList:showError('Error - retrying in 3 seconds') + os.sleep(3) + end + if not items then +-- error('could not check inventory') +term.clear() +jobList:showError('Error - rebooting in 5 seconds') +print('Communication failure') +print('rebooting in 5 secs') +os.sleep(5) +os.reboot() + end + + return items +end + +local function filterItems(t, filter, displayMode) + if filter or displayMode > 0 then + local r = { } + if filter then + filter = filter:lower() + end + for _,v in pairs(t) do + if not filter or string.find(v.lname, filter, 1, true) then + if not displayMode or + displayMode == 0 or + displayMode == 1 and v.count > 0 or + displayMode == 2 and v.has_recipe then + table.insert(r, v) + end + end + end + return r + end + return t +end + +local function clearGrid() + local function clear() + for i = 1, 16 do + local count = turtle.getItemCount(i) + if count > 0 then + inventoryAdapter:insert(i, count) + if turtle.getItemCount(i) ~= 0 then + return false + end + end + end + return true + end + return clear() or clear() +end + +local function addCraftingRequest(item, craftList, count) + local key = uniqueKey(item) + local request = craftList[key] + if not craftList[key] then + request = { name = item.name, damage = item.damage, nbtHash = item.nbtHash, count = 0 } + request.displayName = itemDB:getName(request) + craftList[key] = request + end + request.count = request.count + count + return request +end + +-- Craft +local function craftItem(recipe, items, originalItem, craftList, count) + local missing = { } + local toCraft = Craft.getCraftableAmount(recipe, count, items, missing) + if missing.name then + originalItem.status = string.format('%s missing', itemDB:getName(missing.name)) + originalItem.statusCode = STATUS_WARNING + end + + local crafted = 0 + + if toCraft > 0 then + crafted = Craft.craftRecipe(recipe, toCraft, inventoryAdapter) + clearGrid() + items = listItems() + count = count - crafted + end + + if count > 0 and items then + local ingredients = Craft.getResourceList4(recipe, items, count) + for _,ingredient in pairs(ingredients) do + if ingredient.need > 0 then + local item = addCraftingRequest(ingredient, craftList, ingredient.need) + if Craft.findRecipe(item) then + item.status = string.format('%s missing', itemDB:getName(ingredient)) + item.statusCode = STATUS_WARNING + else + item.status = 'no recipe' + item.statusCode = STATUS_ERROR + end + end + end + end + return crafted +end + +-- Craft as much as possible regardless if all ingredients are available +local function forceCraftItem(inRecipe, items, originalItem, craftList, inCount) + local summed = { } + local throttle = Util.throttle() + + local function sumItems(recipe, count) + count = math.ceil(count / recipe.count) + local craftable = count + + for key,iqty in pairs(Craft.sumIngredients(recipe)) do + throttle() + local item = itemDB:splitKey(key) + local summedItem = summed[key] + if not summedItem then + summedItem = Util.shallowCopy(item) + summedItem.recipe = Craft.findRecipe(item) + summedItem.count = Craft.getItemCount(items, key) + summedItem.need = 0 + summedItem.used = 0 + summedItem.craftable = 0 + summed[key] = summedItem + end + + local total = count * iqty -- 4 * 2 + local used = math.min(summedItem.count, total) -- 5 + local need = total - used -- 3 + + if recipe.craftingTools and recipe.craftingTools[key] then + if summedItem.count > 0 then + summedItem.used = 1 + summedItem.need = 0 + need = 0 + elseif not summedItem.recipe then + summedItem.need = 1 + need = 1 + else + need = 1 + end + else + summedItem.count = summedItem.count - used + summedItem.used = summedItem.used + used + end + + if need > 0 then + if not summedItem.recipe then + craftable = math.min(craftable, math.floor(used / iqty)) + summedItem.need = summedItem.need + need + else + local c = sumItems(summedItem.recipe, need) -- 4 + craftable = math.min(craftable, math.floor((used + c) / iqty)) + summedItem.craftable = summedItem.craftable + c + end + end + end + if craftable > 0 then + craftable = Craft.craftRecipe(recipe, craftable * recipe.count, inventoryAdapter) / recipe.count + clearGrid() + end + + return craftable * recipe.count + end + + local count = sumItems(inRecipe, inCount) + + if count < inCount then + for _,ingredient in pairs(summed) do + if ingredient.need > 0 then + local item = addCraftingRequest(ingredient, craftList, ingredient.need) + if Craft.findRecipe(item) then + item.status = string.format('%s missing', itemDB:getName(ingredient)) + item.statusCode = STATUS_WARNING + else + item.status = '(no recipe)' + item.statusCode = STATUS_ERROR + end + end + end + end + return count +end + +local function craft(recipe, items, item, craftList) + item.status = nil + item.statusCode = nil + item.crafted = 0 + + if craftingPaused or not canCraft then + return + end + + if not clearGrid() then + item.status = 'Grid obstructed' + item.statusCode = STATUS_ERROR + return + end + + if item.forceCrafting then + item.crafted = forceCraftItem(recipe, items, item, craftList, item.count) + else + item.crafted = craftItem(recipe, items, item, craftList, item.count) + end +end + +local function craftItems(craftList, allItems) + -- turtle crafting + if canCraft then + for _,key in pairs(Util.keys(craftList)) do + local item = craftList[key] + local recipe = Craft.recipes[key] + if recipe then + craft(recipe, allItems, item, craftList) + allItems = listItems() -- refresh counts + if not allItems then + break + end + elseif not controllerAdapter then + item.status = '(no recipe)' + item.statusCode = STATUS_ERROR + end + end + end + + -- redstone control + for _,item in pairs(craftList) do + if item.rsControl then + item.status = '(activated)' + item.statusCode = STATUS_INFO + end + end + + -- controller + if controllerAdapter then + for key,item in pairs(craftList) do + if (not canCraft or not Craft.recipes[key]) and not item.rsControl then + if controllerAdapter:isCrafting(item) then + item.status = '(crafting)' + item.statusCode = STATUS_INFO + elseif not controllerAdapter:isCPUAvailable() then + item.status = '(waiting)' + item.statusCode = STATUS_WARNING + else + local count = item.count + item.crafted = 0 + while count >= 1 do -- try to request smaller quantities until successful + local s = pcall(function() + item.status = '(no recipe)' + item.statusCode = STATUS_ERROR + if not controllerAdapter:craft(item, count) then + item.status = '(missing ingredients)' + item.statusCode = STATUS_WARNING + error('failed') + end + item.status = '(crafting)' + item.statusCode = STATUS_INFO + item.crafted = count + end) + if s then + break -- successfully requested crafting + end + count = math.floor(count / 2) + end + end + end + end + end + + if not controllerAdapter and not canCraft then + for _,item in pairs(craftList) do + if not item.rsControl then + item.status = 'Invalid setup' + item.statusCode = STATUS_INFO + end + end + end +end + +local function eject(item, qty) + if _G.turtle then + local s, m = pcall(function() + inventoryAdapter:provide(item, qty) + _G.turtle.emptyInventory() + end) + end +end + +local function jobMonitor() + local mon = Peripheral.lookup(config.monitor) + + if mon then + mon = UI.Device({ + device = mon, + textScale = .5, + }) + else + mon = UI.Device({ + device = Terminal.getNullTerm(term.current()) + }) + end + + jobList = UI.Page { + parent = mon, + grid = UI.Grid { + sortColumn = 'displayName', + backgroundFocusColor = colors.black, + columns = { + { heading = 'Qty', key = 'count', width = 6 }, + { heading = 'Crafting', key = 'displayName', width = mon.width / 2 - 10 }, + { heading = 'Status', key = 'status', width = mon.width - 10 }, + }, + }, + } + + function jobList:showError(msg) + self.grid:clear() + self.grid:centeredWrite(math.ceil(self.grid.height / 2), msg) + self:sync() + end + + function jobList:updateList(craftList) + self.grid:setValues(craftList) + self.grid:update() + self:draw() + self:sync() + end + + function jobList.grid:getRowTextColor(row, selected) + if row.statusCode == STATUS_ERROR then + return colors.red + elseif row.statusCode == STATUS_WARNING then + return colors.yellow + elseif row.statusCode == STATUS_INFO then + return colors.lime + end + return UI.Grid:getRowTextColor(row, selected) + end + + jobList:enable() + jobList:draw() + jobList:sync() +end + +local function getAutocraftItems() + local craftList = { } + + for _,res in pairs(resources) do + if res.auto then + res = Util.shallowCopy(res) + res.count = 256 -- this could be higher to increase autocrafting speed + local key = uniqueKey(res) + craftList[key] = res + end + end + return craftList +end + +local function getItemWithQty(items, res, ignoreDamage, ignoreNbtHash) + local item = getItem(items, res, ignoreDamage, ignoreNbtHash) + + if item and (ignoreDamage or ignoreNbtHash) then + local count = 0 + + for _,v in pairs(items) do + if item.name == v.name and + (ignoreDamage or item.damage == v.damage) and + (ignoreNbtHash or item.nbtHash == v.nbtHash) then + count = count + v.count + end + end + item.count = count + end + + return item +end + +local function watchResources(items) + local craftList = { } + local outputs = { } + + for _,res in pairs(resources) do + local item = getItemWithQty(items, res, res.ignoreDamage, res.ignoreNbtHash) + if not item then + item = { + damage = res.damage, + nbtHash = res.nbtHash, + name = res.name, + displayName = itemDB:getName(res), + count = 0 + } + end + + if res.limit and item.count > res.limit then + inventoryAdapter:provide( + { name = item.name, damage = item.damage, nbtHash = item.nbtHash }, + item.count - res.limit, + nil, + config.trashDirection) + + elseif res.low and item.count < res.low then + if res.ignoreDamage then + item.damage = 0 + end + local key = uniqueKey(res) + + craftList[key] = { + damage = item.damage, + nbtHash = item.nbtHash, + count = res.low - item.count, + name = item.name, + displayName = item.displayName, + status = '', + rsControl = res.rsControl, + } + end + + if res.rsControl and res.rsDevice and res.rsSide then + local enable = item.count < res.low + if not outputs[res.rsDevice] then + outputs[res.rsDevice] = { } + end + outputs[res.rsDevice][res.rsSide] = outputs[res.rsDevice][res.rsSide] or enable + end + end + + for rsDevice, sides in pairs(outputs) do + for side, enable in pairs(sides) do + pcall(function() + device[rsDevice].setOutput(side, enable) + end) + end + end + + return craftList +end + +local function loadResources() + resources = Util.readTable(RESOURCE_FILE) or { } + for k,v in pairs(resources) do + Util.merge(v, itemDB:splitKey(k)) + end +end + +local function saveResources() + local t = { } + + for k,v in pairs(resources) do + v = Util.shallowCopy(v) + local keys = Util.transpose({ 'auto', 'low', 'limit', + 'ignoreDamage', 'ignoreNbtHash', + 'rsControl', 'rsDevice', 'rsSide' }) + + for _,key in pairs(Util.keys(v)) do + if not keys[key] then + v[key] = nil + end + end + if not Util.empty(v) then + t[k] = v + end + end + + Util.writeTable(RESOURCE_FILE, t) +end + +local itemPage = UI.Page { + titleBar = UI.TitleBar { + title = 'Limit Resource', + previousPage = true, + event = 'form_cancel', + }, + form = UI.Form { + x = 1, y = 2, height = 10, ex = -1, + [1] = UI.TextEntry { + width = 7, + formLabel = 'Min', formKey = 'low', help = 'Craft if below min' + }, + [2] = UI.TextEntry { + width = 7, + formLabel = 'Max', formKey = 'limit', help = 'Eject if above max' + }, + [3] = UI.Chooser { + width = 7, + formLabel = 'Autocraft', formKey = 'auto', + nochoice = 'No', + choices = { + { name = 'Yes', value = true }, + { name = 'No', value = false }, + }, + help = 'Craft until out of ingredients' + }, + [4] = UI.Chooser { + width = 7, + formLabel = 'Ignore Dmg', formKey = 'ignoreDamage', + nochoice = 'No', + choices = { + { name = 'Yes', value = true }, + { name = 'No', value = false }, + }, + help = 'Ignore damage of item' + }, + [5] = UI.Chooser { + width = 7, + formLabel = 'Ignore NBT', formKey = 'ignoreNbtHash', + nochoice = 'No', + choices = { + { name = 'Yes', value = true }, + { name = 'No', value = false }, + }, + help = 'Ignore NBT of item' + }, +--[[ + [6] = UI.Button { + x = 2, y = -2, width = 10, + formLabel = 'Redstone', + event = 'show_rs', + text = 'Configure', + }, +]] + infoButton = UI.Button { + x = 2, y = -2, + event = 'show_info', + text = 'Info', + }, + }, + rsControl = UI.SlideOut { + backgroundColor = colors.cyan, + titleBar = UI.TitleBar { + title = "Redstone Control", + }, + form = UI.Form { + y = 2, + [1] = UI.Chooser { + width = 7, + formLabel = 'RS Control', formKey = 'rsControl', + nochoice = 'No', + choices = { + { name = 'Yes', value = true }, + { name = 'No', value = false }, + }, + help = 'Control via redstone' + }, + [2] = UI.Chooser { + width = 25, + formLabel = 'RS Device', formKey = 'rsDevice', + --choices = devices, + help = 'Redstone Device' + }, + [3] = UI.Chooser { + width = 10, + formLabel = 'RS Side', formKey = 'rsSide', + --nochoice = 'No', + choices = { + { name = 'up', value = 'up' }, + { name = 'down', value = 'down' }, + { name = 'east', value = 'east' }, + { name = 'north', value = 'north' }, + { name = 'west', value = 'west' }, + { name = 'south', value = 'south' }, + }, + help = 'Output side' + }, + }, + }, + info = UI.SlideOut { + titleBar = UI.TitleBar { + title = "Information", + }, + textArea = UI.TextArea { + x = 2, ex = -2, y = 3, ey = -4, + backgroundColor = colors.black, + }, + cancel = UI.Button { + ex = -2, y = -2, width = 6, + text = 'Okay', + event = 'hide_info', + }, + }, + statusBar = UI.StatusBar { } +} + +function itemPage:enable(item) + self.item = item + + self.form:setValues(item) + self.titleBar.title = item.displayName or item.name + + UI.Page.enable(self) + self:focusFirst() +end + +function itemPage.rsControl:enable() + local devices = self.form[1].choices + Util.clear(devices) + for _,dev in pairs(device) do + if dev.setOutput then + table.insert(devices, { name = dev.name, value = dev.name }) + end + end + + if Util.size(devices) == 0 then + table.insert(devices, { name = 'None found', values = '' }) + end + + UI.SlideOut.enable(self) +end + +function itemPage.rsControl:eventHandler(event) + if event.type == 'form_cancel' then + self:hide() + elseif event.type == 'form_complete' then + self:hide() + else + return UI.SlideOut.eventHandler(self, event) + end + return true +end + +function itemPage:eventHandler(event) + if event.type == 'form_cancel' then + UI:setPreviousPage() + + elseif event.type == 'show_rs' then + self.rsControl:show() + + elseif event.type == 'show_info' then + local value = + string.format('%s%s%s\n%s\n', + Ansi.orange, self.item.displayName, Ansi.reset, + self.item.name) + + if self.item.nbtHash then + value = value .. self.item.nbtHash .. '\n' + end + + value = value .. string.format('\n%sDamage:%s %s', + Ansi.yellow, Ansi.reset, self.item.damage) + + if self.item.maxDamage and self.item.maxDamage > 0 then + value = value .. string.format(' (max: %s)', self.item.maxDamage) + end + + if self.item.maxCount then + value = value .. string.format('\n%sStack Size: %s%s', + Ansi.yellow, Ansi.reset, self.item.maxCount) + end + + self.info.textArea.value = value + self.info:show() + + elseif event.type == 'hide_info' then + self.info:hide() + + elseif event.type == 'focus_change' then + self.statusBar:setStatus(event.focused.help) + self.statusBar:draw() + + elseif event.type == 'form_complete' then + local values = self.form.values + local originalKey = uniqueKey(self.item) + + local filtered = Util.shallowCopy(values) + filtered.low = tonumber(filtered.low) + filtered.limit = tonumber(filtered.limit) + + if filtered.auto ~= true then + filtered.auto = nil + end + + if filtered.rsControl ~= true then + filtered.rsControl = nil + filtered.rsSide = nil + filtered.rsDevice = nil + end + + if filtered.ignoreDamage == true then + filtered.damage = 0 + else + filtered.ignoreDamage = nil + end + + if filtered.ignoreNbtHash == true then + filtered.nbtHash = nil + else + filtered.ignoreNbtHash = nil + end + resources[originalKey] = nil + resources[uniqueKey(filtered)] = filtered + + filtered.count = nil + saveResources() + + UI:setPreviousPage() + + else + return UI.Page.eventHandler(self, event) + end + return true +end + +local listingPage = UI.Page { + menuBar = UI.MenuBar { + buttons = { + { text = 'Learn', event = 'learn' }, + { text = 'Forget', event = 'forget' }, + { text = 'Craft', event = 'craft' }, + { text = 'Refresh', event = 'refresh', x = -9 }, + }, + }, + grid = UI.Grid { + y = 2, ey = -2, + columns = { + { heading = 'Name', key = 'displayName' }, + { heading = 'Qty', key = 'count' , width = 4 }, + { heading = 'Min', key = 'low' , width = 4 }, + { heading = 'Max', key = 'limit' , width = 4 }, + }, + sortColumn = 'displayName', + }, + statusBar = UI.StatusBar { + filter = UI.TextEntry { + x = 1, ex = -4, + limit = 50, + shadowText = 'filter', + shadowTextColor = colors.gray, + backgroundColor = colors.cyan, + backgroundFocusColor = colors.cyan, + }, + display = UI.Button { + x = -3, + event = 'toggle_display', + value = 0, + text = 'A', + }, + }, + notification = UI.Notification(), + accelerators = { + r = 'refresh', + q = 'quit', + grid_select_right = 'craft', + }, + displayMode = 0, +} + +function listingPage.statusBar:draw() + return UI.Window.draw(self) +end + +function listingPage.grid:getRowTextColor(row, selected) + if row.is_craftable then + return colors.yellow + end + if canCraft and row.has_recipe then + return colors.cyan + end + return UI.Grid:getRowTextColor(row, selected) +end + +function listingPage.grid:getDisplayValues(row) + row = Util.shallowCopy(row) + row.count = Util.toBytes(row.count) + if row.low then + row.low = Util.toBytes(row.low) + end + if row.limit then + row.limit = Util.toBytes(row.limit) + end + return row +end + +function listingPage:eventHandler(event) + if event.type == 'quit' then + UI:exitPullEvents() + + elseif event.type == 'grid_select' then + local selected = event.selected + UI:setPage('item', selected) + + elseif event.type == 'refresh' then + self:refresh() + self.grid:draw() + self.statusBar.filter:focus() + + elseif event.type == 'toggle_display' then + local values = { + [0] = 'A', + [1] = 'I', + [2] = 'C', + } + + event.button.value = (event.button.value + 1) % 3 + self.displayMode = event.button.value + event.button.text = values[event.button.value] + event.button:draw() + self:applyFilter() + self.grid:draw() + + elseif event.type == 'learn' then + if canLearn then + UI:setPage('learn') + else + self.notification:error('Missing a crafting chest or workbench\nCheck configuration') + end + + elseif event.type == 'craft' or event.type == 'grid_select_right' then + local item = self.grid:getSelected() + if Craft.findRecipe(item) or true then -- or item.is_craftable then + UI:setPage('craft', self.grid:getSelected()) + else + self.notification:error('No recipe defined') + end + + elseif event.type == 'forget' then + local item = self.grid:getSelected() + if item then + local key = uniqueKey(item) + + if userRecipes[key] then + userRecipes[key] = nil + Util.writeTable(RECIPES_FILE, userRecipes) + Craft.loadRecipes() + end + + if resources[key] then + resources[key] = nil + saveResources() + end + + self.notification:info('Forgot: ' .. item.name) + self:refresh() + self.grid:draw() + end + + elseif event.type == 'text_change' then + self.filter = event.text + if #self.filter == 0 then + self.filter = nil + end + self:applyFilter() + self.grid:draw() + self.statusBar.filter:focus() + + else + UI.Page.eventHandler(self, event) + end + return true +end + +function listingPage:enable() + self:refresh() + self:setFocus(self.statusBar.filter) + UI.Page.enable(self) +end + +function listingPage:refresh() + self.allItems = listItems() + mergeResources(self.allItems) + self:applyFilter() +end + +function listingPage:applyFilter() + local t = filterItems(self.allItems, self.filter, self.displayMode) + self.grid:setValues(t) +end + +local function getTurtleInventory() + local list = { } + for i = 1,16 do + list[i] = introspectionModule.getInventory().getItemMeta(i) + end + return list +end + +local function learnRecipe(page) + local ingredients = getTurtleInventory() + if ingredients then + turtle.select(1) + if canLearn and turtle.craft() then + local results = getTurtleInventory() + if results and results[1] then + clearGrid() + + local maxCount + local newRecipe = { + ingredients = ingredients, + } + + local numResults = 0 + for _,v in pairs(results) do + if v.count > 0 then + numResults = numResults + 1 + end + end + if numResults > 1 then + for _,v1 in pairs(results) do + for _,v2 in pairs(ingredients) do + if v1.name == v2.name and + v1.nbtHash == v2.nbtHash and + (v1.damage == v2.damage or + (v1.maxDamage > 0 and v2.maxDamage > 0 and + v1.damage ~= v2.damage)) then + if not newRecipe.crafingTools then + newRecipe.craftingTools = { } + end + local tool = Util.shallowCopy(v2) + if tool.maxDamage > 0 then + tool.damage = '*' + end + + --[[ + Turtles can only craft one item at a time using a tool :( + ]]-- + maxCount = 1 + + newRecipe.craftingTools[uniqueKey(tool)] = true + v1.craftingTool = true + break + end + end + end + end + + local recipe + for _,v in pairs(results) do + if not v.craftingTool then + recipe = v + if maxCount then + recipe.maxCount = maxCount + end + break + end + end + + if not recipe then + debug(results) + debug(newRecipe) + error('Failed - view system log') + end + + newRecipe.count = recipe.count + + local key = uniqueKey(recipe) + if recipe.maxCount ~= 64 then + newRecipe.maxCount = recipe.maxCount + end + for k,ingredient in pairs(Util.shallowCopy(ingredients)) do + if ingredient.maxDamage > 0 then + -- ingredient.damage = '*' -- I don't think this is right + end + ingredients[k] = uniqueKey(ingredient) + end + + userRecipes[key] = newRecipe + + Util.writeTable(RECIPES_FILE, userRecipes) + Craft.loadRecipes() + + local displayName = itemDB:getName(recipe) + + listingPage.statusBar.filter:setValue(displayName) + listingPage.notification:success('Learned: ' .. displayName) + listingPage.filter = displayName + listingPage:refresh() + listingPage.grid:draw() + + eject(recipe, recipe.count) + return true + end + else + listingPage.notification:error('Failed to craft', 3) + end + else + listingPage.notification:error('No recipe defined', 3) + end +end + +local learnPage = UI.Dialog { + height = 7, width = UI.term.width - 6, + title = 'Learn Recipe', + idField = UI.Text { + x = 5, + y = 3, + width = UI.term.width - 10, + value = 'Place recipe in turtle' + }, + accept = UI.Button { + x = -14, y = -3, + text = 'Ok', event = 'accept', + }, + cancel = UI.Button { + x = -9, y = -3, + text = 'Cancel', event = 'cancel' + }, + statusBar = UI.StatusBar { + status = 'Crafting paused' + } +} + +function learnPage:enable() + craftingPaused = true + self:focusFirst() + UI.Dialog.enable(self) +end + +function learnPage:disable() + craftingPaused = false + UI.Dialog.disable(self) +end + +function learnPage:eventHandler(event) + if event.type == 'cancel' then + UI:setPreviousPage() + elseif event.type == 'accept' then + if learnRecipe(self) then + UI:setPreviousPage() + end + else + return UI.Dialog.eventHandler(self, event) + end + return true +end + +local craftPage = UI.Page { + titleBar = UI.TitleBar { }, + wizard = UI.Wizard { + y = 2, ey = -2, + pages = { + quantity = UI.Window { + index = 1, + text = UI.Text { + x = 6, y = 3, + value = 'Quantity', + }, + count = UI.TextEntry { + x = 15, y = 3, width = 10, + limit = 6, + value = 1, + }, + ejectText = UI.Text { + x = 6, y = 4, + value = 'Eject', + }, + eject = UI.Chooser { + x = 15, y = 4, width = 7, + value = true, + nochoice = 'No', + choices = { + { name = 'Yes', value = true }, + { name = 'No', value = false }, + }, + }, + }, + resources = UI.Window { + index = 2, + grid = UI.ScrollingGrid { + y = 2, ey = -2, + columns = { + { heading = 'Name', key = 'displayName' }, + { heading = 'Total', key = 'total' , width = 5 }, + { heading = 'Used', key = 'used' , width = 5 }, + { heading = 'Need', key = 'need' , width = 5 }, + }, + sortColumn = 'displayName', + }, + }, + }, + }, +} + +function craftPage:enable(item) + self.item = item + self:focusFirst() + self.titleBar.title = itemDB:getName(item) +-- self.wizard.pages.quantity.eject.value = true + UI.Page.enable(self) +end + +function craftPage.wizard.pages.resources.grid:getDisplayValues(row) + local function dv(v) + if v == 0 then + return '' + end + return Util.toBytes(v) + end + row = Util.shallowCopy(row) + row.total = Util.toBytes(row.total) + row.used = dv(row.used) + row.need = dv(row.need) + return row +end + +function craftPage.wizard.pages.resources.grid:getRowTextColor(row, selected) + if row.need > 0 then + return colors.orange + end + return UI.Grid:getRowTextColor(row, selected) +end + +function craftPage.wizard:eventHandler(event) + if event.type == 'nextView' then + local count = tonumber(self.pages.quantity.count.value) + if not count or count <= 0 then + self.pages.quantity.count.backgroundColor = colors.red + self.pages.quantity.count:draw() + return false + end + self.pages.quantity.count.backgroundColor = colors.black + end + return UI.Wizard.eventHandler(self, event) +end + +function craftPage.wizard.pages.resources:enable() + local items = listItems() + local count = tonumber(self.parent.quantity.count.value) + local recipe = Craft.findRecipe(craftPage.item) + if recipe then + local ingredients = Craft.getResourceList4(recipe, items, count) + for _,v in pairs(ingredients) do + v.displayName = itemDB:getName(v) + end + self.grid:setValues(ingredients) + else + self.grid:setValues({ }) + end + return UI.Window.enable(self) +end + +function craftPage:eventHandler(event) + if event.type == 'cancel' then + UI:setPreviousPage() + + elseif event.type == 'accept' then + local key = uniqueKey(self.item) + demandCrafting[key] = Util.shallowCopy(self.item) + demandCrafting[key].count = tonumber(self.wizard.pages.quantity.count.value) + demandCrafting[key].ocount = demandCrafting[key].count + demandCrafting[key].forceCrafting = true + demandCrafting[key].eject = self.wizard.pages.quantity.eject.value == true + UI:setPreviousPage() + else + return UI.Page.eventHandler(self, event) + end + return true +end + +loadResources() +if canCraft then + clearGrid() +end + +UI:setPages({ + listing = listingPage, + item = itemPage, + learn = learnPage, + craft = craftPage, +}) + +jobMonitor() +UI:setPage(listingPage) +listingPage:setFocus(listingPage.statusBar.filter) + +Event.onInterval(5, function() + + if not craftingPaused then + local items = listItems() + if not items or Util.size(items) == 0 then + jobList:showError('No items in system') + else + local demandCrafted + if Util.size(demandCrafting) > 0 then + items = listItems() + if items then + demandCrafted = Util.shallowCopy(demandCrafting) + craftItems(demandCrafted, items) + end + end + + items = listItems() + local craftList + if items then + craftList = watchResources(items) + craftItems(craftList, items) + end + + if demandCrafted and craftList then + for k,v in pairs(demandCrafted) do + craftList[k] = v + end + end + + for _,key in pairs(Util.keys(demandCrafting)) do + local item = demandCrafting[key] + if item.crafted then + item.count = math.max(0, item.count - item.crafted) + if item.count <= 0 then + demandCrafting[key] = nil + item.statusCode = 'success' + if item.eject then + eject(item, item.ocount) + end + end + end + end + + jobList:updateList(craftList) + + craftList = getAutocraftItems(items) -- autocrafted items don't show on job monitor + craftItems(craftList, items) + end + end +end) + +UI:pullEvents() +jobList.parent:reset() From d5c102054acee5481537adae05240ef55a024de0 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sat, 20 Oct 2018 23:53:49 -0400 Subject: [PATCH 015/165] plethora networked autocrafting --- apis/chestAdapter18.lua | 3 +- inventoryManager/apis/craft.lua | 22 +++--- inventoryManager/apis/networkedAdapter18.lua | 73 ++++++++++++++++---- inventoryManager/apps/inventoryManager.lua | 47 ++++++++++--- 4 files changed, 109 insertions(+), 36 deletions(-) diff --git a/apis/chestAdapter18.lua b/apis/chestAdapter18.lua index 3053aa4..d749ecf 100644 --- a/apis/chestAdapter18.lua +++ b/apis/chestAdapter18.lua @@ -8,6 +8,7 @@ local ChestAdapter = class() function ChestAdapter:init(args) local defaults = { name = 'chest', + adapter = 'ChestAdapter18' } Util.merge(self, defaults) Util.merge(self, args) @@ -124,7 +125,7 @@ end function ChestAdapter:provide(item, qty, slot, direction) local total = 0 - local s, m = pcall(function() + local _, m = pcall(function() local stacks = self.list() for key,stack in Util.rpairs(stacks) do if stack.name == item.name and diff --git a/inventoryManager/apis/craft.lua b/inventoryManager/apis/craft.lua index 1b7b676..549fde2 100644 --- a/inventoryManager/apis/craft.lua +++ b/inventoryManager/apis/craft.lua @@ -10,17 +10,17 @@ local USER_RECIPES = 'usr/config/recipes.db' local Craft = { } local function clearGrid(inventoryAdapter) - for i = 1, 16 do - local count = turtle.getItemCount(i) - if count > 0 then - inventoryAdapter:insert(i, count) - if turtle.getItemCount(i) ~= 0 then - -- inventory is possibly full - return false - end - end - end - return true + turtle.eachFilledSlot(function(slot) + inventoryAdapter:insert(slot.index, slot.count, nil, slot) + end) + + for i = 1, 16 do + if turtle.getItemCount(i) ~= 0 then + return false + end + end + + return true end local function splitKey(key) diff --git a/inventoryManager/apis/networkedAdapter18.lua b/inventoryManager/apis/networkedAdapter18.lua index 2e3305f..9ea368c 100644 --- a/inventoryManager/apis/networkedAdapter18.lua +++ b/inventoryManager/apis/networkedAdapter18.lua @@ -1,7 +1,6 @@ local class = require('class') local Util = require('util') local InventoryAdapter = require('inventoryAdapter') -local itemDB = require('itemDB') local Peripheral = require('peripheral') local NetworkedAdapter = class() @@ -10,19 +9,23 @@ function NetworkedAdapter:init(args) local defaults = { name = 'Networked Adapter', remotes = { }, + remoteDefaults = { }, } Util.merge(self, defaults) Util.merge(self, args) if not self.side or self.side == 'network' then - self.chests = { } self.modem = Peripheral.get('wired_modem') if self.modem and self.modem.getNameLocal then self.localName = self.modem.getNameLocal() for _, v in pairs(self.modem.getNamesRemote()) do local remote = Peripheral.get({ name = v }) - if remote and remote.size and remote.size() >= 27 and remote.list then + if remote and + remote.size and + remote.size() >= 27 and + remote.list and + not (self.remoteDefaults[v] and self.remoteDefaults[v].ignore) then local adapter = InventoryAdapter.wrap({ side = v, direction = self.localName }) if adapter then @@ -31,9 +34,22 @@ function NetworkedAdapter:init(args) 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 - _G._p = self +_G._p = self --------------------------------------------- DEBUG end function NetworkedAdapter:isValid() @@ -50,13 +66,13 @@ function NetworkedAdapter:listItems(throttle) local items = { } throttle = throttle or Util.throttle() - for _, v in pairs(self.remotes) do - v.__cache = v:listItems(throttle) + for _, remote in pairs(self.remotes) do + remote:listItems(throttle) + local rcache = remote.cache or { } - for k,v in pairs(v.__cache) do +-- 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 key = table.concat({ v.name, v.damage, v.nbtHash }, ':') - local entry = cache[key] if not entry then entry = Util.shallowCopy(v) @@ -97,9 +113,9 @@ end function NetworkedAdapter:provide(item, qty, slot, direction) local total = 0 - for _, remote in pairs(self.remotes) do -debug('%s -> slot %d: %d %s', remote.side, slot, qty, item.name) - local amount = remote:provide(item, qty, slot) + for _, remote in ipairs(self.remotes) do +debug('%s -> slot %d: %d %s', remote.side, slot or -1, qty, item.name) + local amount = remote:provide(item, qty, slot, direction) qty = qty - amount total = total + amount if qty <= 0 then @@ -127,16 +143,45 @@ debug('extract %d slot:%d', qty, slot) return total end -function NetworkedAdapter:insert(slot, qty, toSlot) +function NetworkedAdapter:insert(slot, qty, toSlot, item) local total = 0 - for _, remote in pairs(self.remotes) do + + -- 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) debug('slot %d -> %s: %s', slot, remote.side, qty) local amount = remote:insert(slot, qty, toSlot) qty = qty - amount total = total + amount + 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] then + insert(remote) + end + end + end + + -- high to low priority + for _, remote in ipairs(self.remotes) do if qty <= 0 then break end + insert(remote) end return total diff --git a/inventoryManager/apps/inventoryManager.lua b/inventoryManager/apps/inventoryManager.lua index a4808eb..98536e2 100644 --- a/inventoryManager/apps/inventoryManager.lua +++ b/inventoryManager/apps/inventoryManager.lua @@ -76,7 +76,6 @@ local Terminal = require('terminal') local UI = require('ui') local Util = require('util') -local ControllerAdapter = require('controllerAdapter') local InventoryAdapter = require('inventoryAdapter') local colors = _G.colors @@ -94,7 +93,6 @@ local config = { computerFacing = 'north', -- direction turtle is facing inventory = 'network', -- main inventory - controller = 'none', -- AE / RS controller trashDirection = 'up', -- trash/chest in relation to inventory monitor = 'type/monitor', @@ -102,9 +100,8 @@ local config = { Config.loadWithCheck('inventoryManager', config) ---local controllerAdapter = ControllerAdapter.wrap({ side = config.controller, facing = config.computerFacing }) local inventoryAdapter = config.inventory == 'network' and - InventoryAdapter.wrap() or + InventoryAdapter.wrap({ remoteDefaults = config.remoteDefaults }) or InventoryAdapter.wrap({ side = config.inventory, facing = config.computerFacing }) if not inventoryAdapter then @@ -118,6 +115,20 @@ if inventoryAdapter.craft then controllerAdapter = inventoryAdapter end +local feederAdapter + +if config.remoteDefaults then + for k, v in pairs(config.remoteDefaults) do + if v.feeder then + local modem = Peripheral.get('wired_modem') + if modem and modem.getNameLocal then + feederAdapter = InventoryAdapter.wrap({ side = k, direction = modem.getNameLocal() }) + end + break + end + end +end + local STATUS_INFO = 'info' local STATUS_WARNING = 'warning' local STATUS_ERROR = 'error' @@ -224,13 +235,13 @@ end local function clearGrid() local function clear() + turtle.eachFilledSlot(function(slot) + inventoryAdapter:insert(slot.index, slot.count, nil, slot) + end) + for i = 1, 16 do - local count = turtle.getItemCount(i) - if count > 0 then - inventoryAdapter:insert(i, count) - if turtle.getItemCount(i) ~= 0 then - return false - end + if turtle.getItemCount(i) ~= 0 then + return false end end return true @@ -468,6 +479,9 @@ local function eject(item, qty) inventoryAdapter:provide(item, qty) _G.turtle.emptyInventory() end) + if not s and m then + debug(m) + end end end @@ -620,6 +634,17 @@ local function watchResources(items) return craftList end +local function emptyFeederChest() +_G._p2 =feederAdapter + if feederAdapter then + local list = feederAdapter.list() -- raw list ! + for k,v in pairs(list) do + feederAdapter:extract(k, v.count, 1) + inventoryAdapter:insert(1, v.count, nil, v) + end + end +end + local function loadResources() resources = Util.readTable(RESOURCE_FILE) or { } for k,v in pairs(resources) do @@ -1347,6 +1372,8 @@ listingPage:setFocus(listingPage.statusBar.filter) Event.onInterval(5, function() + emptyFeederChest() + if not craftingPaused then local items = listItems() if not items or Util.size(items) == 0 then From 782850b94d1de73df3e488616be740e4154fe839 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sun, 21 Oct 2018 00:38:24 -0400 Subject: [PATCH 016/165] plethora autocrafting wip --- inventoryManager/apis/{ => turtle}/craft.lua | 0 inventoryManager/apps/inventoryManager.lua | 14 ++++++++++++++ 2 files changed, 14 insertions(+) rename inventoryManager/apis/{ => turtle}/craft.lua (100%) diff --git a/inventoryManager/apis/craft.lua b/inventoryManager/apis/turtle/craft.lua similarity index 100% rename from inventoryManager/apis/craft.lua rename to inventoryManager/apis/turtle/craft.lua diff --git a/inventoryManager/apps/inventoryManager.lua b/inventoryManager/apps/inventoryManager.lua index 98536e2..583d3d8 100644 --- a/inventoryManager/apps/inventoryManager.lua +++ b/inventoryManager/apps/inventoryManager.lua @@ -949,6 +949,8 @@ local listingPage = UI.Page { r = 'refresh', q = 'quit', grid_select_right = 'craft', + [ 'control-e' ] = 'eject', + [ 'control-s' ] = 'eject_stack', }, displayMode = 0, } @@ -983,6 +985,18 @@ function listingPage:eventHandler(event) if event.type == 'quit' then UI:exitPullEvents() + elseif event.type == 'eject' then + local item = self.grid:getSelected() + if item then + eject(item, 1) + end + + elseif event.type == 'eject_stack' then + local item = self.grid:getSelected() + if item then + eject(item, itemDB:getMaxCount(item)) + end + elseif event.type == 'grid_select' then local selected = event.selected UI:setPage('item', selected) From f56611d957a7356105ed8159ad8f24c683ad358c Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sun, 21 Oct 2018 04:44:10 -0400 Subject: [PATCH 017/165] plethora autocrafting wip --- apps/simpleMiner.lua | 11 +- inventoryManager/{apps => }/Crafter.lua | 0 inventoryManager/apis/networkedAdapter18.lua | 28 ++- .../{apps => }/inventoryManager.lua | 217 +++++++++++++++--- 4 files changed, 217 insertions(+), 39 deletions(-) rename inventoryManager/{apps => }/Crafter.lua (100%) rename inventoryManager/{apps => }/inventoryManager.lua (87%) diff --git a/apps/simpleMiner.lua b/apps/simpleMiner.lua index 8c726b2..c4c8d6e 100644 --- a/apps/simpleMiner.lua +++ b/apps/simpleMiner.lua @@ -219,7 +219,16 @@ local function safeGoto(x, z, y, h) local oldStatus = turtle.getStatus() -- only pathfind above or around other turtles (never down) - Pathing.setBox({ x = turtle.point.x, y = turtle.point.y, z = turtle.point.z, ex = x, ey = y, ez = z }) + local box = Point.normalizeBox({ x = turtle.point.x, y = turtle.point.y, z = turtle.point.z, + ex = x, ey = y, ez = z }) + box.x = box.x - 1 + box.z = box.z - 1 + box.ex = box.ex + 1 + box.ey = box.ey + 1 + box.ez = box.ez + 1 + + Pathing.setBox(box) + while not turtle.pathfind({ x = x, z = z, y = y or turtle.point.y, heading = h }) do --status('stuck') if turtle.isAborted() then diff --git a/inventoryManager/apps/Crafter.lua b/inventoryManager/Crafter.lua similarity index 100% rename from inventoryManager/apps/Crafter.lua rename to inventoryManager/Crafter.lua diff --git a/inventoryManager/apis/networkedAdapter18.lua b/inventoryManager/apis/networkedAdapter18.lua index 9ea368c..99e36aa 100644 --- a/inventoryManager/apis/networkedAdapter18.lua +++ b/inventoryManager/apis/networkedAdapter18.lua @@ -19,15 +19,11 @@ function NetworkedAdapter:init(args) if self.modem and self.modem.getNameLocal then self.localName = self.modem.getNameLocal() - for _, v in pairs(self.modem.getNamesRemote()) do - local remote = Peripheral.get({ name = v }) - if remote and - remote.size and - remote.size() >= 27 and - remote.list and - not (self.remoteDefaults[v] and self.remoteDefaults[v].ignore) then - local adapter = InventoryAdapter.wrap({ side = v, direction = self.localName }) + 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 @@ -157,6 +153,8 @@ function NetworkedAdapter:insert(slot, qty, toSlot, item) self:listItems() end +debug('attempting to insert ' .. item.name) + local function insert(remote) debug('slot %d -> %s: %s', slot, remote.side, qty) local amount = remote:insert(slot, qty, toSlot) @@ -164,13 +162,21 @@ debug('slot %d -> %s: %s', slot, remote.side, qty) 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] then + if remote.cache and remote.cache[key] and not remote.lockWith then insert(remote) end end @@ -181,7 +187,9 @@ debug('slot %d -> %s: %s', slot, remote.side, qty) if qty <= 0 then break end - insert(remote) + if not remote.lockWith then + insert(remote) + end end return total diff --git a/inventoryManager/apps/inventoryManager.lua b/inventoryManager/inventoryManager.lua similarity index 87% rename from inventoryManager/apps/inventoryManager.lua rename to inventoryManager/inventoryManager.lua index 583d3d8..9a5fa5c 100644 --- a/inventoryManager/apps/inventoryManager.lua +++ b/inventoryManager/inventoryManager.lua @@ -90,41 +90,53 @@ if multishell then end local config = { - computerFacing = 'north', -- direction turtle is facing - - inventory = 'network', -- main inventory - - trashDirection = 'up', -- trash/chest in relation to inventory - monitor = 'type/monitor', + monitor = 'type/monitor', + remoteDefaults = { }, } +Config.load('inventoryManager', config) -Config.loadWithCheck('inventoryManager', config) +local modem = Peripheral.get('wired_modem') +if not modem or not modem.getNameLocal then + error('Wired modem is not connected') +end -local inventoryAdapter = config.inventory == 'network' and - InventoryAdapter.wrap({ remoteDefaults = config.remoteDefaults }) or - InventoryAdapter.wrap({ side = config.inventory, facing = config.computerFacing }) +local feederAdapter +local controllerAdapter +local storage = { } +for k,v in pairs(config.remoteDefaults) do + if v.mtype == 'storage' then + storage[k] = v + elseif v.mtype == 'input' then + feederAdapter = InventoryAdapter.wrap({ side = k, direction = modem.getNameLocal() }) + elseif v.mtype == 'controller' then + -- TODO: look for controller + end +end +debug(storage) + +local inventoryAdapter = InventoryAdapter.wrap({ remoteDefaults = storage }) if not inventoryAdapter then error('Invalid inventory configuration') end -local introspectionModule = device['plethora:introspection'] +local introspectionModule = device['plethora:introspection'] or + error('introspection module not found') -local controllerAdapter -if inventoryAdapter.craft then - controllerAdapter = inventoryAdapter -end - -local feederAdapter - -if config.remoteDefaults then - for k, v in pairs(config.remoteDefaults) do - if v.feeder then - local modem = Peripheral.get('wired_modem') - if modem and modem.getNameLocal then - feederAdapter = InventoryAdapter.wrap({ side = k, direction = modem.getNameLocal() }) - end - break +-- TODO: cleanup +for _, v in pairs(modem.getNamesRemote()) do + local remote = Peripheral.get({ name = v }) + if remote.pullItems then + if not config.remoteDefaults[v] then + config.remoteDefaults[v] = { + name = v, + mtype = 'ignore', + } + else + config.remoteDefaults[v].name = v + end + if not config.remoteDefaults[v].mtype then + config.remoteDefaults[v].mtype = 'ignore' end end end @@ -473,6 +485,13 @@ local function craftItems(craftList, allItems) end end +local function queue(fn) + while craftingPaused do + os.sleep(1) + end + fn() +end + local function eject(item, qty) if _G.turtle then local s, m = pcall(function() @@ -674,6 +693,143 @@ local function saveResources() Util.writeTable(RESOURCE_FILE, t) end +local machinesPage = UI.Page { + titleBar = UI.TitleBar { + previousPage = true, + title = 'Machines', + }, + grid = UI.ScrollingGrid { + y = 2, ey = -2, + values = config.remoteDefaults, + columns = { + { heading = 'Name', key = 'displayName' }, + { heading = 'Priority', key = 'priority', width = 5 }, + { heading = 'Type', key = 'mtype', width = 5 }, + }, + sortColumn = 'name', + }, + detail = UI.SlideOut { + backgroundColor = colors.cyan, + form = UI.Form { + x = 1, y = 2, ex = -1, ey = -2, + [7] = UI.Text { + x = 12, y = 1, + width = 20, + value = 'test', + }, + [1] = UI.TextEntry { + formLabel = 'Name', formKey = 'displayName', help = '...', + limit = 64, + }, + [2] = UI.Chooser { + width = 15, + formLabel = 'Type', formKey = 'mtype', + nochoice = 'Storage', + choices = { + { name = 'Storage', value = 'storage' }, + { name = 'Trashcan', value = 'trashcan' }, + { name = 'Input chest', value = 'input' }, + { name = 'Ignore', value = 'ignore' }, + }, + help = 'Check if machine is empty before crafting' + }, + [3] = UI.Chooser { + width = 7, + formLabel = 'Empty', formKey = 'empty', + nochoice = 'No', + choices = { + { name = 'Yes', value = true }, + { name = 'No', value = false }, + }, + help = 'Check if machine is empty before crafting' + }, + [4] = UI.TextEntry { + formLabel = 'Priority', formKey = 'priority', help = '...', + limit = 4, + }, + [5] = UI.TextEntry { + formLabel = 'Max Craft', formKey = 'maxCount', help = '...', + limit = 4, + }, + [6] = UI.TextEntry { + formLabel = 'Lock to', formKey = 'lockWith', help = '...', + limit = 64, + }, + }, + statusBar = UI.StatusBar(), + }, + statusBar = UI.StatusBar { + values = 'Select Machine', + }, + accelerators = { + h = 'toggle_hidden', + } +} + +function machinesPage:enable() + self.grid:update() + UI.Page.enable(self) +end + +function machinesPage.detail:eventHandler(event) + if event.type == 'focus_change' then + self.statusBar:setStatus(event.focused.help) + end + return UI.SlideOut.eventHandler(self, event) +end + +function machinesPage.grid:getDisplayValues(row) + row = Util.shallowCopy(row) + row.displayName = row.displayName or row.name + return row +end + +function machinesPage.grid:getRowTextColor(row, selected) + if row.mtype == 'ignore' then + return colors.lightGray + end + return UI.Grid:getRowTextColor(row, selected) +end + +function machinesPage:eventHandler(event) + if event.type == 'grid_select' then + self.detail.form:setValues(event.selected) + self.detail.form[7].value = event.selected.name +debug(event.selected) + self.detail:show() + + elseif event.type == 'toggle_hidden' then + local selected = self.grid:getSelected() + if selected then + selected.ignore = not selected.ignore +-- Util.writeTable(MACHINES_FILE, machines) + self:draw() + end + + elseif event.type == 'form_complete' then + self.detail.form.values.empty = self.detail.form.values.empty == true or nil + self.detail.form.values.ignore = self.detail.form.values.ignore == true or nil + self.detail.form.values.priority = tonumber(self.detail.form.values.priority) + self.detail.form.values.maxCount = tonumber(self.detail.form.values.maxCount) + if #self.detail.form.values.displayName == 0 then + self.detail.form.values.displayName = nil + end + if #self.detail.form.values.lockWith == 0 then + self.detail.form.values.lockWith = nil + end + Config.update('inventoryManager', config) + self.detail:hide() + self.grid:update() + + elseif event.type == 'form_cancel' then + self.detail:hide() + + else + UI.Page.eventHandler(self, event) + end + return true +end + local itemPage = UI.Page { titleBar = UI.TitleBar { title = 'Limit Resource', @@ -951,6 +1107,7 @@ local listingPage = UI.Page { grid_select_right = 'craft', [ 'control-e' ] = 'eject', [ 'control-s' ] = 'eject_stack', + [ 'control-m' ] = 'machines', }, displayMode = 0, } @@ -988,15 +1145,18 @@ function listingPage:eventHandler(event) elseif event.type == 'eject' then local item = self.grid:getSelected() if item then - eject(item, 1) + queue(function() eject(item, 1) end) end elseif event.type == 'eject_stack' then local item = self.grid:getSelected() if item then - eject(item, itemDB:getMaxCount(item)) + queue(function() eject(item, itemDB:getMaxCount(item)) end) end + elseif event.type == 'machines' then + UI:setPage('machines') + elseif event.type == 'grid_select' then local selected = event.selected UI:setPage('item', selected) @@ -1378,6 +1538,7 @@ UI:setPages({ item = itemPage, learn = learnPage, craft = craftPage, + machines = machinesPage, }) jobMonitor() From 9e5487b10059f5d09323283400d6c1e7e23fcc1a Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sun, 21 Oct 2018 18:43:04 -0400 Subject: [PATCH 018/165] Lora major refactor --- inventoryManager/apis/lora/lora.lua | 334 ++++ inventoryManager/apis/lora/tasks.lua | 12 + inventoryManager/apis/networkedAdapter18.lua | 34 +- inventoryManager/inventoryManager.lua | 1509 +----------------- inventoryManager/plugins/autocraftTask.lua | 24 + inventoryManager/plugins/craft.lua | 167 ++ inventoryManager/plugins/inputChestTask.lua | 37 + inventoryManager/plugins/item.lua | 246 +++ inventoryManager/plugins/jobList.lua | 65 + inventoryManager/plugins/learn.lua | 174 ++ inventoryManager/plugins/limitTask.lua | 36 + inventoryManager/plugins/listing.lua | 250 +++ inventoryManager/plugins/machines.lua | 152 ++ inventoryManager/plugins/replenishTask.lua | 47 + 14 files changed, 1605 insertions(+), 1482 deletions(-) create mode 100644 inventoryManager/apis/lora/lora.lua create mode 100644 inventoryManager/apis/lora/tasks.lua create mode 100644 inventoryManager/plugins/autocraftTask.lua create mode 100644 inventoryManager/plugins/craft.lua create mode 100644 inventoryManager/plugins/inputChestTask.lua create mode 100644 inventoryManager/plugins/item.lua create mode 100644 inventoryManager/plugins/jobList.lua create mode 100644 inventoryManager/plugins/learn.lua create mode 100644 inventoryManager/plugins/limitTask.lua create mode 100644 inventoryManager/plugins/listing.lua create mode 100644 inventoryManager/plugins/machines.lua create mode 100644 inventoryManager/plugins/replenishTask.lua diff --git a/inventoryManager/apis/lora/lora.lua b/inventoryManager/apis/lora/lora.lua new file mode 100644 index 0000000..a274aca --- /dev/null +++ b/inventoryManager/apis/lora/lora.lua @@ -0,0 +1,334 @@ +local Craft = require('turtle.craft') +local itemDB = require('itemDB') +local Util = require('util') + +local os = _G.os +local term = _G.term +local turtle = _G.turtle + +local Lora = { + RECIPES_FILE = 'usr/config/recipes.db', + RESOURCE_FILE = 'usr/config/resources.db', + + STATUS_INFO = 'info', + STATUS_WARNING = 'warning', + STATUS_ERROR = 'error', + + tasks = { }, + craftingStatus = { }, +} + +function Lora:init(context) + self.context = context +end + +function Lora:getContext() + return self.context +end + +function Lora:pauseCrafting() + self.craftingPaused = true +end + +function Lora:resumeCrafting() + self.craftingPaused = false +end + +function Lora:isCraftingPaused() + return self.craftingPaused +end + +function Lora:uniqueKey(item) + return table.concat({ item.name, item.damage, item.nbtHash }, ':') +end + +function Lora:getCraftingStatus() + return self.craftingStatus +end + +function Lora:resetCraftingStatus() + self.craftingStatus = { } +end + +function Lora:updateCraftingStatus(list) + for k,v in pairs(list) do + self.craftingStatus[k] = v + end + + self.context.jobList:updateList(self.craftingStatus) +end + +function Lora:registerTask(task) + table.insert(self.tasks, task) +end + +function Lora:showError(msg) + term.clear() + self.context.jobList:showError() + print(msg) + print('rebooting in 5 secs') + os.sleep(5) + os.reboot() +end + +function Lora:getItem(items, inItem, ignoreDamage, ignoreNbtHash) + for _,item in pairs(items) do + if item.name == inItem.name and + (ignoreDamage or item.damage == inItem.damage) and + (ignoreNbtHash or item.nbtHash == inItem.nbtHash) then + return item + end + end +end + +function Lora:getItemWithQty(res, ignoreDamage, ignoreNbtHash) + local items = self:listItems() + local item = self:getItem(items, res, ignoreDamage, ignoreNbtHash) + + if item and (ignoreDamage or ignoreNbtHash) then + local count = 0 + + for _,v in pairs(items) do + if item.name == v.name and + (ignoreDamage or item.damage == v.damage) and + (ignoreNbtHash or item.nbtHash == v.nbtHash) then + count = count + v.count + end + end + item.count = count + end + + return item +end + +function Lora:clearGrid() + local function clear() + turtle.eachFilledSlot(function(slot) + self.context.inventoryAdapter:insert(slot.index, slot.count, nil, slot) + end) + + for i = 1, 16 do + if turtle.getItemCount(i) ~= 0 then + return false + end + end + return true + end + return clear() or clear() +end + +function Lora:eject(item, qty) + local s, m = pcall(function() + self.context.inventoryAdapter:provide(item, qty) + turtle.emptyInventory() + end) + if not s and m then + debug(m) + end +end + +function Lora:saveResources() + local t = { } + + for k,v in pairs(self.context.resources) do + v = Util.shallowCopy(v) + local keys = Util.transpose({ 'auto', 'low', 'limit', + 'ignoreDamage', 'ignoreNbtHash', + 'rsControl', 'rsDevice', 'rsSide' }) + + for _,key in pairs(Util.keys(v)) do + if not keys[key] then + v[key] = nil + end + end + if not Util.empty(v) then + t[k] = v + end + end + + Util.writeTable(Lora.RESOURCE_FILE, t) +end + +-- Return a list of everything in the system +function Lora:listItems() + for _ = 1, 5 do + self.items = self.context.inventoryAdapter:listItems() + if self.items then + break + end +-- jobList:showError('Error - retrying in 3 seconds') + os.sleep(3) + end + if not self.items then + self:showError('Error - rebooting in 5 seconds') + end + + return self.items +end + +function Lora:addCraftingRequest(item, craftList, count) + local key = self:uniqueKey(item) + local request = craftList[key] + if not craftList[key] then + request = { name = item.name, damage = item.damage, nbtHash = item.nbtHash, count = 0 } + request.displayName = itemDB:getName(request) + craftList[key] = request + end + request.count = request.count + count + return request +end + +-- Craft +function Lora:craftItem(recipe, items, originalItem, craftList, count) + local missing = { } + local toCraft = Craft.getCraftableAmount(recipe, count, items, missing) + if missing.name then + originalItem.status = string.format('%s missing', itemDB:getName(missing.name)) + originalItem.statusCode = self.STATUS_WARNING + end + + local crafted = 0 + + if toCraft > 0 then + crafted = Craft.craftRecipe(recipe, toCraft, self.context.inventoryAdapter) + self:clearGrid() + items = self:listItems() + count = count - crafted + end + + if count > 0 and items then + local ingredients = Craft.getResourceList4(recipe, items, count) + for _,ingredient in pairs(ingredients) do + if ingredient.need > 0 then + local item = self:addCraftingRequest(ingredient, craftList, ingredient.need) + if Craft.findRecipe(item) then + item.status = string.format('%s missing', itemDB:getName(ingredient)) + item.statusCode = self.STATUS_WARNING + else + item.status = 'no recipe' + item.statusCode = self.STATUS_ERROR + end + end + end + end + return crafted +end + +-- Craft as much as possible regardless if all ingredients are available +function Lora:forceCraftItem(inRecipe, items, originalItem, craftList, inCount) + local summed = { } + local throttle = Util.throttle() + + local function sumItems(recipe, count) + count = math.ceil(count / recipe.count) + local craftable = count + + for key,iqty in pairs(Craft.sumIngredients(recipe)) do + throttle() + local item = itemDB:splitKey(key) + local summedItem = summed[key] + if not summedItem then + summedItem = Util.shallowCopy(item) + summedItem.recipe = Craft.findRecipe(item) + summedItem.count = Craft.getItemCount(items, key) + summedItem.need = 0 + summedItem.used = 0 + summedItem.craftable = 0 + summed[key] = summedItem + end + + local total = count * iqty -- 4 * 2 + local used = math.min(summedItem.count, total) -- 5 + local need = total - used -- 3 + + if recipe.craftingTools and recipe.craftingTools[key] then + if summedItem.count > 0 then + summedItem.used = 1 + summedItem.need = 0 + need = 0 + elseif not summedItem.recipe then + summedItem.need = 1 + need = 1 + else + need = 1 + end + else + summedItem.count = summedItem.count - used + summedItem.used = summedItem.used + used + end + + if need > 0 then + if not summedItem.recipe then + craftable = math.min(craftable, math.floor(used / iqty)) + summedItem.need = summedItem.need + need + else + local c = sumItems(summedItem.recipe, need) -- 4 + craftable = math.min(craftable, math.floor((used + c) / iqty)) + summedItem.craftable = summedItem.craftable + c + end + end + end + if craftable > 0 then + craftable = Craft.craftRecipe(recipe, craftable * recipe.count, + self.context.inventoryAdapter) / recipe.count + self:clearGrid() + end + + return craftable * recipe.count + end + + local count = sumItems(inRecipe, inCount) + + if count < inCount then + for _,ingredient in pairs(summed) do + if ingredient.need > 0 then + local item = self:addCraftingRequest(ingredient, craftList, ingredient.need) + if Craft.findRecipe(item) then + item.status = string.format('%s missing', itemDB:getName(ingredient)) + item.statusCode = self.STATUS_WARNING + else + item.status = '(no recipe)' + item.statusCode = self.STATUS_ERROR + end + end + end + end + return count +end + +function Lora:craft(recipe, items, item, craftList) + item.status = nil + item.statusCode = nil + item.crafted = 0 + + if self:isCraftingPaused() then + return + end + + if not self:clearGrid() then + item.status = 'Grid obstructed' + item.statusCode = self.STATUS_ERROR + return + end + + if item.forceCrafting then + item.crafted = self:forceCraftItem(recipe, items, item, craftList, item.count) + else + item.crafted = self:craftItem(recipe, items, item, craftList, item.count) + end +end + +function Lora:craftItems(craftList) + for _,key in pairs(Util.keys(craftList)) do + local item = craftList[key] + local recipe = Craft.recipes[key] + if recipe then + self:craft(recipe, self:listItems(), item, craftList) + elseif not self.context.controllerAdapter then + item.status = '(no recipe)' + item.statusCode = self.STATUS_ERROR + end + end +end + +return Lora diff --git a/inventoryManager/apis/lora/tasks.lua b/inventoryManager/apis/lora/tasks.lua new file mode 100644 index 0000000..ac3ca07 --- /dev/null +++ b/inventoryManager/apis/lora/tasks.lua @@ -0,0 +1,12 @@ +local Util = require('util') + +local fs = _G.fs +local shell = _ENV.shell + +local Tasks = { + list = { } +} + + + +return Tasks diff --git a/inventoryManager/apis/networkedAdapter18.lua b/inventoryManager/apis/networkedAdapter18.lua index 99e36aa..bd7a538 100644 --- a/inventoryManager/apis/networkedAdapter18.lua +++ b/inventoryManager/apis/networkedAdapter18.lua @@ -10,6 +10,7 @@ function NetworkedAdapter:init(args) name = 'Networked Adapter', remotes = { }, remoteDefaults = { }, + dirty = true, } Util.merge(self, defaults) Util.merge(self, args) @@ -53,17 +54,25 @@ function NetworkedAdapter:isValid() 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 + local cache = { } local items = { } throttle = throttle or Util.throttle() for _, remote in pairs(self.remotes) do - remote:listItems(throttle) + if not remote:listItems(throttle) then + debug('no List') + error('Listing failed: ', remote.name) + end local rcache = remote.cache or { } -- TODO: add a method in each adapter that only updates a passed cache @@ -84,10 +93,10 @@ function NetworkedAdapter:listItems(throttle) end end - if not Util.empty(items) then - self.cache = cache - return items - end + self.dirty = false + self.cache = cache + self.items = items + return items end function NetworkedAdapter:getItemInfo(item) @@ -99,19 +108,16 @@ function NetworkedAdapter:getItemInfo(item) return items[key] end -function NetworkedAdapter:getPercentUsed() - if self.cache and self.getDrawerCount then - return math.floor(Util.size(self.cache) / self.getDrawerCount() * 100) - end - return 0 -end - function NetworkedAdapter:provide(item, qty, slot, direction) local total = 0 for _, remote in ipairs(self.remotes) do debug('%s -> slot %d: %d %s', remote.side, slot or -1, qty, item.name) local amount = remote:provide(item, qty, slot, direction) + if amount > 0 then + self.dirty = true + remote.dirty = true + end qty = qty - amount total = total + amount if qty <= 0 then @@ -158,6 +164,10 @@ debug('attempting to insert ' .. item.name) local function insert(remote) debug('slot %d -> %s: %s', slot, remote.side, qty) local amount = remote:insert(slot, qty, toSlot) + if amount > 0 then + self.dirty = true + remote.dirty = true + end qty = qty - amount total = total + amount end diff --git a/inventoryManager/inventoryManager.lua b/inventoryManager/inventoryManager.lua index 9a5fa5c..d751fe8 100644 --- a/inventoryManager/inventoryManager.lua +++ b/inventoryManager/inventoryManager.lua @@ -19,7 +19,7 @@ Refined storage TODO: add required block - Turtle crafting (optional): + Turtle crafting: 1. The turtle must have a crafting table equipped. 2. Equip the turtle with an introspection module. @@ -42,48 +42,35 @@ Configuration: Configuration file is usr/config/inventoryManager - valid sides: - top, bottom, left, right, front, back - - valid directions: - up, down, north, south, east, west - - Required: - computerFacing : direction turtle is facing - inventory : side for the main inventory (can be the same as the controller) - - Optional: - craftingChest : side for the chest used for crafting - controller : side for AE cable/interface or RS controller - trashDirection : direction of trash block (trashcan/inventory/etc) in - relationship to the main inventory. This block does not - need to touch the turtle, only the main inventory block. monitor : valid options include: type/monitor - will use the first monitor found side/north - specify a direction (top/bottom/east/etc) name/monitor_1 - specify the exact name of the peripheral ]]-- +--[[ +limit +organize +replenish +autocraft +]] + _G.requireInjector() -local Ansi = require('ansi') local Config = require('config') -local Craft = require('turtle.craft') local Event = require('event') local itemDB = require('itemDB') +local Lora = require('lora/lora') local Peripheral = require('peripheral') -local Terminal = require('terminal') local UI = require('ui') local Util = require('util') local InventoryAdapter = require('inventoryAdapter') -local colors = _G.colors -local device = _G.device +local fs = _G.fs local multishell = _ENV.multishell -local os = _G.os -local term = _G.term -local turtle = _G.turtle +local shell = _ENV.shell + if multishell then multishell.setTitle(multishell.getCurrent(), 'Resource Manager') @@ -100,29 +87,20 @@ if not modem or not modem.getNameLocal then error('Wired modem is not connected') end -local feederAdapter -local controllerAdapter - local storage = { } for k,v in pairs(config.remoteDefaults) do if v.mtype == 'storage' then storage[k] = v - elseif v.mtype == 'input' then - feederAdapter = InventoryAdapter.wrap({ side = k, direction = modem.getNameLocal() }) elseif v.mtype == 'controller' then -- TODO: look for controller end end -debug(storage) local inventoryAdapter = InventoryAdapter.wrap({ remoteDefaults = storage }) if not inventoryAdapter then error('Invalid inventory configuration') end -local introspectionModule = device['plethora:introspection'] or - error('introspection module not found') - -- TODO: cleanup for _, v in pairs(modem.getNamesRemote()) do local remote = Peripheral.get({ name = v }) @@ -141,1462 +119,53 @@ for _, v in pairs(modem.getNamesRemote()) do end end -local STATUS_INFO = 'info' -local STATUS_WARNING = 'warning' -local STATUS_ERROR = 'error' - -local RESOURCE_FILE = 'usr/config/resources.db' -local RECIPES_FILE = 'usr/config/recipes.db' - -local craftingPaused = false -local canCraft = not not (turtle and turtle.craft) -local canLearn = not not (canCraft and introspectionModule) -local userRecipes = Util.readTable(RECIPES_FILE) or { } -local jobList -local resources -local demandCrafting = { } - -local function getItem(items, inItem, ignoreDamage, ignoreNbtHash) - for _,item in pairs(items) do - if item.name == inItem.name and - (ignoreDamage or item.damage == inItem.damage) and - (ignoreNbtHash or item.nbtHash == inItem.nbtHash) then - return item - end - end -end - -local function uniqueKey(item) - return table.concat({ item.name, item.damage, item.nbtHash }, ':') -end - -local function mergeResources(t) - for _,v in pairs(resources) do - local item = getItem(t, v) - if item then - Util.merge(item, v) - else - item = Util.shallowCopy(v) - item.count = 0 - table.insert(t, item) - end - end - - for k in pairs(Craft.recipes) do - local v = itemDB:splitKey(k) - local item = getItem(t, v) - if not item then - item = Util.shallowCopy(v) - item.count = 0 - table.insert(t, item) - end - item.has_recipe = true - end - - for _,v in pairs(t) do - if not v.displayName then - v.displayName = itemDB:getName(v) - end - v.lname = v.displayName:lower() - end -end - -local function listItems() - local items - for _ = 1, 5 do - items = inventoryAdapter:listItems() - if items then - break - end - jobList:showError('Error - retrying in 3 seconds') - os.sleep(3) - end - if not items then --- error('could not check inventory') -term.clear() -jobList:showError('Error - rebooting in 5 seconds') -print('Communication failure') -print('rebooting in 5 secs') -os.sleep(5) -os.reboot() - end - - return items -end - -local function filterItems(t, filter, displayMode) - if filter or displayMode > 0 then - local r = { } - if filter then - filter = filter:lower() - end - for _,v in pairs(t) do - if not filter or string.find(v.lname, filter, 1, true) then - if not displayMode or - displayMode == 0 or - displayMode == 1 and v.count > 0 or - displayMode == 2 and v.has_recipe then - table.insert(r, v) - end - end - end - return r - end - return t -end - -local function clearGrid() - local function clear() - turtle.eachFilledSlot(function(slot) - inventoryAdapter:insert(slot.index, slot.count, nil, slot) - end) - - for i = 1, 16 do - if turtle.getItemCount(i) ~= 0 then - return false - end - end - return true - end - return clear() or clear() -end - -local function addCraftingRequest(item, craftList, count) - local key = uniqueKey(item) - local request = craftList[key] - if not craftList[key] then - request = { name = item.name, damage = item.damage, nbtHash = item.nbtHash, count = 0 } - request.displayName = itemDB:getName(request) - craftList[key] = request - end - request.count = request.count + count - return request -end - --- Craft -local function craftItem(recipe, items, originalItem, craftList, count) - local missing = { } - local toCraft = Craft.getCraftableAmount(recipe, count, items, missing) - if missing.name then - originalItem.status = string.format('%s missing', itemDB:getName(missing.name)) - originalItem.statusCode = STATUS_WARNING - end - - local crafted = 0 - - if toCraft > 0 then - crafted = Craft.craftRecipe(recipe, toCraft, inventoryAdapter) - clearGrid() - items = listItems() - count = count - crafted - end - - if count > 0 and items then - local ingredients = Craft.getResourceList4(recipe, items, count) - for _,ingredient in pairs(ingredients) do - if ingredient.need > 0 then - local item = addCraftingRequest(ingredient, craftList, ingredient.need) - if Craft.findRecipe(item) then - item.status = string.format('%s missing', itemDB:getName(ingredient)) - item.statusCode = STATUS_WARNING - else - item.status = 'no recipe' - item.statusCode = STATUS_ERROR - end - end - end - end - return crafted -end - --- Craft as much as possible regardless if all ingredients are available -local function forceCraftItem(inRecipe, items, originalItem, craftList, inCount) - local summed = { } - local throttle = Util.throttle() - - local function sumItems(recipe, count) - count = math.ceil(count / recipe.count) - local craftable = count - - for key,iqty in pairs(Craft.sumIngredients(recipe)) do - throttle() - local item = itemDB:splitKey(key) - local summedItem = summed[key] - if not summedItem then - summedItem = Util.shallowCopy(item) - summedItem.recipe = Craft.findRecipe(item) - summedItem.count = Craft.getItemCount(items, key) - summedItem.need = 0 - summedItem.used = 0 - summedItem.craftable = 0 - summed[key] = summedItem - end - - local total = count * iqty -- 4 * 2 - local used = math.min(summedItem.count, total) -- 5 - local need = total - used -- 3 - - if recipe.craftingTools and recipe.craftingTools[key] then - if summedItem.count > 0 then - summedItem.used = 1 - summedItem.need = 0 - need = 0 - elseif not summedItem.recipe then - summedItem.need = 1 - need = 1 - else - need = 1 - end - else - summedItem.count = summedItem.count - used - summedItem.used = summedItem.used + used - end - - if need > 0 then - if not summedItem.recipe then - craftable = math.min(craftable, math.floor(used / iqty)) - summedItem.need = summedItem.need + need - else - local c = sumItems(summedItem.recipe, need) -- 4 - craftable = math.min(craftable, math.floor((used + c) / iqty)) - summedItem.craftable = summedItem.craftable + c - end - end - end - if craftable > 0 then - craftable = Craft.craftRecipe(recipe, craftable * recipe.count, inventoryAdapter) / recipe.count - clearGrid() - end - - return craftable * recipe.count - end - - local count = sumItems(inRecipe, inCount) - - if count < inCount then - for _,ingredient in pairs(summed) do - if ingredient.need > 0 then - local item = addCraftingRequest(ingredient, craftList, ingredient.need) - if Craft.findRecipe(item) then - item.status = string.format('%s missing', itemDB:getName(ingredient)) - item.statusCode = STATUS_WARNING - else - item.status = '(no recipe)' - item.statusCode = STATUS_ERROR - end - end - end - end - return count -end - -local function craft(recipe, items, item, craftList) - item.status = nil - item.statusCode = nil - item.crafted = 0 - - if craftingPaused or not canCraft then - return - end - - if not clearGrid() then - item.status = 'Grid obstructed' - item.statusCode = STATUS_ERROR - return - end - - if item.forceCrafting then - item.crafted = forceCraftItem(recipe, items, item, craftList, item.count) - else - item.crafted = craftItem(recipe, items, item, craftList, item.count) - end -end - -local function craftItems(craftList, allItems) - -- turtle crafting - if canCraft then - for _,key in pairs(Util.keys(craftList)) do - local item = craftList[key] - local recipe = Craft.recipes[key] - if recipe then - craft(recipe, allItems, item, craftList) - allItems = listItems() -- refresh counts - if not allItems then - break - end - elseif not controllerAdapter then - item.status = '(no recipe)' - item.statusCode = STATUS_ERROR - end - end - end - - -- redstone control - for _,item in pairs(craftList) do - if item.rsControl then - item.status = '(activated)' - item.statusCode = STATUS_INFO - end - end - - -- controller - if controllerAdapter then - for key,item in pairs(craftList) do - if (not canCraft or not Craft.recipes[key]) and not item.rsControl then - if controllerAdapter:isCrafting(item) then - item.status = '(crafting)' - item.statusCode = STATUS_INFO - elseif not controllerAdapter:isCPUAvailable() then - item.status = '(waiting)' - item.statusCode = STATUS_WARNING - else - local count = item.count - item.crafted = 0 - while count >= 1 do -- try to request smaller quantities until successful - local s = pcall(function() - item.status = '(no recipe)' - item.statusCode = STATUS_ERROR - if not controllerAdapter:craft(item, count) then - item.status = '(missing ingredients)' - item.statusCode = STATUS_WARNING - error('failed') - end - item.status = '(crafting)' - item.statusCode = STATUS_INFO - item.crafted = count - end) - if s then - break -- successfully requested crafting - end - count = math.floor(count / 2) - end - end - end - end - end - - if not controllerAdapter and not canCraft then - for _,item in pairs(craftList) do - if not item.rsControl then - item.status = 'Invalid setup' - item.statusCode = STATUS_INFO - end - end - end -end - -local function queue(fn) - while craftingPaused do - os.sleep(1) - end - fn() -end - -local function eject(item, qty) - if _G.turtle then - local s, m = pcall(function() - inventoryAdapter:provide(item, qty) - _G.turtle.emptyInventory() - end) - if not s and m then - debug(m) - end - end -end - -local function jobMonitor() - local mon = Peripheral.lookup(config.monitor) - - if mon then - mon = UI.Device({ - device = mon, - textScale = .5, - }) - else - mon = UI.Device({ - device = Terminal.getNullTerm(term.current()) - }) - end - - jobList = UI.Page { - parent = mon, - grid = UI.Grid { - sortColumn = 'displayName', - backgroundFocusColor = colors.black, - columns = { - { heading = 'Qty', key = 'count', width = 6 }, - { heading = 'Crafting', key = 'displayName', width = mon.width / 2 - 10 }, - { heading = 'Status', key = 'status', width = mon.width - 10 }, - }, - }, - } - - function jobList:showError(msg) - self.grid:clear() - self.grid:centeredWrite(math.ceil(self.grid.height / 2), msg) - self:sync() - end - - function jobList:updateList(craftList) - self.grid:setValues(craftList) - self.grid:update() - self:draw() - self:sync() - end - - function jobList.grid:getRowTextColor(row, selected) - if row.statusCode == STATUS_ERROR then - return colors.red - elseif row.statusCode == STATUS_WARNING then - return colors.yellow - elseif row.statusCode == STATUS_INFO then - return colors.lime - end - return UI.Grid:getRowTextColor(row, selected) - end - - jobList:enable() - jobList:draw() - jobList:sync() -end - -local function getAutocraftItems() - local craftList = { } - - for _,res in pairs(resources) do - if res.auto then - res = Util.shallowCopy(res) - res.count = 256 -- this could be higher to increase autocrafting speed - local key = uniqueKey(res) - craftList[key] = res - end - end - return craftList -end - -local function getItemWithQty(items, res, ignoreDamage, ignoreNbtHash) - local item = getItem(items, res, ignoreDamage, ignoreNbtHash) - - if item and (ignoreDamage or ignoreNbtHash) then - local count = 0 - - for _,v in pairs(items) do - if item.name == v.name and - (ignoreDamage or item.damage == v.damage) and - (ignoreNbtHash or item.nbtHash == v.nbtHash) then - count = count + v.count - end - end - item.count = count - end - - return item -end - -local function watchResources(items) - local craftList = { } - local outputs = { } - - for _,res in pairs(resources) do - local item = getItemWithQty(items, res, res.ignoreDamage, res.ignoreNbtHash) - if not item then - item = { - damage = res.damage, - nbtHash = res.nbtHash, - name = res.name, - displayName = itemDB:getName(res), - count = 0 - } - end - - if res.limit and item.count > res.limit then - inventoryAdapter:provide( - { name = item.name, damage = item.damage, nbtHash = item.nbtHash }, - item.count - res.limit, - nil, - config.trashDirection) - - elseif res.low and item.count < res.low then - if res.ignoreDamage then - item.damage = 0 - end - local key = uniqueKey(res) - - craftList[key] = { - damage = item.damage, - nbtHash = item.nbtHash, - count = res.low - item.count, - name = item.name, - displayName = item.displayName, - status = '', - rsControl = res.rsControl, - } - end - - if res.rsControl and res.rsDevice and res.rsSide then - local enable = item.count < res.low - if not outputs[res.rsDevice] then - outputs[res.rsDevice] = { } - end - outputs[res.rsDevice][res.rsSide] = outputs[res.rsDevice][res.rsSide] or enable - end - end - - for rsDevice, sides in pairs(outputs) do - for side, enable in pairs(sides) do - pcall(function() - device[rsDevice].setOutput(side, enable) - end) - end - end - - return craftList -end - -local function emptyFeederChest() -_G._p2 =feederAdapter - if feederAdapter then - local list = feederAdapter.list() -- raw list ! - for k,v in pairs(list) do - feederAdapter:extract(k, v.count, 1) - inventoryAdapter:insert(1, v.count, nil, v) - end - end -end - local function loadResources() - resources = Util.readTable(RESOURCE_FILE) or { } + local resources = Util.readTable(Lora.RESOURCE_FILE) or { } for k,v in pairs(resources) do Util.merge(v, itemDB:splitKey(k)) end + + return resources end -local function saveResources() - local t = { } - - for k,v in pairs(resources) do - v = Util.shallowCopy(v) - local keys = Util.transpose({ 'auto', 'low', 'limit', - 'ignoreDamage', 'ignoreNbtHash', - 'rsControl', 'rsDevice', 'rsSide' }) - - for _,key in pairs(Util.keys(v)) do - if not keys[key] then - v[key] = nil - end - end - if not Util.empty(v) then - t[k] = v - end - end - - Util.writeTable(RESOURCE_FILE, t) -end - -local machinesPage = UI.Page { - titleBar = UI.TitleBar { - previousPage = true, - title = 'Machines', - }, - grid = UI.ScrollingGrid { - y = 2, ey = -2, - values = config.remoteDefaults, - columns = { - { heading = 'Name', key = 'displayName' }, - { heading = 'Priority', key = 'priority', width = 5 }, - { heading = 'Type', key = 'mtype', width = 5 }, - }, - sortColumn = 'name', - }, - detail = UI.SlideOut { - backgroundColor = colors.cyan, - form = UI.Form { - x = 1, y = 2, ex = -1, ey = -2, - [7] = UI.Text { - x = 12, y = 1, - width = 20, - value = 'test', - }, - [1] = UI.TextEntry { - formLabel = 'Name', formKey = 'displayName', help = '...', - limit = 64, - }, - [2] = UI.Chooser { - width = 15, - formLabel = 'Type', formKey = 'mtype', - nochoice = 'Storage', - choices = { - { name = 'Storage', value = 'storage' }, - { name = 'Trashcan', value = 'trashcan' }, - { name = 'Input chest', value = 'input' }, - { name = 'Ignore', value = 'ignore' }, - }, - help = 'Check if machine is empty before crafting' - }, - [3] = UI.Chooser { - width = 7, - formLabel = 'Empty', formKey = 'empty', - nochoice = 'No', - choices = { - { name = 'Yes', value = true }, - { name = 'No', value = false }, - }, - help = 'Check if machine is empty before crafting' - }, - [4] = UI.TextEntry { - formLabel = 'Priority', formKey = 'priority', help = '...', - limit = 4, - }, - [5] = UI.TextEntry { - formLabel = 'Max Craft', formKey = 'maxCount', help = '...', - limit = 4, - }, - [6] = UI.TextEntry { - formLabel = 'Lock to', formKey = 'lockWith', help = '...', - limit = 64, - }, - }, - statusBar = UI.StatusBar(), - }, - statusBar = UI.StatusBar { - values = 'Select Machine', - }, - accelerators = { - h = 'toggle_hidden', - } +local context = { + config = config, + inventoryAdapter = inventoryAdapter, + resources = loadResources(), + userRecipes = Util.readTable(Lora.RECIPES_FILE) or { }, } -function machinesPage:enable() - self.grid:update() - UI.Page.enable(self) -end +Lora:init(context) -function machinesPage.detail:eventHandler(event) - if event.type == 'focus_change' then - self.statusBar:setStatus(event.focused.help) - end - return UI.SlideOut.eventHandler(self, event) -end +local programDir = fs.getDir(shell.getRunningProgram()) +local pluginDir = fs.combine(programDir, 'plugins') -function machinesPage.grid:getDisplayValues(row) - row = Util.shallowCopy(row) - row.displayName = row.displayName or row.name - return row -end - -function machinesPage.grid:getRowTextColor(row, selected) - if row.mtype == 'ignore' then - return colors.lightGray - end - return UI.Grid:getRowTextColor(row, selected) -end - -function machinesPage:eventHandler(event) - if event.type == 'grid_select' then - self.detail.form:setValues(event.selected) - self.detail.form[7].value = event.selected.name -debug(event.selected) - self.detail:show() - - elseif event.type == 'toggle_hidden' then - local selected = self.grid:getSelected() - if selected then - selected.ignore = not selected.ignore --- Util.writeTable(MACHINES_FILE, machines) - self:draw() - end - - elseif event.type == 'form_complete' then - self.detail.form.values.empty = self.detail.form.values.empty == true or nil - self.detail.form.values.ignore = self.detail.form.values.ignore == true or nil - self.detail.form.values.priority = tonumber(self.detail.form.values.priority) - self.detail.form.values.maxCount = tonumber(self.detail.form.values.maxCount) - if #self.detail.form.values.displayName == 0 then - self.detail.form.values.displayName = nil - end - if #self.detail.form.values.lockWith == 0 then - self.detail.form.values.lockWith = nil - end - Config.update('inventoryManager', config) - self.detail:hide() - self.grid:update() - - elseif event.type == 'form_cancel' then - self.detail:hide() - - else - UI.Page.eventHandler(self, event) - end - return true -end - -local itemPage = UI.Page { - titleBar = UI.TitleBar { - title = 'Limit Resource', - previousPage = true, - event = 'form_cancel', - }, - form = UI.Form { - x = 1, y = 2, height = 10, ex = -1, - [1] = UI.TextEntry { - width = 7, - formLabel = 'Min', formKey = 'low', help = 'Craft if below min' - }, - [2] = UI.TextEntry { - width = 7, - formLabel = 'Max', formKey = 'limit', help = 'Eject if above max' - }, - [3] = UI.Chooser { - width = 7, - formLabel = 'Autocraft', formKey = 'auto', - nochoice = 'No', - choices = { - { name = 'Yes', value = true }, - { name = 'No', value = false }, - }, - help = 'Craft until out of ingredients' - }, - [4] = UI.Chooser { - width = 7, - formLabel = 'Ignore Dmg', formKey = 'ignoreDamage', - nochoice = 'No', - choices = { - { name = 'Yes', value = true }, - { name = 'No', value = false }, - }, - help = 'Ignore damage of item' - }, - [5] = UI.Chooser { - width = 7, - formLabel = 'Ignore NBT', formKey = 'ignoreNbtHash', - nochoice = 'No', - choices = { - { name = 'Yes', value = true }, - { name = 'No', value = false }, - }, - help = 'Ignore NBT of item' - }, ---[[ - [6] = UI.Button { - x = 2, y = -2, width = 10, - formLabel = 'Redstone', - event = 'show_rs', - text = 'Configure', - }, -]] - infoButton = UI.Button { - x = 2, y = -2, - event = 'show_info', - text = 'Info', - }, - }, - rsControl = UI.SlideOut { - backgroundColor = colors.cyan, - titleBar = UI.TitleBar { - title = "Redstone Control", - }, - form = UI.Form { - y = 2, - [1] = UI.Chooser { - width = 7, - formLabel = 'RS Control', formKey = 'rsControl', - nochoice = 'No', - choices = { - { name = 'Yes', value = true }, - { name = 'No', value = false }, - }, - help = 'Control via redstone' - }, - [2] = UI.Chooser { - width = 25, - formLabel = 'RS Device', formKey = 'rsDevice', - --choices = devices, - help = 'Redstone Device' - }, - [3] = UI.Chooser { - width = 10, - formLabel = 'RS Side', formKey = 'rsSide', - --nochoice = 'No', - choices = { - { name = 'up', value = 'up' }, - { name = 'down', value = 'down' }, - { name = 'east', value = 'east' }, - { name = 'north', value = 'north' }, - { name = 'west', value = 'west' }, - { name = 'south', value = 'south' }, - }, - help = 'Output side' - }, - }, - }, - info = UI.SlideOut { - titleBar = UI.TitleBar { - title = "Information", - }, - textArea = UI.TextArea { - x = 2, ex = -2, y = 3, ey = -4, - backgroundColor = colors.black, - }, - cancel = UI.Button { - ex = -2, y = -2, width = 6, - text = 'Okay', - event = 'hide_info', - }, - }, - statusBar = UI.StatusBar { } -} - -function itemPage:enable(item) - self.item = item - - self.form:setValues(item) - self.titleBar.title = item.displayName or item.name - - UI.Page.enable(self) - self:focusFirst() -end - -function itemPage.rsControl:enable() - local devices = self.form[1].choices - Util.clear(devices) - for _,dev in pairs(device) do - if dev.setOutput then - table.insert(devices, { name = dev.name, value = dev.name }) - end - end - - if Util.size(devices) == 0 then - table.insert(devices, { name = 'None found', values = '' }) - end - - UI.SlideOut.enable(self) -end - -function itemPage.rsControl:eventHandler(event) - if event.type == 'form_cancel' then - self:hide() - elseif event.type == 'form_complete' then - self:hide() - else - return UI.SlideOut.eventHandler(self, event) - end - return true -end - -function itemPage:eventHandler(event) - if event.type == 'form_cancel' then - UI:setPreviousPage() - - elseif event.type == 'show_rs' then - self.rsControl:show() - - elseif event.type == 'show_info' then - local value = - string.format('%s%s%s\n%s\n', - Ansi.orange, self.item.displayName, Ansi.reset, - self.item.name) - - if self.item.nbtHash then - value = value .. self.item.nbtHash .. '\n' - end - - value = value .. string.format('\n%sDamage:%s %s', - Ansi.yellow, Ansi.reset, self.item.damage) - - if self.item.maxDamage and self.item.maxDamage > 0 then - value = value .. string.format(' (max: %s)', self.item.maxDamage) - end - - if self.item.maxCount then - value = value .. string.format('\n%sStack Size: %s%s', - Ansi.yellow, Ansi.reset, self.item.maxCount) - end - - self.info.textArea.value = value - self.info:show() - - elseif event.type == 'hide_info' then - self.info:hide() - - elseif event.type == 'focus_change' then - self.statusBar:setStatus(event.focused.help) - self.statusBar:draw() - - elseif event.type == 'form_complete' then - local values = self.form.values - local originalKey = uniqueKey(self.item) - - local filtered = Util.shallowCopy(values) - filtered.low = tonumber(filtered.low) - filtered.limit = tonumber(filtered.limit) - - if filtered.auto ~= true then - filtered.auto = nil - end - - if filtered.rsControl ~= true then - filtered.rsControl = nil - filtered.rsSide = nil - filtered.rsDevice = nil - end - - if filtered.ignoreDamage == true then - filtered.damage = 0 - else - filtered.ignoreDamage = nil - end - - if filtered.ignoreNbtHash == true then - filtered.nbtHash = nil - else - filtered.ignoreNbtHash = nil - end - resources[originalKey] = nil - resources[uniqueKey(filtered)] = filtered - - filtered.count = nil - saveResources() - - UI:setPreviousPage() - - else - return UI.Page.eventHandler(self, event) - end - return true -end - -local listingPage = UI.Page { - menuBar = UI.MenuBar { - buttons = { - { text = 'Learn', event = 'learn' }, - { text = 'Forget', event = 'forget' }, - { text = 'Craft', event = 'craft' }, - { text = 'Refresh', event = 'refresh', x = -9 }, - }, - }, - grid = UI.Grid { - y = 2, ey = -2, - columns = { - { heading = 'Name', key = 'displayName' }, - { heading = 'Qty', key = 'count' , width = 4 }, - { heading = 'Min', key = 'low' , width = 4 }, - { heading = 'Max', key = 'limit' , width = 4 }, - }, - sortColumn = 'displayName', - }, - statusBar = UI.StatusBar { - filter = UI.TextEntry { - x = 1, ex = -4, - limit = 50, - shadowText = 'filter', - shadowTextColor = colors.gray, - backgroundColor = colors.cyan, - backgroundFocusColor = colors.cyan, - }, - display = UI.Button { - x = -3, - event = 'toggle_display', - value = 0, - text = 'A', - }, - }, - notification = UI.Notification(), - accelerators = { - r = 'refresh', - q = 'quit', - grid_select_right = 'craft', - [ 'control-e' ] = 'eject', - [ 'control-s' ] = 'eject_stack', - [ 'control-m' ] = 'machines', - }, - displayMode = 0, -} - -function listingPage.statusBar:draw() - return UI.Window.draw(self) -end - -function listingPage.grid:getRowTextColor(row, selected) - if row.is_craftable then - return colors.yellow - end - if canCraft and row.has_recipe then - return colors.cyan - end - return UI.Grid:getRowTextColor(row, selected) -end - -function listingPage.grid:getDisplayValues(row) - row = Util.shallowCopy(row) - row.count = Util.toBytes(row.count) - if row.low then - row.low = Util.toBytes(row.low) - end - if row.limit then - row.limit = Util.toBytes(row.limit) - end - return row -end - -function listingPage:eventHandler(event) - if event.type == 'quit' then - UI:exitPullEvents() - - elseif event.type == 'eject' then - local item = self.grid:getSelected() - if item then - queue(function() eject(item, 1) end) - end - - elseif event.type == 'eject_stack' then - local item = self.grid:getSelected() - if item then - queue(function() eject(item, itemDB:getMaxCount(item)) end) - end - - elseif event.type == 'machines' then - UI:setPage('machines') - - elseif event.type == 'grid_select' then - local selected = event.selected - UI:setPage('item', selected) - - elseif event.type == 'refresh' then - self:refresh() - self.grid:draw() - self.statusBar.filter:focus() - - elseif event.type == 'toggle_display' then - local values = { - [0] = 'A', - [1] = 'I', - [2] = 'C', - } - - event.button.value = (event.button.value + 1) % 3 - self.displayMode = event.button.value - event.button.text = values[event.button.value] - event.button:draw() - self:applyFilter() - self.grid:draw() - - elseif event.type == 'learn' then - if canLearn then - UI:setPage('learn') - else - self.notification:error('Missing a crafting chest or workbench\nCheck configuration') - end - - elseif event.type == 'craft' or event.type == 'grid_select_right' then - local item = self.grid:getSelected() - if Craft.findRecipe(item) or true then -- or item.is_craftable then - UI:setPage('craft', self.grid:getSelected()) - else - self.notification:error('No recipe defined') - end - - elseif event.type == 'forget' then - local item = self.grid:getSelected() - if item then - local key = uniqueKey(item) - - if userRecipes[key] then - userRecipes[key] = nil - Util.writeTable(RECIPES_FILE, userRecipes) - Craft.loadRecipes() - end - - if resources[key] then - resources[key] = nil - saveResources() - end - - self.notification:info('Forgot: ' .. item.name) - self:refresh() - self.grid:draw() - end - - elseif event.type == 'text_change' then - self.filter = event.text - if #self.filter == 0 then - self.filter = nil - end - self:applyFilter() - self.grid:draw() - self.statusBar.filter:focus() - - else - UI.Page.eventHandler(self, event) - end - return true -end - -function listingPage:enable() - self:refresh() - self:setFocus(self.statusBar.filter) - UI.Page.enable(self) -end - -function listingPage:refresh() - self.allItems = listItems() - mergeResources(self.allItems) - self:applyFilter() -end - -function listingPage:applyFilter() - local t = filterItems(self.allItems, self.filter, self.displayMode) - self.grid:setValues(t) -end - -local function getTurtleInventory() - local list = { } - for i = 1,16 do - list[i] = introspectionModule.getInventory().getItemMeta(i) - end - return list -end - -local function learnRecipe(page) - local ingredients = getTurtleInventory() - if ingredients then - turtle.select(1) - if canLearn and turtle.craft() then - local results = getTurtleInventory() - if results and results[1] then - clearGrid() - - local maxCount - local newRecipe = { - ingredients = ingredients, - } - - local numResults = 0 - for _,v in pairs(results) do - if v.count > 0 then - numResults = numResults + 1 - end - end - if numResults > 1 then - for _,v1 in pairs(results) do - for _,v2 in pairs(ingredients) do - if v1.name == v2.name and - v1.nbtHash == v2.nbtHash and - (v1.damage == v2.damage or - (v1.maxDamage > 0 and v2.maxDamage > 0 and - v1.damage ~= v2.damage)) then - if not newRecipe.crafingTools then - newRecipe.craftingTools = { } - end - local tool = Util.shallowCopy(v2) - if tool.maxDamage > 0 then - tool.damage = '*' - end - - --[[ - Turtles can only craft one item at a time using a tool :( - ]]-- - maxCount = 1 - - newRecipe.craftingTools[uniqueKey(tool)] = true - v1.craftingTool = true - break - end - end - end - end - - local recipe - for _,v in pairs(results) do - if not v.craftingTool then - recipe = v - if maxCount then - recipe.maxCount = maxCount - end - break - end - end - - if not recipe then - debug(results) - debug(newRecipe) - error('Failed - view system log') - end - - newRecipe.count = recipe.count - - local key = uniqueKey(recipe) - if recipe.maxCount ~= 64 then - newRecipe.maxCount = recipe.maxCount - end - for k,ingredient in pairs(Util.shallowCopy(ingredients)) do - if ingredient.maxDamage > 0 then - -- ingredient.damage = '*' -- I don't think this is right - end - ingredients[k] = uniqueKey(ingredient) - end - - userRecipes[key] = newRecipe - - Util.writeTable(RECIPES_FILE, userRecipes) - Craft.loadRecipes() - - local displayName = itemDB:getName(recipe) - - listingPage.statusBar.filter:setValue(displayName) - listingPage.notification:success('Learned: ' .. displayName) - listingPage.filter = displayName - listingPage:refresh() - listingPage.grid:draw() - - eject(recipe, recipe.count) - return true - end - else - listingPage.notification:error('Failed to craft', 3) - end - else - listingPage.notification:error('No recipe defined', 3) +for _, file in pairs(fs.list(pluginDir)) do + local s, m = Util.run(_ENV, fs.combine(pluginDir, file)) + if not s and m then + error(m or 'Unknown error') end end -local learnPage = UI.Dialog { - height = 7, width = UI.term.width - 6, - title = 'Learn Recipe', - idField = UI.Text { - x = 5, - y = 3, - width = UI.term.width - 10, - value = 'Place recipe in turtle' - }, - accept = UI.Button { - x = -14, y = -3, - text = 'Ok', event = 'accept', - }, - cancel = UI.Button { - x = -9, y = -3, - text = 'Cancel', event = 'cancel' - }, - statusBar = UI.StatusBar { - status = 'Crafting paused' - } -} +table.sort(Lora.tasks, function(a, b) + return a.priority < b.priority +end) -function learnPage:enable() - craftingPaused = true - self:focusFirst() - UI.Dialog.enable(self) -end +Lora:clearGrid() -function learnPage:disable() - craftingPaused = false - UI.Dialog.disable(self) -end - -function learnPage:eventHandler(event) - if event.type == 'cancel' then - UI:setPreviousPage() - elseif event.type == 'accept' then - if learnRecipe(self) then - UI:setPreviousPage() - end - else - return UI.Dialog.eventHandler(self, event) - end - return true -end - -local craftPage = UI.Page { - titleBar = UI.TitleBar { }, - wizard = UI.Wizard { - y = 2, ey = -2, - pages = { - quantity = UI.Window { - index = 1, - text = UI.Text { - x = 6, y = 3, - value = 'Quantity', - }, - count = UI.TextEntry { - x = 15, y = 3, width = 10, - limit = 6, - value = 1, - }, - ejectText = UI.Text { - x = 6, y = 4, - value = 'Eject', - }, - eject = UI.Chooser { - x = 15, y = 4, width = 7, - value = true, - nochoice = 'No', - choices = { - { name = 'Yes', value = true }, - { name = 'No', value = false }, - }, - }, - }, - resources = UI.Window { - index = 2, - grid = UI.ScrollingGrid { - y = 2, ey = -2, - columns = { - { heading = 'Name', key = 'displayName' }, - { heading = 'Total', key = 'total' , width = 5 }, - { heading = 'Used', key = 'used' , width = 5 }, - { heading = 'Need', key = 'need' , width = 5 }, - }, - sortColumn = 'displayName', - }, - }, - }, - }, -} - -function craftPage:enable(item) - self.item = item - self:focusFirst() - self.titleBar.title = itemDB:getName(item) --- self.wizard.pages.quantity.eject.value = true - UI.Page.enable(self) -end - -function craftPage.wizard.pages.resources.grid:getDisplayValues(row) - local function dv(v) - if v == 0 then - return '' - end - return Util.toBytes(v) - end - row = Util.shallowCopy(row) - row.total = Util.toBytes(row.total) - row.used = dv(row.used) - row.need = dv(row.need) - return row -end - -function craftPage.wizard.pages.resources.grid:getRowTextColor(row, selected) - if row.need > 0 then - return colors.orange - end - return UI.Grid:getRowTextColor(row, selected) -end - -function craftPage.wizard:eventHandler(event) - if event.type == 'nextView' then - local count = tonumber(self.pages.quantity.count.value) - if not count or count <= 0 then - self.pages.quantity.count.backgroundColor = colors.red - self.pages.quantity.count:draw() - return false - end - self.pages.quantity.count.backgroundColor = colors.black - end - return UI.Wizard.eventHandler(self, event) -end - -function craftPage.wizard.pages.resources:enable() - local items = listItems() - local count = tonumber(self.parent.quantity.count.value) - local recipe = Craft.findRecipe(craftPage.item) - if recipe then - local ingredients = Craft.getResourceList4(recipe, items, count) - for _,v in pairs(ingredients) do - v.displayName = itemDB:getName(v) - end - self.grid:setValues(ingredients) - else - self.grid:setValues({ }) - end - return UI.Window.enable(self) -end - -function craftPage:eventHandler(event) - if event.type == 'cancel' then - UI:setPreviousPage() - - elseif event.type == 'accept' then - local key = uniqueKey(self.item) - demandCrafting[key] = Util.shallowCopy(self.item) - demandCrafting[key].count = tonumber(self.wizard.pages.quantity.count.value) - demandCrafting[key].ocount = demandCrafting[key].count - demandCrafting[key].forceCrafting = true - demandCrafting[key].eject = self.wizard.pages.quantity.eject.value == true - UI:setPreviousPage() - else - return UI.Page.eventHandler(self, event) - end - return true -end - -loadResources() -if canCraft then - clearGrid() -end - -UI:setPages({ - listing = listingPage, - item = itemPage, - learn = learnPage, - craft = craftPage, - machines = machinesPage, -}) - -jobMonitor() -UI:setPage(listingPage) -listingPage:setFocus(listingPage.statusBar.filter) +local page = UI:getPage('listing') +UI:setPage(page) +page:setFocus(page.statusBar.filter) Event.onInterval(5, function() + if not Lora:isCraftingPaused() then + Lora:resetCraftingStatus() + context.inventoryAdapter:refresh() - emptyFeederChest() - - if not craftingPaused then - local items = listItems() - if not items or Util.size(items) == 0 then - jobList:showError('No items in system') - else - local demandCrafted - if Util.size(demandCrafting) > 0 then - items = listItems() - if items then - demandCrafted = Util.shallowCopy(demandCrafting) - craftItems(demandCrafted, items) - end - end - - items = listItems() - local craftList - if items then - craftList = watchResources(items) - craftItems(craftList, items) - end - - if demandCrafted and craftList then - for k,v in pairs(demandCrafted) do - craftList[k] = v - end - end - - for _,key in pairs(Util.keys(demandCrafting)) do - local item = demandCrafting[key] - if item.crafted then - item.count = math.max(0, item.count - item.crafted) - if item.count <= 0 then - demandCrafting[key] = nil - item.statusCode = 'success' - if item.eject then - eject(item, item.ocount) - end - end - end - end - - jobList:updateList(craftList) - - craftList = getAutocraftItems(items) -- autocrafted items don't show on job monitor - craftItems(craftList, items) + for _, task in ipairs(Lora.tasks) do + task:cycle(context) end end end) UI:pullEvents() -jobList.parent:reset() diff --git a/inventoryManager/plugins/autocraftTask.lua b/inventoryManager/plugins/autocraftTask.lua new file mode 100644 index 0000000..2d089d9 --- /dev/null +++ b/inventoryManager/plugins/autocraftTask.lua @@ -0,0 +1,24 @@ +local Lora = require('lora/lora') +local Util = require('util') + +local Autocraft = { + priority = 100, +} + +function Autocraft:cycle(context) + local list = { } + + for _,res in pairs(context.resources) do + if res.auto then + res = Util.shallowCopy(res) + res.count = 256 + list[Lora:uniqueKey(res)] = res + end + end + + if not Util.empty(list) then + Lora:craftItems(list) + end +end + +Lora:registerTask(Autocraft) diff --git a/inventoryManager/plugins/craft.lua b/inventoryManager/plugins/craft.lua new file mode 100644 index 0000000..e7b4f39 --- /dev/null +++ b/inventoryManager/plugins/craft.lua @@ -0,0 +1,167 @@ +local Craft = require('turtle.craft') +local itemDB = require('itemDB') +local Lora = require('lora/lora') +local UI = require('ui') +local Util = require('util') + +local colors = _G.colors + +local demandCrafting = { } + +local craftPage = UI.Page { + titleBar = UI.TitleBar { }, + wizard = UI.Wizard { + y = 2, ey = -2, + pages = { + quantity = UI.Window { + index = 1, + text = UI.Text { + x = 6, y = 3, + value = 'Quantity', + }, + count = UI.TextEntry { + x = 15, y = 3, width = 10, + limit = 6, + value = 1, + }, + ejectText = UI.Text { + x = 6, y = 4, + value = 'Eject', + }, + eject = UI.Chooser { + x = 15, y = 4, width = 7, + value = true, + nochoice = 'No', + choices = { + { name = 'Yes', value = true }, + { name = 'No', value = false }, + }, + }, + }, + resources = UI.Window { + index = 2, + grid = UI.ScrollingGrid { + y = 2, ey = -2, + columns = { + { heading = 'Name', key = 'displayName' }, + { heading = 'Total', key = 'total' , width = 5 }, + { heading = 'Used', key = 'used' , width = 5 }, + { heading = 'Need', key = 'need' , width = 5 }, + }, + sortColumn = 'displayName', + }, + }, + }, + }, +} + +function craftPage:enable(item) + self.item = item + self:focusFirst() + self.titleBar.title = itemDB:getName(item) +-- self.wizard.pages.quantity.eject.value = true + UI.Page.enable(self) +end + +function craftPage.wizard.pages.resources.grid:getDisplayValues(row) + local function dv(v) + if v == 0 then + return '' + end + return Util.toBytes(v) + end + row = Util.shallowCopy(row) + row.total = Util.toBytes(row.total) + row.used = dv(row.used) + row.need = dv(row.need) + return row +end + +function craftPage.wizard.pages.resources.grid:getRowTextColor(row, selected) + if row.need > 0 then + return colors.orange + end + return UI.Grid:getRowTextColor(row, selected) +end + +function craftPage.wizard:eventHandler(event) + if event.type == 'nextView' then + local count = tonumber(self.pages.quantity.count.value) + if not count or count <= 0 then + self.pages.quantity.count.backgroundColor = colors.red + self.pages.quantity.count:draw() + return false + end + self.pages.quantity.count.backgroundColor = colors.black + end + return UI.Wizard.eventHandler(self, event) +end + +function craftPage.wizard.pages.resources:enable() + local items = Lora:listItems() + local count = tonumber(self.parent.quantity.count.value) + local recipe = Craft.findRecipe(craftPage.item) + if recipe then + local ingredients = Craft.getResourceList4(recipe, items, count) + for _,v in pairs(ingredients) do + v.displayName = itemDB:getName(v) + end + self.grid:setValues(ingredients) + else + self.grid:setValues({ }) + end + return UI.Window.enable(self) +end + +function craftPage:eventHandler(event) + if event.type == 'cancel' then + UI:setPreviousPage() + + elseif event.type == 'accept' then + local key = Lora:uniqueKey(self.item) + demandCrafting[key] = Util.shallowCopy(self.item) + demandCrafting[key].count = tonumber(self.wizard.pages.quantity.count.value) + demandCrafting[key].ocount = demandCrafting[key].count + demandCrafting[key].forceCrafting = true + demandCrafting[key].eject = self.wizard.pages.quantity.eject.value == true + UI:setPreviousPage() + else + return UI.Page.eventHandler(self, event) + end + return true +end + +local demandCraftingTask = { + priority = 20, +} + +function demandCraftingTask:cycle() + if Util.size(demandCrafting) > 0 then + local demandCrafted = Util.shallowCopy(demandCrafting) + Lora:craftItems(demandCrafted) + + for _, item in pairs(demandCrafting) do + if item.crafted then + item.count = math.max(0, item.count - item.crafted) + if item.count <= 0 then + item.statusCode = 'success' + end + end + end + + Lora:updateCraftingStatus(demandCrafted) + + for _,key in pairs(Util.keys(demandCrafting)) do + local item = demandCrafting[key] + if item.crafted and item.count <= 0 then + demandCrafting[key] = nil + if item.eject then + Lora:eject(item, item.ocount) + end + end + end + end +end + +UI:addPage('craft', craftPage) +Lora:registerTask(demandCraftingTask) diff --git a/inventoryManager/plugins/inputChestTask.lua b/inventoryManager/plugins/inputChestTask.lua new file mode 100644 index 0000000..516ea81 --- /dev/null +++ b/inventoryManager/plugins/inputChestTask.lua @@ -0,0 +1,37 @@ +local InventoryAdapter = require('inventoryAdapter') +local Lora = require('lora/lora') + +local device = _G.device +local modem = device.wired_modem + +local InputChest = { + priority = 1, + adapters = { }, +} + +function InputChest:init(context) + for k,v in pairs(context.config.remoteDefaults) do + if v.mtype == 'input' then + local adapter = InventoryAdapter.wrap({ side = k, direction = modem.getNameLocal() }) + if adapter then + table.insert(self.adapters, adapter) + end + end + end +end + +-- TODO: clear grid + +function InputChest:cycle(context) + for _, adapter in pairs(self.adapters) do + local list = adapter.list() -- raw list ! + for k,v in pairs(list) do + adapter:extract(k, v.count, 1) + context.inventoryAdapter:insert(1, v.count, nil, v) + end + end +end + +InputChest:init(Lora:getContext()) + +Lora:registerTask(InputChest) diff --git a/inventoryManager/plugins/item.lua b/inventoryManager/plugins/item.lua new file mode 100644 index 0000000..e6c8b0c --- /dev/null +++ b/inventoryManager/plugins/item.lua @@ -0,0 +1,246 @@ +local Ansi = require('ansi') +local Lora = require('lora/lora') +local UI = require('ui') +local Util = require('util') + +local colors = _G.colors +local device = _G.device + +local context = Lora:getContext() + +local itemPage = UI.Page { + titleBar = UI.TitleBar { + title = 'Limit Resource', + previousPage = true, + event = 'form_cancel', + }, + form = UI.Form { + x = 1, y = 2, height = 10, ex = -1, + [1] = UI.TextEntry { + width = 7, + formLabel = 'Min', formKey = 'low', help = 'Craft if below min' + }, + [2] = UI.TextEntry { + width = 7, + formLabel = 'Max', formKey = 'limit', help = 'Eject if above max' + }, + [3] = UI.Chooser { + width = 7, + formLabel = 'Autocraft', formKey = 'auto', + nochoice = 'No', + choices = { + { name = 'Yes', value = true }, + { name = 'No', value = false }, + }, + help = 'Craft until out of ingredients' + }, + [4] = UI.Chooser { + width = 7, + formLabel = 'Ignore Dmg', formKey = 'ignoreDamage', + nochoice = 'No', + choices = { + { name = 'Yes', value = true }, + { name = 'No', value = false }, + }, + help = 'Ignore damage of item' + }, + [5] = UI.Chooser { + width = 7, + formLabel = 'Ignore NBT', formKey = 'ignoreNbtHash', + nochoice = 'No', + choices = { + { name = 'Yes', value = true }, + { name = 'No', value = false }, + }, + help = 'Ignore NBT of item' + }, +--[[ + [6] = UI.Button { + x = 2, y = -2, width = 10, + formLabel = 'Redstone', + event = 'show_rs', + text = 'Configure', + }, +]] + infoButton = UI.Button { + x = 2, y = -2, + event = 'show_info', + text = 'Info', + }, + }, + rsControl = UI.SlideOut { + backgroundColor = colors.cyan, + titleBar = UI.TitleBar { + title = "Redstone Control", + }, + form = UI.Form { + y = 2, + [1] = UI.Chooser { + width = 7, + formLabel = 'RS Control', formKey = 'rsControl', + nochoice = 'No', + choices = { + { name = 'Yes', value = true }, + { name = 'No', value = false }, + }, + help = 'Control via redstone' + }, + [2] = UI.Chooser { + width = 25, + formLabel = 'RS Device', formKey = 'rsDevice', + --choices = devices, + help = 'Redstone Device' + }, + [3] = UI.Chooser { + width = 10, + formLabel = 'RS Side', formKey = 'rsSide', + --nochoice = 'No', + choices = { + { name = 'up', value = 'up' }, + { name = 'down', value = 'down' }, + { name = 'east', value = 'east' }, + { name = 'north', value = 'north' }, + { name = 'west', value = 'west' }, + { name = 'south', value = 'south' }, + }, + help = 'Output side' + }, + }, + }, + info = UI.SlideOut { + titleBar = UI.TitleBar { + title = "Information", + }, + textArea = UI.TextArea { + x = 2, ex = -2, y = 3, ey = -4, + backgroundColor = colors.black, + }, + cancel = UI.Button { + ex = -2, y = -2, width = 6, + text = 'Okay', + event = 'hide_info', + }, + }, + statusBar = UI.StatusBar { } +} + +function itemPage:enable(item) + self.item = item + + self.form:setValues(item) + self.titleBar.title = item.displayName or item.name + + UI.Page.enable(self) + self:focusFirst() +end + +function itemPage.rsControl:enable() + local devices = self.form[1].choices + Util.clear(devices) + for _,dev in pairs(device) do + if dev.setOutput then + table.insert(devices, { name = dev.name, value = dev.name }) + end + end + + if Util.size(devices) == 0 then + table.insert(devices, { name = 'None found', values = '' }) + end + + UI.SlideOut.enable(self) +end + +function itemPage.rsControl:eventHandler(event) + if event.type == 'form_cancel' then + self:hide() + elseif event.type == 'form_complete' then + self:hide() + else + return UI.SlideOut.eventHandler(self, event) + end + return true +end + +function itemPage:eventHandler(event) + if event.type == 'form_cancel' then + UI:setPreviousPage() + + elseif event.type == 'show_rs' then + self.rsControl:show() + + elseif event.type == 'show_info' then + local value = + string.format('%s%s%s\n%s\n', + Ansi.orange, self.item.displayName, Ansi.reset, + self.item.name) + + if self.item.nbtHash then + value = value .. self.item.nbtHash .. '\n' + end + + value = value .. string.format('\n%sDamage:%s %s', + Ansi.yellow, Ansi.reset, self.item.damage) + + if self.item.maxDamage and self.item.maxDamage > 0 then + value = value .. string.format(' (max: %s)', self.item.maxDamage) + end + + if self.item.maxCount then + value = value .. string.format('\n%sStack Size: %s%s', + Ansi.yellow, Ansi.reset, self.item.maxCount) + end + + self.info.textArea.value = value + self.info:show() + + elseif event.type == 'hide_info' then + self.info:hide() + + elseif event.type == 'focus_change' then + self.statusBar:setStatus(event.focused.help) + self.statusBar:draw() + + elseif event.type == 'form_complete' then + local values = self.form.values + local originalKey = Lora:uniqueKey(self.item) + + local filtered = Util.shallowCopy(values) + filtered.low = tonumber(filtered.low) + filtered.limit = tonumber(filtered.limit) + + if filtered.auto ~= true then + filtered.auto = nil + end + + if filtered.rsControl ~= true then + filtered.rsControl = nil + filtered.rsSide = nil + filtered.rsDevice = nil + end + + if filtered.ignoreDamage == true then + filtered.damage = 0 + else + filtered.ignoreDamage = nil + end + + if filtered.ignoreNbtHash == true then + filtered.nbtHash = nil + else + filtered.ignoreNbtHash = nil + end + context.resources[originalKey] = nil + context.resources[Lora:uniqueKey(filtered)] = filtered + + filtered.count = nil + Lora:saveResources() + + UI:setPreviousPage() + + else + return UI.Page.eventHandler(self, event) + end + return true +end + +UI:addPage('item', itemPage) diff --git a/inventoryManager/plugins/jobList.lua b/inventoryManager/plugins/jobList.lua new file mode 100644 index 0000000..3b4e7c5 --- /dev/null +++ b/inventoryManager/plugins/jobList.lua @@ -0,0 +1,65 @@ +local Lora = require('lora/lora') +local Peripheral = require('peripheral') +local UI = require('ui') + +local colors = _G.colors + +local context = Lora:getContext() +local mon = Peripheral.lookup(context.config.monitor) or + error('Monitor is not attached') +local display = UI.Device { + device = mon, + textScale = .5, +} + +local jobList = UI.Page { + parent = display, + grid = UI.Grid { + sortColumn = 'displayName', + backgroundFocusColor = colors.black, + columns = { + { heading = 'Qty', key = 'count', width = 6 }, + { heading = 'Crafting', key = 'displayName', width = display.width / 2 - 10 }, + { heading = 'Status', key = 'status', width = display.width - 10 }, + }, + }, +} + +function jobList:showError(msg) + self.grid:clear() + self.grid:centeredWrite(math.ceil(self.grid.height / 2), msg) + self:sync() +end + +function jobList:updateList(craftList) + self.grid:setValues(craftList) + self.grid:update() + self:draw() + self:sync() +end + +function jobList.grid:getRowTextColor(row, selected) + if row.statusCode == Lora.STATUS_ERROR then + return colors.red + elseif row.statusCode == Lora.STATUS_WARNING then + return colors.yellow + elseif row.statusCode == Lora.STATUS_INFO then + return colors.lime + end + return UI.Grid:getRowTextColor(row, selected) +end + +jobList:enable() +jobList:draw() +jobList:sync() + +local JobListTask = { + priority = 80, +} + +function JobListTask:cycle() + jobList:updateList(Lora:getCraftingStatus()) +end + +Lora:registerTask(JobListTask) +context.jobList = jobList diff --git a/inventoryManager/plugins/learn.lua b/inventoryManager/plugins/learn.lua new file mode 100644 index 0000000..139b55f --- /dev/null +++ b/inventoryManager/plugins/learn.lua @@ -0,0 +1,174 @@ +local Craft = require('turtle.craft') +local itemDB = require('itemDB') +local Lora = require('lora/lora') +local UI = require('ui') +local Util = require('util') + +local device = _G.device +local turtle = _G.turtle + +local context = Lora:getContext() + +-- TODO: try networked module +local introspectionModule = device['plethora:introspection'] or + error('Introspection module not found') + +local function getTurtleInventory() + local list = { } + for i = 1,16 do + list[i] = introspectionModule.getInventory().getItemMeta(i) + end + return list +end + +local function learnRecipe() + local ingredients = getTurtleInventory() + local listingPage = UI:getPage('listing') + + if ingredients then + turtle.select(1) + if turtle.craft() then + local results = getTurtleInventory() + if results and results[1] then + Lora:clearGrid() + + local maxCount + local newRecipe = { + ingredients = ingredients, + } + + local numResults = 0 + for _,v in pairs(results) do + if v.count > 0 then + numResults = numResults + 1 + end + end + if numResults > 1 then + for _,v1 in pairs(results) do + for _,v2 in pairs(ingredients) do + if v1.name == v2.name and + v1.nbtHash == v2.nbtHash and + (v1.damage == v2.damage or + (v1.maxDamage > 0 and v2.maxDamage > 0 and + v1.damage ~= v2.damage)) then + if not newRecipe.crafingTools then + newRecipe.craftingTools = { } + end + local tool = Util.shallowCopy(v2) + if tool.maxDamage > 0 then + tool.damage = '*' + end + + --[[ + Turtles can only craft one item at a time using a tool :( + ]]-- + maxCount = 1 + + newRecipe.craftingTools[Lora:uniqueKey(tool)] = true + v1.craftingTool = true + break + end + end + end + end + + local recipe + for _,v in pairs(results) do + if not v.craftingTool then + recipe = v + if maxCount then + recipe.maxCount = maxCount + end + break + end + end + + if not recipe then + debug(results) + debug(newRecipe) + error('Failed - view system log') + end + + newRecipe.count = recipe.count + + local key = Lora:uniqueKey(recipe) + if recipe.maxCount ~= 64 then + newRecipe.maxCount = recipe.maxCount + end + for k,ingredient in pairs(Util.shallowCopy(ingredients)) do + if ingredient.maxDamage > 0 then + -- ingredient.damage = '*' -- I don't think this is right + end + ingredients[k] = Lora:uniqueKey(ingredient) + end + + context.userRecipes[key] = newRecipe + Util.writeTable(Lora.RECIPES_FILE, context.userRecipes) + Craft.loadRecipes() + + local displayName = itemDB:getName(recipe) + + listingPage.statusBar.filter:setValue(displayName) + listingPage.notification:success('Learned: ' .. displayName) + listingPage.filter = displayName + listingPage:refresh() + listingPage.grid:draw() + + Lora:eject(recipe, recipe.count) + return true + end + else + listingPage.notification:error('Failed to craft', 3) + end + else + listingPage.notification:error('No recipe defined', 3) + end +end + +local learnPage = UI.Dialog { + height = 7, width = UI.term.width - 6, + title = 'Learn Recipe', + idField = UI.Text { + x = 5, + y = 3, + width = UI.term.width - 10, + value = 'Place recipe in turtle' + }, + accept = UI.Button { + x = -14, y = -3, + text = 'Ok', event = 'accept', + }, + cancel = UI.Button { + x = -9, y = -3, + text = 'Cancel', event = 'cancel' + }, + statusBar = UI.StatusBar { + status = 'Crafting paused' + } +} + +function learnPage:enable() + Lora:pauseCrafting() + self:focusFirst() + UI.Dialog.enable(self) +end + +function learnPage:disable() + Lora:resumeCrafting() + UI.Dialog.disable(self) +end + +function learnPage:eventHandler(event) + if event.type == 'cancel' then + UI:setPreviousPage() + elseif event.type == 'accept' then + if learnRecipe(self) then + UI:setPreviousPage() + end + else + return UI.Dialog.eventHandler(self, event) + end + return true +end + +UI:addPage('learn', learnPage) diff --git a/inventoryManager/plugins/limitTask.lua b/inventoryManager/plugins/limitTask.lua new file mode 100644 index 0000000..3fc86d7 --- /dev/null +++ b/inventoryManager/plugins/limitTask.lua @@ -0,0 +1,36 @@ +local Lora = require('lora/lora') + +local LimitTask = { + priority = 10, +} + +function LimitTask:init(context) + for k,v in pairs(context.config.remoteDefaults) do + if v.mtype == 'trashcan' then + self.trashcan = k + break + end + end +end + +function LimitTask:cycle(context) + if not self.trashcan then + return + end + + for _,res in pairs(context.resources) do + if res.limit then + local item = Lora: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, + nil, + self.trashcan) + end + end + end +end + +LimitTask:init(Lora:getContext()) +Lora:registerTask(LimitTask) diff --git a/inventoryManager/plugins/listing.lua b/inventoryManager/plugins/listing.lua new file mode 100644 index 0000000..3ac7031 --- /dev/null +++ b/inventoryManager/plugins/listing.lua @@ -0,0 +1,250 @@ +local Craft = require('turtle.craft') +local itemDB = require('itemDB') +local Lora = require('lora/lora') +local UI = require('ui') +local Util = require('util') + +local colors = _G.colors +local os = _G.os + +local context = Lora:getContext() + +local function queue(fn) + while Lora:isCraftingPaused() do + os.sleep(1) + end + fn() +end + +local function mergeResources(t) + for _,v in pairs(context.resources) do + local item = Lora:getItem(t, v) + if item then + Util.merge(item, v) + else + item = Util.shallowCopy(v) + item.count = 0 + table.insert(t, item) + end + end + + for k in pairs(Craft.recipes) do + local v = itemDB:splitKey(k) + local item = Lora:getItem(t, v) + if not item then + item = Util.shallowCopy(v) + item.count = 0 + table.insert(t, item) + end + item.has_recipe = true + end + + for _,v in pairs(t) do + if not v.displayName then + v.displayName = itemDB:getName(v) + end + v.lname = v.displayName:lower() + end +end + +local function filterItems(t, filter, displayMode) + if filter or displayMode > 0 then + local r = { } + if filter then + filter = filter:lower() + end + for _,v in pairs(t) do + if not filter or string.find(v.lname, filter, 1, true) then + if not displayMode or + displayMode == 0 or + displayMode == 1 and v.count > 0 or + displayMode == 2 and v.has_recipe then + table.insert(r, v) + end + end + end + return r + end + return t +end + +local listingPage = UI.Page { + menuBar = UI.MenuBar { + buttons = { + { text = 'Learn', event = 'learn' }, + { text = 'Forget', event = 'forget' }, + { text = 'Craft', event = 'craft' }, + { text = 'Refresh', event = 'refresh', x = -9 }, + }, + }, + grid = UI.Grid { + y = 2, ey = -2, + columns = { + { heading = 'Name', key = 'displayName' }, + { heading = 'Qty', key = 'count' , width = 4 }, + { heading = 'Min', key = 'low' , width = 4 }, + { heading = 'Max', key = 'limit' , width = 4 }, + }, + sortColumn = 'displayName', + }, + statusBar = UI.StatusBar { + filter = UI.TextEntry { + x = 1, ex = -4, + limit = 50, + shadowText = 'filter', + shadowTextColor = colors.gray, + backgroundColor = colors.cyan, + backgroundFocusColor = colors.cyan, + }, + display = UI.Button { + x = -3, + event = 'toggle_display', + value = 0, + text = 'A', + }, + }, + notification = UI.Notification(), + accelerators = { + r = 'refresh', + q = 'quit', + grid_select_right = 'craft', + [ 'control-e' ] = 'eject', + [ 'control-s' ] = 'eject_stack', + [ 'control-m' ] = 'machines', + }, + displayMode = 0, +} + +function listingPage.statusBar:draw() + return UI.Window.draw(self) +end + +function listingPage.grid:getRowTextColor(row, selected) + if row.is_craftable then + return colors.yellow + end + if row.has_recipe then + return colors.cyan + end + return UI.Grid:getRowTextColor(row, selected) +end + +function listingPage.grid:getDisplayValues(row) + row = Util.shallowCopy(row) + row.count = Util.toBytes(row.count) + if row.low then + row.low = Util.toBytes(row.low) + end + if row.limit then + row.limit = Util.toBytes(row.limit) + end + return row +end + +function listingPage:eventHandler(event) + if event.type == 'quit' then + UI:exitPullEvents() + + elseif event.type == 'eject' then + local item = self.grid:getSelected() + if item then + queue(function() Lora:eject(item, 1) end) + end + + elseif event.type == 'eject_stack' then + local item = self.grid:getSelected() + if item then + queue(function() Lora:eject(item, itemDB:getMaxCount(item)) end) + end + + elseif event.type == 'machines' then + UI:setPage('machines') + + elseif event.type == 'grid_select' then + local selected = event.selected + UI:setPage('item', selected) + + elseif event.type == 'refresh' then + self:refresh() + self.grid:draw() + self.statusBar.filter:focus() + + elseif event.type == 'toggle_display' then + local values = { + [0] = 'A', + [1] = 'I', + [2] = 'C', + } + + event.button.value = (event.button.value + 1) % 3 + self.displayMode = event.button.value + event.button.text = values[event.button.value] + event.button:draw() + self:applyFilter() + self.grid:draw() + + elseif event.type == 'learn' then + UI:setPage('learn') + + elseif event.type == 'craft' or event.type == 'grid_select_right' then + local item = self.grid:getSelected() + if Craft.findRecipe(item) or true then -- or item.is_craftable then + UI:setPage('craft', self.grid:getSelected()) + else + self.notification:error('No recipe defined') + end + + elseif event.type == 'forget' then + local item = self.grid:getSelected() + if item then + local key = Lora:uniqueKey(item) + + if context.userRecipes[key] then + context.userRecipes[key] = nil + Util.writeTable(Lora.RECIPES_FILE, context.userRecipes) + Craft.loadRecipes() + end + + if context.resources[key] then + context.resources[key] = nil + Lora:saveResources() + end + + self.notification:info('Forgot: ' .. item.name) + self:refresh() + self.grid:draw() + end + + elseif event.type == 'text_change' then + self.filter = event.text + if #self.filter == 0 then + self.filter = nil + end + self:applyFilter() + self.grid:draw() + self.statusBar.filter:focus() + + else + UI.Page.eventHandler(self, event) + end + return true +end + +function listingPage:enable() + self:refresh() + self:setFocus(self.statusBar.filter) + UI.Page.enable(self) +end + +function listingPage:refresh() + self.allItems = Lora:listItems() + mergeResources(self.allItems) + self:applyFilter() +end + +function listingPage:applyFilter() + local t = filterItems(self.allItems, self.filter, self.displayMode) + self.grid:setValues(t) +end + +UI:addPage('listing', listingPage) diff --git a/inventoryManager/plugins/machines.lua b/inventoryManager/plugins/machines.lua new file mode 100644 index 0000000..4ded5d8 --- /dev/null +++ b/inventoryManager/plugins/machines.lua @@ -0,0 +1,152 @@ +local Config = require('config') +local Lora = require('lora/lora') +local UI = require('ui') +local Util = require('util') + +local colors = _G.colors + +local context = Lora:getContext() + +local machinesPage = UI.Page { + titleBar = UI.TitleBar { + previousPage = true, + title = 'Machines', + }, + grid = UI.ScrollingGrid { + y = 2, ey = -2, + values = context.config.remoteDefaults, + columns = { + { heading = 'Name', key = 'displayName' }, + { heading = 'Priority', key = 'priority', width = 5 }, + { heading = 'Type', key = 'mtype', width = 5 }, + }, + sortColumn = 'name', + }, + detail = UI.SlideOut { + backgroundColor = colors.cyan, + form = UI.Form { + x = 1, y = 2, ex = -1, ey = -2, + [7] = UI.Text { + x = 12, y = 1, + width = 28, + }, + [1] = UI.TextEntry { + formLabel = 'Name', formKey = 'displayName', help = '...', + limit = 64, + }, + [2] = UI.Chooser { + width = 15, + formLabel = 'Type', formKey = 'mtype', + nochoice = 'Storage', + choices = { + { name = 'Storage', value = 'storage' }, + { name = 'Trashcan', value = 'trashcan' }, + { name = 'Input chest', value = 'input' }, + { name = 'Ignore', value = 'ignore' }, + }, + help = 'Check if machine is empty before crafting' + }, + [3] = UI.Chooser { + width = 7, + formLabel = 'Empty', formKey = 'empty', + nochoice = 'No', + choices = { + { name = 'Yes', value = true }, + { name = 'No', value = false }, + }, + help = 'Check if machine is empty before crafting' + }, + [4] = UI.TextEntry { + formLabel = 'Priority', formKey = 'priority', help = '...', + limit = 4, + }, + [5] = UI.TextEntry { + formLabel = 'Max Craft', formKey = 'maxCount', help = '...', + limit = 4, + }, + [6] = UI.TextEntry { + formLabel = 'Lock to', formKey = 'lockWith', help = '...', + width = 18, + limit = 64, + }, + [8] = UI.Button { + x = -9, ey = -4, + text = 'Detect', help = '...', + limit = 64, + }, + }, + statusBar = UI.StatusBar(), + }, + statusBar = UI.StatusBar { + values = 'Select Machine', + }, + accelerators = { + h = 'toggle_hidden', + } +} + +function machinesPage:enable() + self.grid:update() + UI.Page.enable(self) +end + +function machinesPage.detail:eventHandler(event) + if event.type == 'focus_change' then + self.statusBar:setStatus(event.focused.help) + end + return UI.SlideOut.eventHandler(self, event) +end + +function machinesPage.grid:getDisplayValues(row) + row = Util.shallowCopy(row) + row.displayName = row.displayName or row.name + return row +end + +function machinesPage.grid:getRowTextColor(row, selected) + if row.mtype == 'ignore' then + return colors.lightGray + end + return UI.Grid:getRowTextColor(row, selected) +end + +function machinesPage:eventHandler(event) + if event.type == 'grid_select' then + self.detail.form:setValues(event.selected) + self.detail.form[7].value = event.selected.name +debug(event.selected) + self.detail:show() + + elseif event.type == 'toggle_hidden' then + local selected = self.grid:getSelected() + if selected then + selected.ignore = not selected.ignore +-- Util.writeTable(MACHINES_FILE, machines) + self:draw() + end + + elseif event.type == 'form_complete' then + self.detail.form.values.empty = self.detail.form.values.empty == true or nil + self.detail.form.values.ignore = self.detail.form.values.ignore == true or nil + self.detail.form.values.priority = tonumber(self.detail.form.values.priority) + self.detail.form.values.maxCount = tonumber(self.detail.form.values.maxCount) + if #self.detail.form.values.displayName == 0 then + self.detail.form.values.displayName = nil + end + if #self.detail.form.values.lockWith == 0 then + self.detail.form.values.lockWith = nil + end + Config.update('inventoryManager', context.config) + self.detail:hide() + self.grid:update() + + elseif event.type == 'form_cancel' then + self.detail:hide() + + else + UI.Page.eventHandler(self, event) + end + return true +end + +UI:addPage('machines', machinesPage) diff --git a/inventoryManager/plugins/replenishTask.lua b/inventoryManager/plugins/replenishTask.lua new file mode 100644 index 0000000..f06ac0e --- /dev/null +++ b/inventoryManager/plugins/replenishTask.lua @@ -0,0 +1,47 @@ +local itemDB = require('itemDB') +local Lora = require('lora/lora') + +local ReplenishTask = { + priority = 30, +} + +function ReplenishTask:cycle(context) + local craftList = { } + + for _,res in pairs(context.resources) do + if res.low then + local item = Lora:getItemWithQty(res, res.ignoreDamage, res.ignoreNbtHash) + if not item then + item = { + damage = res.damage, + nbtHash = res.nbtHash, + name = res.name, + displayName = itemDB:getName(res), + count = 0 + } + end + + if item.count < res.low then + if res.ignoreDamage then + item.damage = 0 + end + local key = Lora:uniqueKey(res) + + craftList[key] = { + damage = item.damage, + nbtHash = item.nbtHash, + count = res.low - item.count, + name = item.name, + displayName = item.displayName, + status = '', + rsControl = res.rsControl, + } + end + end + end + + Lora:craftItems(craftList) + Lora:updateCraftingStatus(craftList) +end + +Lora:registerTask(ReplenishTask) From f40f7ae7a892704e000733aed07e43966c84eca4 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Mon, 22 Oct 2018 01:24:36 -0400 Subject: [PATCH 019/165] lora wip --- apis/chestAdapter18.lua | 8 +++--- inventoryManager/apis/networkedAdapter18.lua | 16 +++++------ inventoryManager/inventoryManager.lua | 1 - inventoryManager/plugins/exportTask.lua | 30 ++++++++++++++++++++ inventoryManager/plugins/importTask.lua | 27 ++++++++++++++++++ inventoryManager/plugins/inputChestTask.lua | 5 ++-- inventoryManager/plugins/listing.lua | 6 ++-- 7 files changed, 75 insertions(+), 18 deletions(-) create mode 100644 inventoryManager/plugins/exportTask.lua create mode 100644 inventoryManager/plugins/importTask.lua diff --git a/apis/chestAdapter18.lua b/apis/chestAdapter18.lua index d749ecf..7e76a57 100644 --- a/apis/chestAdapter18.lua +++ b/apis/chestAdapter18.lua @@ -146,12 +146,12 @@ function ChestAdapter:provide(item, qty, slot, direction) return total, m end -function ChestAdapter:extract(slot, qty, toSlot) - return self.pushItems(self.direction, slot, qty, toSlot) +function ChestAdapter:extract(slot, qty, toSlot, direction) + return self.pushItems(direction or self.direction, slot, qty, toSlot) end -function ChestAdapter:insert(slot, qty, toSlot) - return self.pullItems(self.direction, slot, qty, toSlot) +function ChestAdapter:insert(slot, qty, toSlot, direction) + return self.pullItems(direction or self.direction, slot, qty, toSlot) end return ChestAdapter diff --git a/inventoryManager/apis/networkedAdapter18.lua b/inventoryManager/apis/networkedAdapter18.lua index bd7a538..936ca08 100644 --- a/inventoryManager/apis/networkedAdapter18.lua +++ b/inventoryManager/apis/networkedAdapter18.lua @@ -11,6 +11,7 @@ function NetworkedAdapter:init(args) remotes = { }, remoteDefaults = { }, dirty = true, +listCount = 0, } Util.merge(self, defaults) Util.merge(self, args) @@ -63,15 +64,16 @@ function NetworkedAdapter:listItems(throttle) if not self.dirty then return self.items end - +self.listCount = self.listCount + 1 +debug(self.listCount) local cache = { } local items = { } throttle = throttle or Util.throttle() for _, remote in pairs(self.remotes) do if not remote:listItems(throttle) then - debug('no List') - error('Listing failed: ', remote.name) + debug('no List: ' .. remote.name) + --error('Listing failed: ' .. remote.name) end local rcache = remote.cache or { } @@ -145,7 +147,7 @@ debug('extract %d slot:%d', qty, slot) return total end -function NetworkedAdapter:insert(slot, qty, toSlot, item) +function NetworkedAdapter:insert(slot, qty, toSlot, item, source) local total = 0 -- toSlot is not really valid with this adapter @@ -159,12 +161,10 @@ function NetworkedAdapter:insert(slot, qty, toSlot, item) self:listItems() end -debug('attempting to insert ' .. item.name) - local function insert(remote) -debug('slot %d -> %s: %s', slot, remote.side, qty) - local amount = remote:insert(slot, qty, toSlot) + local amount = remote:insert(slot, qty, toSlot, source or self.direction) if amount > 0 then +debug('%s(%d) -> %s: %d', source or self.direction, slot, remote.side, amount) self.dirty = true remote.dirty = true end diff --git a/inventoryManager/inventoryManager.lua b/inventoryManager/inventoryManager.lua index d751fe8..8abadd0 100644 --- a/inventoryManager/inventoryManager.lua +++ b/inventoryManager/inventoryManager.lua @@ -71,7 +71,6 @@ local fs = _G.fs local multishell = _ENV.multishell local shell = _ENV.shell - if multishell then multishell.setTitle(multishell.getCurrent(), 'Resource Manager') end diff --git a/inventoryManager/plugins/exportTask.lua b/inventoryManager/plugins/exportTask.lua new file mode 100644 index 0000000..6e26330 --- /dev/null +++ b/inventoryManager/plugins/exportTask.lua @@ -0,0 +1,30 @@ +local itemDB = require('itemDB') +local Lora = require('lora/lora') + +local device = _G.device + +local ExportTask = { + priority = 5, +} + +function ExportTask:cycle(context) + for target, v in pairs(context.config.remoteDefaults) do + if v.exports then + local machine = device[target] + if machine and machine.getItemMeta then + for slotNo, item in pairs(v.exports) do + local slot = machine.getItemMeta(slotNo) or { count = 0 } + local maxCount = slot.maxCount or itemDB:getMaxCount(item) + local count = maxCount - slot.count + if count > 0 then + context.inventoryAdapter:provide(itemDB:splitKey(item), count, slotNo, target) + end + end + else + debug('Invalid export target: ' .. target) + end + end + end +end + +Lora:registerTask(ExportTask) diff --git a/inventoryManager/plugins/importTask.lua b/inventoryManager/plugins/importTask.lua new file mode 100644 index 0000000..165790b --- /dev/null +++ b/inventoryManager/plugins/importTask.lua @@ -0,0 +1,27 @@ +local Lora = require('lora/lora') + +local device = _G.device + +local ImportTask = { + priority = 3, +} + +function ImportTask:cycle(context) + for source, v in pairs(context.config.remoteDefaults) do + if v.exports 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) + end + end + else + debug('Invalid import source: ' .. source) + end + end + end +end + +Lora:registerTask(ImportTask) diff --git a/inventoryManager/plugins/inputChestTask.lua b/inventoryManager/plugins/inputChestTask.lua index 516ea81..6589275 100644 --- a/inventoryManager/plugins/inputChestTask.lua +++ b/inventoryManager/plugins/inputChestTask.lua @@ -1,5 +1,5 @@ -local InventoryAdapter = require('inventoryAdapter') -local Lora = require('lora/lora') +local InventoryAdapter = require('inventoryAdapter') +local Lora = require('lora/lora') local device = _G.device local modem = device.wired_modem @@ -21,6 +21,7 @@ function InputChest:init(context) end -- TODO: clear grid +-- TODO: extract directly to target function InputChest:cycle(context) for _, adapter in pairs(self.adapters) do diff --git a/inventoryManager/plugins/listing.lua b/inventoryManager/plugins/listing.lua index 3ac7031..652e885 100644 --- a/inventoryManager/plugins/listing.lua +++ b/inventoryManager/plugins/listing.lua @@ -1,8 +1,8 @@ local Craft = require('turtle.craft') local itemDB = require('itemDB') -local Lora = require('lora/lora') -local UI = require('ui') -local Util = require('util') +local Lora = require('lora/lora') +local UI = require('ui') +local Util = require('util') local colors = _G.colors local os = _G.os From a19960959b08f0ebc6ba32db23fb6ac6a09f1d7c Mon Sep 17 00:00:00 2001 From: kepler155c Date: Tue, 23 Oct 2018 03:04:34 -0400 Subject: [PATCH 020/165] lora wip --- inventoryManager/apis/lora.lua | 370 ++++++++++++++++++ inventoryManager/apis/lora/lora.lua | 334 ---------------- inventoryManager/apis/lora/tasks.lua | 12 - inventoryManager/apis/networkedAdapter18.lua | 16 +- inventoryManager/apis/turtle/craft.lua | 52 ++- inventoryManager/core/machines.lua | 197 ++++++++++ inventoryManager/inventoryManager.lua | 31 +- inventoryManager/plugins/autocraftTask.lua | 6 +- .../plugins/{craft.lua => demandCraft.lua} | 52 ++- inventoryManager/plugins/exportTask.lua | 13 +- inventoryManager/plugins/exportView.lua | 121 ++++++ inventoryManager/plugins/importTask.lua | 4 +- inventoryManager/plugins/importView.lua | 56 +++ inventoryManager/plugins/inputChestTask.lua | 40 +- inventoryManager/plugins/item.lua | 8 +- inventoryManager/plugins/jobList.lua | 2 +- inventoryManager/plugins/learn.lua | 2 +- inventoryManager/plugins/limitTask.lua | 45 ++- inventoryManager/plugins/listing.lua | 56 +-- inventoryManager/plugins/machines.lua | 152 ------- inventoryManager/plugins/replenishTask.lua | 3 +- inventoryManager/plugins/storageView.lua | 44 +++ 22 files changed, 965 insertions(+), 651 deletions(-) create mode 100644 inventoryManager/apis/lora.lua delete mode 100644 inventoryManager/apis/lora/lora.lua delete mode 100644 inventoryManager/apis/lora/tasks.lua create mode 100644 inventoryManager/core/machines.lua rename inventoryManager/plugins/{craft.lua => demandCraft.lua} (76%) create mode 100644 inventoryManager/plugins/exportView.lua create mode 100644 inventoryManager/plugins/importView.lua delete mode 100644 inventoryManager/plugins/machines.lua create mode 100644 inventoryManager/plugins/storageView.lua diff --git a/inventoryManager/apis/lora.lua b/inventoryManager/apis/lora.lua new file mode 100644 index 0000000..d866d48 --- /dev/null +++ b/inventoryManager/apis/lora.lua @@ -0,0 +1,370 @@ +local Craft = require('turtle.craft') +local itemDB = require('itemDB') +local Util = require('util') + +local os = _G.os +local term = _G.term +local turtle = _G.turtle + +local Lora = { + RECIPES_FILE = 'usr/config/recipes.db', + RESOURCE_FILE = 'usr/config/resources.db', + + STATUS_INFO = 'info', + STATUS_WARNING = 'warning', + STATUS_ERROR = 'error', + + tasks = { }, + craftingStatus = { }, +} + +function Lora:init(context) + self.context = context +end + +function Lora:getContext() + return self.context +end + +function Lora:pauseCrafting() + self.craftingPaused = true +end + +function Lora:resumeCrafting() + self.craftingPaused = false +end + +function Lora:isCraftingPaused() + return self.craftingPaused +end + +function Lora:uniqueKey(item) + return table.concat({ item.name, item.damage, item.nbtHash }, ':') +end + +function Lora:getCraftingStatus() + return self.craftingStatus +end + +function Lora:resetCraftingStatus() + self.craftingStatus = { } + self.context.inventoryAdapter.activity = { } +end + +function Lora:updateCraftingStatus(list) + for k,v in pairs(list) do + self.craftingStatus[k] = v + end +end + +function Lora:registerTask(task) + table.insert(self.tasks, task) +end + +function Lora:showError(msg) + term.clear() + self.context.jobList:showError() + print(msg) + print('rebooting in 5 secs') + os.sleep(5) + os.reboot() +end + +function Lora:getItem(items, inItem, ignoreDamage, ignoreNbtHash) + for _,item in pairs(items) do + if item.name == inItem.name and + (ignoreDamage or item.damage == inItem.damage) and + (ignoreNbtHash or item.nbtHash == inItem.nbtHash) then + return item + end + end +end + +function Lora:getItemWithQty(res, ignoreDamage, ignoreNbtHash) + local items = self:listItems() + local item = self:getItem(items, res, ignoreDamage, ignoreNbtHash) + + if item and (ignoreDamage or ignoreNbtHash) then + local count = 0 + + for _,v in pairs(items) do + if item.name == v.name and + (ignoreDamage or item.damage == v.damage) and + (ignoreNbtHash or item.nbtHash == v.nbtHash) then + count = count + v.count + end + end + item.count = count + end + + return item +end + +function Lora:clearGrid() + local function clear() + turtle.eachFilledSlot(function(slot) + self.context.inventoryAdapter:insert(slot.index, slot.count, nil, slot) + end) + + for i = 1, 16 do + if turtle.getItemCount(i) ~= 0 then + return false + end + end + return true + end + return clear() or clear() +end + +function Lora:eject(item, qty) + local s, m = pcall(function() + self.context.inventoryAdapter:provide(item, qty) + turtle.emptyInventory() + end) + if not s and m then + debug(m) + end +end + +function Lora:mergeResources(t) + for _,v in pairs(self.context.resources) do + local item = Lora:getItem(t, v) + if item then + Util.merge(item, v) + else + item = Util.shallowCopy(v) + item.count = 0 + table.insert(t, item) + end + end + + for k in pairs(Craft.recipes) do + local v = itemDB:splitKey(k) + local item = Lora:getItem(t, v) + if not item then + item = Util.shallowCopy(v) + item.count = 0 + table.insert(t, item) + end + item.has_recipe = true + end + + for _,v in pairs(t) do + if not v.displayName then + v.displayName = itemDB:getName(v) + end + v.lname = v.displayName:lower() + end +end + +function Lora:saveResources() + local t = { } + + for k,v in pairs(self.context.resources) do + v = Util.shallowCopy(v) + local keys = Util.transpose({ 'auto', 'low', 'limit', + 'ignoreDamage', 'ignoreNbtHash', + 'rsControl', 'rsDevice', 'rsSide' }) + + for _,key in pairs(Util.keys(v)) do + if not keys[key] then + v[key] = nil + end + end + if not Util.empty(v) then + t[k] = v + end + end + + Util.writeTable(Lora.RESOURCE_FILE, t) +end + +-- Return a list of everything in the system +function Lora:listItems() + for _ = 1, 5 do + self.items = self.context.inventoryAdapter:listItems() + if self.items then + break + end +-- jobList:showError('Error - retrying in 3 seconds') + os.sleep(3) + end + if not self.items then + self:showError('Error - rebooting in 5 seconds') + end + + return self.items +end + +function Lora:addCraftingRequest(item, craftList, count) + local key = self:uniqueKey(item) + local request = craftList[key] + if not craftList[key] then + request = { name = item.name, damage = item.damage, nbtHash = item.nbtHash, count = 0 } + request.displayName = itemDB:getName(request) + craftList[key] = request + end + request.count = request.count + count + return request +end + +-- Craft +function Lora:craftItem(recipe, items, originalItem, craftList, count) + local missing = { } + local toCraft = Craft.getCraftableAmount(recipe, count, items, missing) + if missing.name then + originalItem.status = string.format('%s missing', itemDB:getName(missing.name)) + originalItem.statusCode = self.STATUS_WARNING + end + + local crafted = 0 + + if toCraft > 0 then + crafted = Craft.craftRecipe(recipe, toCraft, self.context.inventoryAdapter) + self:clearGrid() + items = self:listItems() + count = count - crafted + end + + if count > 0 and items then + local ingredients = Craft.getResourceList4(recipe, items, count) + for _,ingredient in pairs(ingredients) do + if ingredient.need > 0 then + local item = self:addCraftingRequest(ingredient, craftList, ingredient.need) + if Craft.findRecipe(item) then + item.status = string.format('%s missing', itemDB:getName(ingredient)) + item.statusCode = self.STATUS_WARNING + else + item.status = 'no recipe' + item.statusCode = self.STATUS_ERROR + end + end + end + end + return crafted +end + +-- Craft as much as possible regardless if all ingredients are available +function Lora:forceCraftItem(inRecipe, items, originalItem, craftList, inCount) + local summed = { } + local throttle = Util.throttle() + + local function sumItems(recipe, count) + count = math.ceil(count / recipe.count) + local craftable = count + + for key,iqty in pairs(Craft.sumIngredients(recipe)) do + throttle() + local item = itemDB:splitKey(key) + local summedItem = summed[key] + if not summedItem then + summedItem = Util.shallowCopy(item) + summedItem.recipe = Craft.findRecipe(item) + summedItem.count = Craft.getItemCount(items, key) + summedItem.need = 0 + summedItem.used = 0 + summedItem.craftable = 0 + summed[key] = summedItem + end + + local total = count * iqty -- 4 * 2 + local used = math.min(summedItem.count, total) -- 5 + local need = total - used -- 3 + + if recipe.craftingTools and recipe.craftingTools[key] then + if summedItem.count > 0 then + summedItem.used = 1 + summedItem.need = 0 + need = 0 + elseif not summedItem.recipe then + summedItem.need = 1 + need = 1 + else + need = 1 + end + else + summedItem.count = summedItem.count - used + summedItem.used = summedItem.used + used + end + + if need > 0 then + if not summedItem.recipe then + craftable = math.min(craftable, math.floor(used / iqty)) + summedItem.need = summedItem.need + need + else + local c = sumItems(summedItem.recipe, need) -- 4 + craftable = math.min(craftable, math.floor((used + c) / iqty)) + summedItem.craftable = summedItem.craftable + c + end + end + end + if craftable > 0 then + craftable = Craft.craftRecipe(recipe, craftable * recipe.count, + self.context.inventoryAdapter) / recipe.count + self:clearGrid() + end + + return craftable * recipe.count + end + + local count = sumItems(inRecipe, inCount) + + if count < inCount then + for _,ingredient in pairs(summed) do + if ingredient.need > 0 then + local item = self:addCraftingRequest(ingredient, craftList, ingredient.need) + if Craft.findRecipe(item) then + item.status = string.format('%s missing', itemDB:getName(ingredient)) + item.statusCode = self.STATUS_WARNING + else + item.status = '(no recipe)' + item.statusCode = self.STATUS_ERROR + end + end + end + end + return count +end + +function Lora:craft(recipe, items, item, craftList) + item.status = nil + item.statusCode = nil + item.crafted = 0 + + if self:isCraftingPaused() then + return + end + + if not self:clearGrid() then + item.status = 'Grid obstructed' + item.statusCode = self.STATUS_ERROR + return + end + + if item.forceCrafting then + item.crafted = self:forceCraftItem(recipe, items, item, craftList, item.count) + else + item.crafted = self:craftItem(recipe, items, item, craftList, item.count) + end +end + +function Lora:craftItems(craftList) + for _,key in pairs(Util.keys(craftList)) do + local item = craftList[key] + if item.count > 0 then + local recipe = Craft.recipes[key] + if recipe then + self:craft(recipe, self:listItems(), item, craftList) + elseif not self.context.controllerAdapter then + item.status = '(no recipe)' + item.statusCode = self.STATUS_ERROR + end + end + end + self:updateCraftingStatus(craftList) + for _,v in pairs(craftList) do + debug(v) + end +end + +return Lora diff --git a/inventoryManager/apis/lora/lora.lua b/inventoryManager/apis/lora/lora.lua deleted file mode 100644 index a274aca..0000000 --- a/inventoryManager/apis/lora/lora.lua +++ /dev/null @@ -1,334 +0,0 @@ -local Craft = require('turtle.craft') -local itemDB = require('itemDB') -local Util = require('util') - -local os = _G.os -local term = _G.term -local turtle = _G.turtle - -local Lora = { - RECIPES_FILE = 'usr/config/recipes.db', - RESOURCE_FILE = 'usr/config/resources.db', - - STATUS_INFO = 'info', - STATUS_WARNING = 'warning', - STATUS_ERROR = 'error', - - tasks = { }, - craftingStatus = { }, -} - -function Lora:init(context) - self.context = context -end - -function Lora:getContext() - return self.context -end - -function Lora:pauseCrafting() - self.craftingPaused = true -end - -function Lora:resumeCrafting() - self.craftingPaused = false -end - -function Lora:isCraftingPaused() - return self.craftingPaused -end - -function Lora:uniqueKey(item) - return table.concat({ item.name, item.damage, item.nbtHash }, ':') -end - -function Lora:getCraftingStatus() - return self.craftingStatus -end - -function Lora:resetCraftingStatus() - self.craftingStatus = { } -end - -function Lora:updateCraftingStatus(list) - for k,v in pairs(list) do - self.craftingStatus[k] = v - end - - self.context.jobList:updateList(self.craftingStatus) -end - -function Lora:registerTask(task) - table.insert(self.tasks, task) -end - -function Lora:showError(msg) - term.clear() - self.context.jobList:showError() - print(msg) - print('rebooting in 5 secs') - os.sleep(5) - os.reboot() -end - -function Lora:getItem(items, inItem, ignoreDamage, ignoreNbtHash) - for _,item in pairs(items) do - if item.name == inItem.name and - (ignoreDamage or item.damage == inItem.damage) and - (ignoreNbtHash or item.nbtHash == inItem.nbtHash) then - return item - end - end -end - -function Lora:getItemWithQty(res, ignoreDamage, ignoreNbtHash) - local items = self:listItems() - local item = self:getItem(items, res, ignoreDamage, ignoreNbtHash) - - if item and (ignoreDamage or ignoreNbtHash) then - local count = 0 - - for _,v in pairs(items) do - if item.name == v.name and - (ignoreDamage or item.damage == v.damage) and - (ignoreNbtHash or item.nbtHash == v.nbtHash) then - count = count + v.count - end - end - item.count = count - end - - return item -end - -function Lora:clearGrid() - local function clear() - turtle.eachFilledSlot(function(slot) - self.context.inventoryAdapter:insert(slot.index, slot.count, nil, slot) - end) - - for i = 1, 16 do - if turtle.getItemCount(i) ~= 0 then - return false - end - end - return true - end - return clear() or clear() -end - -function Lora:eject(item, qty) - local s, m = pcall(function() - self.context.inventoryAdapter:provide(item, qty) - turtle.emptyInventory() - end) - if not s and m then - debug(m) - end -end - -function Lora:saveResources() - local t = { } - - for k,v in pairs(self.context.resources) do - v = Util.shallowCopy(v) - local keys = Util.transpose({ 'auto', 'low', 'limit', - 'ignoreDamage', 'ignoreNbtHash', - 'rsControl', 'rsDevice', 'rsSide' }) - - for _,key in pairs(Util.keys(v)) do - if not keys[key] then - v[key] = nil - end - end - if not Util.empty(v) then - t[k] = v - end - end - - Util.writeTable(Lora.RESOURCE_FILE, t) -end - --- Return a list of everything in the system -function Lora:listItems() - for _ = 1, 5 do - self.items = self.context.inventoryAdapter:listItems() - if self.items then - break - end --- jobList:showError('Error - retrying in 3 seconds') - os.sleep(3) - end - if not self.items then - self:showError('Error - rebooting in 5 seconds') - end - - return self.items -end - -function Lora:addCraftingRequest(item, craftList, count) - local key = self:uniqueKey(item) - local request = craftList[key] - if not craftList[key] then - request = { name = item.name, damage = item.damage, nbtHash = item.nbtHash, count = 0 } - request.displayName = itemDB:getName(request) - craftList[key] = request - end - request.count = request.count + count - return request -end - --- Craft -function Lora:craftItem(recipe, items, originalItem, craftList, count) - local missing = { } - local toCraft = Craft.getCraftableAmount(recipe, count, items, missing) - if missing.name then - originalItem.status = string.format('%s missing', itemDB:getName(missing.name)) - originalItem.statusCode = self.STATUS_WARNING - end - - local crafted = 0 - - if toCraft > 0 then - crafted = Craft.craftRecipe(recipe, toCraft, self.context.inventoryAdapter) - self:clearGrid() - items = self:listItems() - count = count - crafted - end - - if count > 0 and items then - local ingredients = Craft.getResourceList4(recipe, items, count) - for _,ingredient in pairs(ingredients) do - if ingredient.need > 0 then - local item = self:addCraftingRequest(ingredient, craftList, ingredient.need) - if Craft.findRecipe(item) then - item.status = string.format('%s missing', itemDB:getName(ingredient)) - item.statusCode = self.STATUS_WARNING - else - item.status = 'no recipe' - item.statusCode = self.STATUS_ERROR - end - end - end - end - return crafted -end - --- Craft as much as possible regardless if all ingredients are available -function Lora:forceCraftItem(inRecipe, items, originalItem, craftList, inCount) - local summed = { } - local throttle = Util.throttle() - - local function sumItems(recipe, count) - count = math.ceil(count / recipe.count) - local craftable = count - - for key,iqty in pairs(Craft.sumIngredients(recipe)) do - throttle() - local item = itemDB:splitKey(key) - local summedItem = summed[key] - if not summedItem then - summedItem = Util.shallowCopy(item) - summedItem.recipe = Craft.findRecipe(item) - summedItem.count = Craft.getItemCount(items, key) - summedItem.need = 0 - summedItem.used = 0 - summedItem.craftable = 0 - summed[key] = summedItem - end - - local total = count * iqty -- 4 * 2 - local used = math.min(summedItem.count, total) -- 5 - local need = total - used -- 3 - - if recipe.craftingTools and recipe.craftingTools[key] then - if summedItem.count > 0 then - summedItem.used = 1 - summedItem.need = 0 - need = 0 - elseif not summedItem.recipe then - summedItem.need = 1 - need = 1 - else - need = 1 - end - else - summedItem.count = summedItem.count - used - summedItem.used = summedItem.used + used - end - - if need > 0 then - if not summedItem.recipe then - craftable = math.min(craftable, math.floor(used / iqty)) - summedItem.need = summedItem.need + need - else - local c = sumItems(summedItem.recipe, need) -- 4 - craftable = math.min(craftable, math.floor((used + c) / iqty)) - summedItem.craftable = summedItem.craftable + c - end - end - end - if craftable > 0 then - craftable = Craft.craftRecipe(recipe, craftable * recipe.count, - self.context.inventoryAdapter) / recipe.count - self:clearGrid() - end - - return craftable * recipe.count - end - - local count = sumItems(inRecipe, inCount) - - if count < inCount then - for _,ingredient in pairs(summed) do - if ingredient.need > 0 then - local item = self:addCraftingRequest(ingredient, craftList, ingredient.need) - if Craft.findRecipe(item) then - item.status = string.format('%s missing', itemDB:getName(ingredient)) - item.statusCode = self.STATUS_WARNING - else - item.status = '(no recipe)' - item.statusCode = self.STATUS_ERROR - end - end - end - end - return count -end - -function Lora:craft(recipe, items, item, craftList) - item.status = nil - item.statusCode = nil - item.crafted = 0 - - if self:isCraftingPaused() then - return - end - - if not self:clearGrid() then - item.status = 'Grid obstructed' - item.statusCode = self.STATUS_ERROR - return - end - - if item.forceCrafting then - item.crafted = self:forceCraftItem(recipe, items, item, craftList, item.count) - else - item.crafted = self:craftItem(recipe, items, item, craftList, item.count) - end -end - -function Lora:craftItems(craftList) - for _,key in pairs(Util.keys(craftList)) do - local item = craftList[key] - local recipe = Craft.recipes[key] - if recipe then - self:craft(recipe, self:listItems(), item, craftList) - elseif not self.context.controllerAdapter then - item.status = '(no recipe)' - item.statusCode = self.STATUS_ERROR - end - end -end - -return Lora diff --git a/inventoryManager/apis/lora/tasks.lua b/inventoryManager/apis/lora/tasks.lua deleted file mode 100644 index ac3ca07..0000000 --- a/inventoryManager/apis/lora/tasks.lua +++ /dev/null @@ -1,12 +0,0 @@ -local Util = require('util') - -local fs = _G.fs -local shell = _ENV.shell - -local Tasks = { - list = { } -} - - - -return Tasks diff --git a/inventoryManager/apis/networkedAdapter18.lua b/inventoryManager/apis/networkedAdapter18.lua index 936ca08..1313c15 100644 --- a/inventoryManager/apis/networkedAdapter18.lua +++ b/inventoryManager/apis/networkedAdapter18.lua @@ -12,6 +12,7 @@ function NetworkedAdapter:init(args) remoteDefaults = { }, dirty = true, listCount = 0, + activity = { }, } Util.merge(self, defaults) Util.merge(self, args) @@ -43,11 +44,9 @@ listCount = 0, elseif not b.priority then return true end - return a.priority < b.priority + return a.priority > b.priority end) end - -_G._p = self --------------------------------------------- DEBUG end function NetworkedAdapter:isValid() @@ -66,6 +65,10 @@ function NetworkedAdapter:listItems(throttle) end self.listCount = self.listCount + 1 debug(self.listCount) + + -- todo: only listItems from dirty remotes + -- todo: better handling of empty inventories + local cache = { } local items = { } throttle = throttle or Util.throttle() @@ -111,6 +114,7 @@ function NetworkedAdapter:getItemInfo(item) end function NetworkedAdapter:provide(item, qty, slot, direction) + local key = table.concat({ item.name, item.damage, item.nbtHash }, ':') local total = 0 for _, remote in ipairs(self.remotes) do @@ -119,6 +123,8 @@ debug('%s -> slot %d: %d %s', remote.side, slot or -1, qty, item.name) if amount > 0 then 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 @@ -164,9 +170,11 @@ function NetworkedAdapter:insert(slot, qty, toSlot, item, source) local function insert(remote) local amount = remote:insert(slot, qty, toSlot, source or self.direction) if amount > 0 then -debug('%s(%d) -> %s: %d', source or self.direction, slot, remote.side, amount) +debug('%s(%d) -> %s: %d', source or self.localName, slot, remote.side, amount) 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 diff --git a/inventoryManager/apis/turtle/craft.lua b/inventoryManager/apis/turtle/craft.lua index 549fde2..9e1b1a0 100644 --- a/inventoryManager/apis/turtle/craft.lua +++ b/inventoryManager/apis/turtle/craft.lua @@ -1,11 +1,13 @@ local itemDB = require('itemDB') local Util = require('util') +local device = _G.device local fs = _G.fs local turtle = _G.turtle -local RECIPES_DIR = 'usr/etc/recipes' -local USER_RECIPES = 'usr/config/recipes.db' +local RECIPES_DIR = 'usr/etc/recipes' +local USER_RECIPES = 'usr/config/recipes.db' +local MACHINE_LOOKUP = 'usr/config/machine_crafting.db' local Craft = { } @@ -53,6 +55,26 @@ function Craft.getItemCount(items, item) return count end +local function machineCraft(recipe, qty, inventoryAdapter, machineName) + local machine = device[machineName] + if not machine then + debug('machine not found') + else + for k in pairs(recipe.ingredients) do + if machine.getItemMeta(k) then + debug('machine in use: ' .. k) + return false + end + end + + for k,v in pairs(recipe.ingredients) do + inventoryAdapter:provide(splitKey(v), qty, k, machineName) + end + end + + return false +end + local function turtleCraft(recipe, qty, inventoryAdapter) if not clearGrid(inventoryAdapter) then return false @@ -92,6 +114,12 @@ function Craft.loadRecipes() local recipes = Util.readTable(USER_RECIPES) or { } Util.merge(Craft.recipes, recipes) + + for k,v in pairs(Craft.recipes) do + v.result = k + end + + Craft.machineLookup = Util.readTable(MACHINE_LOOKUP) or { } end function Craft.sumIngredients(recipe) @@ -104,6 +132,13 @@ function Craft.sumIngredients(recipe) return t end +local function makeRecipeKey(item) + if type(item) == 'string' then + item = splitKey(item) + end + return table.concat({ item.name, item.damage or 0, item.nbtHash }, ':') +end + function Craft.craftRecipe(recipe, count, inventoryAdapter) if type(recipe) == 'string' then recipe = Craft.recipes[recipe] @@ -142,7 +177,11 @@ function Craft.craftRecipe(recipe, count, inventoryAdapter) local crafted = 0 repeat - if not turtleCraft(recipe, math.min(count, maxCount), inventoryAdapter) then +-- fix + if Craft.machineLookup[recipe.result] then + machineCraft(recipe, math.min(count, maxCount), inventoryAdapter, Craft.machineLookup[recipe.result]) + break + elseif not turtleCraft(recipe, math.min(count, maxCount), inventoryAdapter) then turtle.select(1) break end @@ -154,13 +193,6 @@ function Craft.craftRecipe(recipe, count, inventoryAdapter) return crafted * recipe.count end -local function makeRecipeKey(item) - if type(item) == 'string' then - item = splitKey(item) - end - return table.concat({ item.name, item.damage or 0, item.nbtHash }, ':') -end - function Craft.findRecipe(key) if type(key) ~= 'string' then key = itemDB:makeKey(key) diff --git a/inventoryManager/core/machines.lua b/inventoryManager/core/machines.lua new file mode 100644 index 0000000..675554b --- /dev/null +++ b/inventoryManager/core/machines.lua @@ -0,0 +1,197 @@ +local Config = require('config') +local Lora = require('lora') +local UI = require('ui') +local Util = require('util') + +local colors = _G.colors + +local context = Lora:getContext() + +local machinesPage = UI.Page { + titleBar = UI.TitleBar { + previousPage = true, + title = 'Machines', + }, + grid = UI.ScrollingGrid { + y = 2, ey = -2, + values = context.config.remoteDefaults, + columns = { + { heading = 'Name', key = 'displayName' }, + { heading = 'Priority', key = 'priority', width = 5 }, + { heading = 'Type', key = 'mtype', width = 5 }, + }, + sortColumn = 'displayName', + }, + statusBar = UI.StatusBar { + values = 'Select Machine', + }, +} + +function machinesPage:enable() + self.grid:update() + UI.Page.enable(self) +end + +function machinesPage.grid:getDisplayValues(row) + row = Util.shallowCopy(row) + row.displayName = row.displayName or row.name + return row +end + +function machinesPage.grid:getRowTextColor(row, selected) + if row.mtype == 'ignore' then + return colors.lightGray + end + return UI.Grid:getRowTextColor(row, selected) +end + +function machinesPage:eventHandler(event) + if event.type == 'grid_select' then + UI:setPage('machineWizard', event.selected) + else + UI.Page.eventHandler(self, event) + end + return true +end + +local machineWizard = UI.Page { + titleBar = UI.TitleBar { title = 'Configure' }, + wizard = UI.Wizard { + y = 2, ey = -3, + pages = { + general = UI.Window { + index = 1, + backgroundColor = colors.cyan, + form = UI.Form { + x = 1, y = 2, ex = -1, ey = -2, + manualControls = true, + [1] = UI.TextEntry { + formLabel = 'Name', formKey = 'displayName', + help = 'Set a friendly name', + limit = 64, pruneEmpty = true, + }, + [2] = UI.Chooser { + width = 15, + formLabel = 'Type', formKey = 'mtype', + nochoice = 'Storage', + choices = { + { name = 'Storage', value = 'storage' }, + { name = 'Trashcan', value = 'trashcan' }, + { name = 'Input chest', value = 'input' }, + { name = 'Ignore', value = 'ignore' }, + { name = 'Machine', value = 'machine' }, + }, + help = 'Select type', + }, + }, + }, + confirmation = UI.Window { + title = 'Confirm changes', + index = 2, + grid = UI.TextArea { + x = 2, ex = -2, y = 2, ey = -2, + value = +[[Press accept to save the changes. + +The settings will take effect immediately!]], + }, + }, + }, + }, + statusBar = UI.StatusBar(), + notification = UI.Notification { }, +} + +function machineWizard.wizard.pages.general:enable() + UI.Window.enable(self) + self:focusFirst() +end + +function machineWizard.wizard.pages.general:validate() + return self.form:save() +end + +function machineWizard.wizard:eventHandler(event) + if event.type == 'nextView' and + Util.find(self.pages, 'enabled', true) == self.pages.general then + + if self.pages.general.form:save() then + local index = 2 + for _, page in pairs(self.pages) do + if page.mtype == machineWizard.machine.mtype then + page.index = index + index = index + 1 + elseif page.index ~= 1 then + page.index = nil + end + end + self.pages.confirmation.index = index + return UI.Wizard.eventHandler(self, event) + end + else + return UI.Wizard.eventHandler(self, event) + end +end + +function machineWizard:enable(machine) + self.machine = Util.deepCopy(machine) + self.wizard.pages.general.form:setValues(self.machine) + self.wizard.pages.general.form[1].shadowText = machine.name + + -- restore indices + for _, page in pairs(self.wizard.pages) do + if not page.oindex then + page.oindex = page.index + end + page.index = page.oindex + end + + UI.Page.enable(self) + + for _, v in pairs(self.wizard.pages) do + if v.setMachine then + v:setMachine(self.machine) + end + end +end + +function machineWizard:eventHandler(event) + if event.type == 'cancel' then + UI:setPreviousPage() + + elseif event.type == 'accept' then + + -- todo: no need for calling this function - use validate instead + for _, v in pairs(self.wizard.pages) do + if v.save and v.index then -- only save if the page was valid for this mtype + v:save(self.machine) + end + end + context.config.remoteDefaults[self.machine.name] = self.machine + Config.update('inventoryManagerX', context.config) + + UI:setPreviousPage() + + elseif event.type == 'collapse' then + self.items:hide() + + elseif event.type == 'enable_view' then + local current = event.next or event.prev + self.titleBar.title = current.title or 'Machine' + self.titleBar:draw() + + elseif event.type == 'focus_change' then + self.statusBar:setStatus(event.focused.help) + + elseif event.type == 'form_invalid' then + self.notification:error(event.message) + self:setFocus(event.field) + + else + return UI.Page.eventHandler(self, event) + end + return true +end + +UI:addPage('machines', machinesPage) +UI:addPage('machineWizard', machineWizard) diff --git a/inventoryManager/inventoryManager.lua b/inventoryManager/inventoryManager.lua index 8abadd0..02d08bd 100644 --- a/inventoryManager/inventoryManager.lua +++ b/inventoryManager/inventoryManager.lua @@ -46,6 +46,12 @@ type/monitor - will use the first monitor found side/north - specify a direction (top/bottom/east/etc) name/monitor_1 - specify the exact name of the peripheral + + + + -- Internal + Imports are at < 20 + ]]-- --[[ @@ -60,7 +66,7 @@ _G.requireInjector() local Config = require('config') local Event = require('event') local itemDB = require('itemDB') -local Lora = require('lora/lora') +local Lora = require('lora') local Peripheral = require('peripheral') local UI = require('ui') local Util = require('util') @@ -136,16 +142,19 @@ local context = { Lora:init(context) -local programDir = fs.getDir(shell.getRunningProgram()) -local pluginDir = fs.combine(programDir, 'plugins') - -for _, file in pairs(fs.list(pluginDir)) do - local s, m = Util.run(_ENV, fs.combine(pluginDir, file)) - if not s and m then - error(m or 'Unknown error') +local function loadDirectory(dir) + for _, file in pairs(fs.list(dir)) do + local s, m = Util.run(_ENV, fs.combine(dir, file)) + if not s and m then + error(m or 'Unknown error') + end end end +local programDir = fs.getDir(shell.getRunningProgram()) +loadDirectory(fs.combine(programDir, 'core')) +loadDirectory(fs.combine(programDir, 'plugins')) + table.sort(Lora.tasks, function(a, b) return a.priority < b.priority end) @@ -162,7 +171,11 @@ Event.onInterval(5, function() context.inventoryAdapter:refresh() for _, task in ipairs(Lora.tasks) do - task:cycle(context) + local s, m = pcall(function() task:cycle(context) end) + if not s and m then + Util.print(task) + error(m) + end end end end) diff --git a/inventoryManager/plugins/autocraftTask.lua b/inventoryManager/plugins/autocraftTask.lua index 2d089d9..697e5ff 100644 --- a/inventoryManager/plugins/autocraftTask.lua +++ b/inventoryManager/plugins/autocraftTask.lua @@ -1,4 +1,4 @@ -local Lora = require('lora/lora') +local Lora = require('lora') local Util = require('util') local Autocraft = { @@ -17,8 +17,8 @@ function Autocraft:cycle(context) end if not Util.empty(list) then - Lora:craftItems(list) - end + Lora:craftItems(list) + end end Lora:registerTask(Autocraft) diff --git a/inventoryManager/plugins/craft.lua b/inventoryManager/plugins/demandCraft.lua similarity index 76% rename from inventoryManager/plugins/craft.lua rename to inventoryManager/plugins/demandCraft.lua index e7b4f39..6a0c635 100644 --- a/inventoryManager/plugins/craft.lua +++ b/inventoryManager/plugins/demandCraft.lua @@ -1,10 +1,10 @@ -local Craft = require('turtle.craft') -local itemDB = require('itemDB') -local Lora = require('lora/lora') -local UI = require('ui') -local Util = require('util') +local Craft = require('turtle.craft') +local itemDB = require('itemDB') +local Lora = require('lora') +local UI = require('ui') +local Util = require('util') -local colors = _G.colors +local colors = _G.colors local demandCrafting = { } @@ -135,25 +135,35 @@ local demandCraftingTask = { priority = 20, } -function demandCraftingTask:cycle() - if Util.size(demandCrafting) > 0 then - local demandCrafted = Util.shallowCopy(demandCrafting) - Lora:craftItems(demandCrafted) +function demandCraftingTask:cycle(context) + local demandCrafted = { } - for _, item in pairs(demandCrafting) do - if item.crafted then - item.count = math.max(0, item.count - item.crafted) - if item.count <= 0 then - item.statusCode = 'success' - end - end + -- look directly at the adapter import activity to determine + -- if the item was imported into storage from any source. + -- The item does NOT need to come from the machine that did + -- the crafting. + for _,key in pairs(Util.keys(demandCrafting)) do + local item = demandCrafting[key] + + local imported = context.inventoryAdapter.activity[key] + if imported then + item.crafted = math.min(imported, item.count) + item.count = math.max(0, item.count - item.crafted) + context.inventoryAdapter.activity[key] = imported - item.crafted end + demandCrafted[key] = item + end - Lora:updateCraftingStatus(demandCrafted) + if Util.size(demandCrafted) > 0 then + Lora:craftItems(demandCrafted) + end - for _,key in pairs(Util.keys(demandCrafting)) do - local item = demandCrafting[key] - if item.crafted and item.count <= 0 then + for _,key in pairs(Util.keys(demandCrafting)) do + local item = demandCrafting[key] + if item.crafted then + item.count = math.max(0, item.count - item.crafted) + if item.count <= 0 then + item.statusCode = 'success' demandCrafting[key] = nil if item.eject then Lora:eject(item, item.ocount) diff --git a/inventoryManager/plugins/exportTask.lua b/inventoryManager/plugins/exportTask.lua index 6e26330..937610a 100644 --- a/inventoryManager/plugins/exportTask.lua +++ b/inventoryManager/plugins/exportTask.lua @@ -1,5 +1,5 @@ local itemDB = require('itemDB') -local Lora = require('lora/lora') +local Lora = require('lora') local device = _G.device @@ -12,19 +12,20 @@ function ExportTask:cycle(context) if v.exports then local machine = device[target] if machine and machine.getItemMeta then - for slotNo, item in pairs(v.exports) do - local slot = machine.getItemMeta(slotNo) or { count = 0 } - local maxCount = slot.maxCount or itemDB:getMaxCount(item) + for _, entry in pairs(v.exports) do + local slot = machine.getItemMeta(entry.slot) or { count = 0 } + local maxCount = slot.maxCount or itemDB:getMaxCount(entry.name) local count = maxCount - slot.count if count > 0 then - context.inventoryAdapter:provide(itemDB:splitKey(item), count, slotNo, target) + context.inventoryAdapter:provide( + itemDB:splitKey(entry.name), count, entry.slot, target) end end else debug('Invalid export target: ' .. target) end end - end + end end Lora:registerTask(ExportTask) diff --git a/inventoryManager/plugins/exportView.lua b/inventoryManager/plugins/exportView.lua new file mode 100644 index 0000000..fbdc130 --- /dev/null +++ b/inventoryManager/plugins/exportView.lua @@ -0,0 +1,121 @@ +local itemDB = require('itemDB') +local Lora = require('lora') +local UI = require('ui') +local Util = require('util') + +local colors = _G.colors +local device = _G.device + +local itemSlideout = UI.SlideOut { + backgroundColor = colors.cyan, + grid = UI.ScrollingGrid { + y = 3, ey = -2, + columns = { + { heading = 'Name', key = 'displayName', width = 31 }, + { heading = 'Qty', key = 'count' , width = 5 }, + }, + sortColumn = 'displayName', + }, + filter = UI.TextEntry { + x = 2, ex = -2, y = 2, + limit = 50, + shadowText = 'filter', + backgroundColor = colors.lightGray, + backgroundFocusColor = colors.lightGray, + }, + button1 = UI.Button { + x = -14, y = -1, + text = 'Ok', event = 'accept', + }, + button2 = UI.Button { + x = -9, y = -1, + text = 'Cancel', event = 'collapse', + }, +} + +function itemSlideout:filterItems(t, filter) + if filter then + local r = {} + filter = filter:lower() + for _,v in pairs(t) do + if string.find(v.lname, filter) then + table.insert(r, v) + end + end + return r + end + return t +end + +function itemSlideout.grid:enable() + if not self.allItems then + self.allItems = Lora:listItems() + Lora:mergeResources(self.allItems) + self:setValues(self.allItems) + end + UI.Grid.enable(self) +end + +function itemSlideout:eventHandler(event) + if event.type == 'text_change' and event.element == self.filter then + local t = self:filterItems(self.grid.allItems, event.text) + self.grid:setValues(t) + self.grid:draw() + end + return UI.SlideOut.eventHandler(self, event) +end + +local exportView = UI.Window { + mtype = 'machine', + title = 'Export item into machine', + index = 3, + grid = UI.ScrollingGrid { + x = 2, ex = -6, y = 2, ey = -2, + columns = { + { heading = 'Slot', key = 'slot', width = 4 }, + { heading = 'Item', key = 'displayName' }, + }, + sortColumn = 'slot', + }, + add = UI.Button { + x = -4, y = 4, + text = '+', event = 'add_export', help = '...', + }, + remove = UI.Button { + x = -4, y = 6, + text = '-', event = 'remove_export', help = '...', + }, +} + +function exportView:save(machine) + machine.exports = not Util.empty(self.grid.values) and self.grid.values or nil + return true +end + +function exportView:setMachine(machine) + local m = device[machine.name] + self.slotCount = m.size() + self.grid:setValues(machine.exports or { }) +end + +function exportView.grid:getDisplayValues(row) + row = Util.shallowCopy(row) + row.displayName = itemDB:getName(row.name) + return row +end + +function exportView:eventHandler(event) + if event.type == 'grid_select' or event.type == 'add_export' then + itemSlideout:show() + elseif event.type == 'remove_export' then + local row = self.grid:getSelected() + if row then + Util.removeByValue(self.grid.values, row) + self.grid:update() + self.grid:draw() + end + end +end + +UI:getPage('machineWizard'):add({ items = itemSlideout }) +UI:getPage('machineWizard').wizard:add({ export = exportView }) diff --git a/inventoryManager/plugins/importTask.lua b/inventoryManager/plugins/importTask.lua index 165790b..b9fc3a3 100644 --- a/inventoryManager/plugins/importTask.lua +++ b/inventoryManager/plugins/importTask.lua @@ -1,4 +1,4 @@ -local Lora = require('lora/lora') +local Lora = require('lora') local device = _G.device @@ -21,7 +21,7 @@ function ImportTask:cycle(context) debug('Invalid import source: ' .. source) end end - end + end end Lora:registerTask(ImportTask) diff --git a/inventoryManager/plugins/importView.lua b/inventoryManager/plugins/importView.lua new file mode 100644 index 0000000..92abe68 --- /dev/null +++ b/inventoryManager/plugins/importView.lua @@ -0,0 +1,56 @@ +local UI = require('ui') +local Util = require('util') + +local device = _G.device + +local importView = UI.Window { + mtype = 'machine', + title = 'Import item from machine', + index = 4, + grid = UI.ScrollingGrid { + y = 2, ey = -2, + columns = { + { heading = 'Slot', key = 'slot', width = 4 }, + { heading = 'Import', key = 'import' }, + }, + sortColumn = 'slot', + help = 'Double-click to toggle' + }, +} + +function importView:setMachine(machine) + local m = device[machine.name] + + local t = { } + for k = 1, m.size() do + t[k] = { slot = k } + end + + if machine.imports then + for k,v in pairs(machine.imports) do + t[k] = { slot = k, import = v } + end + end + + self.grid:setValues(t) +end + +function importView:save(machine) + local t = { } + for k,v in pairs(self.grid.values) do + if v.import then + t[k] = true + end + end + machine.imports = not Util.empty(t) and t or nil + return true +end + +function importView:eventHandler(event) + if event.type == 'grid_select' then + event.selected.import = not event.selected.import + self.grid:draw() + end +end + +UI:getPage('machineWizard').wizard:add({ import = importView }) diff --git a/inventoryManager/plugins/inputChestTask.lua b/inventoryManager/plugins/inputChestTask.lua index 6589275..29e5dcc 100644 --- a/inventoryManager/plugins/inputChestTask.lua +++ b/inventoryManager/plugins/inputChestTask.lua @@ -1,38 +1,24 @@ -local InventoryAdapter = require('inventoryAdapter') -local Lora = require('lora/lora') +local Lora = require('lora') local device = _G.device -local modem = device.wired_modem local InputChest = { priority = 1, - adapters = { }, } -function InputChest:init(context) - for k,v in pairs(context.config.remoteDefaults) do - if v.mtype == 'input' then - local adapter = InventoryAdapter.wrap({ side = k, direction = modem.getNameLocal() }) - if adapter then - table.insert(self.adapters, adapter) - end - end +function InputChest:cycle(context) + for name,v in pairs(context.config.remoteDefaults) do + if v.mtype == 'input' then + local inventory = device[name] + + 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) + end + end + end end end --- TODO: clear grid --- TODO: extract directly to target - -function InputChest:cycle(context) - for _, adapter in pairs(self.adapters) do - local list = adapter.list() -- raw list ! - for k,v in pairs(list) do - adapter:extract(k, v.count, 1) - context.inventoryAdapter:insert(1, v.count, nil, v) - end - end -end - -InputChest:init(Lora:getContext()) - Lora:registerTask(InputChest) diff --git a/inventoryManager/plugins/item.lua b/inventoryManager/plugins/item.lua index e6c8b0c..bcbe553 100644 --- a/inventoryManager/plugins/item.lua +++ b/inventoryManager/plugins/item.lua @@ -1,5 +1,5 @@ local Ansi = require('ansi') -local Lora = require('lora/lora') +local Lora = require('lora') local UI = require('ui') local Util = require('util') @@ -24,6 +24,7 @@ local itemPage = UI.Page { width = 7, formLabel = 'Max', formKey = 'limit', help = 'Eject if above max' }, +--[[ [3] = UI.Chooser { width = 7, formLabel = 'Autocraft', formKey = 'auto', @@ -34,6 +35,7 @@ local itemPage = UI.Page { }, help = 'Craft until out of ingredients' }, +]] [4] = UI.Chooser { width = 7, formLabel = 'Ignore Dmg', formKey = 'ignoreDamage', @@ -125,9 +127,9 @@ local itemPage = UI.Page { } function itemPage:enable(item) - self.item = item + self.item = Util.shallowCopy(item) - self.form:setValues(item) + self.form:setValues(self.item) self.titleBar.title = item.displayName or item.name UI.Page.enable(self) diff --git a/inventoryManager/plugins/jobList.lua b/inventoryManager/plugins/jobList.lua index 3b4e7c5..efba70f 100644 --- a/inventoryManager/plugins/jobList.lua +++ b/inventoryManager/plugins/jobList.lua @@ -1,4 +1,4 @@ -local Lora = require('lora/lora') +local Lora = require('lora') local Peripheral = require('peripheral') local UI = require('ui') diff --git a/inventoryManager/plugins/learn.lua b/inventoryManager/plugins/learn.lua index 139b55f..a8ebf06 100644 --- a/inventoryManager/plugins/learn.lua +++ b/inventoryManager/plugins/learn.lua @@ -1,6 +1,6 @@ local Craft = require('turtle.craft') local itemDB = require('itemDB') -local Lora = require('lora/lora') +local Lora = require('lora') local UI = require('ui') local Util = require('util') diff --git a/inventoryManager/plugins/limitTask.lua b/inventoryManager/plugins/limitTask.lua index 3fc86d7..82767d3 100644 --- a/inventoryManager/plugins/limitTask.lua +++ b/inventoryManager/plugins/limitTask.lua @@ -1,36 +1,35 @@ -local Lora = require('lora/lora') +local Lora = require('lora') local LimitTask = { priority = 10, } -function LimitTask:init(context) - for k,v in pairs(context.config.remoteDefaults) do - if v.mtype == 'trashcan' then - self.trashcan = k - break - end - end -end - function LimitTask:cycle(context) - if not self.trashcan then + local trashcan + + for k,v in pairs(context.config.remoteDefaults) do + if v.mtype == 'trashcan' then + trashcan = k + break + end + end + + if not trashcan then return end - for _,res in pairs(context.resources) do + for _,res in pairs(context.resources) do if res.limit then - local item = Lora: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, - nil, - self.trashcan) - end - end - end + local item = Lora: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, + nil, + trashcan) + end + end + end end -LimitTask:init(Lora:getContext()) Lora:registerTask(LimitTask) diff --git a/inventoryManager/plugins/listing.lua b/inventoryManager/plugins/listing.lua index 652e885..8a9aeb8 100644 --- a/inventoryManager/plugins/listing.lua +++ b/inventoryManager/plugins/listing.lua @@ -1,13 +1,13 @@ local Craft = require('turtle.craft') local itemDB = require('itemDB') -local Lora = require('lora/lora') +local Lora = require('lora') local UI = require('ui') local Util = require('util') local colors = _G.colors local os = _G.os -local context = Lora:getContext() +local context = Lora:getContext() local function queue(fn) while Lora:isCraftingPaused() do @@ -16,37 +16,6 @@ local function queue(fn) fn() end -local function mergeResources(t) - for _,v in pairs(context.resources) do - local item = Lora:getItem(t, v) - if item then - Util.merge(item, v) - else - item = Util.shallowCopy(v) - item.count = 0 - table.insert(t, item) - end - end - - for k in pairs(Craft.recipes) do - local v = itemDB:splitKey(k) - local item = Lora:getItem(t, v) - if not item then - item = Util.shallowCopy(v) - item.count = 0 - table.insert(t, item) - end - item.has_recipe = true - end - - for _,v in pairs(t) do - if not v.displayName then - v.displayName = itemDB:getName(v) - end - v.lname = v.displayName:lower() - end -end - local function filterItems(t, filter, displayMode) if filter or displayMode > 0 then local r = { } @@ -80,8 +49,8 @@ local listingPage = UI.Page { grid = UI.Grid { y = 2, ey = -2, columns = { + { heading = ' Qty', key = 'count' , width = 4, justify = 'right' }, { heading = 'Name', key = 'displayName' }, - { heading = 'Qty', key = 'count' , width = 4 }, { heading = 'Min', key = 'low' , width = 4 }, { heading = 'Max', key = 'limit' , width = 4 }, }, @@ -95,6 +64,9 @@ local listingPage = UI.Page { shadowTextColor = colors.gray, backgroundColor = colors.cyan, backgroundFocusColor = colors.cyan, + accelerators = { + [ 'enter' ] = 'craft', + }, }, display = UI.Button { x = -3, @@ -107,7 +79,6 @@ local listingPage = UI.Page { accelerators = { r = 'refresh', q = 'quit', - grid_select_right = 'craft', [ 'control-e' ] = 'eject', [ 'control-s' ] = 'eject_stack', [ 'control-m' ] = 'machines', @@ -131,7 +102,7 @@ end function listingPage.grid:getDisplayValues(row) row = Util.shallowCopy(row) - row.count = Util.toBytes(row.count) + row.count = row.count > 0 and Util.toBytes(row.count) or '' if row.low then row.low = Util.toBytes(row.low) end @@ -142,6 +113,7 @@ function listingPage.grid:getDisplayValues(row) end function listingPage:eventHandler(event) +debug(event) if event.type == 'quit' then UI:exitPullEvents() @@ -160,9 +132,11 @@ function listingPage:eventHandler(event) elseif event.type == 'machines' then UI:setPage('machines') - elseif event.type == 'grid_select' then - local selected = event.selected - UI:setPage('item', selected) + elseif event.type == 'details' or event.type == 'grid_select_right' then + local item = self.grid:getSelected() + if item then + UI:setPage('item', item) + end elseif event.type == 'refresh' then self:refresh() @@ -186,7 +160,7 @@ function listingPage:eventHandler(event) elseif event.type == 'learn' then UI:setPage('learn') - elseif event.type == 'craft' or event.type == 'grid_select_right' then + elseif event.type == 'craft' or event.type == 'grid_select' then local item = self.grid:getSelected() if Craft.findRecipe(item) or true then -- or item.is_craftable then UI:setPage('craft', self.grid:getSelected()) @@ -238,7 +212,7 @@ end function listingPage:refresh() self.allItems = Lora:listItems() - mergeResources(self.allItems) + Lora:mergeResources(self.allItems) self:applyFilter() end diff --git a/inventoryManager/plugins/machines.lua b/inventoryManager/plugins/machines.lua deleted file mode 100644 index 4ded5d8..0000000 --- a/inventoryManager/plugins/machines.lua +++ /dev/null @@ -1,152 +0,0 @@ -local Config = require('config') -local Lora = require('lora/lora') -local UI = require('ui') -local Util = require('util') - -local colors = _G.colors - -local context = Lora:getContext() - -local machinesPage = UI.Page { - titleBar = UI.TitleBar { - previousPage = true, - title = 'Machines', - }, - grid = UI.ScrollingGrid { - y = 2, ey = -2, - values = context.config.remoteDefaults, - columns = { - { heading = 'Name', key = 'displayName' }, - { heading = 'Priority', key = 'priority', width = 5 }, - { heading = 'Type', key = 'mtype', width = 5 }, - }, - sortColumn = 'name', - }, - detail = UI.SlideOut { - backgroundColor = colors.cyan, - form = UI.Form { - x = 1, y = 2, ex = -1, ey = -2, - [7] = UI.Text { - x = 12, y = 1, - width = 28, - }, - [1] = UI.TextEntry { - formLabel = 'Name', formKey = 'displayName', help = '...', - limit = 64, - }, - [2] = UI.Chooser { - width = 15, - formLabel = 'Type', formKey = 'mtype', - nochoice = 'Storage', - choices = { - { name = 'Storage', value = 'storage' }, - { name = 'Trashcan', value = 'trashcan' }, - { name = 'Input chest', value = 'input' }, - { name = 'Ignore', value = 'ignore' }, - }, - help = 'Check if machine is empty before crafting' - }, - [3] = UI.Chooser { - width = 7, - formLabel = 'Empty', formKey = 'empty', - nochoice = 'No', - choices = { - { name = 'Yes', value = true }, - { name = 'No', value = false }, - }, - help = 'Check if machine is empty before crafting' - }, - [4] = UI.TextEntry { - formLabel = 'Priority', formKey = 'priority', help = '...', - limit = 4, - }, - [5] = UI.TextEntry { - formLabel = 'Max Craft', formKey = 'maxCount', help = '...', - limit = 4, - }, - [6] = UI.TextEntry { - formLabel = 'Lock to', formKey = 'lockWith', help = '...', - width = 18, - limit = 64, - }, - [8] = UI.Button { - x = -9, ey = -4, - text = 'Detect', help = '...', - limit = 64, - }, - }, - statusBar = UI.StatusBar(), - }, - statusBar = UI.StatusBar { - values = 'Select Machine', - }, - accelerators = { - h = 'toggle_hidden', - } -} - -function machinesPage:enable() - self.grid:update() - UI.Page.enable(self) -end - -function machinesPage.detail:eventHandler(event) - if event.type == 'focus_change' then - self.statusBar:setStatus(event.focused.help) - end - return UI.SlideOut.eventHandler(self, event) -end - -function machinesPage.grid:getDisplayValues(row) - row = Util.shallowCopy(row) - row.displayName = row.displayName or row.name - return row -end - -function machinesPage.grid:getRowTextColor(row, selected) - if row.mtype == 'ignore' then - return colors.lightGray - end - return UI.Grid:getRowTextColor(row, selected) -end - -function machinesPage:eventHandler(event) - if event.type == 'grid_select' then - self.detail.form:setValues(event.selected) - self.detail.form[7].value = event.selected.name -debug(event.selected) - self.detail:show() - - elseif event.type == 'toggle_hidden' then - local selected = self.grid:getSelected() - if selected then - selected.ignore = not selected.ignore --- Util.writeTable(MACHINES_FILE, machines) - self:draw() - end - - elseif event.type == 'form_complete' then - self.detail.form.values.empty = self.detail.form.values.empty == true or nil - self.detail.form.values.ignore = self.detail.form.values.ignore == true or nil - self.detail.form.values.priority = tonumber(self.detail.form.values.priority) - self.detail.form.values.maxCount = tonumber(self.detail.form.values.maxCount) - if #self.detail.form.values.displayName == 0 then - self.detail.form.values.displayName = nil - end - if #self.detail.form.values.lockWith == 0 then - self.detail.form.values.lockWith = nil - end - Config.update('inventoryManager', context.config) - self.detail:hide() - self.grid:update() - - elseif event.type == 'form_cancel' then - self.detail:hide() - - else - UI.Page.eventHandler(self, event) - end - return true -end - -UI:addPage('machines', machinesPage) diff --git a/inventoryManager/plugins/replenishTask.lua b/inventoryManager/plugins/replenishTask.lua index f06ac0e..1568f5b 100644 --- a/inventoryManager/plugins/replenishTask.lua +++ b/inventoryManager/plugins/replenishTask.lua @@ -1,5 +1,5 @@ local itemDB = require('itemDB') -local Lora = require('lora/lora') +local Lora = require('lora') local ReplenishTask = { priority = 30, @@ -41,7 +41,6 @@ function ReplenishTask:cycle(context) end Lora:craftItems(craftList) - Lora:updateCraftingStatus(craftList) end Lora:registerTask(ReplenishTask) diff --git a/inventoryManager/plugins/storageView.lua b/inventoryManager/plugins/storageView.lua new file mode 100644 index 0000000..9a855bd --- /dev/null +++ b/inventoryManager/plugins/storageView.lua @@ -0,0 +1,44 @@ +local UI = require('ui') + +local colors = _G.colors + +local storageView = UI.Window { + mtype = 'storage', + title = 'Storage Options', + index = 2, + backgroundColor = colors.cyan, + form = UI.Form { + x = 1, y = 2, ex = -1, ey = -2, + manualControls = true, + [1] = UI.TextEntry { + formLabel = 'Priority', formKey = 'priority', + help = 'Larger values get precedence', + limit = 4, + validate = 'numeric', pruneEmpty = true, + }, + [2] = UI.TextEntry { + formLabel = 'Lock to', formKey = 'lockWith', + help = 'Locks chest to a single item type', + width = 18, limit = 64, pruneEmpty = true, + }, + [3] = UI.Button { + x = -9, ey = -4, + text = 'Detect', help = 'Determine what is currently present', + }, + }, +} + +function storageView:enable() + UI.Window.enable(self) + self:focusFirst() +end + +function storageView:validate() + return self.form:save() +end + +function storageView:setMachine(machine) + self.form:setValues(machine) +end + +UI:getPage('machineWizard').wizard:add({ storage = storageView }) From 9664077f2e5be700129fe87d8b31acac5f961b87 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Tue, 23 Oct 2018 22:32:25 -0400 Subject: [PATCH 021/165] plethora crafter rename to milo - wip --- inventoryManager/Crafter.lua | 1130 ----------------- inventoryManager/plugins/exportView.lua | 121 -- inventoryManager/plugins/learn.lua | 174 --- {inventoryManager => milo}/.package | 2 +- .../inventoryManager.lua => milo/Milo.lua | 28 +- .../apis/inventoryAdapter.lua | 0 .../apis/lora.lua => milo/apis/milo.lua | 74 +- .../apis/networkedAdapter18.lua | 0 .../apis/turtle/craft.lua | 0 {inventoryManager => milo}/core/machines.lua | 47 +- .../plugins/autocraftTask.lua | 8 +- .../plugins/demandCraft.lua | 12 +- .../plugins/exportTask.lua | 4 +- milo/plugins/exportView.lua | 213 ++++ .../plugins/importTask.lua | 6 +- .../plugins/importView.lua | 0 .../plugins/inputChestTask.lua | 4 +- {inventoryManager => milo}/plugins/item.lua | 10 +- .../plugins/jobList.lua | 14 +- milo/plugins/learn.lua | 61 + .../plugins/limitTask.lua | 6 +- .../plugins/listing.lua | 20 +- milo/plugins/machineLearn.lua | 172 +++ milo/plugins/potionImportTask.lua | 24 + .../plugins/replenishTask.lua | 10 +- .../plugins/storageView.lua | 0 milo/plugins/turtleLearn.lua | 175 +++ 27 files changed, 795 insertions(+), 1520 deletions(-) delete mode 100644 inventoryManager/Crafter.lua delete mode 100644 inventoryManager/plugins/exportView.lua delete mode 100644 inventoryManager/plugins/learn.lua rename {inventoryManager => milo}/.package (88%) rename inventoryManager/inventoryManager.lua => milo/Milo.lua (87%) rename {inventoryManager => milo}/apis/inventoryAdapter.lua (100%) rename inventoryManager/apis/lora.lua => milo/apis/milo.lua (84%) rename {inventoryManager => milo}/apis/networkedAdapter18.lua (100%) rename {inventoryManager => milo}/apis/turtle/craft.lua (100%) rename {inventoryManager => milo}/core/machines.lua (80%) rename {inventoryManager => milo}/plugins/autocraftTask.lua (71%) rename {inventoryManager => milo}/plugins/demandCraft.lua (95%) rename {inventoryManager => milo}/plugins/exportTask.lua (92%) create mode 100644 milo/plugins/exportView.lua rename {inventoryManager => milo}/plugins/importTask.lua (86%) rename {inventoryManager => milo}/plugins/importView.lua (100%) rename {inventoryManager => milo}/plugins/inputChestTask.lua (88%) rename {inventoryManager => milo}/plugins/item.lua (96%) rename {inventoryManager => milo}/plugins/jobList.lua (81%) create mode 100644 milo/plugins/learn.lua rename {inventoryManager => milo}/plugins/limitTask.lua (82%) rename {inventoryManager => milo}/plugins/listing.lua (92%) create mode 100644 milo/plugins/machineLearn.lua create mode 100644 milo/plugins/potionImportTask.lua rename {inventoryManager => milo}/plugins/replenishTask.lua (81%) rename {inventoryManager => milo}/plugins/storageView.lua (100%) create mode 100644 milo/plugins/turtleLearn.lua diff --git a/inventoryManager/Crafter.lua b/inventoryManager/Crafter.lua deleted file mode 100644 index 0589d0e..0000000 --- a/inventoryManager/Crafter.lua +++ /dev/null @@ -1,1130 +0,0 @@ ---[[ - Turtle/machine crafting. - - Requirements: - Turtle must be restricted forward and back by some obstacle. - The turtle must have access to the main inventory at the most forward location. - Machines must be placed above or below the line along the turtle's backwards travel. - - Optional: - Monitors can be placed touching the turtle at the most forward position to - display crafting status. - - Sample setups: - M = machine, I = inventory, O = obstacle, T = turtle - - Turtle facing <--- - - MMMM - IT O - MMM - - IMMMM - O O - MMMMMM -]]-- - -_G.requireInjector() - -local InventoryAdapter = require('inventoryAdapter') -local Config = require('config') -local Event = require('event') -local itemDB = require('itemDB') -local Peripheral = require('peripheral') -local UI = require('ui') -local Terminal = require('terminal') -local Util = require('util') - -local colors = _G.colors -local os = _G.os -local term = _G.term -local turtle = _G.turtle - -local config = { - computerFacing = 'north', - inventorySide = 'front', - monitor = 'type/monitor', -} -Config.load('crafter', config) - -repeat until not turtle.forward() - -local inventoryAdapter = InventoryAdapter.wrap({ - side = config.inventorySide, - facing = config.computerFacing -}) -if not inventoryAdapter then - error('Invalid inventory configuration') -end - -local RESOURCE_FILE = 'usr/config/resources.db' -local RECIPES_FILE = 'usr/config/recipes2.db' -local MACHINES_FILE = 'usr/config/machines.db' - -local STATUS_ERROR = 'error' -local STATUS_INFO = 'info' -local STATUS_SUCCESS = 'success' -local STATUS_WARNING = 'warning' - -local recipes = Util.readTable(RECIPES_FILE) or { } -local resources -local machines = { } -local jobListGrid -local listing, docked = false, false - -local function getItem(items, inItem, ignoreDamage, ignoreNbtHash) - for _,item in pairs(items) do - if item.name == inItem.name and - (ignoreDamage or item.damage == inItem.damage) and - (ignoreNbtHash or item.nbtHash == inItem.nbtHash) then - return item - end - end -end - -local function uniqueKey(item) - return table.concat({ item.name, item.damage, item.nbtHash }, ':') -end - -local function getItemQuantity(items, res) - local count = 0 - for _,v in pairs(items) do - if res.name == v.name and - ((not res.damage and v.maxDamage > 0) or res.damage == v.damage) and - ((not res.nbtHash and v.nbtHash) or res.nbtHash == v.nbtHash) then - count = count + v.count - end - end - return count -end - -local function mergeResources(t) - for _,v in pairs(resources) do - local item = getItem(t, v) - if item then - Util.merge(item, v) - else - item = Util.shallowCopy(v) - item.count = 0 - table.insert(t, item) - end - end - - for k in pairs(recipes) do - local v = itemDB:splitKey(k) - local item = getItem(t, v) - if not item then - item = Util.shallowCopy(v) - item.count = 0 - table.insert(t, item) - end - item.has_recipe = true - end - - for _,v in pairs(t) do - if not v.displayName then - v.displayName = itemDB:getName(v) - end - v.lname = v.displayName:lower() - end -end - -local function filterItems(t, filter) - if filter then - local r = {} - filter = filter:lower() - for _,v in pairs(t) do - if string.find(v.lname, filter) then - table.insert(r, v) - end - end - return r - end - return t -end - -local function clearGrid() - for i = 1, 16 do - local count = turtle.getItemCount(i) - if count > 0 then - inventoryAdapter:insert(i, count) - if turtle.getItemCount(i) ~= 0 then - return false - end - end - end - return true -end - -local function undock() - while listing do - os.sleep(.5) - end - docked = false -end - -local function gotoMachine(machine) - undock() - for _ = 1, machine.index do - if not turtle.back() then - return - end - end - - return true -end - -local function dock() - if not docked then - repeat until not turtle.forward() - end - docked = true -end - -local function getItems() - while not docked do - os.sleep(.5) - end - - listing = true - - local items - for _ = 1, 5 do - items = inventoryAdapter:listItems() - if items then - break - end - end - if not items then - error('could not check inventory') - end - - listing = false - - return items -end - -local function isMachineEmpty(machine, item) - local list = { true } - - pcall(function() -- fails randomly in 1.7x - local side = turtle.getAction(machine.dir).side - local methods = Util.transpose(Peripheral.getMethods(side)) - - if methods.getAllStacks then -- 1.7x - list = Peripheral.call(side, 'getAllStacks', false) - elseif methods.list then - list = Peripheral.call(side, 'list') - elseif methods.getProgress then - if Peripheral.call(side, 'getProgress') == 0 then - return true - end - else - item.statusCode = STATUS_ERROR - item.status = 'Unable to check empty status' - return - end - - if tonumber(machine.ignoreSlot) then - list[tonumber(machine.ignoreSlot)] = nil - end - end) - - if Util.empty(list) then - return true - end - item.statusCode = STATUS_INFO - item.status = 'machine busy' -end - -local function craftItem(ikey, item, items, machineStatus) - dock() - - local resource = resources[ikey] - if not resource or not resource.machine then - item.statusCode = STATUS_ERROR - item.status = 'machine not defined' - return - end - - local machine = Util.find(machines, 'order', resource.machine) - if not machine then - item.statusCode = STATUS_ERROR - item.status = 'invalid machine' - return - end - - local ms = machineStatus[machine.order] - if not ms then - ms = { count = 0 } - machineStatus[machine.order] = ms - end - - local slot = 1 - local maxCount = math.ceil(item.need / item.recipe.count) - maxCount = math.min(machine.maxCount or 64, maxCount) - - if maxCount > 0 and maxCount - ms.count <= 0 then - item.statusCode = STATUS_INFO - item.status = 'machine busy' - return - end - - maxCount = maxCount - ms.count - - for key,qty in pairs(item.recipe.ingredients) do - local ingredient = itemDB:get(key) - local c = math.min(maxCount * qty, getItemQuantity(items, ingredient)) - c = math.min(c, ingredient.maxCount) - c = math.floor(c / qty) - if c < maxCount then - maxCount = c - end - if maxCount <= 0 then - item.status = 'Missing ' .. ingredient.displayName - item.statusCode = STATUS_WARNING - return - end - end - - if machine.maxCount then - ms.count = ms.count + maxCount - end - - turtle.setStatus('Craft: ' .. itemDB:getName(ikey)) - for key,qty in pairs(item.recipe.ingredients) do - local ingredient = itemDB:get(key) --- local c = item.craftable * qty --- while c > 0 do ---debug(key) - inventoryAdapter:provide(ingredient, maxCount * qty, slot) - if turtle.getItemCount(slot) ~= maxCount * qty then - item.status = 'Extract failed: ' .. (ingredient.displayName or itemDB:getName(ingredient)) - item.statusCode = STATUS_ERROR - return - end --- c = c - maxCount - slot = slot + 1 - --end - end - - if not gotoMachine(machine) then - item.status = 'failed to find machine' - item.statusCode = STATUS_ERROR - else - if machine.empty and not isMachineEmpty(machine, item) then - return - end - - if machine.dir == 'up' then - turtle.emptyInventory(turtle.dropUp) - else - turtle.emptyInventory(turtle.dropDown) - end - if #turtle.getFilledSlots() ~= 0 then - item.statusCode = STATUS_INFO - item.status = 'machine busy' - else - item.statusCode = STATUS_SUCCESS - item.status = 'crafting' - end - end -end - -local function expandList(list, items) - local summed = { } - - local function sumItems(key, count) - local item = itemDB:splitKey(key) - local summedItem = summed[key] - if not summedItem then - summedItem = Util.shallowCopy(item) - summedItem.recipe = recipes[key] - summedItem.count = getItemQuantity(items, item) - summedItem.displayName = itemDB:getName(item) - summedItem.total = 0 - summedItem.need = 0 - summedItem.used = 0 - summedItem.craftable = 0 - summed[key] = summedItem - end - local total = count - local used = math.min(summedItem.count, total) - local need = total - used - - summedItem.total = summedItem.total + total - summedItem.count = summedItem.count - used - summedItem.used = summedItem.used + used - summedItem.need = summedItem.need + need - - if need > 0 and summedItem.recipe then - need = math.ceil(need / summedItem.recipe.count) - for ikey,iqty in pairs(summedItem.recipe.ingredients) do - sumItems(ikey, math.ceil(need * iqty)) - end - end - end - - for key, item in pairs(list) do - sumItems(key, item.count) - end - - return Util.filter(summed, function(a) return a.need > 0 end) -end - -local function watchResources(items) - local craftList = { } - - for _,res in pairs(resources) do - if res.low then - local item = Util.shallowCopy(res) - item.nbtHash = res.nbtHash - item.damage = res.damage - if res.ignoreDamage then - item.damage = nil - end - item.count = getItemQuantity(items, item) - if item.count < res.low then - item.displayName = itemDB:getName(res) - item.count = res.low -- - item.count - craftList[uniqueKey(res)] = item - end - end - end - - return craftList -end - -local function craftItems() - local machineStatus = { } - local items = getItems() - local craftList = watchResources(items) - local list = expandList(craftList, items) - jobListGrid:setValues(list) - jobListGrid:update() - jobListGrid:draw() - jobListGrid:sync() - for key, item in pairs(list) do - if item.need > 0 and item.recipe then - craftItem(key, item, items, machineStatus) - dock() - items = getItems() - clearGrid() - elseif item.need > 0 then - item.status = 'no recipe' - item.statusCode = STATUS_ERROR - end - jobListGrid:update() - jobListGrid:draw() - jobListGrid:sync() - end - turtle.setStatus('idle') -end - -local function loadResources() - resources = Util.readTable(RESOURCE_FILE) or { } - for k,v in pairs(resources) do - Util.merge(v, itemDB:splitKey(k)) - end -end - -local function saveResources() - local t = { } - - for k,v in pairs(resources) do - v = Util.shallowCopy(v) - - v.name = nil - v.damage = nil - v.nbtHash = nil - t[k] = v - end - - Util.writeTable(RESOURCE_FILE, t) -end - -local function findMachines() - turtle.setStatus('Inspecting machines') - - dock() - - local function getName(dir) - local side = turtle.getAction(dir).side - if Peripheral.isPresent(side) then - local methods = Util.transpose(Peripheral.getMethods(side)) - if methods.getMetadata then - local name = Peripheral.call(side, 'getMetadata').displayName - if name and not string.find(name, '.', 1, true) then - return name - end - elseif methods.getInventoryName then -- 1.7x - return Peripheral.call(side, 'getInventoryName') - end - return Peripheral.getType(side) - end - local _, machine = turtle.getAction(dir).inspect() - if not machine or type(machine) ~= 'table' then - return 'Unknown' - end - return machine.name or 'Unknown' - end - - local index = 0 - - local function getMachine(dir) - local name = getName(dir) - table.insert(machines, { - name = name, - rawName = name, - index = index, - dir = dir, - order = #machines + 1 - }) - end - - repeat - getMachine('down') - getMachine('up') - index = index + 1 - undock() - until not turtle.back() - - local mf = Util.readTable(MACHINES_FILE) or { } - for _,m in pairs(machines) do - local m2 = Util.find(mf, 'order', m.order) - if m2 then - if not m2.rawName then - m2.rawName = m.rawName - end - if m.rawName == m2.rawName then - m.name = m2.name or m.name - end - m.empty = m2.empty - m.ignore = m2.ignore - m.ignoreSlot = m2.ignoreSlot - m.maxCount = m2.maxCount - end - end -end - -local function jobMonitor() - local mon = Peripheral.lookup(config.monitor) - - if mon then - mon = UI.Device({ - device = mon, - textScale = .5, - }) - else - mon = UI.Device({ - device = Terminal.getNullTerm(term.current()) - }) - end - - jobListGrid = UI.Grid({ - parent = mon, - sortColumn = 'displayName', - columns = { - { heading = 'Qty', key = 'need', width = 6 }, - { heading = 'Crafting', key = 'displayName', width = (mon.width - 18) / 2 }, - { heading = 'Status', key = 'status', }, - }, - }) - - function jobListGrid:getRowTextColor(row, selected) - if row.statusCode == STATUS_ERROR then - return colors.red - elseif row.statusCode == STATUS_WARNING then - return colors.yellow - elseif row.statusCode == STATUS_SUCCESS then - return colors.lime - end - - return UI.Grid:getRowTextColor(row, selected) - end - - jobListGrid:draw() - jobListGrid:sync() -end - -local itemPage = UI.Page { - titleBar = UI.TitleBar { - title = 'Limit Resource', - previousPage = true, - event = 'form_cancel', - }, - form = UI.Form { - x = 1, y = 2, height = 10, ex = -1, - [1] = UI.TextEntry { - width = 7, - formLabel = 'Min', formKey = 'low', help = 'Craft if below min' - }, - [2] = UI.Chooser { - width = 7, - formLabel = 'Ignore Dmg', formKey = 'ignoreDamage', - nochoice = 'No', - choices = { - { name = 'Yes', value = true }, - { name = 'No', value = false }, - }, - help = 'Ignore damage of item' - }, - [3] = UI.Button { - text = 'Select', event= 'selectMachine', - formLabel = 'Machine' - }, - info = UI.TextArea { - x = 2, ex = -2, y = 6, height = 3, - textColor = colors.gray, - }, - button = UI.Button { - x = 2, y = 9, - text = 'Recipe', event = 'learn', - }, - }, - machines = UI.SlideOut { - backgroundColor = colors.cyan, - titleBar = UI.TitleBar { - title = 'Select Machine', - previousPage = true, - }, - grid = UI.ScrollingGrid { - y = 2, ey = -4, - values = machines, - disableHeader = true, - columns = { - { heading = '', key = 'index', width = 2 }, - { heading = 'Name', key = 'name'}, - }, - sortColumn = 'order', - }, - button1 = UI.Button { - x = -14, y = -2, - text = 'Ok', event = 'setMachine', - }, - button2 = UI.Button { - x = -9, y = -2, - text = 'Cancel', event = 'cancelMachine', - }, - }, - statusBar = UI.StatusBar { } -} - -function itemPage:enable(item) - if item then - self.item = Util.shallowCopy(item) - self.form:setValues(item) - self.titleBar.title = item.displayName or item.name - end - UI.Page.enable(self) - self:focusFirst() -end - -function itemPage.form.info:draw() - local recipe = recipes[uniqueKey(itemPage.item)] - self.value = '' - if recipe and itemPage.item.machine then - self.value = string.format('Crafts %d using the %s machine', - recipe.count, - machines[itemPage.item.machine].name) - end - UI.TextArea.draw(self) -end - -function itemPage.machines.grid:getRowTextColor(row, selected) - if itemPage.item.machine == row.order then - return colors.yellow - end - return UI.Grid:getRowTextColor(row, selected) -end - ---[[ -function itemPage.machines:eventHandler(event) - if event.type == 'grid_focus_row' then - self.statusBar:setStatus(string.format('%d %s', event.selected.index, event.selected.dir)) - else - return UI.SlideOut.eventHandler(self, event) - end - return true -end -]] - -function itemPage:eventHandler(event) - if event.type == 'form_cancel' then - UI:setPreviousPage() - - elseif event.type == 'learn' then - UI:setPage('learn', self.item) - - elseif event.type == 'setMachine' then - self.item.machine = self.machines.grid:getSelected().order - self.machines:hide() - - elseif event.type == 'cancelMachine' then - self.machines:hide() - - elseif event.type == 'selectMachine' then - local machineCopy = Util.shallowCopy(machines) - Util.filterInplace(machineCopy, function(m) return not m.ignore end) - self.machines.grid:setValues(machineCopy) - if self.item.machine then - local _, index = Util.find(machineCopy, 'order', self.item.machine) - if index then - self.machines.grid:setIndex(index) - end - else - self.machines.grid:setIndex(1) - end - self.machines:show() - - elseif event.type == 'focus_change' then - self.statusBar:setStatus(event.focused.help) - self.statusBar:draw() - - elseif event.type == 'form_complete' then - local values = self.form.values - local keys = { 'name', 'low', 'damage', 'nbtHash', 'machine' } - - local filtered = { } - for _,key in pairs(keys) do - filtered[key] = values[key] - end - filtered.low = tonumber(filtered.low) - filtered.machine = self.item.machine - - if values.ignoreDamage == true then - filtered.damage = 0 - filtered.ignoreDamage = true - end - - local key = uniqueKey(filtered) - - resources[key] = filtered - saveResources() - - UI:setPreviousPage() - - else - return UI.Page.eventHandler(self, event) - end - return true -end - -local learnPage = UI.Page { - ingredients = UI.ScrollingGrid { - y = 2, height = 3, - disableHeader = true, - columns = { - { heading = 'Name', key = 'displayName', width = 31 }, - { heading = 'Qty', key = 'count' , width = 5 }, - }, - sortColumn = 'displayName', - }, - grid = UI.ScrollingGrid { - y = 6, height = 5, - disableHeader = true, - columns = { - { heading = 'Name', key = 'displayName', width = 31 }, - { heading = 'Qty', key = 'count' , width = 5 }, - }, - sortColumn = 'displayName', - }, - filter = UI.TextEntry { - x = 20, ex = -2, y = 5, - limit = 50, - shadowText = 'filter', - backgroundColor = colors.lightGray, - backgroundFocusColor = colors.lightGray, - }, - count = UI.TextEntry { - x = 11, y = -1, width = 5, - limit = 50, - }, - button1 = UI.Button { - x = -14, y = -1, - text = 'Ok', event = 'accept', - }, - button2 = UI.Button { - x = -9, y = -1, - text = 'Cancel', event = 'cancel', - }, -} - -function learnPage:enable(target) - self.target = target - self.allItems = getItems() - mergeResources(self.allItems) - - self.filter.value = '' - self.grid.values = self.allItems - self.grid:update() - self.ingredients.values = { } - self.count.value = 1 - - if target.has_recipe then - local recipe = recipes[uniqueKey(target)] - self.count.value = recipe.count - for k,v in pairs(recipe.ingredients) do - self.ingredients.values[k] = - { name = k, count = v, displayName = itemDB:getName(k) } - end - end - self.ingredients:update() - - self:setFocus(self.filter) - UI.Page.enable(self) -end - -function learnPage:draw() - UI.Window.draw(self) - self:write(2, 1, 'Ingredients', nil, colors.yellow) - self:write(2, 5, 'Inventory', nil, colors.yellow) - self:write(2, 12, 'Produces') -end - -function learnPage:eventHandler(event) - - if event.type == 'text_change' and event.element == self.filter then - local t = filterItems(learnPage.allItems, event.text) - self.grid:setValues(t) - self.grid:draw() - - elseif event.type == 'cancel' then - UI:setPreviousPage() - - elseif event.type == 'accept' then - - local recipe = { - count = tonumber(self.count.value) or 1, - ingredients = { }, - } - for key, item in pairs(self.ingredients.values) do - recipe.ingredients[key] = item.count - end - recipes[uniqueKey(self.target)] = recipe - Util.writeTable(RECIPES_FILE, recipes) - - UI:setPreviousPage() - - elseif event.type == 'grid_select' then - if event.element == self.grid then - local key = uniqueKey(event.selected) - if not self.ingredients.values[key] then - self.ingredients.values[key] = Util.shallowCopy(event.selected) - self.ingredients.values[key].count = 0 - end - self.ingredients.values[key].count = self.ingredients.values[key].count + 1 - self.ingredients:update() - self.ingredients:draw() - elseif event.element == self.ingredients then - event.selected.count = event.selected.count - 1 - if event.selected.count == 0 then - self.ingredients.values[uniqueKey(event.selected)] = nil - self.ingredients:update() - end - self.ingredients:draw() - end - - else - return UI.Page.eventHandler(self, event) - end - return true -end - -local machinesPage = UI.Page { - titleBar = UI.TitleBar { - previousPage = true, - title = 'Machines', - }, - grid = UI.ScrollingGrid { - y = 2, ey = -2, - values = machines, - columns = { - { heading = 'Name', key = 'name' }, - { heading = 'Side', key = 'dir', width = 5 }, - { heading = 'Index', key = 'index', width = 5 }, - }, - sortColumn = 'order', - }, - detail = UI.SlideOut { - backgroundColor = colors.cyan, - form = UI.Form { - x = 1, y = 2, ex = -1, ey = -2, - [1] = UI.TextEntry { - formLabel = 'Name', formKey = 'name', help = '...', - limit = 64, - }, - [2] = UI.Chooser { - width = 7, - formLabel = 'Hidden', formKey = 'ignore', - nochoice = 'No', - choices = { - { name = 'Yes', value = true }, - { name = 'No', value = false }, - }, - help = 'Do not show this machine' - }, - [3] = UI.Chooser { - width = 7, - formLabel = 'Empty', formKey = 'empty', - nochoice = 'No', - choices = { - { name = 'Yes', value = true }, - { name = 'No', value = false }, - }, - help = 'Check if machine is empty before crafting' - }, - [4] = UI.TextEntry { - formLabel = 'Ignore Slot', formKey = 'ignoreSlot', help = '...', - limit = 4, - }, - [5] = UI.TextEntry { - formLabel = 'Max Craft', formKey = 'maxCount', help = '...', - limit = 4, - }, - }, - statusBar = UI.StatusBar(), - }, - statusBar = UI.StatusBar { - values = 'Select Machine', - }, - accelerators = { - h = 'toggle_hidden', - } -} - -function machinesPage:enable() - self.grid:update() - UI.Page.enable(self) -end - -function machinesPage.detail:eventHandler(event) - if event.type == 'focus_change' then - self.statusBar:setStatus(event.focused.help) - end - return UI.SlideOut.eventHandler(self, event) -end - -function machinesPage.grid:getRowTextColor(row, selected) - if row.ignore then - return colors.yellow - end - return UI.Grid:getRowTextColor(row, selected) -end - -function machinesPage:eventHandler(event) - if event.type == 'grid_select' then - self.detail.form:setValues(event.selected) - self.detail:show() - - elseif event.type == 'toggle_hidden' then - local selected = self.grid:getSelected() - if selected then - selected.ignore = not selected.ignore - Util.writeTable(MACHINES_FILE, machines) - self:draw() - end - - elseif event.type == 'form_complete' then - self.detail.form.values.empty = self.detail.form.values.empty == true - self.detail.form.values.ignore = self.detail.form.values.ignore == true - self.detail.form.values.ignoreSlot = tonumber(self.detail.form.values.ignoreSlot) - self.detail.form.values.maxCount = tonumber(self.detail.form.values.maxCount) - Util.writeTable(MACHINES_FILE, machines) - self.detail:hide() - - elseif event.type == 'form_cancel' then - self.detail:hide() - - else - UI.Page.eventHandler(self, event) - end - return true -end - -local listingPage = UI.Page { - menuBar = UI.MenuBar { - buttons = { - { text = 'Forget', event = 'forget' }, - { text = 'Machines', event = 'machines' }, - { text = 'Refresh', event = 'refresh', x = -9 }, - }, - }, - grid = UI.Grid { - y = 2, height = UI.term.height - 2, - columns = { - { heading = 'Name', key = 'displayName' }, - { heading = 'Qty', key = 'count' , width = 5 }, - { heading = 'Min', key = 'low' , width = 4 }, - }, - sortColumn = 'displayName', - }, - statusBar = UI.Window { - y = -1, - filter = UI.TextEntry { - limit = 50, - shadowText = 'filter', - shadowTextColor = colors.lightGray, - backgroundColor = colors.gray, - backgroundFocusColor = colors.gray, - }, - }, - accelerators = { - r = 'refresh', - q = 'quit', - } -} - -function listingPage.grid:getRowTextColor(row, selected) - if row.is_craftable then - return colors.yellow - end - if row.has_recipe then - return colors.cyan - end - return UI.Grid:getRowTextColor(row, selected) -end - -function listingPage.grid:getDisplayValues(row) - row = Util.shallowCopy(row) - row.count = Util.toBytes(row.count) - if row.low then - row.low = Util.toBytes(row.low) - end - return row -end - -function listingPage.statusBar.filter:eventHandler(event) - if event.type == 'mouse_rightclick' then - self.value = '' - self:draw() - local page = UI:getCurrentPage() - page.filter = nil - page:applyFilter() - page.grid:draw() - page:setFocus(self) - end - return UI.TextEntry.eventHandler(self, event) -end - -function listingPage:eventHandler(event) - if event.type == 'quit' then - UI:exitPullEvents() - - elseif event.type == 'grid_select' then - local selected = event.selected - UI:setPage('item', selected) - - elseif event.type == 'refresh' then - self:refresh() - self.grid:draw() - self.statusBar.filter:focus() - - elseif event.type == 'machines' then - UI:setPage('machines') - - elseif event.type == 'craft' then - UI:setPage('craft', self.grid:getSelected()) - - elseif event.type == 'forget' then - local item = self.grid:getSelected() - if item then - local key = uniqueKey(item) - - if recipes[key] then - recipes[key] = nil - Util.writeTable(RECIPES_FILE, recipes) - end - - if resources[key] then - resources[key] = nil - Util.writeTable(RESOURCE_FILE, resources) - end - - self.statusBar:timedStatus('Forgot: ' .. item.name, 3) - self:refresh() - self.grid:draw() - end - - elseif event.type == 'text_change' then - self.filter = event.text - if #self.filter == 0 then - self.filter = nil - end - self:applyFilter() - self.grid:draw() - self.statusBar.filter:focus() - - else - UI.Page.eventHandler(self, event) - end - return true -end - -function listingPage:enable() - self:refresh() - self:setFocus(self.statusBar.filter) - UI.Page.enable(self) -end - -function listingPage:refresh() - self.allItems = getItems() - mergeResources(self.allItems) - self:applyFilter() -end - -function listingPage:applyFilter() - local t = filterItems(self.allItems, self.filter) - self.grid:setValues(t) -end - --- randomly errors in 1.7x with "you are not attached to this computer" -print('Inspecting machines') -local retryCount = 0 -while true do - Util.clear(machines) - local s, m = pcall(findMachines) - if not s and m then - _G.printError(m) - else - break - end - retryCount = retryCount + 1 - if retryCount > 3 then - error(m) - end - print('retrying...') -end - -loadResources() -dock() -clearGrid() -jobMonitor() - -UI:setPages({ - listing = listingPage, - machines = machinesPage, - item = itemPage, - learn = learnPage, -}) - -UI:setPage(listingPage) -listingPage:setFocus(listingPage.statusBar.filter) - -Event.on('turtle_abort', function() - UI:exitPullEvents() -end) - -Event.onInterval(30, function() - dock() - if turtle.getFuelLevel() < 100 then - turtle.select(1) - inventoryAdapter:provide({ name = 'minecraft:coal', damage = 1 }, 16, 1) - turtle.refuel() - end - craftItems() -end) - -turtle.setStatus('idle') -UI:pullEvents() -jobListGrid.parent:reset() diff --git a/inventoryManager/plugins/exportView.lua b/inventoryManager/plugins/exportView.lua deleted file mode 100644 index fbdc130..0000000 --- a/inventoryManager/plugins/exportView.lua +++ /dev/null @@ -1,121 +0,0 @@ -local itemDB = require('itemDB') -local Lora = require('lora') -local UI = require('ui') -local Util = require('util') - -local colors = _G.colors -local device = _G.device - -local itemSlideout = UI.SlideOut { - backgroundColor = colors.cyan, - grid = UI.ScrollingGrid { - y = 3, ey = -2, - columns = { - { heading = 'Name', key = 'displayName', width = 31 }, - { heading = 'Qty', key = 'count' , width = 5 }, - }, - sortColumn = 'displayName', - }, - filter = UI.TextEntry { - x = 2, ex = -2, y = 2, - limit = 50, - shadowText = 'filter', - backgroundColor = colors.lightGray, - backgroundFocusColor = colors.lightGray, - }, - button1 = UI.Button { - x = -14, y = -1, - text = 'Ok', event = 'accept', - }, - button2 = UI.Button { - x = -9, y = -1, - text = 'Cancel', event = 'collapse', - }, -} - -function itemSlideout:filterItems(t, filter) - if filter then - local r = {} - filter = filter:lower() - for _,v in pairs(t) do - if string.find(v.lname, filter) then - table.insert(r, v) - end - end - return r - end - return t -end - -function itemSlideout.grid:enable() - if not self.allItems then - self.allItems = Lora:listItems() - Lora:mergeResources(self.allItems) - self:setValues(self.allItems) - end - UI.Grid.enable(self) -end - -function itemSlideout:eventHandler(event) - if event.type == 'text_change' and event.element == self.filter then - local t = self:filterItems(self.grid.allItems, event.text) - self.grid:setValues(t) - self.grid:draw() - end - return UI.SlideOut.eventHandler(self, event) -end - -local exportView = UI.Window { - mtype = 'machine', - title = 'Export item into machine', - index = 3, - grid = UI.ScrollingGrid { - x = 2, ex = -6, y = 2, ey = -2, - columns = { - { heading = 'Slot', key = 'slot', width = 4 }, - { heading = 'Item', key = 'displayName' }, - }, - sortColumn = 'slot', - }, - add = UI.Button { - x = -4, y = 4, - text = '+', event = 'add_export', help = '...', - }, - remove = UI.Button { - x = -4, y = 6, - text = '-', event = 'remove_export', help = '...', - }, -} - -function exportView:save(machine) - machine.exports = not Util.empty(self.grid.values) and self.grid.values or nil - return true -end - -function exportView:setMachine(machine) - local m = device[machine.name] - self.slotCount = m.size() - self.grid:setValues(machine.exports or { }) -end - -function exportView.grid:getDisplayValues(row) - row = Util.shallowCopy(row) - row.displayName = itemDB:getName(row.name) - return row -end - -function exportView:eventHandler(event) - if event.type == 'grid_select' or event.type == 'add_export' then - itemSlideout:show() - elseif event.type == 'remove_export' then - local row = self.grid:getSelected() - if row then - Util.removeByValue(self.grid.values, row) - self.grid:update() - self.grid:draw() - end - end -end - -UI:getPage('machineWizard'):add({ items = itemSlideout }) -UI:getPage('machineWizard').wizard:add({ export = exportView }) diff --git a/inventoryManager/plugins/learn.lua b/inventoryManager/plugins/learn.lua deleted file mode 100644 index a8ebf06..0000000 --- a/inventoryManager/plugins/learn.lua +++ /dev/null @@ -1,174 +0,0 @@ -local Craft = require('turtle.craft') -local itemDB = require('itemDB') -local Lora = require('lora') -local UI = require('ui') -local Util = require('util') - -local device = _G.device -local turtle = _G.turtle - -local context = Lora:getContext() - --- TODO: try networked module -local introspectionModule = device['plethora:introspection'] or - error('Introspection module not found') - -local function getTurtleInventory() - local list = { } - for i = 1,16 do - list[i] = introspectionModule.getInventory().getItemMeta(i) - end - return list -end - -local function learnRecipe() - local ingredients = getTurtleInventory() - local listingPage = UI:getPage('listing') - - if ingredients then - turtle.select(1) - if turtle.craft() then - local results = getTurtleInventory() - if results and results[1] then - Lora:clearGrid() - - local maxCount - local newRecipe = { - ingredients = ingredients, - } - - local numResults = 0 - for _,v in pairs(results) do - if v.count > 0 then - numResults = numResults + 1 - end - end - if numResults > 1 then - for _,v1 in pairs(results) do - for _,v2 in pairs(ingredients) do - if v1.name == v2.name and - v1.nbtHash == v2.nbtHash and - (v1.damage == v2.damage or - (v1.maxDamage > 0 and v2.maxDamage > 0 and - v1.damage ~= v2.damage)) then - if not newRecipe.crafingTools then - newRecipe.craftingTools = { } - end - local tool = Util.shallowCopy(v2) - if tool.maxDamage > 0 then - tool.damage = '*' - end - - --[[ - Turtles can only craft one item at a time using a tool :( - ]]-- - maxCount = 1 - - newRecipe.craftingTools[Lora:uniqueKey(tool)] = true - v1.craftingTool = true - break - end - end - end - end - - local recipe - for _,v in pairs(results) do - if not v.craftingTool then - recipe = v - if maxCount then - recipe.maxCount = maxCount - end - break - end - end - - if not recipe then - debug(results) - debug(newRecipe) - error('Failed - view system log') - end - - newRecipe.count = recipe.count - - local key = Lora:uniqueKey(recipe) - if recipe.maxCount ~= 64 then - newRecipe.maxCount = recipe.maxCount - end - for k,ingredient in pairs(Util.shallowCopy(ingredients)) do - if ingredient.maxDamage > 0 then - -- ingredient.damage = '*' -- I don't think this is right - end - ingredients[k] = Lora:uniqueKey(ingredient) - end - - context.userRecipes[key] = newRecipe - Util.writeTable(Lora.RECIPES_FILE, context.userRecipes) - Craft.loadRecipes() - - local displayName = itemDB:getName(recipe) - - listingPage.statusBar.filter:setValue(displayName) - listingPage.notification:success('Learned: ' .. displayName) - listingPage.filter = displayName - listingPage:refresh() - listingPage.grid:draw() - - Lora:eject(recipe, recipe.count) - return true - end - else - listingPage.notification:error('Failed to craft', 3) - end - else - listingPage.notification:error('No recipe defined', 3) - end -end - -local learnPage = UI.Dialog { - height = 7, width = UI.term.width - 6, - title = 'Learn Recipe', - idField = UI.Text { - x = 5, - y = 3, - width = UI.term.width - 10, - value = 'Place recipe in turtle' - }, - accept = UI.Button { - x = -14, y = -3, - text = 'Ok', event = 'accept', - }, - cancel = UI.Button { - x = -9, y = -3, - text = 'Cancel', event = 'cancel' - }, - statusBar = UI.StatusBar { - status = 'Crafting paused' - } -} - -function learnPage:enable() - Lora:pauseCrafting() - self:focusFirst() - UI.Dialog.enable(self) -end - -function learnPage:disable() - Lora:resumeCrafting() - UI.Dialog.disable(self) -end - -function learnPage:eventHandler(event) - if event.type == 'cancel' then - UI:setPreviousPage() - elseif event.type == 'accept' then - if learnRecipe(self) then - UI:setPreviousPage() - end - else - return UI.Dialog.eventHandler(self, event) - end - return true -end - -UI:addPage('learn', learnPage) diff --git a/inventoryManager/.package b/milo/.package similarity index 88% rename from inventoryManager/.package rename to milo/.package index f1522ad..737e1e0 100644 --- a/inventoryManager/.package +++ b/milo/.package @@ -1,6 +1,6 @@ { required = { - 'opus-inventory-manager', + 'opus-develop-1.8', }, title = 'Inventory manager for Opus OS', description = [[ diff --git a/inventoryManager/inventoryManager.lua b/milo/Milo.lua similarity index 87% rename from inventoryManager/inventoryManager.lua rename to milo/Milo.lua index 02d08bd..4ed142d 100644 --- a/inventoryManager/inventoryManager.lua +++ b/milo/Milo.lua @@ -40,7 +40,7 @@ via CC cables. Configuration: - Configuration file is usr/config/inventoryManager + Configuration file is usr/config/milo monitor : valid options include: type/monitor - will use the first monitor found @@ -66,7 +66,7 @@ _G.requireInjector() local Config = require('config') local Event = require('event') local itemDB = require('itemDB') -local Lora = require('lora') +local Milo = require('milo') local Peripheral = require('peripheral') local UI = require('ui') local Util = require('util') @@ -78,14 +78,14 @@ local multishell = _ENV.multishell local shell = _ENV.shell if multishell then - multishell.setTitle(multishell.getCurrent(), 'Resource Manager') + multishell.setTitle(multishell.getCurrent(), 'Milo') end local config = { monitor = 'type/monitor', remoteDefaults = { }, } -Config.load('inventoryManager', config) +Config.load('milo', config) local modem = Peripheral.get('wired_modem') if not modem or not modem.getNameLocal then @@ -125,7 +125,7 @@ for _, v in pairs(modem.getNamesRemote()) do end local function loadResources() - local resources = Util.readTable(Lora.RESOURCE_FILE) or { } + local resources = Util.readTable(Milo.RESOURCE_FILE) or { } for k,v in pairs(resources) do Util.merge(v, itemDB:splitKey(k)) end @@ -137,10 +137,12 @@ local context = { config = config, inventoryAdapter = inventoryAdapter, resources = loadResources(), - userRecipes = Util.readTable(Lora.RECIPES_FILE) or { }, + userRecipes = Util.readTable(Milo.RECIPES_FILE) or { }, + learnTypes = { }, + machineTypes = { }, } -Lora:init(context) +Milo:init(context) local function loadDirectory(dir) for _, file in pairs(fs.list(dir)) do @@ -155,22 +157,24 @@ local programDir = fs.getDir(shell.getRunningProgram()) loadDirectory(fs.combine(programDir, 'core')) loadDirectory(fs.combine(programDir, 'plugins')) -table.sort(Lora.tasks, function(a, b) +table.sort(Milo.tasks, function(a, b) return a.priority < b.priority end) -Lora:clearGrid() +Milo:clearGrid() local page = UI:getPage('listing') UI:setPage(page) page:setFocus(page.statusBar.filter) +-- TODO: Event.on('device_detach', function() end) + Event.onInterval(5, function() - if not Lora:isCraftingPaused() then - Lora:resetCraftingStatus() + if not Milo:isCraftingPaused() then + Milo:resetCraftingStatus() context.inventoryAdapter:refresh() - for _, task in ipairs(Lora.tasks) do + for _, task in ipairs(Milo.tasks) do local s, m = pcall(function() task:cycle(context) end) if not s and m then Util.print(task) diff --git a/inventoryManager/apis/inventoryAdapter.lua b/milo/apis/inventoryAdapter.lua similarity index 100% rename from inventoryManager/apis/inventoryAdapter.lua rename to milo/apis/inventoryAdapter.lua diff --git a/inventoryManager/apis/lora.lua b/milo/apis/milo.lua similarity index 84% rename from inventoryManager/apis/lora.lua rename to milo/apis/milo.lua index d866d48..e453a9b 100644 --- a/inventoryManager/apis/lora.lua +++ b/milo/apis/milo.lua @@ -1,3 +1,4 @@ +local Config = require('config') local Craft = require('turtle.craft') local itemDB = require('itemDB') local Util = require('util') @@ -6,7 +7,7 @@ local os = _G.os local term = _G.term local turtle = _G.turtle -local Lora = { +local Milo = { RECIPES_FILE = 'usr/config/recipes.db', RESOURCE_FILE = 'usr/config/resources.db', @@ -18,50 +19,67 @@ local Lora = { craftingStatus = { }, } -function Lora:init(context) +function Milo:init(context) self.context = context end -function Lora:getContext() +function Milo:getContext() return self.context end -function Lora:pauseCrafting() +function Milo:pauseCrafting() self.craftingPaused = true end -function Lora:resumeCrafting() +function Milo:resumeCrafting() self.craftingPaused = false end -function Lora:isCraftingPaused() +function Milo:isCraftingPaused() return self.craftingPaused end -function Lora:uniqueKey(item) +function Milo:getState(key) + if not self.state then + self.state = { } + Config.load('milo.state', self.state) + end + return self.state[key] +end + +function Milo:setState(key, value) + if not self.state then + self.state = { } + Config.load('milo.state', self.state) + end + self.state[key] = value + Config.update('milo.state', self.state) +end + +function Milo:uniqueKey(item) return table.concat({ item.name, item.damage, item.nbtHash }, ':') end -function Lora:getCraftingStatus() +function Milo:getCraftingStatus() return self.craftingStatus end -function Lora:resetCraftingStatus() +function Milo:resetCraftingStatus() self.craftingStatus = { } self.context.inventoryAdapter.activity = { } end -function Lora:updateCraftingStatus(list) +function Milo:updateCraftingStatus(list) for k,v in pairs(list) do self.craftingStatus[k] = v end end -function Lora:registerTask(task) +function Milo:registerTask(task) table.insert(self.tasks, task) end -function Lora:showError(msg) +function Milo:showError(msg) term.clear() self.context.jobList:showError() print(msg) @@ -70,7 +88,7 @@ function Lora:showError(msg) os.reboot() end -function Lora:getItem(items, inItem, ignoreDamage, ignoreNbtHash) +function Milo:getItem(items, inItem, ignoreDamage, ignoreNbtHash) for _,item in pairs(items) do if item.name == inItem.name and (ignoreDamage or item.damage == inItem.damage) and @@ -80,7 +98,7 @@ function Lora:getItem(items, inItem, ignoreDamage, ignoreNbtHash) end end -function Lora:getItemWithQty(res, ignoreDamage, ignoreNbtHash) +function Milo:getItemWithQty(res, ignoreDamage, ignoreNbtHash) local items = self:listItems() local item = self:getItem(items, res, ignoreDamage, ignoreNbtHash) @@ -100,7 +118,7 @@ function Lora:getItemWithQty(res, ignoreDamage, ignoreNbtHash) return item end -function Lora:clearGrid() +function Milo:clearGrid() local function clear() turtle.eachFilledSlot(function(slot) self.context.inventoryAdapter:insert(slot.index, slot.count, nil, slot) @@ -116,7 +134,7 @@ function Lora:clearGrid() return clear() or clear() end -function Lora:eject(item, qty) +function Milo:eject(item, qty) local s, m = pcall(function() self.context.inventoryAdapter:provide(item, qty) turtle.emptyInventory() @@ -126,9 +144,9 @@ function Lora:eject(item, qty) end end -function Lora:mergeResources(t) +function Milo:mergeResources(t) for _,v in pairs(self.context.resources) do - local item = Lora:getItem(t, v) + local item = self:getItem(t, v) if item then Util.merge(item, v) else @@ -140,7 +158,7 @@ function Lora:mergeResources(t) for k in pairs(Craft.recipes) do local v = itemDB:splitKey(k) - local item = Lora:getItem(t, v) + local item = self:getItem(t, v) if not item then item = Util.shallowCopy(v) item.count = 0 @@ -157,7 +175,7 @@ function Lora:mergeResources(t) end end -function Lora:saveResources() +function Milo:saveResources() local t = { } for k,v in pairs(self.context.resources) do @@ -176,11 +194,11 @@ function Lora:saveResources() end end - Util.writeTable(Lora.RESOURCE_FILE, t) + Util.writeTable(self.RESOURCE_FILE, t) end -- Return a list of everything in the system -function Lora:listItems() +function Milo:listItems() for _ = 1, 5 do self.items = self.context.inventoryAdapter:listItems() if self.items then @@ -196,7 +214,7 @@ function Lora:listItems() return self.items end -function Lora:addCraftingRequest(item, craftList, count) +function Milo:addCraftingRequest(item, craftList, count) local key = self:uniqueKey(item) local request = craftList[key] if not craftList[key] then @@ -209,7 +227,7 @@ function Lora:addCraftingRequest(item, craftList, count) end -- Craft -function Lora:craftItem(recipe, items, originalItem, craftList, count) +function Milo:craftItem(recipe, items, originalItem, craftList, count) local missing = { } local toCraft = Craft.getCraftableAmount(recipe, count, items, missing) if missing.name then @@ -245,7 +263,7 @@ function Lora:craftItem(recipe, items, originalItem, craftList, count) end -- Craft as much as possible regardless if all ingredients are available -function Lora:forceCraftItem(inRecipe, items, originalItem, craftList, inCount) +function Milo:forceCraftItem(inRecipe, items, originalItem, craftList, inCount) local summed = { } local throttle = Util.throttle() @@ -326,7 +344,7 @@ function Lora:forceCraftItem(inRecipe, items, originalItem, craftList, inCount) return count end -function Lora:craft(recipe, items, item, craftList) +function Milo:craft(recipe, items, item, craftList) item.status = nil item.statusCode = nil item.crafted = 0 @@ -348,7 +366,7 @@ function Lora:craft(recipe, items, item, craftList) end end -function Lora:craftItems(craftList) +function Milo:craftItems(craftList) for _,key in pairs(Util.keys(craftList)) do local item = craftList[key] if item.count > 0 then @@ -367,4 +385,4 @@ function Lora:craftItems(craftList) end end -return Lora +return Milo diff --git a/inventoryManager/apis/networkedAdapter18.lua b/milo/apis/networkedAdapter18.lua similarity index 100% rename from inventoryManager/apis/networkedAdapter18.lua rename to milo/apis/networkedAdapter18.lua diff --git a/inventoryManager/apis/turtle/craft.lua b/milo/apis/turtle/craft.lua similarity index 100% rename from inventoryManager/apis/turtle/craft.lua rename to milo/apis/turtle/craft.lua diff --git a/inventoryManager/core/machines.lua b/milo/core/machines.lua similarity index 80% rename from inventoryManager/core/machines.lua rename to milo/core/machines.lua index 675554b..ada5f32 100644 --- a/inventoryManager/core/machines.lua +++ b/milo/core/machines.lua @@ -1,11 +1,13 @@ local Config = require('config') -local Lora = require('lora') +local itemDB = require('itemDB') +local Milo = require('milo') local UI = require('ui') local Util = require('util') local colors = _G.colors +local device = _G.device -local context = Lora:getContext() +local context = Milo:getContext() local machinesPage = UI.Page { titleBar = UI.TitleBar { @@ -57,13 +59,13 @@ end local machineWizard = UI.Page { titleBar = UI.TitleBar { title = 'Configure' }, wizard = UI.Wizard { - y = 2, ey = -3, + y = 2, ey = -2, pages = { general = UI.Window { index = 1, backgroundColor = colors.cyan, form = UI.Form { - x = 1, y = 2, ex = -1, ey = -2, + x = 1, y = 1, ex = -1, ey = 3, manualControls = true, [1] = UI.TextEntry { formLabel = 'Name', formKey = 'displayName', @@ -84,11 +86,21 @@ local machineWizard = UI.Page { help = 'Select type', }, }, + grid = UI.ScrollingGrid { + y = 5, ey = -2, x = 2, ex = -2, + columns = { + { heading = 'Slot', key = 'slot', width = 4 }, + { heading = 'Name', key = 'displayName', }, + { heading = 'Qty', key = 'count' , width = 3 }, + }, + sortColumn = 'slot', + help = 'Contents of inventory', + }, }, confirmation = UI.Window { title = 'Confirm changes', index = 2, - grid = UI.TextArea { + notice = UI.TextArea { x = 2, ex = -2, y = 2, ey = -2, value = [[Press accept to save the changes. @@ -98,7 +110,9 @@ The settings will take effect immediately!]], }, }, }, - statusBar = UI.StatusBar(), + statusBar = UI.StatusBar { + backgroundColor = colors.cyan, + }, notification = UI.Notification { }, } @@ -107,6 +121,25 @@ function machineWizard.wizard.pages.general:enable() self:focusFirst() end +function machineWizard.wizard.pages.general:setMachine(machine) + local inventory + + if device[machine.name] and device[machine.name].list then + inventory = device[machine.name].list() + for k,v in pairs(inventory) do + v.slot = k + end + end + + self.grid:setValues(inventory or { }) +end + +function machineWizard.wizard.pages.general.grid:getDisplayValues(row) + row = Util.shallowCopy(row) + row.displayName = itemDB:getName(row) + return row +end + function machineWizard.wizard.pages.general:validate() return self.form:save() end @@ -168,7 +201,7 @@ function machineWizard:eventHandler(event) end end context.config.remoteDefaults[self.machine.name] = self.machine - Config.update('inventoryManagerX', context.config) + Config.update('milo', context.config) UI:setPreviousPage() diff --git a/inventoryManager/plugins/autocraftTask.lua b/milo/plugins/autocraftTask.lua similarity index 71% rename from inventoryManager/plugins/autocraftTask.lua rename to milo/plugins/autocraftTask.lua index 697e5ff..caf83a5 100644 --- a/inventoryManager/plugins/autocraftTask.lua +++ b/milo/plugins/autocraftTask.lua @@ -1,4 +1,4 @@ -local Lora = require('lora') +local Milo = require('milo') local Util = require('util') local Autocraft = { @@ -12,13 +12,13 @@ function Autocraft:cycle(context) if res.auto then res = Util.shallowCopy(res) res.count = 256 - list[Lora:uniqueKey(res)] = res + list[Milo:uniqueKey(res)] = res end end if not Util.empty(list) then - Lora:craftItems(list) + Milo:craftItems(list) end end -Lora:registerTask(Autocraft) +Milo:registerTask(Autocraft) diff --git a/inventoryManager/plugins/demandCraft.lua b/milo/plugins/demandCraft.lua similarity index 95% rename from inventoryManager/plugins/demandCraft.lua rename to milo/plugins/demandCraft.lua index 6a0c635..c30f2cf 100644 --- a/inventoryManager/plugins/demandCraft.lua +++ b/milo/plugins/demandCraft.lua @@ -1,6 +1,6 @@ local Craft = require('turtle.craft') local itemDB = require('itemDB') -local Lora = require('lora') +local Milo = require('milo') local UI = require('ui') local Util = require('util') @@ -98,7 +98,7 @@ function craftPage.wizard:eventHandler(event) end function craftPage.wizard.pages.resources:enable() - local items = Lora:listItems() + local items = Milo:listItems() local count = tonumber(self.parent.quantity.count.value) local recipe = Craft.findRecipe(craftPage.item) if recipe then @@ -118,7 +118,7 @@ function craftPage:eventHandler(event) UI:setPreviousPage() elseif event.type == 'accept' then - local key = Lora:uniqueKey(self.item) + local key = Milo:uniqueKey(self.item) demandCrafting[key] = Util.shallowCopy(self.item) demandCrafting[key].count = tonumber(self.wizard.pages.quantity.count.value) demandCrafting[key].ocount = demandCrafting[key].count @@ -155,7 +155,7 @@ function demandCraftingTask:cycle(context) end if Util.size(demandCrafted) > 0 then - Lora:craftItems(demandCrafted) + Milo:craftItems(demandCrafted) end for _,key in pairs(Util.keys(demandCrafting)) do @@ -166,7 +166,7 @@ function demandCraftingTask:cycle(context) item.statusCode = 'success' demandCrafting[key] = nil if item.eject then - Lora:eject(item, item.ocount) + Milo:eject(item, item.ocount) end end end @@ -174,4 +174,4 @@ function demandCraftingTask:cycle(context) end UI:addPage('craft', craftPage) -Lora:registerTask(demandCraftingTask) +Milo:registerTask(demandCraftingTask) diff --git a/inventoryManager/plugins/exportTask.lua b/milo/plugins/exportTask.lua similarity index 92% rename from inventoryManager/plugins/exportTask.lua rename to milo/plugins/exportTask.lua index 937610a..1e645f0 100644 --- a/inventoryManager/plugins/exportTask.lua +++ b/milo/plugins/exportTask.lua @@ -1,5 +1,5 @@ local itemDB = require('itemDB') -local Lora = require('lora') +local Milo = require('milo') local device = _G.device @@ -28,4 +28,4 @@ function ExportTask:cycle(context) end end -Lora:registerTask(ExportTask) +Milo:registerTask(ExportTask) diff --git a/milo/plugins/exportView.lua b/milo/plugins/exportView.lua new file mode 100644 index 0000000..3d01297 --- /dev/null +++ b/milo/plugins/exportView.lua @@ -0,0 +1,213 @@ +local itemDB = require('itemDB') +local Milo = require('milo') +local UI = require('ui') +local Util = require('util') + +local colors = _G.colors +local device = _G.device + +local itemSlideout = UI.SlideOut { + backgroundColor = colors.cyan, + menuBar = UI.MenuBar { + buttons = { + { text = 'Save', event = 'save' }, + { text = 'Cancel', event = 'cancel' }, + { text = 'Refresh', event = 'refresh', x = -9 }, + }, + }, + grid = UI.ScrollingGrid { + y = 2, ey = -6, + columns = { + { heading = 'Name', key = 'displayName', width = 31 }, + { heading = 'Qty', key = 'count' , width = 5 }, + }, + sortColumn = 'displayName', + help = 'Select item to export', + }, + filter = UI.TextEntry { + x = 2, ex = 18, y = -3, + limit = 50, + shadowText = 'filter', + backgroundColor = colors.lightGray, + backgroundFocusColor = colors.lightGray, + }, + form = UI.Form { + x = 21, y = -4, height = 3, + margin = 1, + manualControls = true, + [1] = UI.Chooser { + width = 7, + formLabel = 'Slot', formKey = 'slot', + nochoice = 1, + help = 'Export into this slot', + }, + [2] = UI.Chooser { + width = 7, + formLabel = 'Ignore Dmg', formKey = 'ignoreDamage', + pruneEmpty = true, + nochoice = 'No', + choices = { + { name = 'Yes', value = true }, + { name = 'No', value = false }, + }, + help = 'Ignore damage of item when exporting' + }, + [3] = UI.Chooser { + width = 7, + formLabel = 'Ignore NBT', formKey = 'ignoreNbtHash', + pruneEmpty = true, + nochoice = 'No', + choices = { + { name = 'Yes', value = true }, + { name = 'No', value = false }, + }, + help = 'Ignore NBT of item when exporting' + }, + }, + statusBar = UI.StatusBar { + backgroundColor = colors.cyan, + }, +} + +function itemSlideout:filterItems(t, filter) + if filter and #filter > 0 then + local r = {} + filter = filter:lower() + for _,v in pairs(t) do + if string.find(v.lname, filter) then + table.insert(r, v) + end + end + return r + end + return t +end + +function itemSlideout.grid:enable() + if not self.allItems then + self.allItems = Milo:listItems() + Milo:mergeResources(self.allItems) + self:setValues(self.allItems) + end + UI.Grid.enable(self) +end + +function itemSlideout:show(machine, entry) + self.machine = machine + self.entry = entry + + self.form.choices = { } + local m = device[machine.name] + for k = 1, m.size() do + table.insert(self.form[1].choices, { + name = k, + value = k, + }) + end + + if not entry.slot then + entry.slot = 1 + end + self.form:setValues(entry) + + UI.SlideOut.show(self) + self:setFocus(self.filter) + --self.filter:focus() +end + +function itemSlideout:eventHandler(event) + if event.type == 'text_change' then + local t = self:filterItems(self.grid.allItems, event.text) + self.grid:setValues(t) + self.grid:draw() + + elseif event.type == 'focus_change' then + self.statusBar:setStatus(event.focused.help) + + elseif event.type == 'save' then + local selected = self.grid:getSelected() + if not selected then + self.statusBar:setStatus('Select an item to export') + else + self.form:save() + self.form.values.name = itemDB:makeKey(selected) + table.insert(self.machine.exports, self.form.values) + self:hide() + end + + elseif event.type == 'cancel' then + self:hide() + + elseif event.type == 'refresh' then + self.allItems = Milo:listItems() + Milo:mergeResources(self.allItems) + local t = self:filterItems(self.allItems, self.filter.value) + self.grid:setValues(t) + self.grid:draw() + self.filter:focus() + else + return UI.SlideOut.eventHandler(self, event) + end + return true +end + +local exportView = UI.Window { + mtype = 'machine', + title = 'Export item into machine', + index = 3, + grid = UI.ScrollingGrid { + x = 2, ex = -6, y = 2, ey = -2, + columns = { + { heading = 'Slot', key = 'slot', width = 4 }, + { heading = 'Item', key = 'displayName' }, + }, + sortColumn = 'slot', + }, + add = UI.Button { + x = -4, y = 4, + text = '+', event = 'add_export', help = '...', + }, + remove = UI.Button { + x = -4, y = 6, + text = '-', event = 'remove_export', help = '...', + }, +} + +function exportView:save(machine) + machine.exports = not Util.empty(self.grid.values) and self.grid.values or nil + return true +end + +function exportView:setMachine(machine) + self.machine = machine + if not self.machine.exports then + self.machine.exports = { } + end + self.grid:setValues(machine.exports) +end + +function exportView.grid:getDisplayValues(row) + row = Util.shallowCopy(row) + row.displayName = itemDB:getName(row.name) + return row +end + +function exportView:eventHandler(event) + if event.type == 'grid_select' then + itemSlideout:show(self.machine, self.grid:getSelected()) + + elseif event.type == 'add_export' then + itemSlideout:show(self.machine, { }) + + elseif event.type == 'remove_export' then + local row = self.grid:getSelected() + if row then + Util.removeByValue(self.grid.values, row) + self.grid:update() + self.grid:draw() + end + end +end + +UI:getPage('machineWizard'):add({ items = itemSlideout }) +UI:getPage('machineWizard').wizard:add({ export = exportView }) diff --git a/inventoryManager/plugins/importTask.lua b/milo/plugins/importTask.lua similarity index 86% rename from inventoryManager/plugins/importTask.lua rename to milo/plugins/importTask.lua index b9fc3a3..9fd9bda 100644 --- a/inventoryManager/plugins/importTask.lua +++ b/milo/plugins/importTask.lua @@ -1,4 +1,4 @@ -local Lora = require('lora') +local Milo = require('milo') local device = _G.device @@ -8,7 +8,7 @@ local ImportTask = { function ImportTask:cycle(context) for source, v in pairs(context.config.remoteDefaults) do - if v.exports then + if v.imports then local machine = device[source] if machine and machine.getItemMeta then for slotNo in pairs(v.imports) do @@ -24,4 +24,4 @@ function ImportTask:cycle(context) end end -Lora:registerTask(ImportTask) +Milo:registerTask(ImportTask) diff --git a/inventoryManager/plugins/importView.lua b/milo/plugins/importView.lua similarity index 100% rename from inventoryManager/plugins/importView.lua rename to milo/plugins/importView.lua diff --git a/inventoryManager/plugins/inputChestTask.lua b/milo/plugins/inputChestTask.lua similarity index 88% rename from inventoryManager/plugins/inputChestTask.lua rename to milo/plugins/inputChestTask.lua index 29e5dcc..fb6072b 100644 --- a/inventoryManager/plugins/inputChestTask.lua +++ b/milo/plugins/inputChestTask.lua @@ -1,4 +1,4 @@ -local Lora = require('lora') +local Milo = require('milo') local device = _G.device @@ -21,4 +21,4 @@ function InputChest:cycle(context) end end -Lora:registerTask(InputChest) +Milo:registerTask(InputChest) diff --git a/inventoryManager/plugins/item.lua b/milo/plugins/item.lua similarity index 96% rename from inventoryManager/plugins/item.lua rename to milo/plugins/item.lua index bcbe553..c315296 100644 --- a/inventoryManager/plugins/item.lua +++ b/milo/plugins/item.lua @@ -1,12 +1,12 @@ local Ansi = require('ansi') -local Lora = require('lora') +local Milo = require('milo') local UI = require('ui') local Util = require('util') local colors = _G.colors local device = _G.device -local context = Lora:getContext() +local context = Milo:getContext() local itemPage = UI.Page { titleBar = UI.TitleBar { @@ -204,7 +204,7 @@ function itemPage:eventHandler(event) elseif event.type == 'form_complete' then local values = self.form.values - local originalKey = Lora:uniqueKey(self.item) + local originalKey = Milo:uniqueKey(self.item) local filtered = Util.shallowCopy(values) filtered.low = tonumber(filtered.low) @@ -232,10 +232,10 @@ function itemPage:eventHandler(event) filtered.ignoreNbtHash = nil end context.resources[originalKey] = nil - context.resources[Lora:uniqueKey(filtered)] = filtered + context.resources[Milo:uniqueKey(filtered)] = filtered filtered.count = nil - Lora:saveResources() + Milo:saveResources() UI:setPreviousPage() diff --git a/inventoryManager/plugins/jobList.lua b/milo/plugins/jobList.lua similarity index 81% rename from inventoryManager/plugins/jobList.lua rename to milo/plugins/jobList.lua index efba70f..a320c1d 100644 --- a/inventoryManager/plugins/jobList.lua +++ b/milo/plugins/jobList.lua @@ -1,10 +1,10 @@ -local Lora = require('lora') +local Milo = require('milo') local Peripheral = require('peripheral') local UI = require('ui') local colors = _G.colors -local context = Lora:getContext() +local context = Milo:getContext() local mon = Peripheral.lookup(context.config.monitor) or error('Monitor is not attached') local display = UI.Device { @@ -39,11 +39,11 @@ function jobList:updateList(craftList) end function jobList.grid:getRowTextColor(row, selected) - if row.statusCode == Lora.STATUS_ERROR then + if row.statusCode == Milo.STATUS_ERROR then return colors.red - elseif row.statusCode == Lora.STATUS_WARNING then + elseif row.statusCode == Milo.STATUS_WARNING then return colors.yellow - elseif row.statusCode == Lora.STATUS_INFO then + elseif row.statusCode == Milo.STATUS_INFO then return colors.lime end return UI.Grid:getRowTextColor(row, selected) @@ -58,8 +58,8 @@ local JobListTask = { } function JobListTask:cycle() - jobList:updateList(Lora:getCraftingStatus()) + jobList:updateList(Milo:getCraftingStatus()) end -Lora:registerTask(JobListTask) +Milo:registerTask(JobListTask) context.jobList = jobList diff --git a/milo/plugins/learn.lua b/milo/plugins/learn.lua new file mode 100644 index 0000000..fdbe940 --- /dev/null +++ b/milo/plugins/learn.lua @@ -0,0 +1,61 @@ +local Milo = require('milo') +local UI = require('ui') + +local context = Milo:getContext() + +local learnPage = UI.Dialog { + height = 6, width = UI.term.width - 6, + title = 'Learn Recipe', + chooser = UI.Chooser { + x = 8, y = 3, + width = 20, + }, + cancel = UI.Button { + x = 3, y = -2, + text = 'Cancel', event = 'cancel' + }, + accept = UI.Button { + ex = -3, y = -2, + width = 8, + text = 'Ok', event = 'accept', + }, +} + +function learnPage:enable() + self.chooser.choices = { } + + for k in pairs(context.learnTypes) do + table.insert(self.chooser.choices, { + name = k, + value = k, + }) + end + self.chooser.value = + Milo:getState('learnType') or + self.chooser.choices[1].value + + self:focusFirst() + UI.Dialog.enable(self) +end + +function learnPage:disable() + UI.Dialog.disable(self) +end + +function learnPage:eventHandler(event) + if event.type == 'cancel' then + UI:setPreviousPage() + + elseif event.type == 'accept' then + local choice = self.chooser.value + + Milo:setState('learnType', choice) + UI:setPage(context.learnTypes[choice]) + + else + return UI.Dialog.eventHandler(self, event) + end + return true +end + +UI:addPage('learn', learnPage) diff --git a/inventoryManager/plugins/limitTask.lua b/milo/plugins/limitTask.lua similarity index 82% rename from inventoryManager/plugins/limitTask.lua rename to milo/plugins/limitTask.lua index 82767d3..a9e123b 100644 --- a/inventoryManager/plugins/limitTask.lua +++ b/milo/plugins/limitTask.lua @@ -1,4 +1,4 @@ -local Lora = require('lora') +local Milo = require('milo') local LimitTask = { priority = 10, @@ -20,7 +20,7 @@ function LimitTask:cycle(context) for _,res in pairs(context.resources) do if res.limit then - local item = Lora:getItemWithQty(res, res.ignoreDamage, res.ignoreNbtHash) + 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 }, @@ -32,4 +32,4 @@ function LimitTask:cycle(context) end end -Lora:registerTask(LimitTask) +Milo:registerTask(LimitTask) diff --git a/inventoryManager/plugins/listing.lua b/milo/plugins/listing.lua similarity index 92% rename from inventoryManager/plugins/listing.lua rename to milo/plugins/listing.lua index 8a9aeb8..421f42d 100644 --- a/inventoryManager/plugins/listing.lua +++ b/milo/plugins/listing.lua @@ -1,16 +1,16 @@ local Craft = require('turtle.craft') local itemDB = require('itemDB') -local Lora = require('lora') +local Milo = require('milo') local UI = require('ui') local Util = require('util') local colors = _G.colors local os = _G.os -local context = Lora:getContext() +local context = Milo:getContext() local function queue(fn) - while Lora:isCraftingPaused() do + while Milo:isCraftingPaused() do os.sleep(1) end fn() @@ -120,13 +120,13 @@ debug(event) elseif event.type == 'eject' then local item = self.grid:getSelected() if item then - queue(function() Lora:eject(item, 1) end) + queue(function() Milo:eject(item, 1) end) end elseif event.type == 'eject_stack' then local item = self.grid:getSelected() if item then - queue(function() Lora:eject(item, itemDB:getMaxCount(item)) end) + queue(function() Milo:eject(item, itemDB:getMaxCount(item)) end) end elseif event.type == 'machines' then @@ -171,17 +171,17 @@ debug(event) elseif event.type == 'forget' then local item = self.grid:getSelected() if item then - local key = Lora:uniqueKey(item) + local key = Milo:uniqueKey(item) if context.userRecipes[key] then context.userRecipes[key] = nil - Util.writeTable(Lora.RECIPES_FILE, context.userRecipes) + Util.writeTable(Milo.RECIPES_FILE, context.userRecipes) Craft.loadRecipes() end if context.resources[key] then context.resources[key] = nil - Lora:saveResources() + Milo:saveResources() end self.notification:info('Forgot: ' .. item.name) @@ -211,8 +211,8 @@ function listingPage:enable() end function listingPage:refresh() - self.allItems = Lora:listItems() - Lora:mergeResources(self.allItems) + self.allItems = Milo:listItems() + Milo:mergeResources(self.allItems) self:applyFilter() end diff --git a/milo/plugins/machineLearn.lua b/milo/plugins/machineLearn.lua new file mode 100644 index 0000000..36e35df --- /dev/null +++ b/milo/plugins/machineLearn.lua @@ -0,0 +1,172 @@ +local Craft = require('turtle.craft') +local itemDB = require('itemDB') +local Milo = require('milo') +local UI = require('ui') +local Util = require('util') + +local colors = _G.colors +local device = _G.device +local turtle = _G.turtle + +local MACHINE_LOOKUP = 'usr/config/machine_crafting.db' + +local context = Milo:getContext() + +local function getTurtleInventory() + local introspectionModule = device['plethora:introspection'] or + error('Introspection module not found') + + local list = { } + for i = 1,16 do + list[i] = introspectionModule.getInventory().getItemMeta(i) + end + return list +end + +local machineLearnWizard = UI.Page { + titleBar = UI.TitleBar { title = 'Learn a crafting recipe' }, + wizard = UI.Wizard { + y = 2, ey = -2, + pages = { + machine = UI.Window { + index = 1, + grid = UI.ScrollingGrid { + y = 2, ey = -2, + values = context.config.remoteDefaults, + columns = { + { heading = 'Name', key = 'displayName' }, + }, + sortColumn = 'displayName', + }, + }, + confirmation = UI.Window { + index = 2, + notice = UI.TextArea { + x = 2, ex = -2, y = 2, ey = -2, + backgroundColor = colors.black, + value = +[[Place items in slots according to the machine's inventory. + +Place the result in the last slot of the turtle. + +Example: Slot 1 is the top slot in a furnace.]], + }, + }, + }, + }, + notification = UI.Notification { }, +} + +local pages = machineLearnWizard.wizard.pages +local machine + +function pages.machine.grid:getDisplayValues(row) + row = Util.shallowCopy(row) + row.displayName = row.displayName or row.name + return row +end + +function pages.machine:validate() + +-- TODO: validation should only be invoked when moving forward (i think) +-- TODO: index number validation in wizard + + local selected = self.grid:getSelected() + if not selected then + machineLearnWizard.notification:error('No machines configured') + return + end + + machine = device[selected.name] + if not machine then + machineLearnWizard.notification:error('Machine not found') + return + end + + if not machine.size then + machineLearnWizard.notification:error('Invalid machine') + return + end + + return true +end + +function pages.confirmation:validate() + local inventory = getTurtleInventory() + local result = inventory[16] + local slotCount = machine.size() + + inventory[16] = nil + + if not result then + machineLearnWizard.notification:error('Result must be placed in last slot') + return + end + + if Util.empty(inventory) then + machineLearnWizard.notification:error('Ingredients not present') + return + end + + for k in pairs(inventory) do + if k > slotCount then + machineLearnWizard.notification:error( + 'Slot ' .. k .. ' is not valid\nThe valid slots are 1 - ' .. machine.size()) + return + end + end + + local recipe = { + count = result.count, + ingredients = { }, + maxCount = result.maxCount ~= 64 and result.maxCount or nil, + } + + for k,v in pairs(inventory) do + recipe.ingredients[k] = Milo:uniqueKey(v) + end + + local key = Milo:uniqueKey(result) + + -- save the recipe + context.userRecipes[key] = recipe + Util.writeTable(Milo.RECIPES_FILE, context.userRecipes) + Craft.loadRecipes() + + -- save the machine association + Craft.machineLookup[key] = machine.name + Util.writeTable(MACHINE_LOOKUP, Craft.machineLookup) + + local listingPage = UI:getPage('listing') + local displayName = itemDB:getName(result) + + listingPage.statusBar.filter:setValue(displayName) + listingPage.notification:success('Learned: ' .. displayName) + listingPage.filter = displayName + listingPage:refresh() + listingPage.grid:draw() + + return true +end + +function machineLearnWizard:enable() + Milo:pauseCrafting() + UI.Page.enable(self) +end + +function machineLearnWizard:disable() + Milo:resumeCrafting() + UI.Page.disable(self) +end + +function machineLearnWizard:eventHandler(event) + if event.type == 'cancel' or event.type == 'accept' then + turtle.emptyInventory() + UI:setPage('listing') + else + return UI.Page.eventHandler(self, event) + end + return true +end + +context.learnTypes['Machine processing'] = machineLearnWizard diff --git a/milo/plugins/potionImportTask.lua b/milo/plugins/potionImportTask.lua new file mode 100644 index 0000000..68edbfb --- /dev/null +++ b/milo/plugins/potionImportTask.lua @@ -0,0 +1,24 @@ +local Milo = require('milo') + +local device = _G.device + +local PotionImportTask = { + priority = 3, +} + +function PotionImportTask:cycle(context) + for _, v in pairs(device) do + if v.type == 'minecraft:brewing_stand' and v.getBrewTime() == 0 then + local list = v.list() + if not list[4] and list[1] then + for i = 1, 3 do + if list[i] then + context.inventoryAdapter:insert(i, 1, nil, list[i], v.name) + end + end + end + end + end +end + +Milo:registerTask(PotionImportTask) diff --git a/inventoryManager/plugins/replenishTask.lua b/milo/plugins/replenishTask.lua similarity index 81% rename from inventoryManager/plugins/replenishTask.lua rename to milo/plugins/replenishTask.lua index 1568f5b..dc5616a 100644 --- a/inventoryManager/plugins/replenishTask.lua +++ b/milo/plugins/replenishTask.lua @@ -1,5 +1,5 @@ local itemDB = require('itemDB') -local Lora = require('lora') +local Milo = require('milo') local ReplenishTask = { priority = 30, @@ -10,7 +10,7 @@ function ReplenishTask:cycle(context) for _,res in pairs(context.resources) do if res.low then - local item = Lora:getItemWithQty(res, res.ignoreDamage, res.ignoreNbtHash) + local item = Milo:getItemWithQty(res, res.ignoreDamage, res.ignoreNbtHash) if not item then item = { damage = res.damage, @@ -25,7 +25,7 @@ function ReplenishTask:cycle(context) if res.ignoreDamage then item.damage = 0 end - local key = Lora:uniqueKey(res) + local key = Milo:uniqueKey(res) craftList[key] = { damage = item.damage, @@ -40,7 +40,7 @@ function ReplenishTask:cycle(context) end end - Lora:craftItems(craftList) + Milo:craftItems(craftList) end -Lora:registerTask(ReplenishTask) +Milo:registerTask(ReplenishTask) diff --git a/inventoryManager/plugins/storageView.lua b/milo/plugins/storageView.lua similarity index 100% rename from inventoryManager/plugins/storageView.lua rename to milo/plugins/storageView.lua diff --git a/milo/plugins/turtleLearn.lua b/milo/plugins/turtleLearn.lua new file mode 100644 index 0000000..6897035 --- /dev/null +++ b/milo/plugins/turtleLearn.lua @@ -0,0 +1,175 @@ +local Craft = require('turtle.craft') +local itemDB = require('itemDB') +local Milo = require('milo') +local UI = require('ui') +local Util = require('util') + +local device = _G.device +local turtle = _G.turtle + +local context = Milo:getContext() + +local function getTurtleInventory() + local introspectionModule = device['plethora:introspection'] or + error('Introspection module not found') + + local list = { } + for i = 1,16 do + list[i] = introspectionModule.getInventory().getItemMeta(i) + end + return list +end + +local function learnRecipe() + local ingredients = getTurtleInventory() + + if not ingredients then + return false, 'No recipe defined' + end + + turtle.select(1) + if not turtle.craft() then + return false, 'Failed to craft' + end + + local results = getTurtleInventory() + if not results or not results[1] then + return false, 'Failed to craft' + end + + local maxCount + local newRecipe = { + ingredients = ingredients, + } + + local numResults = 0 + for _,v in pairs(results) do + if v.count > 0 then + numResults = numResults + 1 + end + end + if numResults > 1 then + for _,v1 in pairs(results) do + for _,v2 in pairs(ingredients) do + if v1.name == v2.name and + v1.nbtHash == v2.nbtHash and + (v1.damage == v2.damage or + (v1.maxDamage > 0 and v2.maxDamage > 0 and + v1.damage ~= v2.damage)) then + if not newRecipe.crafingTools then + newRecipe.craftingTools = { } + end + local tool = Util.shallowCopy(v2) + if tool.maxDamage > 0 then + tool.damage = '*' + end + + --[[ + Turtles can only craft one item at a time using a tool :( + ]]-- + maxCount = 1 + + newRecipe.craftingTools[Milo:uniqueKey(tool)] = true + v1.craftingTool = true + break + end + end + end + end + + local recipe + for _,v in pairs(results) do + if not v.craftingTool then + recipe = v + if maxCount then + recipe.maxCount = maxCount + end + break + end + end + + if not recipe then + debug(results) + debug(newRecipe) + error('Failed - view system log') + end + + newRecipe.count = recipe.count + + local key = Milo:uniqueKey(recipe) + if recipe.maxCount ~= 64 then + newRecipe.maxCount = recipe.maxCount + end + for k,ingredient in pairs(Util.shallowCopy(ingredients)) do + if ingredient.maxDamage > 0 then + -- ingredient.damage = '*' -- I don't think this is right + end + ingredients[k] = Milo:uniqueKey(ingredient) + end + + context.userRecipes[key] = newRecipe + Util.writeTable(Milo.RECIPES_FILE, context.userRecipes) + Craft.loadRecipes() + + turtle.emptyInventory() + + return recipe +end + +local turtleLearnWizard = UI.Page { + titleBar = UI.TitleBar { title = 'Learn a crafting recipe' }, + wizard = UI.Wizard { + y = 2, ey = -3, + pages = { + confirmation = UI.Window { + index = 1, + notice = UI.TextArea { + x = 2, ex = -2, y = 2, ey = -2, + value = +[[Place recipe in turtle!]], + }, + }, + }, + }, + notification = UI.Notification { }, +} + +function turtleLearnWizard:enable() + Milo:pauseCrafting() + UI.Page.enable(self) +end + +function turtleLearnWizard:disable() + Milo:resumeCrafting() + UI.Page.disable(self) +end + +function turtleLearnWizard.wizard.pages.confirmation:validate() + local recipe, msg = learnRecipe(self) + + if recipe then + local listingPage = UI:getPage('listing') + local displayName = itemDB:getName(recipe) + + listingPage.statusBar.filter:setValue(displayName) + listingPage.notification:success('Learned: ' .. displayName) + listingPage.filter = displayName + listingPage:refresh() + listingPage.grid:draw() + + return true + else + turtleLearnWizard.notification:error(msg) + end +end + +function turtleLearnWizard:eventHandler(event) + if event.type == 'cancel' or event.type == 'accept' then + UI:setPage('listing') + else + return UI.Page.eventHandler(self, event) + end + return true +end + +context.learnTypes['Turtle crafting'] = turtleLearnWizard From e45aad7ed0b70f81e1a5e911e8d206007799c0b9 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Wed, 24 Oct 2018 06:51:05 -0400 Subject: [PATCH 022/165] milo wip --- apps/farm.lua | 2 +- apps/mirror.lua | 47 +++++++++++++++---- milo/Milo.lua | 76 ++++++++++++++++--------------- milo/apis/milo.lua | 15 +++--- milo/apis/networkedAdapter18.lua | 2 +- milo/core/machines.lua | 33 ++++++++++++-- milo/plugins/listing.lua | 5 +- milo/plugins/potionImportTask.lua | 2 +- 8 files changed, 122 insertions(+), 60 deletions(-) diff --git a/apps/farm.lua b/apps/farm.lua index 2a1988d..fcbcfdc 100644 --- a/apps/farm.lua +++ b/apps/farm.lua @@ -22,7 +22,7 @@ local crops = Util.readTable(CONFIG_FILE) or { ['minecraft:potatoes'] = { seed = 'minecraft:potato', mature = 7, action = 'plant' }, ['minecraft:beetroots'] = - { seed = 'minecraft:beetroot_seeds', mature = 3, 'plant' }, + { seed = 'minecraft:beetroot_seeds', mature = 3, action = 'plant' }, ['minecraft:reeds'] = { action = 'bash' }, ['minecraft:melon_block'] = { action = 'smash' }, ['minecraft:pumpkin'] = { action = 'smash' }, diff --git a/apps/mirror.lua b/apps/mirror.lua index f2817d4..09ea2ae 100644 --- a/apps/mirror.lua +++ b/apps/mirror.lua @@ -1,28 +1,55 @@ _G.requireInjector() local Terminal = require('terminal') +local Util = require('util') local shell = _ENV.shell local term = _G.term +local options = { + scale = { arg = 's', type = 'flag', value = false, + desc = 'Set monitor to .5 text scaling' }, + resize = { arg = 'r', type = 'flag', value = false, + desc = 'Resize terminal to monitor size' }, + execute = { arg = 'e', type = 'string', + desc = 'Execute a program' }, + monitor = { arg = 'm', type = 'string', value = 'monitor', + desc = 'Name of monitor' }, + help = { arg = 'h', type = 'flag', value = false, + desc = 'Displays the options' }, +} + local args = { ... } -local mon = _G.device[table.remove(args, 1) or 'monitor'] +if not Util.getOptions(options, args) then + return +end + +local mon = _G.device[options.monitor.value] if not mon then - error('mirror: Invalid device') + error('mirror: Invalid device') end mon.clear() -mon.setTextScale(.5) + +if options.scale.value then + mon.setTextScale(.5) +end mon.setCursorPos(1, 1) local oterm = Terminal.copy(term.current()) Terminal.mirror(term.current(), mon) -term.current().getSize = mon.getSize - -if #args > 0 then - shell.run(unpack(args)) - Terminal.copy(oterm, term.current()) - - mon.setCursorBlink(false) +if options.resize.value then + term.current().getSize = mon.getSize +end + +debug(args) +debug(options) + +if options.execute.value then + -- TODO: allow args to be passed + shell.run(options.execute.value) -- unpack(args)) + Terminal.copy(oterm, term.current()) + + mon.setCursorBlink(false) end diff --git a/milo/Milo.lua b/milo/Milo.lua index 4ed142d..5abe8a8 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -73,6 +73,7 @@ local Util = require('util') local InventoryAdapter = require('inventoryAdapter') +local device = _G.device local fs = _G.fs local multishell = _ENV.multishell local shell = _ENV.shell @@ -92,38 +93,6 @@ if not modem or not modem.getNameLocal then error('Wired modem is not connected') end -local storage = { } -for k,v in pairs(config.remoteDefaults) do - if v.mtype == 'storage' then - storage[k] = v - elseif v.mtype == 'controller' then - -- TODO: look for controller - end -end - -local inventoryAdapter = InventoryAdapter.wrap({ remoteDefaults = storage }) -if not inventoryAdapter then - error('Invalid inventory configuration') -end - --- TODO: cleanup -for _, v in pairs(modem.getNamesRemote()) do - local remote = Peripheral.get({ name = v }) - if remote.pullItems then - if not config.remoteDefaults[v] then - config.remoteDefaults[v] = { - name = v, - mtype = 'ignore', - } - else - config.remoteDefaults[v].name = v - end - if not config.remoteDefaults[v].mtype then - config.remoteDefaults[v].mtype = 'ignore' - end - end -end - local function loadResources() local resources = Util.readTable(Milo.RESOURCE_FILE) or { } for k,v in pairs(resources) do @@ -135,13 +104,50 @@ end local context = { config = config, - inventoryAdapter = inventoryAdapter, resources = loadResources(), userRecipes = Util.readTable(Milo.RECIPES_FILE) or { }, learnTypes = { }, machineTypes = { }, } +local function initStorage() + debug('Initializing storage') + local storage = { } + for k,v in pairs(config.remoteDefaults) do + if v.mtype == 'storage' and device[v.name] then + storage[k] = v + end + end +debug(storage) + context.inventoryAdapter = InventoryAdapter.wrap({ 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 +end) + +Event.on({ 'device_detach' }, function(_, dev) + debug('detach: ' .. dev) + if config.remoteDefaults[dev] and + config.remoteDefaults[dev].mtype == 'storage' then + +Milo:pauseCrafting() +debug('Crafting paused') +Milo:showError('Check log') + + initStorage() + end +end) + +initStorage() Milo:init(context) local function loadDirectory(dir) @@ -165,9 +171,7 @@ Milo:clearGrid() local page = UI:getPage('listing') UI:setPage(page) -page:setFocus(page.statusBar.filter) - --- TODO: Event.on('device_detach', function() end) +page:setFocus(page.statusBar.filter) -- todo: move this line into listing code Event.onInterval(5, function() if not Milo:isCraftingPaused() then diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index e453a9b..01957bc 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -29,6 +29,7 @@ end function Milo:pauseCrafting() self.craftingPaused = true + Milo:showError('Crafting Paused') end function Milo:resumeCrafting() @@ -80,12 +81,12 @@ function Milo:registerTask(task) end function Milo:showError(msg) - term.clear() - self.context.jobList:showError() - print(msg) - print('rebooting in 5 secs') - os.sleep(5) - os.reboot() + --term.clear() + self.context.jobList:showError(msg) + --print(msg) + --print('rebooting in 5 secs') + --os.sleep(5) + --os.reboot() end function Milo:getItem(items, inItem, ignoreDamage, ignoreNbtHash) @@ -381,7 +382,7 @@ function Milo:craftItems(craftList) end self:updateCraftingStatus(craftList) for _,v in pairs(craftList) do - debug(v) + --debug(v) end end diff --git a/milo/apis/networkedAdapter18.lua b/milo/apis/networkedAdapter18.lua index 1313c15..4324a00 100644 --- a/milo/apis/networkedAdapter18.lua +++ b/milo/apis/networkedAdapter18.lua @@ -64,7 +64,7 @@ function NetworkedAdapter:listItems(throttle) return self.items end self.listCount = self.listCount + 1 -debug(self.listCount) +--debug(self.listCount) -- todo: only listItems from dirty remotes -- todo: better handling of empty inventories diff --git a/milo/core/machines.lua b/milo/core/machines.lua index ada5f32..64afc55 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -1,4 +1,5 @@ local Config = require('config') +local Event = require('event') local itemDB = require('itemDB') local Milo = require('milo') local UI = require('ui') @@ -29,9 +30,35 @@ local machinesPage = UI.Page { }, } +function machinesPage:getList() + -- TODO: remove dedupe naming in perf code ? + for _, v in pairs(device) do + if v.pullItems then + if not context.config.remoteDefaults[v.name] then + context.config.remoteDefaults[v.name] = { + name = v.name, + mtype = 'ignore', + } + end + end + end +end + function machinesPage:enable() + self:getList() self.grid:update() UI.Page.enable(self) + self.handler = Event.on({ 'device_attach', 'device_detach'}, function() + self:getList() + self.grid:update() + self.grid:draw() + self.grid:sync() + end) +end + +function machinesPage:disable() + UI.Page.disable(self) + Event.off(self.handler) end function machinesPage.grid:getDisplayValues(row) @@ -41,6 +68,9 @@ function machinesPage.grid:getDisplayValues(row) end function machinesPage.grid:getRowTextColor(row, selected) + if not device[row.name] then + return colors.red + end if row.mtype == 'ignore' then return colors.lightGray end @@ -205,9 +235,6 @@ function machineWizard:eventHandler(event) UI:setPreviousPage() - elseif event.type == 'collapse' then - self.items:hide() - elseif event.type == 'enable_view' then local current = event.next or event.prev self.titleBar.title = current.title or 'Machine' diff --git a/milo/plugins/listing.lua b/milo/plugins/listing.lua index 421f42d..50688c0 100644 --- a/milo/plugins/listing.lua +++ b/milo/plugins/listing.lua @@ -82,6 +82,7 @@ local listingPage = UI.Page { [ 'control-e' ] = 'eject', [ 'control-s' ] = 'eject_stack', [ 'control-m' ] = 'machines', + [ 'control-l' ] = 'resume', }, displayMode = 0, } @@ -113,10 +114,12 @@ function listingPage.grid:getDisplayValues(row) end function listingPage:eventHandler(event) -debug(event) if event.type == 'quit' then UI:exitPullEvents() + elseif event.type == 'resume' then + Milo:resumeCrafting() + elseif event.type == 'eject' then local item = self.grid:getSelected() if item then diff --git a/milo/plugins/potionImportTask.lua b/milo/plugins/potionImportTask.lua index 68edbfb..eb0db7b 100644 --- a/milo/plugins/potionImportTask.lua +++ b/milo/plugins/potionImportTask.lua @@ -10,7 +10,7 @@ function PotionImportTask:cycle(context) for _, v in pairs(device) do if v.type == 'minecraft:brewing_stand' and v.getBrewTime() == 0 then local list = v.list() - if not list[4] and list[1] then + if not list[4] then for i = 1, 3 do if list[i] then context.inventoryAdapter:insert(i, 1, nil, list[i], v.name) From 27f38032ca9a10db1e6191b77b68fdab2b12de67 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Wed, 24 Oct 2018 06:52:46 -0400 Subject: [PATCH 023/165] milo wip --- apps/debug.lua | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 apps/debug.lua diff --git a/apps/debug.lua b/apps/debug.lua new file mode 100644 index 0000000..8d11c02 --- /dev/null +++ b/apps/debug.lua @@ -0,0 +1,20 @@ + +local mon = device.monitor_1 +mon.clear() +mon.setTextScale(.5) +_G.requireInjector(_ENV) + +local Util = require('util') + +mon.setCursorPos(1, 1) + +local oldDebug = _G.debug + +_G.debug = function(...) + local oldTerm = term.redirect(mon) + Util.print(...) + term.redirect(oldTerm) +end + +pcall(read) +_G.debug = oldDebug From eb3cdad008475cc7e319bc01cb5c77ca7029732a Mon Sep 17 00:00:00 2001 From: kepler155c Date: Wed, 24 Oct 2018 08:24:44 -0400 Subject: [PATCH 024/165] milo wip --- milo/plugins/exportTask.lua | 15 +++++++++++++-- milo/plugins/listing.lua | 10 ++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/milo/plugins/exportTask.lua b/milo/plugins/exportTask.lua index 1e645f0..e06a102 100644 --- a/milo/plugins/exportTask.lua +++ b/milo/plugins/exportTask.lua @@ -16,9 +16,20 @@ function ExportTask:cycle(context) local slot = machine.getItemMeta(entry.slot) or { count = 0 } local maxCount = slot.maxCount or itemDB:getMaxCount(entry.name) local count = maxCount - slot.count + + -- something else is in this slot + if slot.count > 0 and slot.name ~= entry.name then + count = 0 + end if count > 0 then - context.inventoryAdapter:provide( - itemDB:splitKey(entry.name), count, entry.slot, target) + local item = Milo:getItemWithQty(entry) + if item.count > 0 then + context.inventoryAdapter:provide( + itemDB:splitKey(entry.name), + math.min(count, item.count), + entry.slot, + target) + end end end else diff --git a/milo/plugins/listing.lua b/milo/plugins/listing.lua index 50688c0..1e7e33a 100644 --- a/milo/plugins/listing.lua +++ b/milo/plugins/listing.lua @@ -81,6 +81,16 @@ local listingPage = UI.Page { q = 'quit', [ 'control-e' ] = 'eject', [ 'control-s' ] = 'eject_stack', + [ 'control-1' ] = 'eject_1', + [ 'control-2' ] = 'eject_1', + [ 'control-3' ] = 'eject_1', + [ 'control-4' ] = 'eject_1', + [ 'control-5' ] = 'eject_1', + [ 'control-6' ] = 'eject_1', + [ 'control-7' ] = 'eject_1', + [ 'control-8' ] = 'eject_1', + [ 'control-9' ] = 'eject_1', + [ 'control-0' ] = 'eject_1', [ 'control-m' ] = 'machines', [ 'control-l' ] = 'resume', }, From 55653aa4948c93cd09b8c5ebd21b17cea6db723f Mon Sep 17 00:00:00 2001 From: kepler155c Date: Wed, 24 Oct 2018 09:00:06 -0400 Subject: [PATCH 025/165] milo wip --- milo/plugins/exportTask.lua | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/milo/plugins/exportTask.lua b/milo/plugins/exportTask.lua index e06a102..c285dec 100644 --- a/milo/plugins/exportTask.lua +++ b/milo/plugins/exportTask.lua @@ -14,21 +14,22 @@ function ExportTask:cycle(context) if machine and machine.getItemMeta then for _, entry in pairs(v.exports) do local slot = machine.getItemMeta(entry.slot) or { count = 0 } - local maxCount = slot.maxCount or itemDB:getMaxCount(entry.name) - local count = maxCount - slot.count + local item = itemDB:splitKey(entry.name) - -- something else is in this slot - if slot.count > 0 and slot.name ~= entry.name then - count = 0 - end - if count > 0 then - local item = Milo:getItemWithQty(entry) - if item.count > 0 then - context.inventoryAdapter:provide( - itemDB:splitKey(entry.name), - math.min(count, item.count), - entry.slot, - target) + -- is something else is in this slot + if not slot.name or slot.name == item.name then + local maxCount = slot.maxCount or itemDB:getMaxCount(item) + local count = maxCount - slot.count + + if count > 0 then + item = Milo:getItemWithQty(item) + if item and count > 0 then + context.inventoryAdapter:provide( + itemDB:splitKey(entry.name), + math.min(count, item.count), + entry.slot, + target) + end end end end From 3687df3d3661cf3f3f56473ac5ce288a924152c0 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Wed, 24 Oct 2018 19:12:03 -0400 Subject: [PATCH 026/165] milo wip --- apps/debug.lua | 31 +++-- milo/Milo.lua | 37 ++++-- milo/MiloRemote.lua | 193 +++++++++++++++++++++++++++++++ milo/apis/milo.lua | 6 - milo/apis/networkedAdapter18.lua | 8 +- milo/core/machines.lua | 12 +- milo/plugins/exportTask.lua | 2 +- milo/plugins/exportView.lua | 19 ++- milo/plugins/jobList.lua | 10 +- milo/plugins/listing.lua | 1 + milo/plugins/remote.lua | 52 +++++++++ 11 files changed, 333 insertions(+), 38 deletions(-) create mode 100644 milo/MiloRemote.lua create mode 100644 milo/plugins/remote.lua diff --git a/apps/debug.lua b/apps/debug.lua index 8d11c02..78b032e 100644 --- a/apps/debug.lua +++ b/apps/debug.lua @@ -1,20 +1,33 @@ - -local mon = device.monitor_1 -mon.clear() -mon.setTextScale(.5) _G.requireInjector(_ENV) local Util = require('util') +local device = _G.device +local os = _G.os +local term = _G.term + +local args = { ... } +local mon = device[args[1] or 'monitor'] or error('Syntax: debug ') + +mon.clear() +mon.setTextScale(.5) mon.setCursorPos(1, 1) local oldDebug = _G.debug _G.debug = function(...) - local oldTerm = term.redirect(mon) - Util.print(...) - term.redirect(oldTerm) + local oldTerm = term.redirect(mon) + Util.print(...) + term.redirect(oldTerm) end - -pcall(read) + +repeat + local e, side = os.pullEventRaw('monitor_touch') + if e == 'monitor_touch' and side == mon.side then + mon.clear() + mon.setTextScale(.5) + mon.setCursorPos(1, 1) + end +until e == 'terminate' + _G.debug = oldDebug diff --git a/milo/Milo.lua b/milo/Milo.lua index 5abe8a8..a6478a4 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -61,7 +61,7 @@ replenish autocraft ]] -_G.requireInjector() +_G.requireInjector(_ENV) local Config = require('config') local Event = require('event') @@ -108,17 +108,39 @@ local context = { userRecipes = Util.readTable(Milo.RECIPES_FILE) or { }, learnTypes = { }, machineTypes = { }, + localName = modem.getNameLocal(), } -local function initStorage() +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' and device[v.name] then - storage[k] = v + 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 + context.inventoryAdapter = InventoryAdapter.wrap({ remoteDefaults = storage }) if not context.inventoryAdapter then @@ -138,12 +160,7 @@ Event.on({ 'device_detach' }, function(_, dev) debug('detach: ' .. dev) if config.remoteDefaults[dev] and config.remoteDefaults[dev].mtype == 'storage' then - -Milo:pauseCrafting() -debug('Crafting paused') -Milo:showError('Check log') - - initStorage() + initStorage(dev) end end) diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua new file mode 100644 index 0000000..8ef6989 --- /dev/null +++ b/milo/MiloRemote.lua @@ -0,0 +1,193 @@ +_G.requireInjector(_ENV) + +local Socket = require('socket') +local UI = require('ui') +local Util = require('util') + +local colors = _G.colors + +local socket, msg = Socket.connect(1, 4242) +if not socket then + error(msg) +end + +local page = UI.Page { + menuBar = UI.MenuBar { + buttons = { + { text = 'Craft', event = 'craft' }, + { text = 'Refresh', event = 'refresh', x = -9 }, + }, + }, + grid = UI.Grid { + y = 2, ey = -2, + columns = { + { heading = ' Qty', key = 'count' , width = 4, justify = 'right' }, + { heading = 'Name', key = 'displayName' }, + }, + sortColumn = 'displayName', + }, + statusBar = UI.StatusBar { + filter = UI.TextEntry { + x = 1, ex = -4, + limit = 50, + shadowText = 'filter', + shadowTextColor = colors.gray, + backgroundColor = colors.cyan, + backgroundFocusColor = colors.cyan, + accelerators = { + [ 'enter' ] = 'craft', + }, + }, + display = UI.Button { + x = -3, + event = 'toggle_display', + value = 0, + text = 'A', + }, + }, + notification = UI.Notification(), + accelerators = { + r = 'refresh', + q = 'quit', + [ 'control-e' ] = 'eject', + [ 'control-r' ] = 'refresh', + [ 'control-s' ] = 'eject_stack', + [ 'control-1' ] = 'eject_1', + [ 'control-2' ] = 'eject_1', + [ 'control-3' ] = 'eject_1', + [ 'control-4' ] = 'eject_1', + [ 'control-5' ] = 'eject_1', + [ 'control-6' ] = 'eject_1', + [ 'control-7' ] = 'eject_1', + [ 'control-8' ] = 'eject_1', + [ 'control-9' ] = 'eject_1', + [ 'control-0' ] = 'eject_1', + [ 'control-m' ] = 'machines', + [ 'control-l' ] = 'resume', + }, + displayMode = 0, +} + +local function filterItems(t, filter, displayMode) + if filter or displayMode > 0 then + local r = { } + if filter then + filter = filter:lower() + end + for _,v in pairs(t) do + if not filter or string.find(v.lname, filter, 1, true) then + if not displayMode or + displayMode == 0 or + displayMode == 1 and v.count > 0 or + displayMode == 2 and v.has_recipe then + table.insert(r, v) + end + end + end + return r + end + return t +end + +function page.statusBar:draw() + return UI.Window.draw(self) +end + +function page.grid:getRowTextColor(row, selected) + if row.is_craftable then + return colors.yellow + end + if row.has_recipe then + return colors.cyan + end + return UI.Grid:getRowTextColor(row, selected) +end + +function page.grid:getDisplayValues(row) + row = Util.shallowCopy(row) + row.count = row.count > 0 and Util.toBytes(row.count) or '' + if row.low then + row.low = Util.toBytes(row.low) + end + if row.limit then + row.limit = Util.toBytes(row.limit) + end + return row +end + +function page:eventHandler(event) + if event.type == 'quit' then + UI:exitPullEvents() + + elseif event.type == 'eject' then + local item = self.grid:getSelected() + if item then + socket:write({ request = 'transfer', item = item, count = 1 }) + end + + elseif event.type == 'eject_stack' then + local item = self.grid:getSelected() + if item then + socket:write({ request = 'transfer', item = item, count = 64 }) + end + + elseif event.type == 'refresh' then + self:refresh() + self.grid:draw() + self.statusBar.filter:focus() + + elseif event.type == 'toggle_display' then + local values = { + [0] = 'A', + [1] = 'I', + [2] = 'C', + } + + event.button.value = (event.button.value + 1) % 3 + self.displayMode = event.button.value + event.button.text = values[event.button.value] + event.button:draw() + self:applyFilter() + self.grid:draw() + + elseif event.type == 'text_change' then + self.filter = event.text + if #self.filter == 0 then + self.filter = nil + end + self:applyFilter() + self.grid:draw() + self.statusBar.filter:focus() + + else + UI.Page.eventHandler(self, event) + end + return true +end + +function page:enable() + self:refresh() + self:setFocus(self.statusBar.filter) + UI.Page.enable(self) +end + +function page:refresh() + socket:write({ request = 'list' }) + self.items = socket:read() + + if not self.items then + UI:exitPullEvents() + else + self:applyFilter() + end +end + +function page:applyFilter() + local t = filterItems(self.items, self.filter, self.displayMode) + self.grid:setValues(t) +end + +UI:setPage(page) +UI:pullEvents() + +socket:close() diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index 01957bc..f506a3b 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -4,7 +4,6 @@ local itemDB = require('itemDB') local Util = require('util') local os = _G.os -local term = _G.term local turtle = _G.turtle local Milo = { @@ -81,12 +80,7 @@ function Milo:registerTask(task) end function Milo:showError(msg) - --term.clear() self.context.jobList:showError(msg) - --print(msg) - --print('rebooting in 5 secs') - --os.sleep(5) - --os.reboot() end function Milo:getItem(items, inItem, ignoreDamage, ignoreNbtHash) diff --git a/milo/apis/networkedAdapter18.lua b/milo/apis/networkedAdapter18.lua index 4324a00..54546c5 100644 --- a/milo/apis/networkedAdapter18.lua +++ b/milo/apis/networkedAdapter18.lua @@ -118,9 +118,11 @@ function NetworkedAdapter:provide(item, qty, slot, direction) local total = 0 for _, remote in ipairs(self.remotes) do -debug('%s -> slot %d: %d %s', remote.side, slot or -1, qty, item.name) local amount = remote:provide(item, qty, slot, direction) if amount > 0 then +debug('%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 local entry = self.activity[key] or 0 @@ -170,7 +172,9 @@ function NetworkedAdapter:insert(slot, qty, toSlot, item, source) local function insert(remote) local amount = remote:insert(slot, qty, toSlot, source or self.direction) if amount > 0 then -debug('%s(%d) -> %s: %d', source or self.localName, slot, remote.side, amount) +debug('%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 diff --git a/milo/core/machines.lua b/milo/core/machines.lua index 64afc55..18d03b5 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -230,7 +230,17 @@ function machineWizard:eventHandler(event) v:save(self.machine) end end - context.config.remoteDefaults[self.machine.name] = self.machine + context.config.remoteDefaults[self.machine.name] = + Util.prune(self.machine, function(v) + if type(v) == 'boolean' then + return v + elseif type(v) == 'string' then + return #v > 0 + elseif type(v) == 'table' then + return not Util.empty(v) + end + return true + end) Config.update('milo', context.config) UI:setPreviousPage() diff --git a/milo/plugins/exportTask.lua b/milo/plugins/exportTask.lua index c285dec..4e25ee9 100644 --- a/milo/plugins/exportTask.lua +++ b/milo/plugins/exportTask.lua @@ -25,7 +25,7 @@ function ExportTask:cycle(context) item = Milo:getItemWithQty(item) if item and count > 0 then context.inventoryAdapter:provide( - itemDB:splitKey(entry.name), + item, math.min(count, item.count), entry.slot, target) diff --git a/milo/plugins/exportView.lua b/milo/plugins/exportView.lua index 3d01297..6bd20da 100644 --- a/milo/plugins/exportView.lua +++ b/milo/plugins/exportView.lua @@ -92,9 +92,9 @@ function itemSlideout.grid:enable() UI.Grid.enable(self) end -function itemSlideout:show(machine, entry) - self.machine = machine +function itemSlideout:show(machine, entry, callback) self.entry = entry + self.callback = callback self.form.choices = { } local m = device[machine.name] @@ -131,8 +131,9 @@ function itemSlideout:eventHandler(event) else self.form:save() self.form.values.name = itemDB:makeKey(selected) - table.insert(self.machine.exports, self.form.values) self:hide() + debug('calling cllbck') + self.callback() end elseif event.type == 'cancel' then @@ -194,10 +195,18 @@ end function exportView:eventHandler(event) if event.type == 'grid_select' then - itemSlideout:show(self.machine, self.grid:getSelected()) + itemSlideout:show(self.machine, self.grid:getSelected(), function() + self.grid:update() + self.grid:draw() + end) elseif event.type == 'add_export' then - itemSlideout:show(self.machine, { }) + local export = { } + itemSlideout:show(self.machine, export, function() + table.insert(self.machine.exports, export) + self.grid:update() + self.grid:draw() + end) elseif event.type == 'remove_export' then local row = self.grid:getSelected() diff --git a/milo/plugins/jobList.lua b/milo/plugins/jobList.lua index a320c1d..4b233e8 100644 --- a/milo/plugins/jobList.lua +++ b/milo/plugins/jobList.lua @@ -32,10 +32,12 @@ function jobList:showError(msg) end function jobList:updateList(craftList) - self.grid:setValues(craftList) - self.grid:update() - self:draw() - self:sync() + if not Milo:isCraftingPaused() then + self.grid:setValues(craftList) + self.grid:update() + self:draw() + self:sync() + end end function jobList.grid:getRowTextColor(row, selected) diff --git a/milo/plugins/listing.lua b/milo/plugins/listing.lua index 1e7e33a..c59f7ac 100644 --- a/milo/plugins/listing.lua +++ b/milo/plugins/listing.lua @@ -78,6 +78,7 @@ local listingPage = UI.Page { notification = UI.Notification(), accelerators = { r = 'refresh', + [ 'control-r' ] = 'refresh', q = 'quit', [ 'control-e' ] = 'eject', [ 'control-s' ] = 'eject_stack', diff --git a/milo/plugins/remote.lua b/milo/plugins/remote.lua new file mode 100644 index 0000000..ca3c368 --- /dev/null +++ b/milo/plugins/remote.lua @@ -0,0 +1,52 @@ +local Event = require('event') +local Milo = require('milo') +local Socket = require('socket') + +local device = _G.device +local manipulator = device.manipulator_1 +local turtle = _G.turtle + +local context = Milo:getContext() + +local function client(socket) + repeat + local data = socket:read() + if not data then + break + end + if data.request == 'list' then + local items = Milo:listItems() + Milo:mergeResources(items) + socket:write(items) + + elseif data.request == 'transfer' then + context.inventoryAdapter:provide( + data.item, + data.count, + nil, + context.localName) + + turtle.eachFilledSlot(function(slot) + manipulator.getInventory().pullItems( + context.localName, + slot.index, + slot.count) + end) + end + until not socket.connected +end + +if device.wireless_modem then + Event.addRoutine(function() + debug('Milo: listening on port 4242') + while true do + local socket = Socket.server(4242) + + debug('connection from ' .. socket.dhost) + + Event.addRoutine(function() + client(socket) + end) + end + end) +end From d5b0ad73b939747cfa97f0d7c98acf861412e459 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Wed, 24 Oct 2018 19:52:27 -0400 Subject: [PATCH 027/165] milo wip --- milo/MiloRemote.lua | 52 ++++++++++++++++++++++++++++++----------- milo/plugins/remote.lua | 11 ++++++--- 2 files changed, 47 insertions(+), 16 deletions(-) diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index 8ef6989..a0a555e 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -5,11 +5,7 @@ local UI = require('ui') local Util = require('util') local colors = _G.colors - -local socket, msg = Socket.connect(1, 4242) -if not socket then - error(msg) -end +local socket local page = UI.Page { menuBar = UI.MenuBar { @@ -89,6 +85,26 @@ local function filterItems(t, filter, displayMode) return t end +function page:sendRequest(data) + local msg + + for _ = 1, 2 do + if not socket or not socket.connected then + socket, msg = Socket.connect(1, 4242) + end + if socket then + if socket:write(data) then + local response = socket:read(2) + if response then + return response + end + end + socket:close() + end + end + self.notification:error(msg or 'Failed to connect') +end + function page.statusBar:draw() return UI.Window.draw(self) end @@ -122,13 +138,23 @@ function page:eventHandler(event) elseif event.type == 'eject' then local item = self.grid:getSelected() if item then - socket:write({ request = 'transfer', item = item, count = 1 }) + local items = self:sendRequest({ request = 'transfer', item = item, count = 1 }) + if items then + self.items = items + self:applyFilter() + self.grid:draw() + end end elseif event.type == 'eject_stack' then local item = self.grid:getSelected() if item then - socket:write({ request = 'transfer', item = item, count = 64 }) + local items = self:sendRequest({ request = 'transfer', item = item, count = 64 }) + if items then + self.items = items + self:applyFilter() + self.grid:draw() + end end elseif event.type == 'refresh' then @@ -172,12 +198,10 @@ function page:enable() end function page:refresh() - socket:write({ request = 'list' }) - self.items = socket:read() + local items = self:sendRequest({ request = 'list' }) - if not self.items then - UI:exitPullEvents() - else + if items then + self.items = items self:applyFilter() end end @@ -190,4 +214,6 @@ end UI:setPage(page) UI:pullEvents() -socket:close() +if socket then + socket:close() +end diff --git a/milo/plugins/remote.lua b/milo/plugins/remote.lua index ca3c368..e4bfcf5 100644 --- a/milo/plugins/remote.lua +++ b/milo/plugins/remote.lua @@ -9,6 +9,8 @@ local turtle = _G.turtle local context = Milo:getContext() local function client(socket) + debug('connection from ' .. socket.dhost) + repeat local data = socket:read() if not data then @@ -32,8 +34,14 @@ local function client(socket) slot.index, slot.count) end) + + local items = Milo:listItems() + Milo:mergeResources(items) + socket:write(items) end until not socket.connected + + debug('disconnected from ' .. socket.dhost) end if device.wireless_modem then @@ -41,9 +49,6 @@ if device.wireless_modem then debug('Milo: listening on port 4242') while true do local socket = Socket.server(4242) - - debug('connection from ' .. socket.dhost) - Event.addRoutine(function() client(socket) end) From 6ce4039e8ad6881dff157ad786a29e757b9ae970 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Thu, 25 Oct 2018 00:18:38 -0400 Subject: [PATCH 028/165] milo wip --- milo/Milo.lua | 5 +++ milo/MiloMonitor.lua | 62 +++++++++++++++++++++++++++++++ milo/MiloRemote.lua | 26 ++++++++++++- milo/plugins/autocraftTask.lua | 1 + milo/plugins/demandCraft.lua | 3 +- milo/plugins/exportTask.lua | 3 +- milo/plugins/importTask.lua | 3 +- milo/plugins/inputChestTask.lua | 3 +- milo/plugins/jobList.lua | 1 + milo/plugins/limitTask.lua | 3 +- milo/plugins/potionImportTask.lua | 3 +- milo/plugins/remote.lua | 24 ++++++++++-- milo/plugins/replenishTask.lua | 3 +- 13 files changed, 129 insertions(+), 11 deletions(-) create mode 100644 milo/MiloMonitor.lua diff --git a/milo/Milo.lua b/milo/Milo.lua index a6478a4..8e26af9 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -184,6 +184,11 @@ table.sort(Milo.tasks, function(a, b) return a.priority < b.priority end) +debug('Tasks\n-----') +for _, task in ipairs(Milo.tasks) do + debug('%d: %s', task.priority, task.name) +end + Milo:clearGrid() local page = UI:getPage('listing') diff --git a/milo/MiloMonitor.lua b/milo/MiloMonitor.lua new file mode 100644 index 0000000..c36d4f2 --- /dev/null +++ b/milo/MiloMonitor.lua @@ -0,0 +1,62 @@ +_G.requireInjector(_ENV) + +local Util = require('util') + +local colors = _G.colors +local device = _G.device + +local args = { ... } +local mon = device[args[1] or 'monitor'] or error('Syntax: debug ') +local config = Util.readTable('/usr/config/milo') or error('Milo is not configured') + +local row +local monWidth, monHeight = mon.getSize() +local machines = { } + +local function write(x, y, s, bg, fg) + mon.setCursorPos(x, y) + mon.setBackgroundColor(bg) + if fg then + mon.setTextColor(fg) + end + mon.write(s) +end + +local function progress(y, percent) + local width = math.ceil(percent / 100 * monWidth) + write(2, y, string.rep(' ', monWidth - 2), colors.gray) + write(2, y, string.rep(' ', width), colors.lime) +end + +local function draw(machine, percent) + write(2, row, machine.displayName or machine.name, colors.black, colors.yellow) + progress(row + 1, percent) + row = row + 3 +end + +local function redraw() + row = 1 + mon.setBackgroundColor(colors.black) + mon.clear() + for _,machine in ipairs(machines) do + local dev = device[machine.name] + if dev then + local percent = 50 + if machine.mtype == 'storage' then + percent = Util.size(dev.list()) / dev.size() * 100 + end + draw(machine, percent) + end + end +end + +for _, v in pairs(config.remoteDefaults) do + table.insert(machines, v) +end + +table.sort(machines, function(a, b) + return (a.displayName or a.name) < (b.displayName or b.name) +end) + +mon.setTextScale(.5) +redraw() diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index a0a555e..cd9403d 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -7,6 +7,27 @@ local Util = require('util') local colors = _G.colors local socket +local options = { + user = { arg = 'u', type = 'string', + desc = 'User name associated with bound manipulator' }, + server = { arg = 's', type = 'number', + desc = 'ID of Milo server' }, + help = { arg = 'h', type = 'flag', value = false, + desc = 'Displays the options' }, +} + +local args = { ... } +if not Util.getOptions(options, args) then + print() + error('Invalid arguments') +end + +if not options.user.value or not options.server.value then + Util.showOptions(options) + print() + error('Invalid arguments') +end + local page = UI.Page { menuBar = UI.MenuBar { buttons = { @@ -90,7 +111,10 @@ function page:sendRequest(data) for _ = 1, 2 do if not socket or not socket.connected then - socket, msg = Socket.connect(1, 4242) + socket, msg = Socket.connect(options.server.value, 4242) + if socket then + socket:write(options.user.value) + end end if socket then if socket:write(data) then diff --git a/milo/plugins/autocraftTask.lua b/milo/plugins/autocraftTask.lua index caf83a5..fd59616 100644 --- a/milo/plugins/autocraftTask.lua +++ b/milo/plugins/autocraftTask.lua @@ -2,6 +2,7 @@ local Milo = require('milo') local Util = require('util') local Autocraft = { + name = 'autocraft', priority = 100, } diff --git a/milo/plugins/demandCraft.lua b/milo/plugins/demandCraft.lua index c30f2cf..fbb5d7c 100644 --- a/milo/plugins/demandCraft.lua +++ b/milo/plugins/demandCraft.lua @@ -132,7 +132,8 @@ function craftPage:eventHandler(event) end local demandCraftingTask = { - priority = 20, + name = 'demand crafting', + priority = 60, } function demandCraftingTask:cycle(context) diff --git a/milo/plugins/exportTask.lua b/milo/plugins/exportTask.lua index 4e25ee9..a655ad6 100644 --- a/milo/plugins/exportTask.lua +++ b/milo/plugins/exportTask.lua @@ -4,7 +4,8 @@ local Milo = require('milo') local device = _G.device local ExportTask = { - priority = 5, + name = 'exporter', + priority = 40, } function ExportTask:cycle(context) diff --git a/milo/plugins/importTask.lua b/milo/plugins/importTask.lua index 9fd9bda..0bcccf2 100644 --- a/milo/plugins/importTask.lua +++ b/milo/plugins/importTask.lua @@ -3,7 +3,8 @@ local Milo = require('milo') local device = _G.device local ImportTask = { - priority = 3, + name = 'importer', + priority = 20, } function ImportTask:cycle(context) diff --git a/milo/plugins/inputChestTask.lua b/milo/plugins/inputChestTask.lua index fb6072b..529f6f9 100644 --- a/milo/plugins/inputChestTask.lua +++ b/milo/plugins/inputChestTask.lua @@ -3,7 +3,8 @@ local Milo = require('milo') local device = _G.device local InputChest = { - priority = 1, + name = 'input', + priority = 10, } function InputChest:cycle(context) diff --git a/milo/plugins/jobList.lua b/milo/plugins/jobList.lua index 4b233e8..6e0f82b 100644 --- a/milo/plugins/jobList.lua +++ b/milo/plugins/jobList.lua @@ -56,6 +56,7 @@ jobList:draw() jobList:sync() local JobListTask = { + name = 'job status', priority = 80, } diff --git a/milo/plugins/limitTask.lua b/milo/plugins/limitTask.lua index a9e123b..f86c317 100644 --- a/milo/plugins/limitTask.lua +++ b/milo/plugins/limitTask.lua @@ -1,7 +1,8 @@ local Milo = require('milo') local LimitTask = { - priority = 10, + name = 'limiter', + priority = 50, } function LimitTask:cycle(context) diff --git a/milo/plugins/potionImportTask.lua b/milo/plugins/potionImportTask.lua index eb0db7b..ba42cae 100644 --- a/milo/plugins/potionImportTask.lua +++ b/milo/plugins/potionImportTask.lua @@ -3,7 +3,8 @@ local Milo = require('milo') local device = _G.device local PotionImportTask = { - priority = 3, + name = 'potions', + priority = 30, } function PotionImportTask:cycle(context) diff --git a/milo/plugins/remote.lua b/milo/plugins/remote.lua index e4bfcf5..b0b0088 100644 --- a/milo/plugins/remote.lua +++ b/milo/plugins/remote.lua @@ -2,15 +2,32 @@ local Event = require('event') local Milo = require('milo') local Socket = require('socket') -local device = _G.device -local manipulator = device.manipulator_1 -local turtle = _G.turtle +local device = _G.device +local turtle = _G.turtle local context = Milo:getContext() +local function getManipulatorForUser(user) + for _,v in pairs(device) do + if v.type == 'manipulator' and v.getName and v.getName() == user then + return v + end + end +end + local function client(socket) debug('connection from ' .. socket.dhost) + local user = socket:read(2) + if not user then + return + end + + local manipulator = getManipulatorForUser(user) + if not manipulator then + return + end + repeat local data = socket:read() if not data then @@ -51,6 +68,7 @@ if device.wireless_modem then local socket = Socket.server(4242) Event.addRoutine(function() client(socket) + socket:close() end) end end) diff --git a/milo/plugins/replenishTask.lua b/milo/plugins/replenishTask.lua index dc5616a..729a83c 100644 --- a/milo/plugins/replenishTask.lua +++ b/milo/plugins/replenishTask.lua @@ -2,7 +2,8 @@ local itemDB = require('itemDB') local Milo = require('milo') local ReplenishTask = { - priority = 30, + name = 'replenish', + priority = 70, } function ReplenishTask:cycle(context) From 9f9dcafc607800bbf1d3abcfedb5ddf16af51998 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Thu, 25 Oct 2018 05:51:46 -0400 Subject: [PATCH 029/165] milo wip --- milo/Milo.lua | 8 +- milo/apis/milo.lua | 224 ++++--------------------------- milo/apis/networkedAdapter18.lua | 6 +- milo/apis/turtle/craft.lua | 103 ++++++++++---- milo/plugins/craftTask.lua | 141 +++++++++++++++++++ milo/plugins/demandCraft.lua | 57 +------- milo/plugins/jobList.lua | 25 +++- milo/plugins/replenishTask.lua | 14 +- 8 files changed, 275 insertions(+), 303 deletions(-) create mode 100644 milo/plugins/craftTask.lua diff --git a/milo/Milo.lua b/milo/Milo.lua index 8e26af9..1f1e736 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -109,6 +109,8 @@ local context = { learnTypes = { }, machineTypes = { }, localName = modem.getNameLocal(), + tasks = { }, + craftingQueue = { }, } local function initStorage(detachedDevice) @@ -180,12 +182,12 @@ local programDir = fs.getDir(shell.getRunningProgram()) loadDirectory(fs.combine(programDir, 'core')) loadDirectory(fs.combine(programDir, 'plugins')) -table.sort(Milo.tasks, function(a, b) +table.sort(context.tasks, function(a, b) return a.priority < b.priority end) debug('Tasks\n-----') -for _, task in ipairs(Milo.tasks) do +for _, task in ipairs(context.tasks) do debug('%d: %s', task.priority, task.name) end @@ -200,7 +202,7 @@ Event.onInterval(5, function() Milo:resetCraftingStatus() context.inventoryAdapter:refresh() - for _, task in ipairs(Milo.tasks) do + 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) diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index f506a3b..6c18d5e 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -3,7 +3,6 @@ local Craft = require('turtle.craft') local itemDB = require('itemDB') local Util = require('util') -local os = _G.os local turtle = _G.turtle local Milo = { @@ -13,9 +12,6 @@ local Milo = { STATUS_INFO = 'info', STATUS_WARNING = 'warning', STATUS_ERROR = 'error', - - tasks = { }, - craftingStatus = { }, } function Milo:init(context) @@ -26,6 +22,18 @@ function Milo:getContext() return self.context end +function Milo:requestCrafting(item) + local key = Milo:uniqueKey(item) + + if not self.context.craftingQueue[key] then + item.processing = { } + item.requested = item.count + item.crafted = 0 + + self.context.craftingQueue[key] = item + end +end + function Milo:pauseCrafting() self.craftingPaused = true Milo:showError('Crafting Paused') @@ -60,23 +68,25 @@ function Milo:uniqueKey(item) return table.concat({ item.name, item.damage, item.nbtHash }, ':') end -function Milo:getCraftingStatus() - return self.craftingStatus -end - function Milo:resetCraftingStatus() - self.craftingStatus = { } - self.context.inventoryAdapter.activity = { } -end -function Milo:updateCraftingStatus(list) - for k,v in pairs(list) do - self.craftingStatus[k] = v + -- todo: move to end of processing tasks ? + -- what if someone hoppers in items ? -- this shouldnt be allowed + -- all items must come in via pullItems + self.context.inventoryAdapter.activity = { } + + for _,key in pairs(Util.keys(self.context.craftingQueue)) do + local item = self.context.craftingQueue[key] + if item.crafted >= item.requested then + debug('removing:') + debug(item) + self.context.craftingQueue[key] = nil + end end end function Milo:registerTask(task) - table.insert(self.tasks, task) + table.insert(self.context.tasks, task) end function Milo:showError(msg) @@ -194,190 +204,8 @@ end -- Return a list of everything in the system function Milo:listItems() - for _ = 1, 5 do - self.items = self.context.inventoryAdapter:listItems() - if self.items then - break - end --- jobList:showError('Error - retrying in 3 seconds') - os.sleep(3) - end - if not self.items then - self:showError('Error - rebooting in 5 seconds') - end - + self.items = self.context.inventoryAdapter:listItems() return self.items end -function Milo:addCraftingRequest(item, craftList, count) - local key = self:uniqueKey(item) - local request = craftList[key] - if not craftList[key] then - request = { name = item.name, damage = item.damage, nbtHash = item.nbtHash, count = 0 } - request.displayName = itemDB:getName(request) - craftList[key] = request - end - request.count = request.count + count - return request -end - --- Craft -function Milo:craftItem(recipe, items, originalItem, craftList, count) - local missing = { } - local toCraft = Craft.getCraftableAmount(recipe, count, items, missing) - if missing.name then - originalItem.status = string.format('%s missing', itemDB:getName(missing.name)) - originalItem.statusCode = self.STATUS_WARNING - end - - local crafted = 0 - - if toCraft > 0 then - crafted = Craft.craftRecipe(recipe, toCraft, self.context.inventoryAdapter) - self:clearGrid() - items = self:listItems() - count = count - crafted - end - - if count > 0 and items then - local ingredients = Craft.getResourceList4(recipe, items, count) - for _,ingredient in pairs(ingredients) do - if ingredient.need > 0 then - local item = self:addCraftingRequest(ingredient, craftList, ingredient.need) - if Craft.findRecipe(item) then - item.status = string.format('%s missing', itemDB:getName(ingredient)) - item.statusCode = self.STATUS_WARNING - else - item.status = 'no recipe' - item.statusCode = self.STATUS_ERROR - end - end - end - end - return crafted -end - --- Craft as much as possible regardless if all ingredients are available -function Milo:forceCraftItem(inRecipe, items, originalItem, craftList, inCount) - local summed = { } - local throttle = Util.throttle() - - local function sumItems(recipe, count) - count = math.ceil(count / recipe.count) - local craftable = count - - for key,iqty in pairs(Craft.sumIngredients(recipe)) do - throttle() - local item = itemDB:splitKey(key) - local summedItem = summed[key] - if not summedItem then - summedItem = Util.shallowCopy(item) - summedItem.recipe = Craft.findRecipe(item) - summedItem.count = Craft.getItemCount(items, key) - summedItem.need = 0 - summedItem.used = 0 - summedItem.craftable = 0 - summed[key] = summedItem - end - - local total = count * iqty -- 4 * 2 - local used = math.min(summedItem.count, total) -- 5 - local need = total - used -- 3 - - if recipe.craftingTools and recipe.craftingTools[key] then - if summedItem.count > 0 then - summedItem.used = 1 - summedItem.need = 0 - need = 0 - elseif not summedItem.recipe then - summedItem.need = 1 - need = 1 - else - need = 1 - end - else - summedItem.count = summedItem.count - used - summedItem.used = summedItem.used + used - end - - if need > 0 then - if not summedItem.recipe then - craftable = math.min(craftable, math.floor(used / iqty)) - summedItem.need = summedItem.need + need - else - local c = sumItems(summedItem.recipe, need) -- 4 - craftable = math.min(craftable, math.floor((used + c) / iqty)) - summedItem.craftable = summedItem.craftable + c - end - end - end - if craftable > 0 then - craftable = Craft.craftRecipe(recipe, craftable * recipe.count, - self.context.inventoryAdapter) / recipe.count - self:clearGrid() - end - - return craftable * recipe.count - end - - local count = sumItems(inRecipe, inCount) - - if count < inCount then - for _,ingredient in pairs(summed) do - if ingredient.need > 0 then - local item = self:addCraftingRequest(ingredient, craftList, ingredient.need) - if Craft.findRecipe(item) then - item.status = string.format('%s missing', itemDB:getName(ingredient)) - item.statusCode = self.STATUS_WARNING - else - item.status = '(no recipe)' - item.statusCode = self.STATUS_ERROR - end - end - end - end - return count -end - -function Milo:craft(recipe, items, item, craftList) - item.status = nil - item.statusCode = nil - item.crafted = 0 - - if self:isCraftingPaused() then - return - end - - if not self:clearGrid() then - item.status = 'Grid obstructed' - item.statusCode = self.STATUS_ERROR - return - end - - if item.forceCrafting then - item.crafted = self:forceCraftItem(recipe, items, item, craftList, item.count) - else - item.crafted = self:craftItem(recipe, items, item, craftList, item.count) - end -end - -function Milo:craftItems(craftList) - for _,key in pairs(Util.keys(craftList)) do - local item = craftList[key] - if item.count > 0 then - local recipe = Craft.recipes[key] - if recipe then - self:craft(recipe, self:listItems(), item, craftList) - elseif not self.context.controllerAdapter then - item.status = '(no recipe)' - item.statusCode = self.STATUS_ERROR - end - end - end - self:updateCraftingStatus(craftList) - for _,v in pairs(craftList) do - --debug(v) - end -end - return Milo diff --git a/milo/apis/networkedAdapter18.lua b/milo/apis/networkedAdapter18.lua index 54546c5..64ab686 100644 --- a/milo/apis/networkedAdapter18.lua +++ b/milo/apis/networkedAdapter18.lua @@ -67,17 +67,13 @@ self.listCount = self.listCount + 1 --debug(self.listCount) -- todo: only listItems from dirty remotes - -- todo: better handling of empty inventories local cache = { } local items = { } throttle = throttle or Util.throttle() for _, remote in pairs(self.remotes) do - if not remote:listItems(throttle) then - debug('no List: ' .. remote.name) - --error('Listing failed: ' .. remote.name) - end + remote:listItems(throttle) local rcache = remote.cache or { } -- TODO: add a method in each adapter that only updates a passed cache diff --git a/milo/apis/turtle/craft.lua b/milo/apis/turtle/craft.lua index 9e1b1a0..fb5cb4b 100644 --- a/milo/apis/turtle/craft.lua +++ b/milo/apis/turtle/craft.lua @@ -12,17 +12,17 @@ local MACHINE_LOOKUP = 'usr/config/machine_crafting.db' local Craft = { } local function clearGrid(inventoryAdapter) - turtle.eachFilledSlot(function(slot) - inventoryAdapter:insert(slot.index, slot.count, nil, slot) - end) + turtle.eachFilledSlot(function(slot) + inventoryAdapter:insert(slot.index, slot.count, nil, slot) + end) - for i = 1, 16 do - if turtle.getItemCount(i) ~= 0 then - return false - end - end + for i = 1, 16 do + if turtle.getItemCount(i) ~= 0 then + return false + end + end - return true + return true end local function splitKey(key) @@ -55,29 +55,57 @@ function Craft.getItemCount(items, item) return count end -local function machineCraft(recipe, qty, inventoryAdapter, machineName) +local function machineCraft(recipe, qty, inventoryAdapter, machineName, oitem) + local key = recipe.result + local request = oitem.processing[key] + if request then + request.crafted = request.crafted + (inventoryAdapter.activity[key] or 0) + if request.crafted >= request.requested then + oitem.processing[key] = nil -- TODO: check... + return true + end + return + end + local machine = device[machineName] if not machine then debug('machine not found') + oitem.processing[recipe.result] = { + status = 'machine not found' + } else for k in pairs(recipe.ingredients) do if machine.getItemMeta(k) then + oitem.processing[recipe.result] = { + status = 'machine in use' + } debug('machine in use: ' .. k) - return false end end + debug('processing ' .. recipe.result) for k,v in pairs(recipe.ingredients) do - inventoryAdapter:provide(splitKey(v), qty, k, machineName) + if inventoryAdapter:provide(splitKey(v), qty, k, machineName) ~= qty then + -- TODO: suck em back out + oitem.processing[recipe.result] = { + status = 'unknown error' + } + end end + oitem.processing[recipe.result] = { + status = 'processing', + requested = qty, + crafted = 0, + } end - - return false end -local function turtleCraft(recipe, qty, inventoryAdapter) +local function turtleCraft(recipe, qty, inventoryAdapter, oitem) if not clearGrid(inventoryAdapter) then - return false + oitem.processing[recipe.result] = { + status = 'grid in use', + } + return end for k,v in pairs(recipe.ingredients) do @@ -90,15 +118,22 @@ local function turtleCraft(recipe, qty, inventoryAdapter) provideQty = 1 end ]]-- - inventoryAdapter:provide(item, provideQty, k) - if turtle.getItemCount(k) == 0 then -- ~= qty then + if inventoryAdapter:provide(item, provideQty, k) ~= provideQty then -- FIX: ingredients cannot be stacked --debug('failed ' .. v .. ' - ' .. provideQty) - return false + oitem.processing[recipe.result] = { + status = 'unknown error', + } + return end end - return turtle.craft() + if turtle.craft() then + return true + end + oitem.processing[recipe.result] = { + status = 'processing', + } end function Craft.loadRecipes() @@ -139,16 +174,22 @@ local function makeRecipeKey(item) return table.concat({ item.name, item.damage or 0, item.nbtHash }, ':') end -function Craft.craftRecipe(recipe, count, inventoryAdapter) +function Craft.craftRecipe(recipe, count, inventoryAdapter, origItem) if type(recipe) == 'string' then recipe = Craft.recipes[recipe] if not recipe then + origItem.processing[recipe.result] = { + status = 'no recipe', + } return 0, 'No recipe' end end local items = inventoryAdapter:listItems() if not items then + origItem.processing[recipe.result] = { + status = 'Inventory changed', + } return 0, 'Inventory changed' end @@ -166,7 +207,7 @@ function Craft.craftRecipe(recipe, count, inventoryAdapter) local irecipe = Craft.findRecipe(key) if irecipe then local iqty = need - itemCount - local crafted = Craft.craftRecipe(irecipe, iqty, inventoryAdapter) + local crafted = Craft.craftRecipe(irecipe, iqty, inventoryAdapter, origItem) if crafted ~= iqty then turtle.select(1) return 0 @@ -177,15 +218,19 @@ function Craft.craftRecipe(recipe, count, inventoryAdapter) local crafted = 0 repeat --- fix + local requested = math.min(count, maxCount) + if Craft.machineLookup[recipe.result] then - machineCraft(recipe, math.min(count, maxCount), inventoryAdapter, Craft.machineLookup[recipe.result]) - break - elseif not turtleCraft(recipe, math.min(count, maxCount), inventoryAdapter) then - turtle.select(1) - break + if not machineCraft(recipe, requested, inventoryAdapter, Craft.machineLookup[recipe.result], origItem) then + break + end + else + if not turtleCraft(recipe, requested, inventoryAdapter) then + break + end end - crafted = crafted + math.min(count, maxCount) + + crafted = crafted + requested count = count - maxCount until count <= 0 diff --git a/milo/plugins/craftTask.lua b/milo/plugins/craftTask.lua new file mode 100644 index 0000000..94bb712 --- /dev/null +++ b/milo/plugins/craftTask.lua @@ -0,0 +1,141 @@ +local Craft = require('turtle.craft') +local itemDB = require('itemDB') +local Milo = require('milo') +local Util = require('util') + +local context = Milo:getContext() + +local craftTask = { + name = 'crafting', + priority = 70, +} + +-- Craft +function craftTask:craftItem(recipe, originalItem, count) + local missing = { } + local toCraft = Craft.getCraftableAmount(recipe, count, Milo:listItems(), missing) + if missing.name then + originalItem.status = string.format('%s missing', itemDB:getName(missing.name)) + originalItem.statusCode = Milo.STATUS_WARNING + end + + local crafted = 0 + + if toCraft > 0 then + crafted = Craft.craftRecipe(recipe, toCraft, context.inventoryAdapter, originalItem) + Milo:clearGrid() + end + + return crafted +end + +-- Craft as much as possible regardless if all ingredients are available +function craftTask:forceCraftItem(inRecipe, originalItem, inCount) + local summed = { } + local items = Milo:listItems() + local throttle = Util.throttle() + + local function sumItems(recipe, count) + count = math.ceil(count / recipe.count) + local craftable = count + + for key,iqty in pairs(Craft.sumIngredients(recipe)) do + throttle() + local item = itemDB:splitKey(key) + local summedItem = summed[key] + if not summedItem then + summedItem = Util.shallowCopy(item) + summedItem.recipe = Craft.findRecipe(item) + summedItem.count = Craft.getItemCount(items, key) + summedItem.need = 0 + summedItem.used = 0 + summedItem.craftable = 0 + summed[key] = summedItem + end + + local total = count * iqty -- 4 * 2 + local used = math.min(summedItem.count, total) -- 5 + local need = total - used -- 3 + + if recipe.craftingTools and recipe.craftingTools[key] then + if summedItem.count > 0 then + summedItem.used = 1 + summedItem.need = 0 + need = 0 + elseif not summedItem.recipe then + summedItem.need = 1 + need = 1 + else + need = 1 + end + else + summedItem.count = summedItem.count - used + summedItem.used = summedItem.used + used + end + + if need > 0 then + if not summedItem.recipe then + craftable = math.min(craftable, math.floor(used / iqty)) + summedItem.need = summedItem.need + need + else + local c = sumItems(summedItem.recipe, need) -- 4 + craftable = math.min(craftable, math.floor((used + c) / iqty)) + summedItem.craftable = summedItem.craftable + c + end + end + end + if craftable > 0 then + craftable = Craft.craftRecipe(recipe, craftable * recipe.count, + context.inventoryAdapter, originalItem) / recipe.count + Milo:clearGrid() + end + + return craftable * recipe.count + end + + return sumItems(inRecipe, inCount) +end + +function craftTask:craft(recipe, item) + item.status = nil + item.statusCode = nil + item.crafted = 0 + + if Milo:isCraftingPaused() then + return + end + + -- todo: is this needed ? + if not Milo:clearGrid() then + item.status = 'Grid obstructed' + item.statusCode = Milo.STATUS_ERROR + return + end + + if item.forceCrafting then + item.crafted = self:forceCraftItem(recipe, item, item.count) + else + item.crafted = self:craftItem(recipe, item, item.count) + end +end + +function craftTask:cycle() + for _,key in pairs(Util.keys(context.craftingQueue)) do + local item = context.craftingQueue[key] + if item.count > 0 then + local recipe = Craft.recipes[key] + if recipe then + self:craft(recipe, item) + if item.eject and item.crafted >= item.requested then + Milo:eject(item, item.requested) + end + elseif not context.controllerAdapter then + item.status = '(no recipe)' + item.statusCode = Milo.STATUS_ERROR + item.crafted = 0 + end + end + end +end + +Milo:registerTask(craftTask) \ No newline at end of file diff --git a/milo/plugins/demandCraft.lua b/milo/plugins/demandCraft.lua index fbb5d7c..8bde995 100644 --- a/milo/plugins/demandCraft.lua +++ b/milo/plugins/demandCraft.lua @@ -6,8 +6,6 @@ local Util = require('util') local colors = _G.colors -local demandCrafting = { } - local craftPage = UI.Page { titleBar = UI.TitleBar { }, wizard = UI.Wizard { @@ -118,12 +116,11 @@ function craftPage:eventHandler(event) UI:setPreviousPage() elseif event.type == 'accept' then - local key = Milo:uniqueKey(self.item) - demandCrafting[key] = Util.shallowCopy(self.item) - demandCrafting[key].count = tonumber(self.wizard.pages.quantity.count.value) - demandCrafting[key].ocount = demandCrafting[key].count - demandCrafting[key].forceCrafting = true - demandCrafting[key].eject = self.wizard.pages.quantity.eject.value == true + local item = Util.shallowCopy(self.item) + item.count = tonumber(self.wizard.pages.quantity.count.value) + item.forceCrafting = true + item.eject = self.wizard.pages.quantity.eject.value == true + Milo:requestCrafting(item) UI:setPreviousPage() else return UI.Page.eventHandler(self, event) @@ -131,48 +128,4 @@ function craftPage:eventHandler(event) return true end -local demandCraftingTask = { - name = 'demand crafting', - priority = 60, -} - -function demandCraftingTask:cycle(context) - local demandCrafted = { } - - -- look directly at the adapter import activity to determine - -- if the item was imported into storage from any source. - -- The item does NOT need to come from the machine that did - -- the crafting. - for _,key in pairs(Util.keys(demandCrafting)) do - local item = demandCrafting[key] - - local imported = context.inventoryAdapter.activity[key] - if imported then - item.crafted = math.min(imported, item.count) - item.count = math.max(0, item.count - item.crafted) - context.inventoryAdapter.activity[key] = imported - item.crafted - end - demandCrafted[key] = item - end - - if Util.size(demandCrafted) > 0 then - Milo:craftItems(demandCrafted) - end - - for _,key in pairs(Util.keys(demandCrafting)) do - local item = demandCrafting[key] - if item.crafted then - item.count = math.max(0, item.count - item.crafted) - if item.count <= 0 then - item.statusCode = 'success' - demandCrafting[key] = nil - if item.eject then - Milo:eject(item, item.ocount) - end - end - end - end -end - UI:addPage('craft', craftPage) -Milo:registerTask(demandCraftingTask) diff --git a/milo/plugins/jobList.lua b/milo/plugins/jobList.lua index 6e0f82b..a5d01d9 100644 --- a/milo/plugins/jobList.lua +++ b/milo/plugins/jobList.lua @@ -15,12 +15,14 @@ local display = UI.Device { local jobList = UI.Page { parent = display, grid = UI.Grid { - sortColumn = 'displayName', + sortColumn = 'index', backgroundFocusColor = colors.black, columns = { { heading = 'Qty', key = 'count', width = 6 }, - { heading = 'Crafting', key = 'displayName', width = display.width / 2 - 10 }, - { heading = 'Status', key = 'status', width = display.width - 10 }, + { heading = 'Crafting', key = 'displayName', }, -- width = display.width / 2 - 10 }, + { heading = 'Status', key = 'status', }, -- width = display.width - 10 }, + { heading = 'Req', key = 'requested', width = 6 }, + { heading = 'Cra', key = 'crafted', width = 6 }, }, }, } @@ -33,7 +35,20 @@ end function jobList:updateList(craftList) if not Milo:isCraftingPaused() then - self.grid:setValues(craftList) + local t = { } + local index = 1 + for k,v in pairs(craftList) do + t[k] = v + v.index = index + index = index + 1 + for k2,v2 in pairs(v.processing) do + t[k2] = v2 + v2.displayName = k2 + v2.index = index + index = index + 1 + end + end + self.grid:setValues(t) self.grid:update() self:draw() self:sync() @@ -61,7 +76,7 @@ local JobListTask = { } function JobListTask:cycle() - jobList:updateList(Milo:getCraftingStatus()) + jobList:updateList(context.craftingQueue) end Milo:registerTask(JobListTask) diff --git a/milo/plugins/replenishTask.lua b/milo/plugins/replenishTask.lua index 729a83c..d204245 100644 --- a/milo/plugins/replenishTask.lua +++ b/milo/plugins/replenishTask.lua @@ -3,12 +3,10 @@ local Milo = require('milo') local ReplenishTask = { name = 'replenish', - priority = 70, + priority = 60, } function ReplenishTask:cycle(context) - local craftList = { } - for _,res in pairs(context.resources) do if res.low then local item = Milo:getItemWithQty(res, res.ignoreDamage, res.ignoreNbtHash) @@ -26,22 +24,16 @@ function ReplenishTask:cycle(context) if res.ignoreDamage then item.damage = 0 end - local key = Milo:uniqueKey(res) - - craftList[key] = { + Milo:requestCrafting({ damage = item.damage, nbtHash = item.nbtHash, count = res.low - item.count, name = item.name, displayName = item.displayName, - status = '', - rsControl = res.rsControl, - } + }) end end end - - Milo:craftItems(craftList) end Milo:registerTask(ReplenishTask) From ceb558dcff313bae6e13f40c240c98e9f2e54d3a Mon Sep 17 00:00:00 2001 From: kepler155c Date: Thu, 25 Oct 2018 17:12:37 -0400 Subject: [PATCH 030/165] milo wip --- milo/apis/milo.lua | 13 ++-- milo/apis/networkedAdapter18.lua | 20 ++++-- milo/apis/turtle/craft.lua | 118 +++++++++++++++++++------------ milo/plugins/craftTask.lua | 26 ++----- milo/plugins/jobList.lua | 66 +++++++++-------- 5 files changed, 137 insertions(+), 106 deletions(-) diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index 6c18d5e..c8c5b4f 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -8,10 +8,6 @@ local turtle = _G.turtle local Milo = { RECIPES_FILE = 'usr/config/recipes.db', RESOURCE_FILE = 'usr/config/resources.db', - - STATUS_INFO = 'info', - STATUS_WARNING = 'warning', - STATUS_ERROR = 'error', } function Milo:init(context) @@ -26,8 +22,11 @@ function Milo:requestCrafting(item) local key = Milo:uniqueKey(item) if not self.context.craftingQueue[key] then - item.processing = { } - item.requested = item.count + item.ingredients = { } + --[[ + count = requested amount, + crafted = amount that has been crafted + ]] item.crafted = 0 self.context.craftingQueue[key] = item @@ -77,7 +76,7 @@ function Milo:resetCraftingStatus() for _,key in pairs(Util.keys(self.context.craftingQueue)) do local item = self.context.craftingQueue[key] - if item.crafted >= item.requested then + if item.crafted >= item.count then debug('removing:') debug(item) self.context.craftingQueue[key] = nil diff --git a/milo/apis/networkedAdapter18.lua b/milo/apis/networkedAdapter18.lua index 64ab686..5bb5499 100644 --- a/milo/apis/networkedAdapter18.lua +++ b/milo/apis/networkedAdapter18.lua @@ -13,6 +13,7 @@ function NetworkedAdapter:init(args) dirty = true, listCount = 0, activity = { }, + nameArray = { }, } Util.merge(self, defaults) Util.merge(self, args) @@ -29,6 +30,7 @@ listCount = 0, local adapter = InventoryAdapter.wrap({ side = k, direction = self.localName }) if adapter then table.insert(self.remotes, adapter) + self.nameArray[adapter.direction] = true end end end @@ -47,6 +49,7 @@ listCount = 0, return a.priority > b.priority end) end + debug(self.nameArray) end function NetworkedAdapter:isValid() @@ -116,13 +119,15 @@ function NetworkedAdapter:provide(item, qty, slot, direction) for _, remote in ipairs(self.remotes) do local amount = remote:provide(item, qty, slot, direction) if amount > 0 then -debug('%s(%d): %s -> %s%s', +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 - local entry = self.activity[key] or 0 - self.activity[key] = entry + amount + --if self.nameArray[direction or self.localName] then + -- local entry = self.activity[key] or 0 + -- self.activity[key] = entry + amount + --end end qty = qty - amount total = total + amount @@ -168,13 +173,16 @@ function NetworkedAdapter:insert(slot, qty, toSlot, item, source) local function insert(remote) local amount = remote:insert(slot, qty, toSlot, source or self.direction) if amount > 0 then -debug('%s(%d): %s[%d] -> %s', +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 +debug('insert: ' .. (source or self.localName)) + --if self.nameArray[source or self.localName] then + local entry = self.activity[key] or 0 + self.activity[key] = entry + amount + --end end qty = qty - amount total = total + amount diff --git a/milo/apis/turtle/craft.lua b/milo/apis/turtle/craft.lua index fb5cb4b..0b8fed7 100644 --- a/milo/apis/turtle/craft.lua +++ b/milo/apis/turtle/craft.lua @@ -9,7 +9,12 @@ local RECIPES_DIR = 'usr/etc/recipes' local USER_RECIPES = 'usr/config/recipes.db' local MACHINE_LOOKUP = 'usr/config/machine_crafting.db' -local Craft = { } +local Craft = { + STATUS_INFO = 'info', + STATUS_WARNING = 'warning', + STATUS_ERROR = 'error', + STATUS_SUCCESS = 'success', +} local function clearGrid(inventoryAdapter) turtle.eachFilledSlot(function(slot) @@ -57,11 +62,24 @@ end local function machineCraft(recipe, qty, inventoryAdapter, machineName, oitem) local key = recipe.result - local request = oitem.processing[key] - if request then + local request = oitem.ingredients[key] + +debug('requested: ' .. key) + if not request then + request = { + count = qty, + crafted = 0, + } + oitem.ingredients[recipe.result] = request + end + + if request.pending then request.crafted = request.crafted + (inventoryAdapter.activity[key] or 0) - if request.crafted >= request.requested then - oitem.processing[key] = nil -- TODO: check... +debug({ request.crafted, request.count }) + if request.crafted >= request.count then + request.pending = nil -- TODO: check... + request.statusCode = Craft.STATUS_SUCCESS + request.status = nil return true end return @@ -69,42 +87,50 @@ local function machineCraft(recipe, qty, inventoryAdapter, machineName, oitem) local machine = device[machineName] if not machine then - debug('machine not found') - oitem.processing[recipe.result] = { - status = 'machine not found' - } - else - for k in pairs(recipe.ingredients) do - if machine.getItemMeta(k) then - oitem.processing[recipe.result] = { - status = 'machine in use' - } - debug('machine in use: ' .. k) - end - end - - debug('processing ' .. recipe.result) - for k,v in pairs(recipe.ingredients) do - if inventoryAdapter:provide(splitKey(v), qty, k, machineName) ~= qty then - -- TODO: suck em back out - oitem.processing[recipe.result] = { - status = 'unknown error' - } - end - end - oitem.processing[recipe.result] = { - status = 'processing', - requested = qty, - crafted = 0, - } + request.status = 'machine not found' + request.statusCode = Craft.STATUS_ERROR + return end + + for k in pairs(recipe.ingredients) do + if machine.getItemMeta(k) then + request.status = 'machine in use' + request.statusCode = Craft.STATUS_WARNING + return + end + end + +debug('processing %s %d', key, qty) + + for k,v in pairs(recipe.ingredients) do + if inventoryAdapter:provide(splitKey(v), qty, k, machineName) ~= qty then + -- TODO: suck em back out + request.status = 'unknown error' + request.statusCode = Craft.STATUS_ERROR + return + end + end + request.status = 'processing' + request.statusCode = Craft.STATUS_INFO + request.pending = true end local function turtleCraft(recipe, qty, inventoryAdapter, oitem) - if not clearGrid(inventoryAdapter) then - oitem.processing[recipe.result] = { - status = 'grid in use', + local key = recipe.result + local request = oitem.ingredients[key] + +debug('requested: ' .. key) + if not request then + request = { + count = qty, + crafted = 0, } + oitem.ingredients[recipe.result] = request + end + + if not clearGrid(inventoryAdapter) then + request.status = 'grid in use' + request.statusCode = Craft.STATUS_ERROR return end @@ -121,19 +147,19 @@ local function turtleCraft(recipe, qty, inventoryAdapter, oitem) if inventoryAdapter:provide(item, provideQty, k) ~= provideQty then -- FIX: ingredients cannot be stacked --debug('failed ' .. v .. ' - ' .. provideQty) - oitem.processing[recipe.result] = { - status = 'unknown error', - } + request.status = 'unknown error' + request.statusCode = Craft.STATUS_ERROR return end end if turtle.craft() then + request.status = nil + request.statusCode = Craft.STATUS_SUCCESS return true end - oitem.processing[recipe.result] = { - status = 'processing', - } + request.status = 'failed to craft' + request.statusCode = Craft.STATUS_ERROR end function Craft.loadRecipes() @@ -178,8 +204,9 @@ function Craft.craftRecipe(recipe, count, inventoryAdapter, origItem) if type(recipe) == 'string' then recipe = Craft.recipes[recipe] if not recipe then - origItem.processing[recipe.result] = { + origItem.ingredients[recipe.result] = { status = 'no recipe', + statusCode = Craft.STATUS_ERROR, } return 0, 'No recipe' end @@ -187,8 +214,9 @@ function Craft.craftRecipe(recipe, count, inventoryAdapter, origItem) local items = inventoryAdapter:listItems() if not items then - origItem.processing[recipe.result] = { + origItem.ingredients[recipe.result] = { status = 'Inventory changed', + statusCode = Craft.STATUS_ERROR, } return 0, 'Inventory changed' end @@ -225,7 +253,7 @@ function Craft.craftRecipe(recipe, count, inventoryAdapter, origItem) break end else - if not turtleCraft(recipe, requested, inventoryAdapter) then + if not turtleCraft(recipe, requested, inventoryAdapter, origItem) then break end end diff --git a/milo/plugins/craftTask.lua b/milo/plugins/craftTask.lua index 94bb712..44e413d 100644 --- a/milo/plugins/craftTask.lua +++ b/milo/plugins/craftTask.lua @@ -12,13 +12,7 @@ local craftTask = { -- Craft function craftTask:craftItem(recipe, originalItem, count) - local missing = { } - local toCraft = Craft.getCraftableAmount(recipe, count, Milo:listItems(), missing) - if missing.name then - originalItem.status = string.format('%s missing', itemDB:getName(missing.name)) - originalItem.statusCode = Milo.STATUS_WARNING - end - + local toCraft = Craft.getCraftableAmount(recipe, count, Milo:listItems(), { }) local crafted = 0 if toCraft > 0 then @@ -99,23 +93,15 @@ end function craftTask:craft(recipe, item) item.status = nil item.statusCode = nil - item.crafted = 0 if Milo:isCraftingPaused() then return end - -- todo: is this needed ? - if not Milo:clearGrid() then - item.status = 'Grid obstructed' - item.statusCode = Milo.STATUS_ERROR - return - end - if item.forceCrafting then - item.crafted = self:forceCraftItem(recipe, item, item.count) + item.crafted = item.crafted + self:forceCraftItem(recipe, item, item.count - item.crafted) else - item.crafted = self:craftItem(recipe, item, item.count) + item.crafted = item.crafted + self:craftItem(recipe, item, item.count - item.crafted) end end @@ -126,12 +112,12 @@ function craftTask:cycle() local recipe = Craft.recipes[key] if recipe then self:craft(recipe, item) - if item.eject and item.crafted >= item.requested then - Milo:eject(item, item.requested) + if item.eject and item.crafted >= item.count then + Milo:eject(item, item.count) end elseif not context.controllerAdapter then item.status = '(no recipe)' - item.statusCode = Milo.STATUS_ERROR + item.statusCode = Craft.STATUS_ERROR item.crafted = 0 end end diff --git a/milo/plugins/jobList.lua b/milo/plugins/jobList.lua index a5d01d9..4132abf 100644 --- a/milo/plugins/jobList.lua +++ b/milo/plugins/jobList.lua @@ -1,28 +1,30 @@ +local Craft = require('turtle.craft') +local itemDB = require('itemDB') local Milo = require('milo') local Peripheral = require('peripheral') local UI = require('ui') +local Util = require('util') local colors = _G.colors local context = Milo:getContext() local mon = Peripheral.lookup(context.config.monitor) or error('Monitor is not attached') -local display = UI.Device { - device = mon, - textScale = .5, -} local jobList = UI.Page { - parent = display, + parent = UI.Device { + device = mon, + textScale = .5, + }, grid = UI.Grid { sortColumn = 'index', backgroundFocusColor = colors.black, columns = { - { heading = 'Qty', key = 'count', width = 6 }, - { heading = 'Crafting', key = 'displayName', }, -- width = display.width / 2 - 10 }, - { heading = 'Status', key = 'status', }, -- width = display.width - 10 }, - { heading = 'Req', key = 'requested', width = 6 }, - { heading = 'Cra', key = 'crafted', width = 6 }, + { heading = 'Qty', key = 'remaining', width = 6 }, + { heading = 'Crafting', key = 'displayName', }, + { heading = 'Status', key = 'status', }, + { heading = 'Req', key = 'count', width = 3 }, + { heading = 'Cra', key = 'crafted', width = 3 }, }, }, } @@ -36,16 +38,16 @@ end function jobList:updateList(craftList) if not Milo:isCraftingPaused() then local t = { } - local index = 1 - for k,v in pairs(craftList) do - t[k] = v - v.index = index - index = index + 1 - for k2,v2 in pairs(v.processing) do - t[k2] = v2 - v2.displayName = k2 - v2.index = index - index = index + 1 + for _,v in pairs(craftList) do + table.insert(t, v) + v.index = #t + v.showRemining = true + for k2,v2 in pairs(v.ingredients) do + table.insert(t, v2) + if not v2.displayName then + v2.displayName = itemDB:getName(k2) + end + v2.index = #t end end self.grid:setValues(t) @@ -55,15 +57,23 @@ function jobList:updateList(craftList) end end -function jobList.grid:getRowTextColor(row, selected) - if row.statusCode == Milo.STATUS_ERROR then - return colors.red - elseif row.statusCode == Milo.STATUS_WARNING then - return colors.yellow - elseif row.statusCode == Milo.STATUS_INFO then - return colors.lime +function jobList.grid:getDisplayValues(row) + row = Util.shallowCopy(row) + if row.showRemining then + row.remaining = row.count - row.crafted end - return UI.Grid:getRowTextColor(row, selected) + return row +end + +function jobList.grid:getRowTextColor(row, selected) + local statusColor = { + [ Craft.STATUS_ERROR ] = colors.red, + [ Craft.STATUS_WARNING ] = colors.orange, + [ Craft.STATUS_INFO ] = colors.yellow, + [ Craft.STATUS_SUCCESS ] = colors.green, + } + return row.statusCode and statusColor[row.statusCode] or + UI.Grid:getRowTextColor(row, selected) end jobList:enable() From eb6ab3ed08b7c9875f674b4366104b7f86461fbd Mon Sep 17 00:00:00 2001 From: kepler155c Date: Thu, 25 Oct 2018 17:24:50 -0400 Subject: [PATCH 031/165] milo wip --- milo/apis/networkedAdapter18.lua | 14 ++------------ milo/apis/turtle/craft.lua | 5 ----- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/milo/apis/networkedAdapter18.lua b/milo/apis/networkedAdapter18.lua index 5bb5499..a3d3897 100644 --- a/milo/apis/networkedAdapter18.lua +++ b/milo/apis/networkedAdapter18.lua @@ -13,7 +13,6 @@ function NetworkedAdapter:init(args) dirty = true, listCount = 0, activity = { }, - nameArray = { }, } Util.merge(self, defaults) Util.merge(self, args) @@ -30,7 +29,6 @@ listCount = 0, local adapter = InventoryAdapter.wrap({ side = k, direction = self.localName }) if adapter then table.insert(self.remotes, adapter) - self.nameArray[adapter.direction] = true end end end @@ -49,7 +47,6 @@ listCount = 0, return a.priority > b.priority end) end - debug(self.nameArray) end function NetworkedAdapter:isValid() @@ -113,7 +110,6 @@ function NetworkedAdapter:getItemInfo(item) end function NetworkedAdapter:provide(item, qty, slot, direction) - local key = table.concat({ item.name, item.damage, item.nbtHash }, ':') local total = 0 for _, remote in ipairs(self.remotes) do @@ -124,10 +120,6 @@ debug('EXT: %s(%d): %s -> %s%s', slot and string.format('[%d]', slot) or '') self.dirty = true remote.dirty = true - --if self.nameArray[direction or self.localName] then - -- local entry = self.activity[key] or 0 - -- self.activity[key] = entry + amount - --end end qty = qty - amount total = total + amount @@ -179,10 +171,8 @@ debug('INS: %s(%d): %s[%d] -> %s', self.dirty = true remote.dirty = true debug('insert: ' .. (source or self.localName)) - --if self.nameArray[source or self.localName] then - local entry = self.activity[key] or 0 - self.activity[key] = entry + amount - --end + local entry = self.activity[key] or 0 + self.activity[key] = entry + amount end qty = qty - amount total = total + amount diff --git a/milo/apis/turtle/craft.lua b/milo/apis/turtle/craft.lua index 0b8fed7..a2c1746 100644 --- a/milo/apis/turtle/craft.lua +++ b/milo/apis/turtle/craft.lua @@ -64,7 +64,6 @@ local function machineCraft(recipe, qty, inventoryAdapter, machineName, oitem) local key = recipe.result local request = oitem.ingredients[key] -debug('requested: ' .. key) if not request then request = { count = qty, @@ -75,7 +74,6 @@ debug('requested: ' .. key) if request.pending then request.crafted = request.crafted + (inventoryAdapter.activity[key] or 0) -debug({ request.crafted, request.count }) if request.crafted >= request.count then request.pending = nil -- TODO: check... request.statusCode = Craft.STATUS_SUCCESS @@ -100,8 +98,6 @@ debug({ request.crafted, request.count }) end end -debug('processing %s %d', key, qty) - for k,v in pairs(recipe.ingredients) do if inventoryAdapter:provide(splitKey(v), qty, k, machineName) ~= qty then -- TODO: suck em back out @@ -119,7 +115,6 @@ local function turtleCraft(recipe, qty, inventoryAdapter, oitem) local key = recipe.result local request = oitem.ingredients[key] -debug('requested: ' .. key) if not request then request = { count = qty, From 6d3389c9c6939199cf6447c21f1a2aeb495c0c39 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Fri, 26 Oct 2018 00:30:37 -0400 Subject: [PATCH 032/165] milo wip --- milo/Milo.lua | 10 +- milo/apis/milo.lua | 10 +- milo/apis/networkedAdapter18.lua | 13 +- milo/apis/turtle/craft.lua | 212 ++++++++++++++++--------------- milo/plugins/craftTask.lua | 23 +--- milo/plugins/jobList.lua | 18 ++- milo/plugins/machineLearn.lua | 5 +- 7 files changed, 147 insertions(+), 144 deletions(-) diff --git a/milo/Milo.lua b/milo/Milo.lua index 1f1e736..603d5c8 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -113,6 +113,8 @@ local context = { craftingQueue = { }, } +_G._p = context--debug + local function initStorage(detachedDevice) debug('Initializing storage') local storage = { } @@ -142,7 +144,9 @@ debug(storage) 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 = InventoryAdapter.wrap({ remoteDefaults = storage }) if not context.inventoryAdapter then @@ -151,7 +155,7 @@ debug(storage) end Event.on({ 'device_attach' }, function(_, dev) - debug('attach: ' .. dev) + --debug('attach: ' .. dev) if config.remoteDefaults[dev] and config.remoteDefaults[dev].mtype == 'storage' then initStorage() @@ -159,7 +163,7 @@ Event.on({ 'device_attach' }, function(_, dev) end) Event.on({ 'device_detach' }, function(_, dev) - debug('detach: ' .. dev) + --debug('detach: ' .. dev) if config.remoteDefaults[dev] and config.remoteDefaults[dev].mtype == 'storage' then initStorage(dev) diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index c8c5b4f..9bd90fb 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -22,13 +22,11 @@ function Milo:requestCrafting(item) local key = Milo:uniqueKey(item) if not self.context.craftingQueue[key] then - item.ingredients = { } - --[[ - count = requested amount, - crafted = amount that has been crafted - ]] + item.ingredients = { + [ key ] = item + } +-- item.ingredients[key] = item item.crafted = 0 - self.context.craftingQueue[key] = item end end diff --git a/milo/apis/networkedAdapter18.lua b/milo/apis/networkedAdapter18.lua index a3d3897..2f1295f 100644 --- a/milo/apis/networkedAdapter18.lua +++ b/milo/apis/networkedAdapter18.lua @@ -115,9 +115,9 @@ function NetworkedAdapter:provide(item, qty, slot, direction) 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 '') +--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 @@ -165,12 +165,11 @@ function NetworkedAdapter:insert(slot, qty, toSlot, item, source) 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) +--debug('INS: %s(%d): %s[%d] -> %s', +-- item.name, amount, +-- source or self.localName, slot, remote.side) self.dirty = true remote.dirty = true -debug('insert: ' .. (source or self.localName)) local entry = self.activity[key] or 0 self.activity[key] = entry + amount end diff --git a/milo/apis/turtle/craft.lua b/milo/apis/turtle/craft.lua index a2c1746..be62582 100644 --- a/milo/apis/turtle/craft.lua +++ b/milo/apis/turtle/craft.lua @@ -41,6 +41,13 @@ local function splitKey(key) return item end +local function makeRecipeKey(item) + if type(item) == 'string' then + item = splitKey(item) + end + return table.concat({ item.name, item.damage or 0, item.nbtHash }, ':') +end + function Craft.getItemCount(items, item) if type(item) == 'string' then item = splitKey(item) @@ -60,25 +67,25 @@ function Craft.getItemCount(items, item) return count end -local function machineCraft(recipe, qty, inventoryAdapter, machineName, oitem) - local key = recipe.result - local request = oitem.ingredients[key] - - if not request then - request = { - count = qty, - crafted = 0, - } - oitem.ingredients[recipe.result] = request +function Craft.sumIngredients(recipe) + -- produces { ['minecraft:planks:0'] = 8 } + local t = { } + for _,item in pairs(recipe.ingredients) do + t[item] = (t[item] or 0) + 1 end +-- need a check for crafting tool + return t +end +local function machineCraft(recipe, inventoryAdapter, machineName, request, count) if request.pending then - request.crafted = request.crafted + (inventoryAdapter.activity[key] or 0) - if request.crafted >= request.count then - request.pending = nil -- TODO: check... - request.statusCode = Craft.STATUS_SUCCESS + local imported = (inventoryAdapter.activity[recipe.result] or 0) + request.pending = request.pending - imported + request.crafted = request.crafted + imported + if request.pending <= 0 then + request.pending = nil + request.statusCode = nil request.status = nil - return true end return end @@ -99,7 +106,7 @@ local function machineCraft(recipe, qty, inventoryAdapter, machineName, oitem) end for k,v in pairs(recipe.ingredients) do - if inventoryAdapter:provide(splitKey(v), qty, k, machineName) ~= qty then + if inventoryAdapter:provide(splitKey(v), count, k, machineName) ~= count then -- TODO: suck em back out request.status = 'unknown error' request.statusCode = Craft.STATUS_ERROR @@ -108,21 +115,10 @@ local function machineCraft(recipe, qty, inventoryAdapter, machineName, oitem) end request.status = 'processing' request.statusCode = Craft.STATUS_INFO - request.pending = true + request.pending = count * recipe.count end -local function turtleCraft(recipe, qty, inventoryAdapter, oitem) - local key = recipe.result - local request = oitem.ingredients[key] - - if not request then - request = { - count = qty, - crafted = 0, - } - oitem.ingredients[recipe.result] = request - end - +local function turtleCraft(recipe, inventoryAdapter, request, count) if not clearGrid(inventoryAdapter) then request.status = 'grid in use' request.statusCode = Craft.STATUS_ERROR @@ -131,92 +127,84 @@ local function turtleCraft(recipe, qty, inventoryAdapter, oitem) for k,v in pairs(recipe.ingredients) do local item = splitKey(v) - local provideQty = qty - --[[ - Turtles can only craft 1 item at a time when using a tool. - - if recipe.craftingTools and recipe.craftingTools[k] then - provideQty = 1 - end - ]]-- - if inventoryAdapter:provide(item, provideQty, k) ~= provideQty then + if inventoryAdapter:provide(item, count, k) ~= count then -- FIX: ingredients cannot be stacked ---debug('failed ' .. v .. ' - ' .. provideQty) request.status = 'unknown error' request.statusCode = Craft.STATUS_ERROR return end end + turtle.select(1) if turtle.craft() then request.status = nil request.statusCode = Craft.STATUS_SUCCESS + request.crafted = request.crafted + count * recipe.count return true end - request.status = 'failed to craft' + request.status = 'Failed to craft' request.statusCode = Craft.STATUS_ERROR end -function Craft.loadRecipes() - Craft.recipes = { } - - Util.merge(Craft.recipes, (Util.readTable(fs.combine(RECIPES_DIR, 'minecraft.db')) or { }).recipes) - - local config = Util.readTable('usr/config/recipeBooks.db') or { } - for _, book in pairs(config) do - local recipeFile = Util.readTable(book) - Util.merge(Craft.recipes, recipeFile.recipes) - end - - local recipes = Util.readTable(USER_RECIPES) or { } - Util.merge(Craft.recipes, recipes) - - for k,v in pairs(Craft.recipes) do - v.result = k - end - - Craft.machineLookup = Util.readTable(MACHINE_LOOKUP) or { } -end - -function Craft.sumIngredients(recipe) - -- produces { ['minecraft:planks:0'] = 8 } - local t = { } - for _,item in pairs(recipe.ingredients) do - t[item] = (t[item] or 0) + 1 - end --- need a check for crafting tool - return t -end - -local function makeRecipeKey(item) - if type(item) == 'string' then - item = splitKey(item) - end - return table.concat({ item.name, item.damage or 0, item.nbtHash }, ':') -end - function Craft.craftRecipe(recipe, count, inventoryAdapter, origItem) if type(recipe) == 'string' then recipe = Craft.recipes[recipe] if not recipe then - origItem.ingredients[recipe.result] = { - status = 'no recipe', - statusCode = Craft.STATUS_ERROR, - } return 0, 'No recipe' end end + for _,key in pairs(Util.keys(origItem.ingredients)) do + local e = origItem.ingredients[key] + if e and e.transient then + origItem.ingredients[key] = nil + end + end + return Craft.craftRecipeInternal(recipe, count, inventoryAdapter, origItem) +end + +function Craft.craftRecipeInternal(recipe, count, inventoryAdapter, origItem) local items = inventoryAdapter:listItems() if not items then - origItem.ingredients[recipe.result] = { - status = 'Inventory changed', - statusCode = Craft.STATUS_ERROR, - } return 0, 'Inventory changed' end - count = math.ceil(count / recipe.count) + local request = origItem.ingredients[recipe.result] + if not request then + request = { + crafted = 0, + count = count, + } + origItem.ingredients[recipe.result] = request + end + + if request.pending then + machineCraft(recipe, inventoryAdapter, + Craft.machineLookup[recipe.result], request) + return 0 + end + + local canCraft = Craft.getCraftableAmount(recipe, count, items, { }) + if canCraft == 0 then + + local resourceList = Craft.getResourceList(recipe, items, count) + for k,v in pairs(resourceList) do + if v.need > 0 then + if not origItem.ingredients[k] then + origItem.ingredients[k] = { + status = 'No recipe', + statusCode = Craft.STATUS_ERROR, + count = v.need, + crafted = 0, + transient = true, + } + end + end + end + return 0 + end + + count = math.ceil(canCraft / recipe.count) local maxCount = recipe.maxCount or math.floor(64 / recipe.count) for key,icount in pairs(Craft.sumIngredients(recipe)) do @@ -228,36 +216,33 @@ function Craft.craftRecipe(recipe, count, inventoryAdapter, origItem) maxCount = math.min(maxCount, itemDB:getMaxCount(key)) if itemCount < need then local irecipe = Craft.findRecipe(key) - if irecipe then - local iqty = need - itemCount - local crafted = Craft.craftRecipe(irecipe, iqty, inventoryAdapter, origItem) - if crafted ~= iqty then - turtle.select(1) - return 0 - end + if not irecipe then + return 0 + end + local iqty = need - itemCount + local crafted = Craft.craftRecipeInternal(irecipe, iqty, inventoryAdapter, origItem) + if crafted ~= iqty then + return 0 end end end local crafted = 0 repeat - local requested = math.min(count, maxCount) - + local batch = math.min(count, maxCount) if Craft.machineLookup[recipe.result] then - if not machineCraft(recipe, requested, inventoryAdapter, Craft.machineLookup[recipe.result], origItem) then - break - end - else - if not turtleCraft(recipe, requested, inventoryAdapter, origItem) then + if not machineCraft(recipe, inventoryAdapter, + Craft.machineLookup[recipe.result], request, batch) then break end + elseif not turtleCraft(recipe, inventoryAdapter, request, batch) then + break end - crafted = crafted + requested + crafted = crafted + batch count = count - maxCount until count <= 0 - turtle.select(1) return crafted * recipe.count end @@ -378,6 +363,27 @@ function Craft.getCraftableAmount(inRecipe, count, items, missing) return sumItems(inRecipe, { }, math.ceil(count / inRecipe.count)) end +function Craft.loadRecipes() + Craft.recipes = { } + + Util.merge(Craft.recipes, (Util.readTable(fs.combine(RECIPES_DIR, 'minecraft.db')) or { }).recipes) + + local config = Util.readTable('usr/config/recipeBooks.db') or { } + for _, book in pairs(config) do + local recipeFile = Util.readTable(book) + Util.merge(Craft.recipes, recipeFile.recipes) + end + + local recipes = Util.readTable(USER_RECIPES) or { } + Util.merge(Craft.recipes, recipes) + + for k,v in pairs(Craft.recipes) do + v.result = k + end + + Craft.machineLookup = Util.readTable(MACHINE_LOOKUP) or { } +end + function Craft.canCraft(item, count, items) return Craft.getCraftableAmount(Craft.recipes[item], count, items) == count end diff --git a/milo/plugins/craftTask.lua b/milo/plugins/craftTask.lua index 44e413d..7cd499a 100644 --- a/milo/plugins/craftTask.lua +++ b/milo/plugins/craftTask.lua @@ -10,17 +10,9 @@ local craftTask = { priority = 70, } --- Craft -function craftTask:craftItem(recipe, originalItem, count) - local toCraft = Craft.getCraftableAmount(recipe, count, Milo:listItems(), { }) - local crafted = 0 - - if toCraft > 0 then - crafted = Craft.craftRecipe(recipe, toCraft, context.inventoryAdapter, originalItem) - Milo:clearGrid() - end - - return crafted +function craftTask:craftItem(recipe, item, count) + Craft.craftRecipe(recipe, count, context.inventoryAdapter, item) + Milo:clearGrid() end -- Craft as much as possible regardless if all ingredients are available @@ -91,24 +83,21 @@ function craftTask:forceCraftItem(inRecipe, originalItem, inCount) end function craftTask:craft(recipe, item) - item.status = nil - item.statusCode = nil - if Milo:isCraftingPaused() then return end if item.forceCrafting then - item.crafted = item.crafted + self:forceCraftItem(recipe, item, item.count - item.crafted) + self:forceCraftItem(recipe, item, item.count - item.crafted) else - item.crafted = item.crafted + self:craftItem(recipe, item, item.count - item.crafted) + self:craftItem(recipe, item, item.count - item.crafted) end end function craftTask:cycle() for _,key in pairs(Util.keys(context.craftingQueue)) do local item = context.craftingQueue[key] - if item.count > 0 then + if item.count - item.crafted > 0 then local recipe = Craft.recipes[key] if recipe then self:craft(recipe, item) diff --git a/milo/plugins/jobList.lua b/milo/plugins/jobList.lua index 4132abf..93ed3a8 100644 --- a/milo/plugins/jobList.lua +++ b/milo/plugins/jobList.lua @@ -20,7 +20,7 @@ local jobList = UI.Page { sortColumn = 'index', backgroundFocusColor = colors.black, columns = { - { heading = 'Qty', key = 'remaining', width = 6 }, + { heading = 'Qty', key = 'remaining', width = 4 }, { heading = 'Crafting', key = 'displayName', }, { heading = 'Status', key = 'status', }, { heading = 'Req', key = 'count', width = 3 }, @@ -41,13 +41,15 @@ function jobList:updateList(craftList) for _,v in pairs(craftList) do table.insert(t, v) v.index = #t - v.showRemining = true + v.showRemaining = true for k2,v2 in pairs(v.ingredients) do - table.insert(t, v2) - if not v2.displayName then - v2.displayName = itemDB:getName(k2) + if v2 ~= v then + table.insert(t, v2) + if not v2.displayName then + v2.displayName = itemDB:getName(k2) + end + v2.index = #t end - v2.index = #t end end self.grid:setValues(t) @@ -59,8 +61,10 @@ end function jobList.grid:getDisplayValues(row) row = Util.shallowCopy(row) - if row.showRemining then + if row.showRemaining then row.remaining = row.count - row.crafted + else + row.displayName = ' ' .. row.displayName end return row end diff --git a/milo/plugins/machineLearn.lua b/milo/plugins/machineLearn.lua index 36e35df..d005c92 100644 --- a/milo/plugins/machineLearn.lua +++ b/milo/plugins/machineLearn.lua @@ -116,6 +116,8 @@ function pages.confirmation:validate() end end + -- TODO: maxCount needs to be entered by user ? ie. brewing station can only do 1 at a time + local recipe = { count = result.count, ingredients = { }, @@ -131,12 +133,13 @@ function pages.confirmation:validate() -- save the recipe context.userRecipes[key] = recipe Util.writeTable(Milo.RECIPES_FILE, context.userRecipes) - Craft.loadRecipes() -- save the machine association Craft.machineLookup[key] = machine.name Util.writeTable(MACHINE_LOOKUP, Craft.machineLookup) + Craft.loadRecipes() + local listingPage = UI:getPage('listing') local displayName = itemDB:getName(result) From 9ee67c1fad311233b26e54a744e0eb4bb13b78af Mon Sep 17 00:00:00 2001 From: kepler155c Date: Fri, 26 Oct 2018 01:03:31 -0400 Subject: [PATCH 033/165] milo wip --- apis/chestAdapter18.lua | 16 +++++++--- milo/Milo.lua | 7 ++-- milo/apis/inventoryAdapter.lua | 55 -------------------------------- milo/apis/milo.lua | 3 +- milo/apis/networkedAdapter18.lua | 2 +- milo/plugins/listing.lua | 2 +- 6 files changed, 18 insertions(+), 67 deletions(-) delete mode 100644 milo/apis/inventoryAdapter.lua diff --git a/apis/chestAdapter18.lua b/apis/chestAdapter18.lua index 7e76a57..f79aadb 100644 --- a/apis/chestAdapter18.lua +++ b/apis/chestAdapter18.lua @@ -72,6 +72,16 @@ end -- provide a consolidated list of items function ChestAdapter:listItems(throttle) + for _ = 1, 5 do + local list = self:listItemsInternal(throttle) + if list then + return list + end + end + error('Error accessing inventory: ' .. self.direction) +end + +function ChestAdapter:listItemsInternal(throttle) local cache = { } local items = { } throttle = throttle or Util.throttle() @@ -100,10 +110,8 @@ function ChestAdapter:listItems(throttle) end itemDB:flush() - if not Util.empty(items) then - self.cache = cache - return items - end + self.cache = cache + return items end function ChestAdapter:getItemInfo(item) diff --git a/milo/Milo.lua b/milo/Milo.lua index 603d5c8..6e06b18 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -67,12 +67,11 @@ 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 InventoryAdapter = require('inventoryAdapter') - local device = _G.device local fs = _G.fs local multishell = _ENV.multishell @@ -147,7 +146,7 @@ debug(storage) --TODO: cannot do this, must be able to add and mark inactive -- due to activity table -- add an networkAdapter:scan() - context.inventoryAdapter = InventoryAdapter.wrap({ remoteDefaults = storage }) + context.inventoryAdapter = NetworkAdapter({ remoteDefaults = storage }) if not context.inventoryAdapter then error('Invalid inventory configuration') @@ -201,7 +200,7 @@ local page = UI:getPage('listing') UI:setPage(page) page:setFocus(page.statusBar.filter) -- todo: move this line into listing code -Event.onInterval(5, function() +Event.onInterval(500, function() if not Milo:isCraftingPaused() then Milo:resetCraftingStatus() context.inventoryAdapter:refresh() diff --git a/milo/apis/inventoryAdapter.lua b/milo/apis/inventoryAdapter.lua deleted file mode 100644 index 3f16327..0000000 --- a/milo/apis/inventoryAdapter.lua +++ /dev/null @@ -1,55 +0,0 @@ -local Adapter = { } - -function Adapter.wrap(args) - local adapters = { - 'networkedAdapter18', - 'refinedAdapter', - 'meAdapter18', - 'chestAdapter18', - - -- adapters for version 1.7 - 'meAdapter', - 'chestAdapter', - } - - for _,adapterType in ipairs(adapters) do - local adapter = require(adapterType)(args) - - if adapter:isValid() then - - -- figure out which direction to push/pull items from an inventory - -- based on the side the inventory is attached and which way the - -- turtle/computer is facing - if args and args.facing and adapter.side and not adapter.direction then - local horz = { top = 'down', bottom = 'up' } - adapter.direction = horz[adapter.side] - - if not adapter.direction then - local sides = { - front = 0, - right = 1, - back = 2, - left = 3, - } - -- pretty sure computer/turtle have sides reversed - local cards = { - east = 0, - south = 1, - west = 2, - north = 3, - } - local icards = { - [ 0 ] = 'west', - [ 1 ] = 'north', - [ 2 ] = 'east', - [ 3 ] = 'south', - } - adapter.direction = icards[(cards[args.facing] + sides[adapter.side]) % 4] - end - end - return adapter - end - end -end - -return Adapter diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index 9bd90fb..378cd71 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -201,8 +201,7 @@ end -- Return a list of everything in the system function Milo:listItems() - self.items = self.context.inventoryAdapter:listItems() - return self.items + return self.context.inventoryAdapter:listItems() end return Milo diff --git a/milo/apis/networkedAdapter18.lua b/milo/apis/networkedAdapter18.lua index 2f1295f..5c81d90 100644 --- a/milo/apis/networkedAdapter18.lua +++ b/milo/apis/networkedAdapter18.lua @@ -1,6 +1,6 @@ local class = require('class') local Util = require('util') -local InventoryAdapter = require('inventoryAdapter') +local InventoryAdapter = require('inventoryAdapter') local Peripheral = require('peripheral') local NetworkedAdapter = class() diff --git a/milo/plugins/listing.lua b/milo/plugins/listing.lua index c59f7ac..2a941a6 100644 --- a/milo/plugins/listing.lua +++ b/milo/plugins/listing.lua @@ -225,7 +225,7 @@ function listingPage:enable() end function listingPage:refresh() - self.allItems = Milo:listItems() + self.allItems = context.inventoryAdapter:refresh() Milo:mergeResources(self.allItems) self:applyFilter() end From 2be14cdd4323effa570c10dfad8669488ab3788b Mon Sep 17 00:00:00 2001 From: kepler155c Date: Fri, 26 Oct 2018 01:24:29 -0400 Subject: [PATCH 034/165] milo wip --- apps/mirror.lua | 3 --- milo/Milo.lua | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/mirror.lua b/apps/mirror.lua index 09ea2ae..61a710e 100644 --- a/apps/mirror.lua +++ b/apps/mirror.lua @@ -43,9 +43,6 @@ if options.resize.value then term.current().getSize = mon.getSize end -debug(args) -debug(options) - if options.execute.value then -- TODO: allow args to be passed shell.run(options.execute.value) -- unpack(args)) diff --git a/milo/Milo.lua b/milo/Milo.lua index 6e06b18..5db66ba 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -200,7 +200,7 @@ local page = UI:getPage('listing') UI:setPage(page) page:setFocus(page.statusBar.filter) -- todo: move this line into listing code -Event.onInterval(500, function() +Event.onInterval(5, function() if not Milo:isCraftingPaused() then Milo:resetCraftingStatus() context.inventoryAdapter:refresh() From 6dde2245a62d70530f9383bd87dc2471d9318311 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Fri, 26 Oct 2018 06:19:34 -0400 Subject: [PATCH 035/165] milo wip --- milo/Milo.lua | 3 +- milo/MiloMonitor.lua | 2 +- milo/MiloRemote.lua | 77 +++++++++++++++++++++------------- milo/apis/milo.lua | 12 +++--- milo/apis/turtle/craft.lua | 1 + milo/core/machines.lua | 2 +- milo/plugins/craftTask.lua | 1 + milo/plugins/exportView.lua | 1 - milo/plugins/item.lua | 16 +------ milo/plugins/jobList.lua | 4 +- milo/plugins/listing.lua | 16 ++++--- milo/plugins/remote.lua | 16 ++++--- milo/plugins/replenishTask.lua | 6 +++ milo/plugins/storageView.lua | 50 +++++++++++++++++++--- 14 files changed, 133 insertions(+), 74 deletions(-) diff --git a/milo/Milo.lua b/milo/Milo.lua index 5db66ba..88c06f9 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -198,12 +198,11 @@ Milo:clearGrid() local page = UI:getPage('listing') UI:setPage(page) -page:setFocus(page.statusBar.filter) -- todo: move this line into listing code Event.onInterval(5, function() if not Milo:isCraftingPaused() then Milo:resetCraftingStatus() - context.inventoryAdapter:refresh() + Milo:refreshItems() for _, task in ipairs(context.tasks) do local s, m = pcall(function() task:cycle(context) end) diff --git a/milo/MiloMonitor.lua b/milo/MiloMonitor.lua index c36d4f2..1e59828 100644 --- a/milo/MiloMonitor.lua +++ b/milo/MiloMonitor.lua @@ -6,7 +6,7 @@ local colors = _G.colors local device = _G.device local args = { ... } -local mon = device[args[1] or 'monitor'] or error('Syntax: debug ') +local mon = device[args[1] or 'monitor'] or error('Syntax: MiloMonitor ') local config = Util.readTable('/usr/config/milo') or error('Milo is not configured') local row diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index cd9403d..2b9830c 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -1,18 +1,24 @@ _G.requireInjector(_ENV) +local Event = require('event') local Socket = require('socket') +local sync = require('sync') local UI = require('ui') local Util = require('util') local colors = _G.colors +local device = _G.device local socket +local neural = device.neuralInterface local options = { user = { arg = 'u', type = 'string', desc = 'User name associated with bound manipulator' }, - server = { arg = 's', type = 'number', + slot = { arg = 's', type = 'number', + desc = 'Optional inventory slot to use to transfer to milo' }, + server = { arg = 'm', type = 'number', desc = 'ID of Milo server' }, - help = { arg = 'h', type = 'flag', value = false, + help = { arg = 'h', type = 'flag', value = false, desc = 'Displays the options' }, } @@ -107,26 +113,31 @@ local function filterItems(t, filter, displayMode) end function page:sendRequest(data) - local msg + local response - for _ = 1, 2 do - if not socket or not socket.connected then - socket, msg = Socket.connect(options.server.value, 4242) - if socket then - socket:write(options.user.value) - end - end - if socket then - if socket:write(data) then - local response = socket:read(2) - if response then - return response + sync(self, function() + local msg + for _ = 1, 2 do + if not socket or not socket.connected then + socket, msg = Socket.connect(options.server.value, 4242) + if socket then + socket:write(options.user.value) end end - socket:close() + if socket then + if socket:write(data) then + response = socket:read(2) + if response then + return + end + end + socket:close() + end end - end - self.notification:error(msg or 'Failed to connect') + self.notification:error(msg or 'Failed to connect') + end) + + return response end function page.statusBar:draw() @@ -162,23 +173,17 @@ function page:eventHandler(event) elseif event.type == 'eject' then local item = self.grid:getSelected() if item then - local items = self:sendRequest({ request = 'transfer', item = item, count = 1 }) - if items then - self.items = items - self:applyFilter() - self.grid:draw() - end + local response = self:sendRequest({ request = 'transfer', item = item, count = 1 }) + item.count = item.count - response.count + self.grid:draw() end elseif event.type == 'eject_stack' then local item = self.grid:getSelected() if item then - local items = self:sendRequest({ request = 'transfer', item = item, count = 64 }) - if items then - self.items = items - self:applyFilter() - self.grid:draw() - end + local response = self:sendRequest({ request = 'transfer', item = item, count = 64 }) + item.count = item.count - response.count + self.grid:draw() end elseif event.type == 'refresh' then @@ -235,6 +240,18 @@ function page:applyFilter() self.grid:setValues(t) end +if neural and options.slot.value and neural.getInventory then + Event.onInterval(1, function() + local inv = neural.getInventory() + if inv and inv.getItem(options.slot.value) then + page:sendRequest({ request = 'deposit', slot = options.slot.value }) + -- local item = + -- TODO: update count for this one item + -- page.grid:draw() page:sync() + end + end) +end + UI:setPage(page) UI:pullEvents() diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index 378cd71..f4b8439 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -25,7 +25,6 @@ function Milo:requestCrafting(item) item.ingredients = { [ key ] = item } --- item.ingredients[key] = item item.crafted = 0 self.context.craftingQueue[key] = item end @@ -66,17 +65,11 @@ function Milo:uniqueKey(item) end function Milo:resetCraftingStatus() - - -- todo: move to end of processing tasks ? - -- what if someone hoppers in items ? -- this shouldnt be allowed - -- all items must come in via pullItems self.context.inventoryAdapter.activity = { } for _,key in pairs(Util.keys(self.context.craftingQueue)) do local item = self.context.craftingQueue[key] if item.crafted >= item.count then - debug('removing:') - debug(item) self.context.craftingQueue[key] = nil end end @@ -204,4 +197,9 @@ function Milo:listItems() return self.context.inventoryAdapter:listItems() end +-- force a full rescan of chests +function Milo:refreshItems() + return self.context.inventoryAdapter:refresh() +end + return Milo diff --git a/milo/apis/turtle/craft.lua b/milo/apis/turtle/craft.lua index be62582..ac2fe8f 100644 --- a/milo/apis/turtle/craft.lua +++ b/milo/apis/turtle/craft.lua @@ -188,6 +188,7 @@ function Craft.craftRecipeInternal(recipe, count, inventoryAdapter, origItem) if canCraft == 0 then local resourceList = Craft.getResourceList(recipe, items, count) +_G._p2 = resourceList for k,v in pairs(resourceList) do if v.need > 0 then if not origItem.ingredients[k] then diff --git a/milo/core/machines.lua b/milo/core/machines.lua index 18d03b5..ef1394f 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -253,7 +253,7 @@ function machineWizard:eventHandler(event) elseif event.type == 'focus_change' then self.statusBar:setStatus(event.focused.help) - elseif event.type == 'form_invalid' then + elseif event.type == 'form_invalid' or event.type == 'general_error' then self.notification:error(event.message) self:setFocus(event.field) diff --git a/milo/plugins/craftTask.lua b/milo/plugins/craftTask.lua index 7cd499a..0ff8a84 100644 --- a/milo/plugins/craftTask.lua +++ b/milo/plugins/craftTask.lua @@ -70,6 +70,7 @@ function craftTask:forceCraftItem(inRecipe, originalItem, inCount) end end end + if craftable > 0 then craftable = Craft.craftRecipe(recipe, craftable * recipe.count, context.inventoryAdapter, originalItem) / recipe.count diff --git a/milo/plugins/exportView.lua b/milo/plugins/exportView.lua index 6bd20da..43a5108 100644 --- a/milo/plugins/exportView.lua +++ b/milo/plugins/exportView.lua @@ -132,7 +132,6 @@ function itemSlideout:eventHandler(event) self.form:save() self.form.values.name = itemDB:makeKey(selected) self:hide() - debug('calling cllbck') self.callback() end diff --git a/milo/plugins/item.lua b/milo/plugins/item.lua index c315296..3ef6200 100644 --- a/milo/plugins/item.lua +++ b/milo/plugins/item.lua @@ -36,24 +36,12 @@ local itemPage = UI.Page { help = 'Craft until out of ingredients' }, ]] - [4] = UI.Chooser { - width = 7, + [4] = UI.Checkbox { formLabel = 'Ignore Dmg', formKey = 'ignoreDamage', - nochoice = 'No', - choices = { - { name = 'Yes', value = true }, - { name = 'No', value = false }, - }, help = 'Ignore damage of item' }, - [5] = UI.Chooser { - width = 7, + [5] = UI.Checkbox { formLabel = 'Ignore NBT', formKey = 'ignoreNbtHash', - nochoice = 'No', - choices = { - { name = 'Yes', value = true }, - { name = 'No', value = false }, - }, help = 'Ignore NBT of item' }, --[[ diff --git a/milo/plugins/jobList.lua b/milo/plugins/jobList.lua index 93ed3a8..7a5b9e1 100644 --- a/milo/plugins/jobList.lua +++ b/milo/plugins/jobList.lua @@ -23,8 +23,7 @@ local jobList = UI.Page { { heading = 'Qty', key = 'remaining', width = 4 }, { heading = 'Crafting', key = 'displayName', }, { heading = 'Status', key = 'status', }, - { heading = 'Req', key = 'count', width = 3 }, - { heading = 'Cra', key = 'crafted', width = 3 }, + { heading = 'Progress', key = 'progress', width = 8 }, }, }, } @@ -66,6 +65,7 @@ function jobList.grid:getDisplayValues(row) else row.displayName = ' ' .. row.displayName end + row.progress = string.format('%d/%d', row.crafted, row.count) return row end diff --git a/milo/plugins/listing.lua b/milo/plugins/listing.lua index 2a941a6..f4b9023 100644 --- a/milo/plugins/listing.lua +++ b/milo/plugins/listing.lua @@ -40,9 +40,10 @@ end local listingPage = UI.Page { menuBar = UI.MenuBar { buttons = { - { text = 'Learn', event = 'learn' }, - { text = 'Forget', event = 'forget' }, - { text = 'Craft', event = 'craft' }, + { text = 'Learn', event = 'learn' }, + { text = 'Forget', event = 'forget' }, + { text = 'Craft', event = 'craft' }, + { text = '...', event = 'machines' }, { text = 'Refresh', event = 'refresh', x = -9 }, }, }, @@ -134,7 +135,12 @@ function listingPage:eventHandler(event) elseif event.type == 'eject' then local item = self.grid:getSelected() if item then - queue(function() Milo:eject(item, 1) end) + queue(function() + Milo:eject(item, 1) + local updated = Milo:getItem(Milo:listItems(), item) + item.count = updated and updated.count or 0 + self.grid:draw() + end) end elseif event.type == 'eject_stack' then @@ -225,7 +231,7 @@ function listingPage:enable() end function listingPage:refresh() - self.allItems = context.inventoryAdapter:refresh() + self.allItems = Milo:refreshItems() Milo:mergeResources(self.allItems) self:applyFilter() end diff --git a/milo/plugins/remote.lua b/milo/plugins/remote.lua index b0b0088..b517dcc 100644 --- a/milo/plugins/remote.lua +++ b/milo/plugins/remote.lua @@ -34,12 +34,20 @@ local function client(socket) break end if data.request == 'list' then - local items = Milo:listItems() + local items = Milo:refreshItems() Milo:mergeResources(items) socket:write(items) + elseif data.request == 'deposit' then + local count = manipulator.getInventory().pushItems( + context.localName, + data.slot, + 64) + socket:write({ count = count }) + Milo:clearGrid() + elseif data.request == 'transfer' then - context.inventoryAdapter:provide( + local count = context.inventoryAdapter:provide( data.item, data.count, nil, @@ -52,9 +60,7 @@ local function client(socket) slot.count) end) - local items = Milo:listItems() - Milo:mergeResources(items) - socket:write(items) + socket:write({ count = count }) end until not socket.connected diff --git a/milo/plugins/replenishTask.lua b/milo/plugins/replenishTask.lua index d204245..bed7987 100644 --- a/milo/plugins/replenishTask.lua +++ b/milo/plugins/replenishTask.lua @@ -30,7 +30,13 @@ function ReplenishTask:cycle(context) count = res.low - item.count, name = item.name, displayName = item.displayName, + replenish = true, }) + else + local request = context.craftingQueue[Milo:uniqueKey(item)] + if request and request.replenish then + request.count = request.crafted + end end end end diff --git a/milo/plugins/storageView.lua b/milo/plugins/storageView.lua index 9a855bd..78af345 100644 --- a/milo/plugins/storageView.lua +++ b/milo/plugins/storageView.lua @@ -1,6 +1,8 @@ +local itemDB = require('itemDB') local UI = require('ui') local colors = _G.colors +local device = _G.device local storageView = UI.Window { mtype = 'storage', @@ -16,14 +18,24 @@ local storageView = UI.Window { limit = 4, validate = 'numeric', pruneEmpty = true, }, - [2] = UI.TextEntry { - formLabel = 'Lock to', formKey = 'lockWith', + [2] = UI.Checkbox { + formLabel = 'Locked', formKey = 'lockWith', help = 'Locks chest to a single item type', - width = 18, limit = 64, pruneEmpty = true, + pruneEmpty = true, }, - [3] = UI.Button { - x = -9, ey = -4, - text = 'Detect', help = 'Determine what is currently present', + [3] = UI.Text { + x = 16, ex = -2, y = 3, + value = 'minecraft:xxxxx:0' + }, + [4] = UI.Checkbox { + formLabel = 'Void', formKey = 'voidExcess', + help = 'Void excess if locked - TODO', + pruneEmpty = true, + }, + [5] = UI.Checkbox { + formLabel = 'Partition', formKey = 'voidExcess', + help = 'TODO', + pruneEmpty = true, }, }, } @@ -38,7 +50,33 @@ function storageView:validate() end function storageView:setMachine(machine) + self.machine = machine self.form:setValues(machine) + self.form[3].value = machine.lock and itemDB:getName(machine.lock) or '' +end + +function storageView:eventHandler(event) + if event.type == 'checkbox_change' and event.element.formKey == 'lockWith' then + if event.checked then + if device[self.machine.name] and device[self.machine.name].list then + local _, slot = next(device[self.machine.name].list()) + if slot then + self.machine.lock = itemDB:makeKey(slot) + self.form[3].value = itemDB:getName(slot) + else + self:emit({ + type = 'general_error', + field = event.element, + message = 'The chest must contain the item to lock' }) + self.form[3].value = false + end + end + else + self.machine.lock = nil + self.form[3].value = '' + end + self.form[3]:draw() + end end UI:getPage('machineWizard').wizard:add({ storage = storageView }) From f9ae197cfce9f18496bef7289d3b79ab0198e78b Mon Sep 17 00:00:00 2001 From: kepler155c Date: Fri, 26 Oct 2018 16:08:07 -0400 Subject: [PATCH 036/165] 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( From 87da95ca0530459acdc9ce5ccf8a9ebd7e14a8a3 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Fri, 26 Oct 2018 18:44:42 -0400 Subject: [PATCH 037/165] milo wip --- milo/apis/storage.lua | 116 ++++++++++++++++++++++------------------- milo/core/machines.lua | 29 +++++++++++ 2 files changed, 92 insertions(+), 53 deletions(-) diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 66f45d9..8a9d8f3 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -11,7 +11,6 @@ local NetworkedAdapter = class() function NetworkedAdapter:init(args) local defaults = { - remotes = { }, remoteDefaults = { }, dirty = true, listCount = 0, @@ -25,7 +24,6 @@ listCount = 0, 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() @@ -37,7 +35,6 @@ listCount = 0, 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) @@ -67,56 +64,69 @@ function NetworkedAdapter:initTrashcan(detachedDevice) if (detachedDevice and self.trashcan and self.trashcan.name == detachedDevice) or (trashcan and not device[trashcan.name]) then self.trashcan = nil -debug('Trashcan: none') +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) +debug(' Trashcan: ' .. trashcan.name) self.trashcan = device[trashcan.name] end end end -function NetworkedAdapter:initStorage(detachedDevice) - local storage = { } +function NetworkedAdapter:initStorage() 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]) + for k,v in pairs(self.remoteDefaults) do + if v.adapter then + v.adapter.online = not not device[k] + if v.mtype == 'storage' then + online = online and v.adapter.online end + elseif v.mtype == 'storage' then + v.adapter = InventoryAdapter.wrap({ side = k }) + v.adapter.online = true + end + if v.mtype == 'storage' then + debug(' %s: %s', v.adapter.online and ' online' or 'offline', k) end end - table.sort(self.remotes, function(a, b) + self:setOnline(online) +end + +function NetworkedAdapter:onlineAdapters(reversed) + local iter = { } + for _, v in pairs(self.remoteDefaults) do + if v.adapter and v.adapter.online and v.mtype == 'storage' then + table.insert(iter, v) + end + end + + local function forwardSort(a, b) if not a.priority then return false elseif not b.priority then return true end return a.priority > b.priority - end) + end - self:setOnline(online) + local function backwardSort(a, b) + return not forwardSort(a, b) + end + + table.sort(iter, reversed and backwardSort or forwardSort) + + local i = 0 + return function() + i = i + 1 + local a = iter[i] + if a then + return a, a.adapter + end + end end function NetworkedAdapter:refresh(throttle) @@ -138,9 +148,9 @@ self.listCount = self.listCount + 1 local items = { } throttle = throttle or Util.throttle() - for _, remote in pairs(self.remotes) do - remote:listItems(throttle) - local rcache = remote.cache or { } + for _, adapter in self:onlineAdapters() do + adapter:listItems(throttle) + local rcache = adapter.cache or { } -- TODO: add a method in each adapter that only updates a passed cache for key,v in pairs(rcache) do @@ -173,14 +183,14 @@ 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) + for _, adapter in self:onlineAdapters() do + local amount = adapter: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 + adapter.dirty = true end qty = qty - amount total = total + amount @@ -193,11 +203,11 @@ function NetworkedAdapter:provide(item, qty, slot, direction) end function NetworkedAdapter:trash(source, slot, count) - if not self.trashcan then - return + if self.trashcan then + debug('TRA: %s[%d] (%d)', source, slot, count) + return self.trashcan.pullItems(source, slot, count) end -debug('TRA: %s[%d] (%d)', source, slot, count) - return self.trashcan.pullItems(source, slot, count) + return 0 end function NetworkedAdapter:import(source, slot, count, item) @@ -218,14 +228,14 @@ function NetworkedAdapter:insert(slot, qty, toSlot, item, source) self:listItems() end - local function insert(remote) - local amount = remote:insert(slot, qty, toSlot, source or self.localName) + local function insert(adapter) + local amount = adapter: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) + source or self.localName, slot, adapter.side) self.dirty = true - remote.dirty = true + adapter.dirty = true local entry = self.activity[key] or 0 self.activity[key] = entry + amount end @@ -233,11 +243,11 @@ debug('INS: %s(%d): %s[%d] -> %s', total = total + amount end - -- found a chest locked with this item - for _, remote in pairs(self.remotes) do + -- find a chest locked with this item + for remote in self:onlineAdapters() do -- TODO: proper checking using ignore dmg/nbt if remote.lock == key or remote.lock == item.name then - insert(remote) + insert(remote.adapter) if qty > 0 then -- TODO: only if void flag set total = total + self:trash(source, slot, qty) end @@ -247,23 +257,23 @@ debug('INS: %s(%d): %s[%d] -> %s', 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 + for _, adapter in self:onlineAdapters(true --[[ reversed ]]) do if qty <= 0 then break end - if remote.cache and remote.cache[key] and not remote.lockWith then - insert(remote) + if adapter.cache and adapter.cache[key] and not adapter.lockWith then + insert(adapter) end end end -- high to low priority - for _, remote in ipairs(self.remotes) do + for remote in self:onlineAdapters() do if qty <= 0 then break end if not remote.lockWith then - insert(remote) + insert(remote.adapter) end end diff --git a/milo/core/machines.lua b/milo/core/machines.lua index ef1394f..be7f3c0 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -197,7 +197,11 @@ function machineWizard.wizard:eventHandler(event) end function machineWizard:enable(machine) + local adapter = machine.adapter + machine.adapter = nil -- don't deep copy the adapter self.machine = Util.deepCopy(machine) + machine.adapter = adapter + self.wizard.pages.general.form:setValues(self.machine) self.wizard.pages.general.form[1].shadowText = machine.name @@ -224,12 +228,26 @@ function machineWizard:eventHandler(event) elseif event.type == 'accept' then +_G._p2 = self.machine +debug(1) + -- todo: no need for calling this function - use validate instead for _, v in pairs(self.wizard.pages) do if v.save and v.index then -- only save if the page was valid for this mtype v:save(self.machine) end end +debug(2) + + local adapter = self.machine.adapter + self.machine.adapter = nil + +local t = { } +for k,v in pairs(context.config.remoteDefaults) do + t[k] = v.adapter + v.adapter = nil +end + context.config.remoteDefaults[self.machine.name] = Util.prune(self.machine, function(v) if type(v) == 'boolean' then @@ -243,6 +261,17 @@ function machineWizard:eventHandler(event) end) Config.update('milo', context.config) +for k,v in pairs(t) do + context.config.remoteDefaults[k].adapter = v +end + +debug(3) + Util.clear(context.config.remoteDefaults[self.machine.name]) +debug(4) + Util.merge(context.config.remoteDefaults[self.machine.name], self.machine) +debug(5) + context.config.remoteDefaults[self.machine.name].adapter = adapter + UI:setPreviousPage() elseif event.type == 'enable_view' then From d227d6ebb7ddf1ae7ee9c1bb2f0531a9c845bdde Mon Sep 17 00:00:00 2001 From: kepler155c Date: Fri, 26 Oct 2018 23:08:44 -0400 Subject: [PATCH 038/165] milo wip --- milo/Milo.lua | 6 ++- milo/apis/milo.lua | 2 +- milo/apis/storage.lua | 67 +++++++++--------------------- milo/core/machines.lua | 88 +++++++++++++++++++++++----------------- milo/plugins/listing.lua | 21 +++++++++- 5 files changed, 94 insertions(+), 90 deletions(-) diff --git a/milo/Milo.lua b/milo/Milo.lua index c088274..e902594 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -67,6 +67,7 @@ local Util = require('util') local fs = _G.fs local multishell = _ENV.multishell +local os = _G.os local shell = _ENV.shell if multishell then @@ -113,7 +114,6 @@ end) Milo:init(context) context.storage:initStorage() -context.storage:initTrashcan() local function loadDirectory(dir) for _, file in pairs(fs.list(dir)) do @@ -157,4 +157,8 @@ Event.onInterval(5, function() end end) +os.queueEvent( + context.storage:isOnline() and 'storage_online' or 'storage_offline', + context.storage:isOnline()) + UI:pullEvents() diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index e139353..f02401c 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -116,7 +116,7 @@ end function Milo:clearGrid() local function clear() turtle.eachFilledSlot(function(slot) - self.context.storage:insert(slot.index, slot.count, nil, slot) + self.context.storage:import(self.context.localName, slot.index, slot.count, slot) end) for i = 1, 16 do diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 8a9d8f3..ddb2f93 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -24,32 +24,20 @@ listCount = 0, self.localName = modem.getNameLocal() Event.on({ 'device_attach' }, function(_, 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 +debug('attach: ' .. dev) + self:initStorage() end) Event.on({ 'device_detach' }, function(_, 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 +debug('detach: ' .. dev) + self:initStorage(dev) end) end function NetworkedAdapter:setOnline(online) if online ~= self.storageOnline then self.storageOnline = online - os.queueEvent(self.storageOnline and 'storage_online' or 'storage_offline') + os.queueEvent(self.storageOnline and 'storage_online' or 'storage_offline', online) debug('Storage: %s', self.storageOnline and 'online' or 'offline') end end @@ -58,22 +46,6 @@ 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() local online = true @@ -81,15 +53,13 @@ function NetworkedAdapter:initStorage() for k,v in pairs(self.remoteDefaults) do if v.adapter then v.adapter.online = not not device[k] - if v.mtype == 'storage' then - online = online and v.adapter.online - end - elseif v.mtype == 'storage' then + elseif device[k] and device[k].list and device[k].size and device[k].pullItems then v.adapter = InventoryAdapter.wrap({ side = k }) v.adapter.online = true end if v.mtype == 'storage' then - debug(' %s: %s', v.adapter.online and ' online' or 'offline', k) + online = online and not not (v.adapter and v.adapter.online) + debug(' %s: %s', v.adapter and v.adapter.online and ' online' or 'offline', k) end end @@ -184,11 +154,11 @@ function NetworkedAdapter:provide(item, qty, slot, direction) local total = 0 for _, adapter in self:onlineAdapters() do - local amount = adapter:provide(item, qty, slot, direction) + local amount = adapter:provide(item, qty, slot, direction or self.localName) 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 '') +debug('EXT: %s(%d): %s -> %s%s', + item.name, amount, adapter.name, direction or self.localName, + slot and string.format('[%d]', slot) or '') self.dirty = true adapter.dirty = true end @@ -203,9 +173,10 @@ function NetworkedAdapter:provide(item, qty, slot, direction) end function NetworkedAdapter:trash(source, slot, count) - if self.trashcan then - debug('TRA: %s[%d] (%d)', source, slot, count) - return self.trashcan.pullItems(source, slot, count) + local trashcan = Util.find(self.remoteDefaults, 'mtype', 'trashcan') + if trashcan and trashcan.adapter and trashcan.adapter.online then +debug('TRA: %s[%d] (%d)', source or self.localName, slot, count or 64) + return trashcan.adapter.pullItems(source or self.localName, slot, count) end return 0 end @@ -233,7 +204,7 @@ function NetworkedAdapter:insert(slot, qty, toSlot, item, source) if amount > 0 then debug('INS: %s(%d): %s[%d] -> %s', item.name, amount, - source or self.localName, slot, adapter.side) + source, slot, adapter.name) self.dirty = true adapter.dirty = true local entry = self.activity[key] or 0 @@ -261,7 +232,7 @@ debug('INS: %s(%d): %s[%d] -> %s', if qty <= 0 then break end - if adapter.cache and adapter.cache[key] and not adapter.lockWith then + if adapter.cache and adapter.cache[key] and not adapter.lock then insert(adapter) end end @@ -272,7 +243,7 @@ debug('INS: %s(%d): %s[%d] -> %s', if qty <= 0 then break end - if not remote.lockWith then + if not remote.lock then insert(remote.adapter) end end diff --git a/milo/core/machines.lua b/milo/core/machines.lua index be7f3c0..10d3187 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -10,6 +10,20 @@ local device = _G.device local context = Milo:getContext() +local function saveConfig() + local t = { } + for k,v in pairs(context.config.remoteDefaults) do + t[k] = v.adapter + v.adapter = nil + end + + Config.update('milo', context.config) + + for k,v in pairs(t) do + context.config.remoteDefaults[k].adapter = v + end +end + local machinesPage = UI.Page { titleBar = UI.TitleBar { previousPage = true, @@ -19,14 +33,21 @@ local machinesPage = UI.Page { y = 2, ey = -2, values = context.config.remoteDefaults, columns = { + { key = 'priority', width = 3 }, { heading = 'Name', key = 'displayName' }, - { heading = 'Priority', key = 'priority', width = 5 }, - { heading = 'Type', key = 'mtype', width = 5 }, + { heading = 'Type', key = 'mtype', width = 5 }, }, sortColumn = 'displayName', + help = 'Select Machine', + }, + remove = UI.Button { + y = -1, x = -4, + text = '-', event = 'remove_machine', + help = 'Remove Machine', }, statusBar = UI.StatusBar { - values = 'Select Machine', + ex = -7, + backgroundColor = colors.cyan, }, } @@ -80,6 +101,19 @@ end function machinesPage:eventHandler(event) if event.type == 'grid_select' then UI:setPage('machineWizard', event.selected) + + elseif event.type == 'remove_machine' then + local machine = self.grid:getSelected() + if machine then + context.config.remoteDefaults[machine.name] = nil + saveConfig() + end + self.grid:update() + self.grid:draw() + + elseif event.type == 'focus_change' then + self.statusBar:setStatus(event.focused.help) + else UI.Page.eventHandler(self, event) end @@ -200,10 +234,11 @@ function machineWizard:enable(machine) local adapter = machine.adapter machine.adapter = nil -- don't deep copy the adapter self.machine = Util.deepCopy(machine) + self.machine.adapter = adapter machine.adapter = adapter self.wizard.pages.general.form:setValues(self.machine) - self.wizard.pages.general.form[1].shadowText = machine.name + self.wizard.pages.general.form[1].shadowText = self.machine.name -- restore indices for _, page in pairs(self.wizard.pages) do @@ -227,50 +262,27 @@ function machineWizard:eventHandler(event) UI:setPreviousPage() elseif event.type == 'accept' then - -_G._p2 = self.machine -debug(1) - -- todo: no need for calling this function - use validate instead for _, v in pairs(self.wizard.pages) do if v.save and v.index then -- only save if the page was valid for this mtype v:save(self.machine) end end -debug(2) - local adapter = self.machine.adapter - self.machine.adapter = nil + Util.prune(self.machine, function(v) + if type(v) == 'boolean' then + return v + elseif type(v) == 'string' then + return #v > 0 + elseif type(v) == 'table' then + return not Util.empty(v) + end + return true + end) -local t = { } -for k,v in pairs(context.config.remoteDefaults) do - t[k] = v.adapter - v.adapter = nil -end - - context.config.remoteDefaults[self.machine.name] = - Util.prune(self.machine, function(v) - if type(v) == 'boolean' then - return v - elseif type(v) == 'string' then - return #v > 0 - elseif type(v) == 'table' then - return not Util.empty(v) - end - return true - end) - Config.update('milo', context.config) - -for k,v in pairs(t) do - context.config.remoteDefaults[k].adapter = v -end - -debug(3) Util.clear(context.config.remoteDefaults[self.machine.name]) -debug(4) Util.merge(context.config.remoteDefaults[self.machine.name], self.machine) -debug(5) - context.config.remoteDefaults[self.machine.name].adapter = adapter + saveConfig() UI:setPreviousPage() diff --git a/milo/plugins/listing.lua b/milo/plugins/listing.lua index f4b9023..6a18aef 100644 --- a/milo/plugins/listing.lua +++ b/milo/plugins/listing.lua @@ -1,5 +1,6 @@ local Craft = require('turtle.craft') local itemDB = require('itemDB') +local Event = require('event') local Milo = require('milo') local UI = require('ui') local Util = require('util') @@ -59,7 +60,7 @@ local listingPage = UI.Page { }, statusBar = UI.StatusBar { filter = UI.TextEntry { - x = 1, ex = -4, + x = 1, ex = -13, limit = 50, shadowText = 'filter', shadowTextColor = colors.gray, @@ -69,6 +70,13 @@ local listingPage = UI.Page { [ 'enter' ] = 'craft', }, }, + storageStatus = UI.Button { + x = -12, ex = -4, + event = 'toggle_online', + backgroundColor = colors.cyan, + textColor = colors.lime, + text = '', + }, display = UI.Button { x = -3, event = 'toggle_display', @@ -130,7 +138,7 @@ function listingPage:eventHandler(event) UI:exitPullEvents() elseif event.type == 'resume' then - Milo:resumeCrafting() + context.storage:setOnline(true) elseif event.type == 'eject' then local item = self.grid:getSelected() @@ -241,4 +249,13 @@ function listingPage:applyFilter() self.grid:setValues(t) end +Event.on({ 'storage_offline', 'storage_online' }, function(e, isOnline) + listingPage.statusBar.storageStatus.text = + isOnline and 'online' or 'offline' + listingPage.statusBar.storageStatus.textColor = + isOnline and colors.lime or colors.red + listingPage.statusBar.storageStatus:draw() + listingPage:sync() +end) + UI:addPage('listing', listingPage) From 57e7a574c49373ae570a69561af009496fe0ee2e Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sat, 27 Oct 2018 21:35:21 -0400 Subject: [PATCH 039/165] milo wip --- milo/Milo.lua | 14 ++- milo/MiloRemote.lua | 27 ++-- milo/apis/milo.lua | 14 +++ milo/apis/storage.lua | 20 +-- milo/core/machines.lua | 186 ++++++++++++++++++++++++--- milo/plugins/exportTask.lua | 36 +++--- milo/plugins/exportView.lua | 228 +++++++++------------------------- milo/plugins/importTask.lua | 33 ++++- milo/plugins/importView.lua | 108 +++++++++++----- milo/plugins/jobList.lua | 2 +- milo/plugins/machineLearn.lua | 13 +- milo/plugins/redstoneTask.lua | 28 +++++ milo/plugins/redstoneView.lua | 73 +++++++++++ milo/plugins/remote.lua | 1 + milo/plugins/storageView.lua | 7 +- milo/plugins/turtleLearn.lua | 16 +-- 16 files changed, 518 insertions(+), 288 deletions(-) create mode 100644 milo/plugins/redstoneTask.lua create mode 100644 milo/plugins/redstoneView.lua diff --git a/milo/Milo.lua b/milo/Milo.lua index e902594..7256a09 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -85,6 +85,9 @@ if not modem or not modem.getNameLocal then error('Wired modem is not connected') end +local introspectionModule = Peripheral.get('plethora:introspection') or + error('Introspection module not found') + local function loadResources() local resources = Util.readTable(Milo.RESOURCE_FILE) or { } for k,v in pairs(resources) do @@ -98,12 +101,15 @@ local context = { config = config, resources = loadResources(), userRecipes = Util.readTable(Milo.RECIPES_FILE) or { }, - learnTypes = { }, - machineTypes = { }, - localName = modem.getNameLocal(), - tasks = { }, + craftingQueue = { }, + + learnTypes = { }, + tasks = { }, + + localName = modem.getNameLocal(), storage = Storage(config), + introspectionModule = introspectionModule, } _G._p = context --debug diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index 2b9830c..75fd5f3 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -9,7 +9,6 @@ local Util = require('util') local colors = _G.colors local device = _G.device local socket -local neural = device.neuralInterface local options = { user = { arg = 'u', type = 'string', @@ -34,6 +33,11 @@ if not options.user.value or not options.server.value then error('Invalid arguments') end +if options.slot.value and + not (device.neuralInterface and device.neuralInterface.getInventory) then + error('Introspection module is required for transferring items') +end + local page = UI.Page { menuBar = UI.MenuBar { buttons = { @@ -240,14 +244,21 @@ function page:applyFilter() self.grid:setValues(t) end -if neural and options.slot.value and neural.getInventory then +if options.slot.value then + debug('Transfer items initialized') Event.onInterval(1, function() - local inv = neural.getInventory() - if inv and inv.getItem(options.slot.value) then - page:sendRequest({ request = 'deposit', slot = options.slot.value }) - -- local item = - -- TODO: update count for this one item - -- page.grid:draw() page:sync() + local neural = device.neuralInterface + if neural and neural.getInventory then + local item = neural.getInventory().getItem(options.slot.value) + if item then + debug('depositing') + page:sendRequest({ request = 'deposit', slot = options.slot.value }) + -- local item = + -- TODO: update count for this one item + -- page.grid:draw() page:sync() + end + else + debug('missing Introspection module') end end) end diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index f02401c..bcbbef8 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -129,6 +129,20 @@ function Milo:clearGrid() return clear() or clear() end +function Milo:getTurtleInventory() + local list = { } + for i = 1,16 do + -- TODO: update item db + local item = self.context.introspectionModule.getInventory().getItemMeta(i) + if item then + itemDB:add(item) + list[i] = item + end + end + itemDB:flush() + return list +end + function Milo:eject(item, qty) local s, m = pcall(function() self.context.storage:provide(item, qty) diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index ddb2f93..39ab696 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -23,17 +23,24 @@ listCount = 0, 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) + Event.on({ 'device_attach', 'device_detach' }, function(e, dev) +debug('%s: %s', e, tostring(dev)) self:initStorage() end) - - Event.on({ 'device_detach' }, function(_, dev) -debug('detach: ' .. dev) - self:initStorage(dev) + Event.onInterval(15, function() + self:showStorage() end) end +function NetworkedAdapter:showStorage() + debug('Storage:') + for k,v in pairs(self.remoteDefaults) do + local online = v.adapter and v.adapter.online + debug(' %s: %s', online and ' online' or 'offline', k) + end + debug('') +end + function NetworkedAdapter:setOnline(online) if online ~= self.storageOnline then self.storageOnline = online @@ -59,7 +66,6 @@ function NetworkedAdapter:initStorage() end if v.mtype == 'storage' then online = online and not not (v.adapter and v.adapter.online) - debug(' %s: %s', v.adapter and v.adapter.online and ' online' or 'offline', k) end end diff --git a/milo/core/machines.lua b/milo/core/machines.lua index 10d3187..4201859 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -51,6 +51,22 @@ local machinesPage = UI.Page { }, } +function machinesPage.grid:getDisplayValues(row) + row = Util.shallowCopy(row) + row.displayName = row.displayName or row.name + return row +end + +function machinesPage.grid:getRowTextColor(row, selected) + if not device[row.name] then + return colors.red + end + if row.mtype == 'ignore' then + return colors.lightGray + end + return UI.Grid:getRowTextColor(row, selected) +end + function machinesPage:getList() -- TODO: remove dedupe naming in perf code ? for _, v in pairs(device) do @@ -82,22 +98,6 @@ function machinesPage:disable() Event.off(self.handler) end -function machinesPage.grid:getDisplayValues(row) - row = Util.shallowCopy(row) - row.displayName = row.displayName or row.name - return row -end - -function machinesPage.grid:getRowTextColor(row, selected) - if not device[row.name] then - return colors.red - end - if row.mtype == 'ignore' then - return colors.lightGray - end - return UI.Grid:getRowTextColor(row, selected) -end - function machinesPage:eventHandler(event) if event.type == 'grid_select' then UI:setPage('machineWizard', event.selected) @@ -178,8 +178,146 @@ The settings will take effect immediately!]], backgroundColor = colors.cyan, }, notification = UI.Notification { }, + filter = UI.SlideOut { + backgroundColor = colors.cyan, + menuBar = UI.MenuBar { + buttons = { + { text = 'Save', event = 'save' }, + { text = 'Cancel', event = 'cancel' }, + }, + }, + grid = UI.ScrollingGrid { + x = 2, ex = -6, y = 2, ey = -6, + columns = { + { heading = 'Name', key = 'displayName' }, + }, + sortColumn = 'displayName', + help = 'Select item to export', + }, + remove = UI.Button { + x = -4, y = 4, + text = '-', event = 'remove_entry', help = 'Remove', + }, + form = UI.Form { + x = 2, y = -4, height = 3, + margin = 1, + manualControls = true, + [1] = UI.Chooser { + width = 7, + formLabel = 'Ignore Dmg', formKey = 'ignoreDamage', + pruneEmpty = true, + nochoice = 'No', + choices = { + { name = 'Yes', value = true }, + { name = 'No', value = false }, + }, + help = 'Ignore damage of item when exporting' + }, + [2] = UI.Chooser { + width = 7, + formLabel = 'Ignore NBT', formKey = 'ignoreNbtHash', + pruneEmpty = true, + nochoice = 'No', + choices = { + { name = 'Yes', value = true }, + { name = 'No', value = false }, + }, + help = 'Ignore NBT of item when exporting' + }, + [3] = UI.Chooser { + width = 13, + formLabel = 'Mode', formKey = 'blacklist', + nochoice = 'whitelist', + choices = { + { name = 'whitelist', value = false }, + { name = 'blacklist', value = true }, + }, + help = 'Ignore damage of item when exporting' + }, + scan = UI.Button { + x = -11, y = 1, + text = 'Scan', event = 'scan_turtle', + help = 'Add items to turtle to add to filter', + }, + }, + statusBar = UI.StatusBar { + backgroundColor = colors.cyan, + }, + }, } +--[[ Filter slide out ]] -- +function machineWizard.filter:show(entry, callback, whitelistOnly) + self.entry = entry + self.callback = callback + + if not self.entry.filter then + self.entry.filter = { } + end + + self.form:setValues(entry) + self:resetGrid() + + self.form[3].inactive = whitelistOnly + + UI.SlideOut.show(self) +-- self:setFocus(self.filter) +end + +function machineWizard.filter:resetGrid() + local t = { } + for k in pairs(self.entry.filter) do + table.insert(t, itemDB:splitKey(k)) + end + self.grid:setValues(t) +end + +function machineWizard.filter.grid:getDisplayValues(row) + row = Util.shallowCopy(row) + row.displayName = itemDB:getName(row) + return row +end + +function machineWizard.filter:eventHandler(event) + if event.type == 'focus_change' then + self.statusBar:setStatus(event.focused.help) + + elseif event.type == 'scan_turtle' then + local inventory = Milo:getTurtleInventory() + for _,item in pairs(inventory) do + self.entry.filter[Milo:uniqueKey(item)] = true + end + self:resetGrid() + self.grid:update() + self.grid:draw() + + elseif event.type == 'remove_entry' then + local row = self.grid:getSelected() + if row then + Util.removeByValue(self.grid.values, row) + self.grid:update() + self.grid:draw() + end + + elseif event.type == 'save' then + self.form:save() + self.entry.filter = { } + for _,v in pairs(self.grid.values) do + self.entry.filter[Milo:uniqueKey(v)] = true + end + self:hide() + self.callback() + + elseif event.type == 'cancel' then + self:hide() + + else + return UI.SlideOut.eventHandler(self, event) + end + return true +end + +--[[ General Page ]] -- function machineWizard.wizard.pages.general:enable() UI.Window.enable(self) self:focusFirst() @@ -208,6 +346,7 @@ function machineWizard.wizard.pages.general:validate() return self.form:save() end +--[[ Wizard ]] -- function machineWizard.wizard:eventHandler(event) if event.type == 'nextView' and Util.find(self.pages, 'enabled', true) == self.pages.general then @@ -215,11 +354,16 @@ function machineWizard.wizard:eventHandler(event) if self.pages.general.form:save() then local index = 2 for _, page in pairs(self.pages) do - if page.mtype == machineWizard.machine.mtype then + page.index = nil + end + self.pages.general.index = 1 + self.pages.confirmation.index = 2 + + for k, page in pairs(self.pages) do +debug(k) + if not page.index and page:isValidFor(self.parent.machine) then page.index = index index = index + 1 - elseif page.index ~= 1 then - page.index = nil end end self.pages.confirmation.index = index @@ -237,6 +381,7 @@ function machineWizard:enable(machine) self.machine.adapter = adapter machine.adapter = adapter +_G._p2 = self.machine self.wizard.pages.general.form:setValues(self.machine) self.wizard.pages.general.form[1].shadowText = self.machine.name @@ -286,6 +431,9 @@ function machineWizard:eventHandler(event) UI:setPreviousPage() + elseif event.type == 'edit_filter' then + self.filter:show(event.entry, event.callback, event.whitelistOnly) + elseif event.type == 'enable_view' then local current = event.next or event.prev self.titleBar.title = current.title or 'Machine' diff --git a/milo/plugins/exportTask.lua b/milo/plugins/exportTask.lua index b8314e6..8d2eec4 100644 --- a/milo/plugins/exportTask.lua +++ b/milo/plugins/exportTask.lua @@ -14,22 +14,30 @@ function ExportTask:cycle(context) local machine = device[target] if machine and machine.getItemMeta then for _, entry in pairs(v.exports) do - local slot = machine.getItemMeta(entry.slot) or { count = 0 } - local item = itemDB:splitKey(entry.name) + local slotNo = type(entry.slot) == 'number' and entry.slot or nil -- '*' indicates any slot - -- is something else is in this slot - if not slot.name or slot.name == item.name then - local maxCount = slot.maxCount or itemDB:getMaxCount(item) - local count = maxCount - slot.count + local slot = (slotNo and machine.getItemMeta(slotNo)) or { count = 0 } + for key in pairs(entry.filter) do + local item = itemDB:splitKey(key) - if count > 0 then - item = Milo:getItemWithQty(item) - if item and count > 0 then - context.storage:export( - target, - entry.slot, - math.min(count, item.count), - item) + -- is something else is in this slot + if not slot.name or slot.name == item.name then + local maxCount = slot.maxCount or itemDB:getMaxCount(item) + local count = maxCount - slot.count + if not slotNo then + -- TODO: should we just execute export - + -- or scan all slots for space ?? + count = machine.size() * maxCount - slot.count + end + if count > 0 then + item = Milo:getItemWithQty(item) + if item and count > 0 then + context.storage:export( + target, + slotNo, + math.min(count, item.count), + item) + end end end end diff --git a/milo/plugins/exportView.lua b/milo/plugins/exportView.lua index 43a5108..d231e27 100644 --- a/milo/plugins/exportView.lua +++ b/milo/plugins/exportView.lua @@ -1,181 +1,43 @@ local itemDB = require('itemDB') -local Milo = require('milo') local UI = require('ui') local Util = require('util') -local colors = _G.colors local device = _G.device -local itemSlideout = UI.SlideOut { - backgroundColor = colors.cyan, - menuBar = UI.MenuBar { - buttons = { - { text = 'Save', event = 'save' }, - { text = 'Cancel', event = 'cancel' }, - { text = 'Refresh', event = 'refresh', x = -9 }, - }, - }, - grid = UI.ScrollingGrid { - y = 2, ey = -6, - columns = { - { heading = 'Name', key = 'displayName', width = 31 }, - { heading = 'Qty', key = 'count' , width = 5 }, - }, - sortColumn = 'displayName', - help = 'Select item to export', - }, - filter = UI.TextEntry { - x = 2, ex = 18, y = -3, - limit = 50, - shadowText = 'filter', - backgroundColor = colors.lightGray, - backgroundFocusColor = colors.lightGray, - }, - form = UI.Form { - x = 21, y = -4, height = 3, - margin = 1, - manualControls = true, - [1] = UI.Chooser { - width = 7, - formLabel = 'Slot', formKey = 'slot', - nochoice = 1, - help = 'Export into this slot', - }, - [2] = UI.Chooser { - width = 7, - formLabel = 'Ignore Dmg', formKey = 'ignoreDamage', - pruneEmpty = true, - nochoice = 'No', - choices = { - { name = 'Yes', value = true }, - { name = 'No', value = false }, - }, - help = 'Ignore damage of item when exporting' - }, - [3] = UI.Chooser { - width = 7, - formLabel = 'Ignore NBT', formKey = 'ignoreNbtHash', - pruneEmpty = true, - nochoice = 'No', - choices = { - { name = 'Yes', value = true }, - { name = 'No', value = false }, - }, - help = 'Ignore NBT of item when exporting' - }, - }, - statusBar = UI.StatusBar { - backgroundColor = colors.cyan, - }, -} - -function itemSlideout:filterItems(t, filter) - if filter and #filter > 0 then - local r = {} - filter = filter:lower() - for _,v in pairs(t) do - if string.find(v.lname, filter) then - table.insert(r, v) - end - end - return r - end - return t -end - -function itemSlideout.grid:enable() - if not self.allItems then - self.allItems = Milo:listItems() - Milo:mergeResources(self.allItems) - self:setValues(self.allItems) - end - UI.Grid.enable(self) -end - -function itemSlideout:show(machine, entry, callback) - self.entry = entry - self.callback = callback - - self.form.choices = { } - local m = device[machine.name] - for k = 1, m.size() do - table.insert(self.form[1].choices, { - name = k, - value = k, - }) - end - - if not entry.slot then - entry.slot = 1 - end - self.form:setValues(entry) - - UI.SlideOut.show(self) - self:setFocus(self.filter) - --self.filter:focus() -end - -function itemSlideout:eventHandler(event) - if event.type == 'text_change' then - local t = self:filterItems(self.grid.allItems, event.text) - self.grid:setValues(t) - self.grid:draw() - - elseif event.type == 'focus_change' then - self.statusBar:setStatus(event.focused.help) - - elseif event.type == 'save' then - local selected = self.grid:getSelected() - if not selected then - self.statusBar:setStatus('Select an item to export') - else - self.form:save() - self.form.values.name = itemDB:makeKey(selected) - self:hide() - self.callback() - end - - elseif event.type == 'cancel' then - self:hide() - - elseif event.type == 'refresh' then - self.allItems = Milo:listItems() - Milo:mergeResources(self.allItems) - local t = self:filterItems(self.allItems, self.filter.value) - self.grid:setValues(t) - self.grid:draw() - self.filter:focus() - else - return UI.SlideOut.eventHandler(self, event) - end - return true -end - local exportView = UI.Window { - mtype = 'machine', title = 'Export item into machine', index = 3, grid = UI.ScrollingGrid { - x = 2, ex = -6, y = 2, ey = -2, + x = 2, ex = -6, y = 2, ey = -4, columns = { - { heading = 'Slot', key = 'slot', width = 4 }, - { heading = 'Item', key = 'displayName' }, + { heading = 'Slot', key = 'slot', width = 4 }, + { heading = 'Filter', key = 'filter' }, }, sortColumn = 'slot', + help = 'Edit this entry', + }, + text = UI.Text { + x = 2, y = -2, + value = 'Slot', + }, + slots = UI.Chooser { + x = 7, y = -2, + width = 7, + nochoice = 'All', + help = 'Export to this slot', }, add = UI.Button { - x = -4, y = 4, - text = '+', event = 'add_export', help = '...', + x = 15, y = -2, + text = '+', event = 'add_entry', help = 'Add', }, remove = UI.Button { - x = -4, y = 6, - text = '-', event = 'remove_export', help = '...', + x = -4, y = 4, + text = '-', event = 'remove_entry', help = 'Remove', }, } -function exportView:save(machine) - machine.exports = not Util.empty(self.grid.values) and self.grid.values or nil - return true +function exportView:isValidFor(machine) + return machine.mtype == 'machine' end function exportView:setMachine(machine) @@ -184,30 +46,53 @@ function exportView:setMachine(machine) self.machine.exports = { } end self.grid:setValues(machine.exports) + + self.slots.choices = { + { name = 'All', value = '*' } + } + + -- TODO: what if device is dettached ? + local m = device[machine.name] + for k = 1, m.size() do + table.insert(self.slots.choices, { name = k, value = k }) + end end function exportView.grid:getDisplayValues(row) row = Util.shallowCopy(row) - row.displayName = itemDB:getName(row.name) + if not row.filter or Util.empty(row.filter) then + row.filter = 'none' + else + local t = { } + for key in pairs(row.filter) do + table.insert(t, itemDB:getName(key)) + end + row.filter = table.concat(t, ', ') + end return row end function exportView:eventHandler(event) if event.type == 'grid_select' then - itemSlideout:show(self.machine, self.grid:getSelected(), function() - self.grid:update() - self.grid:draw() - end) + self:emit({ + type = 'edit_filter', + entry = self.grid:getSelected(), + whitelistOnly = true, + callback = function() + self.grid:update() + self.grid:draw() + end, + }) - elseif event.type == 'add_export' then - local export = { } - itemSlideout:show(self.machine, export, function() - table.insert(self.machine.exports, export) - self.grid:update() - self.grid:draw() - end) + elseif event.type == 'add_entry' then + table.insert(self.machine.exports, { + slot = self.slots.value or '*', + filter = { }, + }) + self.grid:update() + self.grid:draw() - elseif event.type == 'remove_export' then + elseif event.type == 'remove_entry' then local row = self.grid:getSelected() if row then Util.removeByValue(self.grid.values, row) @@ -217,5 +102,4 @@ function exportView:eventHandler(event) end end -UI:getPage('machineWizard'):add({ items = itemSlideout }) UI:getPage('machineWizard').wizard:add({ export = exportView }) diff --git a/milo/plugins/importTask.lua b/milo/plugins/importTask.lua index 6020a7e..0e26156 100644 --- a/milo/plugins/importTask.lua +++ b/milo/plugins/importTask.lua @@ -11,11 +11,34 @@ function ImportTask:cycle(context) for source, v in pairs(context.config.remoteDefaults) do if v.imports then 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) + if inventory then + for _, entry in pairs(v.imports) do + + local function matchesFilter(item) + if not entry.filter then + return true + end + + local key = Milo:uniqueKey(item) + if entry.blacklist then + return not entry.filter[key] + end + return entry.filter[key] + end + + local function importSlot(slotNo) + local item = inventory.getItemMeta(slotNo) + if item and matchesFilter(item) then + context.storage:import(source, slotNo, item.count, item) + end + end + + if type(entry.slot) == 'number' then + importSlot(entry.slot) + else + for i = 1, inventory.size() do + importSlot(i) + end end end else diff --git a/milo/plugins/importView.lua b/milo/plugins/importView.lua index 92abe68..38a4dac 100644 --- a/milo/plugins/importView.lua +++ b/milo/plugins/importView.lua @@ -1,55 +1,103 @@ -local UI = require('ui') -local Util = require('util') +local itemDB = require('itemDB') +local UI = require('ui') +local Util = require('util') local device = _G.device local importView = UI.Window { - mtype = 'machine', title = 'Import item from machine', index = 4, grid = UI.ScrollingGrid { - y = 2, ey = -2, + x = 2, ex = -6, y = 2, ey = -4, columns = { { heading = 'Slot', key = 'slot', width = 4 }, - { heading = 'Import', key = 'import' }, + { heading = 'Filter', key = 'filter' }, }, sortColumn = 'slot', - help = 'Double-click to toggle' + help = 'Edit this entry', + }, + text = UI.Text { + x = 2, y = -2, + value = 'Slot', + }, + slots = UI.Chooser { + x = 7, y = -2, + width = 7, + nochoice = 'All', + help = 'Import from this slot', + }, + add = UI.Button { + x = 15, y = -2, + text = '+', event = 'add_entry', help = 'Add', + }, + remove = UI.Button { + x = -4, y = 4, + text = '-', event = 'remove_entry', help = 'Remove', }, } -function importView:setMachine(machine) - local m = device[machine.name] - - local t = { } - for k = 1, m.size() do - t[k] = { slot = k } - end - - if machine.imports then - for k,v in pairs(machine.imports) do - t[k] = { slot = k, import = v } - end - end - - self.grid:setValues(t) +function importView:isValidFor(machine) + return machine.mtype == 'machine' end -function importView:save(machine) - local t = { } - for k,v in pairs(self.grid.values) do - if v.import then - t[k] = true - end +function importView:setMachine(machine) + self.machine = machine + if not self.machine.imports then + self.machine.imports = { } end - machine.imports = not Util.empty(t) and t or nil - return true + self.grid:setValues(machine.imports) + + self.slots.choices = { + { name = 'All', value = '*' } + } + + -- TODO: what if device is dettached ? + local m = device[machine.name] + for k = 1, m.size() do + table.insert(self.slots.choices, { name = k, value = k }) + end +end + +function importView.grid:getDisplayValues(row) + row = Util.shallowCopy(row) + if not row.filter or Util.empty(row.filter) then + row.filter = 'none' + else + local t = { } + for key in pairs(row.filter) do + table.insert(t, itemDB:getName(key)) + end + row.filter = table.concat(t, ', ') + end + return row end function importView:eventHandler(event) if event.type == 'grid_select' then - event.selected.import = not event.selected.import + self:emit({ + type = 'edit_filter', + entry = self.grid:getSelected(), + callback = function() + self.grid:update() + self.grid:draw() + end, + }) + + elseif event.type == 'add_entry' then + table.insert(self.machine.imports, { + slot = self.slots.value or '*', + filter = { }, + }) + self.grid:update() self.grid:draw() + + elseif event.type == 'remove_entry' then + local row = self.grid:getSelected() + if row then + Util.removeByValue(self.grid.values, row) + self.grid:update() + self.grid:draw() + end end end diff --git a/milo/plugins/jobList.lua b/milo/plugins/jobList.lua index 7a5b9e1..4862d3a 100644 --- a/milo/plugins/jobList.lua +++ b/milo/plugins/jobList.lua @@ -61,7 +61,7 @@ end function jobList.grid:getDisplayValues(row) row = Util.shallowCopy(row) if row.showRemaining then - row.remaining = row.count - row.crafted + row.remaining = math.max(0, row.count - row.crafted) else row.displayName = ' ' .. row.displayName end diff --git a/milo/plugins/machineLearn.lua b/milo/plugins/machineLearn.lua index d005c92..1af4b46 100644 --- a/milo/plugins/machineLearn.lua +++ b/milo/plugins/machineLearn.lua @@ -12,17 +12,6 @@ local MACHINE_LOOKUP = 'usr/config/machine_crafting.db' local context = Milo:getContext() -local function getTurtleInventory() - local introspectionModule = device['plethora:introspection'] or - error('Introspection module not found') - - local list = { } - for i = 1,16 do - list[i] = introspectionModule.getInventory().getItemMeta(i) - end - return list -end - local machineLearnWizard = UI.Page { titleBar = UI.TitleBar { title = 'Learn a crafting recipe' }, wizard = UI.Wizard { @@ -92,7 +81,7 @@ function pages.machine:validate() end function pages.confirmation:validate() - local inventory = getTurtleInventory() + local inventory = Milo:getTurtleInventory() local result = inventory[16] local slotCount = machine.size() diff --git a/milo/plugins/redstoneTask.lua b/milo/plugins/redstoneTask.lua new file mode 100644 index 0000000..4cc0a09 --- /dev/null +++ b/milo/plugins/redstoneTask.lua @@ -0,0 +1,28 @@ +local Event = require('event') +local Milo = require('milo') + +local device = _G.device + +local RedstoneTask = { + name = 'redstone', + priority = 40, +} + +function RedstoneTask:cycle(context) + for _,v in pairs(context.config.remoteDefaults) do + if v.redstone then + local ri = device[v.redstone.integrator] + local function conditionsSatisfied() + return not not next(ri.list()) + end + if conditionsSatisfied() then + ri.setOutput(v.redstone.side, true) + Event.onTimeout(.25, function() + ri.setOutput(v.redstone.side, false) + end) + end + end + end +end + +Milo:registerTask(RedstoneTask) diff --git a/milo/plugins/redstoneView.lua b/milo/plugins/redstoneView.lua new file mode 100644 index 0000000..0ed3dfc --- /dev/null +++ b/milo/plugins/redstoneView.lua @@ -0,0 +1,73 @@ +local UI = require('ui') + +local colors = _G.colors +local device = _G.device + +local dispenserView = UI.Window { + index = 10, + title = 'Redstone Control', + backgroundColor = colors.cyan, + form = UI.Form { + x = 1, y = 2, ex = -1, ey = -2, + manualControls = true, + [1] = UI.TextEntry { + formLabel = 'Interval', formKey = 'interval', + help = 'Pulse redstone if items are present', + limit = 6, + validate = 'numeric', + }, + [2] = UI.Chooser { + formLabel = 'Integrator', formKey = 'integrator', + nochoice = 'disable', + help = 'Control via redstone', + }, + [3] = UI.Chooser { + width = 10, + formLabel = 'Side', formKey = 'side', + choices = { + { name = 'up', value = 'up' }, + { name = 'down', value = 'down' }, + { name = 'east', value = 'east' }, + { name = 'north', value = 'north' }, + { name = 'west', value = 'west' }, + { name = 'south', value = 'south' }, + }, + help = 'Output side', + }, + }, +} + +function dispenserView:isValidFor(machine) + if machine.mtype == 'machine' then + local m = device[machine.name] + return m and m.type == 'minecraft:dispenser' + end +end + +function dispenserView:enable() + UI.Window.enable(self) + self:focusFirst() + + self.form[2].choices = { } + for _,m in pairs(device) do + if m.type == 'redstone_integrator' then + table.insert(self.form[2].choices, { + name = m.name, + value = m.name, + }) + end + end +end + +function dispenserView:validate() + return self.form:save() +end + +function dispenserView:setMachine(machine) + if not machine.redstone then + machine.redstone = { } + end + self.form:setValues(machine.redstone) +end + +UI:getPage('machineWizard').wizard:add({ dispenser = dispenserView }) diff --git a/milo/plugins/remote.lua b/milo/plugins/remote.lua index 28d00e2..0869c27 100644 --- a/milo/plugins/remote.lua +++ b/milo/plugins/remote.lua @@ -33,6 +33,7 @@ local function client(socket) if not data then break end +debug('remote: ' .. data.request) if data.request == 'list' then local items = Milo:refreshItems() Milo:mergeResources(items) diff --git a/milo/plugins/storageView.lua b/milo/plugins/storageView.lua index 78af345..e13622b 100644 --- a/milo/plugins/storageView.lua +++ b/milo/plugins/storageView.lua @@ -1,11 +1,10 @@ local itemDB = require('itemDB') -local UI = require('ui') +local UI = require('ui') local colors = _G.colors local device = _G.device local storageView = UI.Window { - mtype = 'storage', title = 'Storage Options', index = 2, backgroundColor = colors.cyan, @@ -49,6 +48,10 @@ function storageView:validate() return self.form:save() end +function storageView:isValidFor(machine) + return machine.mtype == 'storage' +end + function storageView:setMachine(machine) self.machine = machine self.form:setValues(machine) diff --git a/milo/plugins/turtleLearn.lua b/milo/plugins/turtleLearn.lua index 6897035..65bd47f 100644 --- a/milo/plugins/turtleLearn.lua +++ b/milo/plugins/turtleLearn.lua @@ -4,24 +4,12 @@ local Milo = require('milo') local UI = require('ui') local Util = require('util') -local device = _G.device local turtle = _G.turtle local context = Milo:getContext() -local function getTurtleInventory() - local introspectionModule = device['plethora:introspection'] or - error('Introspection module not found') - - local list = { } - for i = 1,16 do - list[i] = introspectionModule.getInventory().getItemMeta(i) - end - return list -end - local function learnRecipe() - local ingredients = getTurtleInventory() + local ingredients = Milo:getTurtleInventory() if not ingredients then return false, 'No recipe defined' @@ -32,7 +20,7 @@ local function learnRecipe() return false, 'Failed to craft' end - local results = getTurtleInventory() + local results = Milo:getTurtleInventory() if not results or not results[1] then return false, 'Failed to craft' end From dc6af1d0c321eef1101bf50694ee22a498fd2b65 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sat, 27 Oct 2018 23:16:16 -0400 Subject: [PATCH 040/165] milo wip --- milo/MiloRemote.lua | 27 +++++++++++++++++++++++++-- milo/apis/storage.lua | 4 +++- milo/core/machines.lua | 3 +-- milo/plugins/remote.lua | 19 +++++++++++++++---- 4 files changed, 44 insertions(+), 9 deletions(-) diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index 75fd5f3..6ca2236 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -10,11 +10,15 @@ local colors = _G.colors local device = _G.device local socket +local SHIELD_SLOT = 2 + local options = { user = { arg = 'u', type = 'string', desc = 'User name associated with bound manipulator' }, slot = { arg = 's', type = 'number', desc = 'Optional inventory slot to use to transfer to milo' }, + shield = { arg = 'e', type = 'flag', + desc = 'Use shield slot to use to transfer to milo' }, server = { arg = 'm', type = 'number', desc = 'ID of Milo server' }, help = { arg = 'h', type = 'flag', value = false, @@ -33,7 +37,7 @@ if not options.user.value or not options.server.value then error('Invalid arguments') end -if options.slot.value and +if (options.slot.value or options.shield.value) and not (device.neuralInterface and device.neuralInterface.getInventory) then error('Introspection module is required for transferring items') end @@ -246,7 +250,7 @@ end if options.slot.value then debug('Transfer items initialized') - Event.onInterval(1, function() + Event.onInterval(2, function() local neural = device.neuralInterface if neural and neural.getInventory then local item = neural.getInventory().getItem(options.slot.value) @@ -263,6 +267,25 @@ if options.slot.value then end) end +if options.slot.value or options.eslot.value then + debug('Transfer items initialized') + Event.onInterval(2, function() + local neural = device.neuralInterface + if neural and neural.getEquipment then + local item = neural.getEquipment().getItem(SHIELD_SLOT) + if item then + debug('depositing') + page:sendRequest({ request = 'deposit', slot = 'shield' }) + -- local item = + -- TODO: update count for this one item + -- page.grid:draw() page:sync() + end + else + debug('missing Introspection module') + end + end) +end + UI:setPage(page) UI:pullEvents() diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 39ab696..c72a827 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -36,7 +36,9 @@ function NetworkedAdapter:showStorage() debug('Storage:') for k,v in pairs(self.remoteDefaults) do local online = v.adapter and v.adapter.online - debug(' %s: %s', online and ' online' or 'offline', k) + if not online then + debug(' %s: %s', online and ' online' or 'offline', k) + end end debug('') end diff --git a/milo/core/machines.lua b/milo/core/machines.lua index 4201859..208f110 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -359,8 +359,7 @@ function machineWizard.wizard:eventHandler(event) self.pages.general.index = 1 self.pages.confirmation.index = 2 - for k, page in pairs(self.pages) do -debug(k) + for _, page in pairs(self.pages) do if not page.index and page:isValidFor(self.parent.machine) then page.index = index index = index + 1 diff --git a/milo/plugins/remote.lua b/milo/plugins/remote.lua index 0869c27..7d2e3f5 100644 --- a/milo/plugins/remote.lua +++ b/milo/plugins/remote.lua @@ -5,6 +5,8 @@ local Socket = require('socket') local device = _G.device local turtle = _G.turtle +local SHIELD_SLOT = 2 + local context = Milo:getContext() local function getManipulatorForUser(user) @@ -40,10 +42,19 @@ debug('remote: ' .. data.request) socket:write(items) elseif data.request == 'deposit' then - local count = manipulator.getInventory().pushItems( - context.localName, - data.slot, - 64) + local count + + if data.slot == 'shield' then + count = manipulator.getEquipment().pushItems( + context.localName, + SHIELD_SLOT, + 64) + else + count = manipulator.getInventory().pushItems( + context.localName, + data.slot, + 64) + end socket:write({ count = count }) Milo:clearGrid() From 763f6bb27ad5c53101cfae485f1cdc4058f064d7 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sat, 27 Oct 2018 23:47:28 -0400 Subject: [PATCH 041/165] milo wip --- milo/MiloRemote.lua | 2 +- milo/apis/storage.lua | 12 +++++++++--- milo/plugins/redstoneTask.lua | 20 ++++++++++++-------- milo/plugins/storageView.lua | 1 + 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index 6ca2236..b6f689c 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -267,7 +267,7 @@ if options.slot.value then end) end -if options.slot.value or options.eslot.value then +if options.shield.value then debug('Transfer items initialized') Event.onInterval(2, function() local neural = device.neuralInterface diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index c72a827..2d0852c 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -33,14 +33,20 @@ debug('%s: %s', e, tostring(dev)) end function NetworkedAdapter:showStorage() - debug('Storage:') + local t = { } for k,v in pairs(self.remoteDefaults) do local online = v.adapter and v.adapter.online if not online then - debug(' %s: %s', online and ' online' or 'offline', k) + table.insert(t, k) end end - debug('') + if #t > 0 then + debug('Storage:') + for _, k in pairs(t) do + debug(' %s: %s', online and ' online' or 'offline', k) + end + debug('') + end end function NetworkedAdapter:setOnline(online) diff --git a/milo/plugins/redstoneTask.lua b/milo/plugins/redstoneTask.lua index 4cc0a09..465902d 100644 --- a/milo/plugins/redstoneTask.lua +++ b/milo/plugins/redstoneTask.lua @@ -12,14 +12,18 @@ function RedstoneTask:cycle(context) for _,v in pairs(context.config.remoteDefaults) do if v.redstone then local ri = device[v.redstone.integrator] - local function conditionsSatisfied() - return not not next(ri.list()) - end - if conditionsSatisfied() then - ri.setOutput(v.redstone.side, true) - Event.onTimeout(.25, function() - ri.setOutput(v.redstone.side, false) - end) + if not ri or not v.adapter or not v.adapter.online then + debug(v.redstone) + else + local function conditionsSatisfied() + return not not next(v.adapter.list()) + end + if conditionsSatisfied() then + ri.setOutput(v.redstone.side, true) + Event.onTimeout(.25, function() + ri.setOutput(v.redstone.side, false) + end) + end end end end diff --git a/milo/plugins/storageView.lua b/milo/plugins/storageView.lua index e13622b..82f51a8 100644 --- a/milo/plugins/storageView.lua +++ b/milo/plugins/storageView.lua @@ -72,6 +72,7 @@ function storageView:eventHandler(event) field = event.element, message = 'The chest must contain the item to lock' }) self.form[3].value = false + self.form[3]:draw() end end else From b96c064431f2dc3d895f2466cc53157bcd507fa3 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sun, 28 Oct 2018 03:24:32 -0400 Subject: [PATCH 042/165] brewing station auto learn --- milo/MiloRemote.lua | 1 + milo/apis/milo.lua | 14 ++++++++ milo/apis/storage.lua | 2 +- milo/plugins/machineLearn.lua | 15 +-------- milo/plugins/potionImportTask.lua | 54 +++++++++++++++++++++++++++---- 5 files changed, 64 insertions(+), 22 deletions(-) diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index b6f689c..8f63f19 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -248,6 +248,7 @@ function page:applyFilter() self.grid:setValues(t) end +debug(options.slot) if options.slot.value then debug('Transfer items initialized') Event.onInterval(2, function() diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index bcbbef8..8fee5e7 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -153,6 +153,20 @@ function Milo:eject(item, qty) end end +function Milo:saveMachineRecipe(recipe, result, machine) + local key = Milo:uniqueKey(result) + + -- save the recipe + self.context.userRecipes[key] = recipe + Util.writeTable(Milo.RECIPES_FILE, self.context.userRecipes) + + -- save the machine association + Craft.machineLookup[key] = machine + Util.writeTable(Craft.MACHINE_LOOKUP, Craft.machineLookup) + + Craft.loadRecipes() +end + function Milo:mergeResources(t) for _,v in pairs(self.context.resources) do local item = self:getItem(t, v) diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 2d0852c..dc0ab7b 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -218,7 +218,7 @@ function NetworkedAdapter:insert(slot, qty, toSlot, item, source) if amount > 0 then debug('INS: %s(%d): %s[%d] -> %s', item.name, amount, - source, slot, adapter.name) + source or self.localName, slot, adapter.name) self.dirty = true adapter.dirty = true local entry = self.activity[key] or 0 diff --git a/milo/plugins/machineLearn.lua b/milo/plugins/machineLearn.lua index 1af4b46..d1197e2 100644 --- a/milo/plugins/machineLearn.lua +++ b/milo/plugins/machineLearn.lua @@ -1,4 +1,3 @@ -local Craft = require('turtle.craft') local itemDB = require('itemDB') local Milo = require('milo') local UI = require('ui') @@ -8,8 +7,6 @@ local colors = _G.colors local device = _G.device local turtle = _G.turtle -local MACHINE_LOOKUP = 'usr/config/machine_crafting.db' - local context = Milo:getContext() local machineLearnWizard = UI.Page { @@ -117,17 +114,7 @@ function pages.confirmation:validate() recipe.ingredients[k] = Milo:uniqueKey(v) end - local key = Milo:uniqueKey(result) - - -- save the recipe - context.userRecipes[key] = recipe - Util.writeTable(Milo.RECIPES_FILE, context.userRecipes) - - -- save the machine association - Craft.machineLookup[key] = machine.name - Util.writeTable(MACHINE_LOOKUP, Craft.machineLookup) - - Craft.loadRecipes() + Milo:saveMachineRecipe(recipe, result, machine.name) local listingPage = UI:getPage('listing') local displayName = itemDB:getName(result) diff --git a/milo/plugins/potionImportTask.lua b/milo/plugins/potionImportTask.lua index e3fd9f7..144d580 100644 --- a/milo/plugins/potionImportTask.lua +++ b/milo/plugins/potionImportTask.lua @@ -1,21 +1,61 @@ -local Milo = require('milo') +local Craft = require('turtle.craft') +local Milo = require('milo') local device = _G.device local PotionImportTask = { name = 'potions', priority = 30, + brewQueue = { }, } function PotionImportTask:cycle(context) for _, v in pairs(device) do - if v.type == 'minecraft:brewing_stand' and v.getBrewTime() == 0 then - local list = v.list() - if not list[4] then - for slot = 1, 3 do - if list[slot] then - context.storage:import(v.name, slot, 1, list[slot]) + if v.type == 'minecraft:brewing_stand' then + if v.getBrewTime() == 0 then + local list = v.list() + + if list[1] and not list[4] then + -- brewing has completd + + if self.brewQueue[v.name] and list[1] then + local key = Milo:uniqueKey(list[1]) + if not Craft.findRecipe(key) then + debug('saving new recipe') + Milo:saveMachineRecipe(self.brewQueue[v.name], list[1], v.name) + end end + for slot = 1, 3 do + if list[slot] then + context.storage:import(v.name, slot, 1, list[slot]) + end + end + end + self.brewQueue[v.name] = nil + + elseif not self.brewQueue[v.name] then + local recipe = { + count = 3, + ingredients = { }, + maxCount = 3, + } + local list = v.list() + + local function valid() + for i = 1, 4 do + if not list[i] then + return false + end + end + return true + end + + if valid() then + for i = 1, 4 do + recipe.ingredients[i] = Milo:uniqueKey(list[i]) + end + + self.brewQueue[v.name] = recipe end end end From 9d45460a3b7f6e50d1dcdda0b4ee9d4d48e70991 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sun, 28 Oct 2018 04:23:24 -0400 Subject: [PATCH 043/165] brewing station auto learn --- milo/Milo.lua | 1 - milo/apis/milo.lua | 4 ++-- milo/apis/turtle/craft.lua | 14 +++++++------- milo/plugins/listing.lua | 2 +- milo/plugins/potionImportTask.lua | 2 +- milo/plugins/turtleLearn.lua | 2 +- 6 files changed, 12 insertions(+), 13 deletions(-) diff --git a/milo/Milo.lua b/milo/Milo.lua index 7256a09..6e2f61f 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -100,7 +100,6 @@ end local context = { config = config, resources = loadResources(), - userRecipes = Util.readTable(Milo.RECIPES_FILE) or { }, craftingQueue = { }, diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index 8fee5e7..97eef95 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -6,12 +6,12 @@ local Util = require('util') local turtle = _G.turtle local Milo = { - RECIPES_FILE = 'usr/config/recipes.db', RESOURCE_FILE = 'usr/config/resources.db', } function Milo:init(context) self.context = context + context.userRecipes = Util.readTable(Craft.USER_RECIPES) or { } end function Milo:getContext() @@ -158,7 +158,7 @@ function Milo:saveMachineRecipe(recipe, result, machine) -- save the recipe self.context.userRecipes[key] = recipe - Util.writeTable(Milo.RECIPES_FILE, self.context.userRecipes) + Util.writeTable(Craft.USER_RECIPES, self.context.userRecipes) -- save the machine association Craft.machineLookup[key] = machine diff --git a/milo/apis/turtle/craft.lua b/milo/apis/turtle/craft.lua index ac2fe8f..a36cfd1 100644 --- a/milo/apis/turtle/craft.lua +++ b/milo/apis/turtle/craft.lua @@ -5,15 +5,15 @@ local device = _G.device local fs = _G.fs local turtle = _G.turtle -local RECIPES_DIR = 'usr/etc/recipes' -local USER_RECIPES = 'usr/config/recipes.db' -local MACHINE_LOOKUP = 'usr/config/machine_crafting.db' - local Craft = { STATUS_INFO = 'info', STATUS_WARNING = 'warning', STATUS_ERROR = 'error', STATUS_SUCCESS = 'success', + + RECIPES_DIR = 'usr/etc/recipes', + USER_RECIPES = 'usr/config/recipes.db', + MACHINE_LOOKUP = 'usr/config/machine_crafting.db', } local function clearGrid(inventoryAdapter) @@ -367,7 +367,7 @@ end function Craft.loadRecipes() Craft.recipes = { } - Util.merge(Craft.recipes, (Util.readTable(fs.combine(RECIPES_DIR, 'minecraft.db')) or { }).recipes) + Util.merge(Craft.recipes, (Util.readTable(fs.combine(Craft.RECIPES_DIR, 'minecraft.db')) or { }).recipes) local config = Util.readTable('usr/config/recipeBooks.db') or { } for _, book in pairs(config) do @@ -375,14 +375,14 @@ function Craft.loadRecipes() Util.merge(Craft.recipes, recipeFile.recipes) end - local recipes = Util.readTable(USER_RECIPES) or { } + local recipes = Util.readTable(Craft.USER_RECIPES) or { } Util.merge(Craft.recipes, recipes) for k,v in pairs(Craft.recipes) do v.result = k end - Craft.machineLookup = Util.readTable(MACHINE_LOOKUP) or { } + Craft.machineLookup = Util.readTable(Craft.MACHINE_LOOKUP) or { } end function Craft.canCraft(item, count, items) diff --git a/milo/plugins/listing.lua b/milo/plugins/listing.lua index 6a18aef..91c1e99 100644 --- a/milo/plugins/listing.lua +++ b/milo/plugins/listing.lua @@ -203,7 +203,7 @@ function listingPage:eventHandler(event) if context.userRecipes[key] then context.userRecipes[key] = nil - Util.writeTable(Milo.RECIPES_FILE, context.userRecipes) + Util.writeTable(Craft.USER_RECIPES, context.userRecipes) Craft.loadRecipes() end diff --git a/milo/plugins/potionImportTask.lua b/milo/plugins/potionImportTask.lua index 144d580..f7586dd 100644 --- a/milo/plugins/potionImportTask.lua +++ b/milo/plugins/potionImportTask.lua @@ -21,7 +21,7 @@ function PotionImportTask:cycle(context) if self.brewQueue[v.name] and list[1] then local key = Milo:uniqueKey(list[1]) if not Craft.findRecipe(key) then - debug('saving new recipe') +debug('saving new recipe') Milo:saveMachineRecipe(self.brewQueue[v.name], list[1], v.name) end end diff --git a/milo/plugins/turtleLearn.lua b/milo/plugins/turtleLearn.lua index 65bd47f..0d0e49c 100644 --- a/milo/plugins/turtleLearn.lua +++ b/milo/plugins/turtleLearn.lua @@ -96,7 +96,7 @@ local function learnRecipe() end context.userRecipes[key] = newRecipe - Util.writeTable(Milo.RECIPES_FILE, context.userRecipes) + Util.writeTable(Craft.USER_RECIPES, context.userRecipes) Craft.loadRecipes() turtle.emptyInventory() From 1c70085450a410d5c6dfb145ceb7bd0cf4b711c3 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sun, 28 Oct 2018 15:24:12 -0400 Subject: [PATCH 044/165] milo storage filter + crafting fixes --- milo/apis/storage.lua | 49 ++++++++++++------ milo/apis/turtle/craft.lua | 39 +++++++++++---- milo/plugins/exportTask.lua | 59 ++++++++++------------ milo/plugins/importTask.lua | 63 +++++++++++------------ milo/plugins/inputChestTask.lua | 15 ++---- milo/plugins/limitTask.lua | 33 +++++------- milo/plugins/potionImportTask.lua | 83 +++++++++++++++---------------- milo/plugins/redstoneTask.lua | 4 +- 8 files changed, 177 insertions(+), 168 deletions(-) diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index dc0ab7b..f7c3422 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -7,9 +7,9 @@ local Util = require('util') local device = _G.device local os = _G.os -local NetworkedAdapter = class() +local Storage = class() -function NetworkedAdapter:init(args) +function Storage:init(args) local defaults = { remoteDefaults = { }, dirty = true, @@ -32,7 +32,7 @@ debug('%s: %s', e, tostring(dev)) end) end -function NetworkedAdapter:showStorage() +function Storage:showStorage() local t = { } for k,v in pairs(self.remoteDefaults) do local online = v.adapter and v.adapter.online @@ -49,7 +49,7 @@ function NetworkedAdapter:showStorage() end end -function NetworkedAdapter:setOnline(online) +function Storage:setOnline(online) if online ~= self.storageOnline then self.storageOnline = online os.queueEvent(self.storageOnline and 'storage_online' or 'storage_offline', online) @@ -57,11 +57,11 @@ function NetworkedAdapter:setOnline(online) end end -function NetworkedAdapter:isOnline() +function Storage:isOnline() return self.storageOnline end -function NetworkedAdapter:initStorage() +function Storage:initStorage() local online = true debug('Initializing storage') @@ -80,7 +80,24 @@ function NetworkedAdapter:initStorage() self:setOnline(online) end -function NetworkedAdapter:onlineAdapters(reversed) +function Storage:filterActive(mtype, filter) + local iter = { } + for _, v in pairs(self.remoteDefaults) do + if v.adapter and v.adapter.online and v.mtype == mtype then + if not filter or filter(v) then + table.insert(iter, v) + end + end + end + + local i = 0 + return function() + i = i + 1 + return iter[i] + end +end + +function Storage:onlineAdapters(reversed) local iter = { } for _, v in pairs(self.remoteDefaults) do if v.adapter and v.adapter.online and v.mtype == 'storage' then @@ -113,13 +130,13 @@ function NetworkedAdapter:onlineAdapters(reversed) end end -function NetworkedAdapter:refresh(throttle) +function Storage:refresh(throttle) self.dirty = true return self:listItems(throttle) end -- provide a consolidated list of items -function NetworkedAdapter:listItems(throttle) +function Storage:listItems(throttle) if not self.dirty then return self.items end @@ -160,11 +177,11 @@ self.listCount = self.listCount + 1 return items end -function NetworkedAdapter:export(target, slot, count, item) +function Storage:export(target, slot, count, item) return self:provide(item, count, slot, target) end -function NetworkedAdapter:provide(item, qty, slot, direction) +function Storage:provide(item, qty, slot, direction) local total = 0 for _, adapter in self:onlineAdapters() do @@ -186,7 +203,7 @@ debug('EXT: %s(%d): %s -> %s%s', return total end -function NetworkedAdapter:trash(source, slot, count) +function Storage:trash(source, slot, count) local trashcan = Util.find(self.remoteDefaults, 'mtype', 'trashcan') if trashcan and trashcan.adapter and trashcan.adapter.online then debug('TRA: %s[%d] (%d)', source or self.localName, slot, count or 64) @@ -195,16 +212,16 @@ debug('TRA: %s[%d] (%d)', source or self.localName, slot, count or 64) return 0 end -function NetworkedAdapter:import(source, slot, count, item) +function Storage:import(source, slot, count, item) return self:insert(slot, count, nil, item, source) end -function NetworkedAdapter:insert(slot, qty, toSlot, item, source) +function Storage: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') + error('Storage: toSlot is not valid') end local key = table.concat({ item.name, item.damage, item.nbtHash }, ':') @@ -265,4 +282,4 @@ debug('INS: %s(%d): %s[%d] -> %s', return total end -return NetworkedAdapter +return Storage diff --git a/milo/apis/turtle/craft.lua b/milo/apis/turtle/craft.lua index a36cfd1..6e891a0 100644 --- a/milo/apis/turtle/craft.lua +++ b/milo/apis/turtle/craft.lua @@ -137,8 +137,6 @@ local function turtleCraft(recipe, inventoryAdapter, request, count) turtle.select(1) if turtle.craft() then - request.status = nil - request.statusCode = Craft.STATUS_SUCCESS request.crafted = request.crafted + count * recipe.count return true end @@ -154,13 +152,36 @@ function Craft.craftRecipe(recipe, count, inventoryAdapter, origItem) end end - for _,key in pairs(Util.keys(origItem.ingredients)) do - local e = origItem.ingredients[key] - if e and e.transient then - origItem.ingredients[key] = nil + -- wait til all requests have been completed + local isPending = false + for key, request in pairs(origItem.ingredients) do + if request.pending then + local irecipe = Craft.findRecipe(key) + machineCraft(irecipe, inventoryAdapter, + Craft.machineLookup[irecipe.result], request) + isPending = request.pending or isPending end end - return Craft.craftRecipeInternal(recipe, count, inventoryAdapter, origItem) + + local crafted = 0 + --if not isPending then + for _,key in pairs(Util.keys(origItem.ingredients)) do + local e = origItem.ingredients[key] + if e and e.transient then + origItem.ingredients[key] = nil + end + end + crafted = Craft.craftRecipeInternal(recipe, count, inventoryAdapter, origItem) + --end + + for _, request in pairs(origItem.ingredients) do + if request.crafted >= request.count then + request.status = nil + request.statusCode = Craft.STATUS_SUCCESS + end + end + + return crafted end function Craft.craftRecipeInternal(recipe, count, inventoryAdapter, origItem) @@ -179,8 +200,8 @@ function Craft.craftRecipeInternal(recipe, count, inventoryAdapter, origItem) end if request.pending then - machineCraft(recipe, inventoryAdapter, - Craft.machineLookup[recipe.result], request) + --machineCraft(recipe, inventoryAdapter, + -- Craft.machineLookup[recipe.result], request) return 0 end diff --git a/milo/plugins/exportTask.lua b/milo/plugins/exportTask.lua index 8d2eec4..5d864d9 100644 --- a/milo/plugins/exportTask.lua +++ b/milo/plugins/exportTask.lua @@ -1,49 +1,44 @@ local itemDB = require('itemDB') local Milo = require('milo') -local device = _G.device - local ExportTask = { name = 'exporter', priority = 40, } +local function filter(a) + return a.exports +end + function ExportTask:cycle(context) - for target, v in pairs(context.config.remoteDefaults) do - if v.exports then - local machine = device[target] - if machine and machine.getItemMeta then - for _, entry in pairs(v.exports) do - local slotNo = type(entry.slot) == 'number' and entry.slot or nil -- '*' indicates any slot + for machine in context.storage:filterActive('machine', filter) do + for _, entry in pairs(machine.exports) do + local slotNo = type(entry.slot) == 'number' and entry.slot or nil -- '*' indicates any slot - local slot = (slotNo and machine.getItemMeta(slotNo)) or { count = 0 } - for key in pairs(entry.filter) do - local item = itemDB:splitKey(key) + local slot = (slotNo and machine.adapter.getItemMeta(slotNo)) or { count = 0 } + for key in pairs(entry.filter or { }) do + local item = itemDB:splitKey(key) - -- is something else is in this slot - if not slot.name or slot.name == item.name then - local maxCount = slot.maxCount or itemDB:getMaxCount(item) - local count = maxCount - slot.count - if not slotNo then - -- TODO: should we just execute export - - -- or scan all slots for space ?? - count = machine.size() * maxCount - slot.count - end - if count > 0 then - item = Milo:getItemWithQty(item) - if item and count > 0 then - context.storage:export( - target, - slotNo, - math.min(count, item.count), - item) - end - end + -- is something else is in this slot + if not slot.name or slot.name == item.name then + local maxCount = slot.maxCount or itemDB:getMaxCount(item) + local count = maxCount - slot.count + if not slotNo then + -- TODO: should we just execute export - + -- or scan all slots for space ?? + count = machine.adapter.size() * maxCount - slot.count + end + if count > 0 then + item = Milo:getItemWithQty(item) + if item and count > 0 then + context.storage:export( + machine.name, + slotNo, + math.min(count, item.count), + item) end end end - else - debug('Invalid export target: ' .. target) end end end diff --git a/milo/plugins/importTask.lua b/milo/plugins/importTask.lua index 0e26156..0b27c52 100644 --- a/milo/plugins/importTask.lua +++ b/milo/plugins/importTask.lua @@ -1,48 +1,43 @@ local Milo = require('milo') -local device = _G.device - local ImportTask = { name = 'importer', priority = 20, } +local function filter(a) + return a.imports +end + function ImportTask:cycle(context) - for source, v in pairs(context.config.remoteDefaults) do - if v.imports then - local inventory = device[source] - if inventory then - for _, entry in pairs(v.imports) do + for inventory in context.storage:filterActive('machine', filter) do + for _, entry in pairs(inventory.imports) do - local function matchesFilter(item) - if not entry.filter then - return true - end - - local key = Milo:uniqueKey(item) - if entry.blacklist then - return not entry.filter[key] - end - return entry.filter[key] - end - - local function importSlot(slotNo) - local item = inventory.getItemMeta(slotNo) - if item and matchesFilter(item) then - context.storage:import(source, slotNo, item.count, item) - end - end - - if type(entry.slot) == 'number' then - importSlot(entry.slot) - else - for i = 1, inventory.size() do - importSlot(i) - end - end + local function matchesFilter(item) + if not entry.filter then + return true end + + local key = Milo:uniqueKey(item) + if entry.blacklist then + return not entry.filter[key] + end + return entry.filter[key] + end + + local function importSlot(slotNo) + local item = inventory.adapter.getItemMeta(slotNo) + if item and matchesFilter(item) then + context.storage:import(inventory.name, slotNo, item.count, item) + end + end + + if type(entry.slot) == 'number' then + importSlot(entry.slot) else - debug('Invalid import source: ' .. source) + for i = 1, inventory.adapter.size() do + importSlot(i) + end end end end diff --git a/milo/plugins/inputChestTask.lua b/milo/plugins/inputChestTask.lua index ae8de01..198d1e8 100644 --- a/milo/plugins/inputChestTask.lua +++ b/milo/plugins/inputChestTask.lua @@ -1,23 +1,14 @@ local Milo = require('milo') -local device = _G.device - local InputChest = { name = 'input', priority = 10, } function InputChest:cycle(context) - for source,v in pairs(context.config.remoteDefaults) do - if v.mtype == 'input' then - local inventory = device[source] - - local list = inventory and inventory.list and inventory.list() - if list then - for slot, item in pairs(list) do - context.storage:import(source, slot, item.count, item) - end - end + for inventory in context.storage:filterActive('input') do + for slot, item in pairs(inventory.adapter.list()) do + context.storage:import(inventory.name, slot, item.count, item) end end end diff --git a/milo/plugins/limitTask.lua b/milo/plugins/limitTask.lua index ba639ce..9f66a2b 100644 --- a/milo/plugins/limitTask.lua +++ b/milo/plugins/limitTask.lua @@ -6,28 +6,19 @@ local LimitTask = { } function LimitTask:cycle(context) - local trashcan + local trashcan = context.storage:filterActive('trashcan')() - for k,v in pairs(context.config.remoteDefaults) do - if v.mtype == 'trashcan' then - trashcan = k - break - end - end - - if not trashcan then - return - end - - for _,res in pairs(context.resources) do - if res.limit then - local item = Milo:getItemWithQty(res, res.ignoreDamage, res.ignoreNbtHash) - if item and item.count > res.limit then - context.storage:export( - trashcan, - nil, - item.count - res.limit, - item) + if trashcan then + for _,res in pairs(context.resources) do + if res.limit then + local item = Milo:getItemWithQty(res, res.ignoreDamage, res.ignoreNbtHash) + if item and item.count > res.limit then + context.storage:export( + trashcan.name, + nil, + item.count - res.limit, + item) + end end end end diff --git a/milo/plugins/potionImportTask.lua b/milo/plugins/potionImportTask.lua index f7586dd..58a6a07 100644 --- a/milo/plugins/potionImportTask.lua +++ b/milo/plugins/potionImportTask.lua @@ -1,62 +1,61 @@ local Craft = require('turtle.craft') local Milo = require('milo') -local device = _G.device - local PotionImportTask = { name = 'potions', priority = 30, brewQueue = { }, } +local function filter(a) + return a.adapter.type == 'minecraft:brewing_stand' +end + function PotionImportTask:cycle(context) - for _, v in pairs(device) do - if v.type == 'minecraft:brewing_stand' then - if v.getBrewTime() == 0 then - local list = v.list() + for bs in context.storage:filterActive('machine', filter) do + if bs.adapter.getBrewTime() == 0 then + local list = bs.adapter.list() - if list[1] and not list[4] then - -- brewing has completd + if list[1] and not list[4] then + -- brewing has completd - if self.brewQueue[v.name] and list[1] then - local key = Milo:uniqueKey(list[1]) - if not Craft.findRecipe(key) then -debug('saving new recipe') - Milo:saveMachineRecipe(self.brewQueue[v.name], list[1], v.name) - end - end - for slot = 1, 3 do - if list[slot] then - context.storage:import(v.name, slot, 1, list[slot]) - end + if self.brewQueue[bs.name] and list[1] then + local key = Milo:uniqueKey(list[1]) + if not Craft.findRecipe(key) then + Milo:saveMachineRecipe(self.brewQueue[bs.name], list[1], bs.name) end end - self.brewQueue[v.name] = nil - - elseif not self.brewQueue[v.name] then - local recipe = { - count = 3, - ingredients = { }, - maxCount = 3, - } - local list = v.list() - - local function valid() - for i = 1, 4 do - if not list[i] then - return false - end + for slot = 1, 3 do + if list[slot] then + context.storage:import(bs.name, slot, 1, list[slot]) end - return true + end + end + self.brewQueue[bs.name] = nil + + elseif not self.brewQueue[bs.name] then + local recipe = { + count = 3, + ingredients = { }, + maxCount = 3, + } + local list = bs.adapter.list() + + local function valid() + for i = 1, 4 do + if not list[i] then + return false + end + end + return true + end + + if valid() then + for i = 1, 4 do + recipe.ingredients[i] = Milo:uniqueKey(list[i]) end - if valid() then - for i = 1, 4 do - recipe.ingredients[i] = Milo:uniqueKey(list[i]) - end - - self.brewQueue[v.name] = recipe - end + self.brewQueue[bs.name] = recipe end end end diff --git a/milo/plugins/redstoneTask.lua b/milo/plugins/redstoneTask.lua index 465902d..f1f286f 100644 --- a/milo/plugins/redstoneTask.lua +++ b/milo/plugins/redstoneTask.lua @@ -9,10 +9,10 @@ local RedstoneTask = { } function RedstoneTask:cycle(context) - for _,v in pairs(context.config.remoteDefaults) do + for v in context.storage:filterActive({ 'mtype', 'machine' }) do if v.redstone then local ri = device[v.redstone.integrator] - if not ri or not v.adapter or not v.adapter.online then + if not ri or not v.adapter then debug(v.redstone) else local function conditionsSatisfied() From ca22b49aafe48a9ced16b04a2004eed02c374ef1 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sun, 28 Oct 2018 20:36:40 -0400 Subject: [PATCH 045/165] milo wip --- milo/MiloRemote.lua | 43 +++++++++++++++++++++++------------- milo/apis/milo.lua | 45 +++++++++++++++++++++++++++++++++----- milo/apis/storage.lua | 1 + milo/apis/turtle/craft.lua | 4 ++++ milo/plugins/craftTask.lua | 8 +++++-- milo/plugins/listing.lua | 34 ++++++++++++++++++++++++---- milo/plugins/remote.lua | 23 ++++++++++--------- 7 files changed, 121 insertions(+), 37 deletions(-) diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index 8f63f19..d35fef3 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -83,6 +83,7 @@ local page = UI.Page { [ 'control-e' ] = 'eject', [ 'control-r' ] = 'refresh', [ 'control-s' ] = 'eject_stack', + [ 'control-a' ] = 'eject_all', [ 'control-1' ] = 'eject_1', [ 'control-2' ] = 'eject_1', [ 'control-3' ] = 'eject_1', @@ -123,6 +124,7 @@ end function page:sendRequest(data) local response +debug(data) sync(self, function() local msg for _ = 1, 2 do @@ -144,7 +146,7 @@ function page:sendRequest(data) end self.notification:error(msg or 'Failed to connect') end) - +debug('got response') return response end @@ -182,7 +184,7 @@ function page:eventHandler(event) local item = self.grid:getSelected() if item then local response = self:sendRequest({ request = 'transfer', item = item, count = 1 }) - item.count = item.count - response.count + item.count = response.count self.grid:draw() end @@ -190,7 +192,15 @@ function page:eventHandler(event) local item = self.grid:getSelected() if item then local response = self:sendRequest({ request = 'transfer', item = item, count = 64 }) - item.count = item.count - response.count + item.count = response.count + self.grid:draw() + end + + elseif event.type == 'eject_all' then + local item = self.grid:getSelected() + if item then + local response = self:sendRequest({ request = 'transfer', item = item, count = item.count }) + item.count = response.count self.grid:draw() end @@ -251,19 +261,22 @@ end debug(options.slot) if options.slot.value then debug('Transfer items initialized') - Event.onInterval(2, function() - local neural = device.neuralInterface - if neural and neural.getInventory then - local item = neural.getInventory().getItem(options.slot.value) - if item then - debug('depositing') - page:sendRequest({ request = 'deposit', slot = options.slot.value }) - -- local item = - -- TODO: update count for this one item - -- page.grid:draw() page:sync() + Event.addRoutine(function() + while true do + os.sleep(1.5) + local neural = device.neuralInterface + if neural and neural.getInventory then + local item = neural.getInventory().getItem(options.slot.value) + if item then + debug('depositing') + page:sendRequest({ request = 'deposit', slot = options.slot.value }) + -- local item = + -- TODO: update count for this one item + -- page.grid:draw() page:sync() + end + else + debug('missing Introspection module') end - else - debug('missing Introspection module') end end) end diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index 97eef95..a81d5f2 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -143,14 +143,45 @@ function Milo:getTurtleInventory() return list end -function Milo:eject(item, qty) - local s, m = pcall(function() - self.context.storage:provide(item, qty) - turtle.emptyInventory() +function Milo:xxx(item, count) + return self:provideItem(item, count, function(providable, currentCount) + -- return the current amount in the system + return currentCount - self:eject(item, providable) end) - if not s and m then - debug(m) +end + +function Milo:provideItem(item, count, callback) + local current = Milo:getItem(Milo:listItems(), item) or { count = 0 } + local toCraft = count - math.min(current.count, count) + + if toCraft > 0 then + local recipe = Craft.findRecipe(self:uniqueKey(item)) + if not recipe then + toCraft = 0 + else + -- if you ask for 1 stick, getCraftableAmount will return 4 (obviously) + toCraft = math.min(toCraft, Craft.getCraftableAmount(recipe, toCraft, Milo:listItems(), { })) + end end + + if toCraft == 0 then + return callback(math.min(count, current.count), current.count) +-- return current.count - self:eject(item, math.min(count, current.count)) + end + + item = Util.shallowCopy(item) + item.count = current.count + toCraft + item.eject = callback + self:requestCrafting(item) + item.crafted = current.count + + return current.count +end + +function Milo:eject(item, count) + count = self.context.storage:provide(item, count) + turtle.emptyInventory() + return count end function Milo:saveMachineRecipe(recipe, result, machine) @@ -175,6 +206,7 @@ function Milo:mergeResources(t) else item = Util.shallowCopy(v) item.count = 0 + item.key = self:uniqueKey(v) table.insert(t, item) end end @@ -185,6 +217,7 @@ function Milo:mergeResources(t) if not item then item = Util.shallowCopy(v) item.count = 0 + item.key = self:uniqueKey(v) table.insert(t, item) end item.has_recipe = true diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index f7c3422..048c86e 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -160,6 +160,7 @@ self.listCount = self.listCount + 1 if not entry then entry = Util.shallowCopy(v) entry.count = v.count + entry.key = key cache[key] = entry table.insert(items, entry) else diff --git a/milo/apis/turtle/craft.lua b/milo/apis/turtle/craft.lua index 6e891a0..c531c7d 100644 --- a/milo/apis/turtle/craft.lua +++ b/milo/apis/turtle/craft.lua @@ -175,6 +175,10 @@ function Craft.craftRecipe(recipe, count, inventoryAdapter, origItem) --end for _, request in pairs(origItem.ingredients) do +if request.pending then + debug('??') + debug(request) +end if request.crafted >= request.count then request.status = nil request.statusCode = Craft.STATUS_SUCCESS diff --git a/milo/plugins/craftTask.lua b/milo/plugins/craftTask.lua index d360810..ca87d97 100644 --- a/milo/plugins/craftTask.lua +++ b/milo/plugins/craftTask.lua @@ -99,11 +99,15 @@ function craftTask:cycle() for _,key in pairs(Util.keys(context.craftingQueue)) do local item = context.craftingQueue[key] if item.count - item.crafted > 0 then - local recipe = Craft.recipes[key] + local recipe = Craft.findRecipe(key) if recipe then self:craft(recipe, item) if item.eject and item.crafted >= item.count then - Milo:eject(item, item.count) + if type(item.eject) == 'boolean' then + Milo:eject(item, item.count) + else + item.eject(item.count, 0) -- unknown amount in system + end end elseif not context.controllerAdapter then item.status = '(no recipe)' diff --git a/milo/plugins/listing.lua b/milo/plugins/listing.lua index 91c1e99..5a5699a 100644 --- a/milo/plugins/listing.lua +++ b/milo/plugins/listing.lua @@ -91,6 +91,8 @@ local listingPage = UI.Page { q = 'quit', [ 'control-e' ] = 'eject', [ 'control-s' ] = 'eject_stack', + [ 'control-a' ] = 'eject_all', + [ 'control-1' ] = 'eject_1', [ 'control-2' ] = 'eject_1', [ 'control-3' ] = 'eject_1', @@ -144,9 +146,7 @@ function listingPage:eventHandler(event) local item = self.grid:getSelected() if item then queue(function() - Milo:eject(item, 1) - local updated = Milo:getItem(Milo:listItems(), item) - item.count = updated and updated.count or 0 + item.count = Milo:xxx(item, 1) self.grid:draw() end) end @@ -154,7 +154,19 @@ function listingPage:eventHandler(event) elseif event.type == 'eject_stack' then local item = self.grid:getSelected() if item then - queue(function() Milo:eject(item, itemDB:getMaxCount(item)) end) + queue(function() + item.count = Milo:xxx(item, itemDB:getMaxCount(item)) + self.grid:draw() + end) + end + + elseif event.type == 'eject_all' then + local item = self.grid:getSelected() + if item then + local updated = Milo:getItem(Milo:listItems(), item) + if updated then + queue(function() Milo:eject(item, updated.count) end) + end end elseif event.type == 'machines' then @@ -235,9 +247,23 @@ end function listingPage:enable() self:refresh() self:setFocus(self.statusBar.filter) + self.timer = Event.onInterval(5, function() + for _,v in pairs(self.allItems) do + if not v.key then debug(v) error('') end + local c = context.storage.cache[v.key] + v.count = c and c.count or 0 + end + self.grid:draw() + self:sync() + end) UI.Page.enable(self) end +function listingPage:disable() + Event.off(self.timer) + UI.Page.disable(self) +end + function listingPage:refresh() self.allItems = Milo:refreshItems() Milo:mergeResources(self.allItems) diff --git a/milo/plugins/remote.lua b/milo/plugins/remote.lua index 7d2e3f5..e386a17 100644 --- a/milo/plugins/remote.lua +++ b/milo/plugins/remote.lua @@ -59,17 +59,20 @@ debug('remote: ' .. data.request) Milo:clearGrid() elseif data.request == 'transfer' then - local count = context.storage:export( - context.localName, - nil, - data.count, - data.item) - - turtle.eachFilledSlot(function(slot) - manipulator.getInventory().pullItems( + local count = Milo:provideItem(data.item, data.count, function(amount, currentCount) + amount = context.storage:export( context.localName, - slot.index, - slot.count) + nil, + amount, + data.item) + + turtle.eachFilledSlot(function(slot) + manipulator.getInventory().pullItems( + context.localName, + slot.index, + slot.count) + end) + return currentCount - amount end) socket:write({ count = count }) From 906d26112b4a229f18b54587b0da1817e833bd80 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sun, 28 Oct 2018 23:37:44 -0400 Subject: [PATCH 046/165] milo wip --- milo/apis/milo.lua | 2 +- milo/plugins/activityMonitor.lua | 159 +++++++++++++++++++ milo/plugins/{jobList.lua => jobMonitor.lua} | 26 +-- milo/plugins/listing.lua | 1 + 4 files changed, 174 insertions(+), 14 deletions(-) create mode 100644 milo/plugins/activityMonitor.lua rename milo/plugins/{jobList.lua => jobMonitor.lua} (82%) diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index a81d5f2..a8b9e9f 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -80,7 +80,7 @@ function Milo:registerTask(task) end function Milo:showError(msg) - self.context.jobList:showError(msg) + self.context.jobMonitor:showError(msg) end function Milo:getItem(items, inItem, ignoreDamage, ignoreNbtHash) diff --git a/milo/plugins/activityMonitor.lua b/milo/plugins/activityMonitor.lua new file mode 100644 index 0000000..684599d --- /dev/null +++ b/milo/plugins/activityMonitor.lua @@ -0,0 +1,159 @@ +local Event = require('event') +local Milo = require('milo') +local Peripheral = require('peripheral') +local UI = require('ui') +local Util = require('util') + +local colors = _G.colors + +local context = Milo:getContext() +local mon = Peripheral.lookup(context.config.activityMonitor) + +if not mon then + return +end + +local changedPage = UI.Window { + parent = UI.Device { + device = mon, + textScale = .5, + }, + grid = UI.Grid { + ey = -6, + columns = { + { heading = 'Qty', key = 'count', width = 6 }, + { heading = 'Change', key = 'change', width = 6 }, + { heading = 'Rate', key = 'rate', width = 6 }, + { heading = 'Name', key = 'displayName' }, + }, + sortColumn = 'displayName', + }, + buttons = UI.Window { + y = -5, height = 5, + backgroundColor = colors.gray, + prevButton = UI.Button { + x = 2, y = 2, height = 3, width = 5, + event = 'previous', + backgroundColor = colors.lightGray, + text = ' < ' + }, + resetButton = UI.Button { + x = 8, y = 2, height = 3, ex = -8, + event = 'reset', + backgroundColor = colors.lightGray, + text = 'Reset' + }, + nextButton = UI.Button { + x = -6, y = 2, height = 3, width = 5, + event = 'next', + backgroundColor = colors.lightGray, + text = ' > ' + }, + }, + accelerators = { + q = 'quit', + } +} + +function changedPage.grid:getDisplayValues(row) + row = Util.shallowCopy(row) + + local ind = '+' + if row.change < 0 then + ind = '' + end + + row.change = ind .. Util.toBytes(row.change) + row.count = Util.toBytes(row.count) + row.rate = Util.toBytes(row.rate) + + return row +end + +function changedPage:eventHandler(event) + if event.type == 'reset' then + self.lastItems = nil + self.grid:setValues({ }) + self.grid:clear() + self.grid:draw() + + elseif event.type == 'next' then + self.grid:nextPage() + + elseif event.type == 'previous' then + self.grid:previousPage() + + elseif event.type == 'quit' then + Event.exitPullEvents() + + else + return UI.Window.eventHandler(self, event) + end + + return true +end + +function changedPage:refresh() + local t = context.storage.cache + + if not self.lastItems then + self.lastItems = { } + for k,v in pairs(t) do + self.lastItems[k] = { + displayName = v.displayName, + lastCount = v.count, + } + end + self.timestamp = os.clock() + self.grid:setValues({ }) + + else + for _,v in pairs(self.lastItems) do + v.count = nil + end + + self.elapsed = os.clock() - self.timestamp + + for k,v in pairs(t) do + local v2 = self.lastItems[k] + if v2 then + v2.count = v.count + else + self.lastItems[k] = { + displayName = v.displayName, + count = v.count, + lastCount = 0, + } + end + end + + local changedItems = { } + for k,v in pairs(self.lastItems) do + if not v.count then + v.count = 0 + end + if v.count ~= v.lastCount then + v.change = v.count - v.lastCount + v.rate = Util.round(60 / self.elapsed * v.change, 1) + changedItems[k] = v + end + end + + self.grid:setValues(changedItems) + end + self.grid:draw() +end + +Event.onInterval(5, function() + if context.storage:isOnline() then + changedPage:refresh() + changedPage:sync() + else + changedPage.grid:clear() + changedPage.grid:centeredWrite(math.ceil(changedPage.height / 2), 'Storage Offline') + changedPage:sync() + end +end) + +changedPage:draw() +changedPage:sync() diff --git a/milo/plugins/jobList.lua b/milo/plugins/jobMonitor.lua similarity index 82% rename from milo/plugins/jobList.lua rename to milo/plugins/jobMonitor.lua index 4862d3a..8d4b535 100644 --- a/milo/plugins/jobList.lua +++ b/milo/plugins/jobMonitor.lua @@ -11,7 +11,7 @@ local context = Milo:getContext() local mon = Peripheral.lookup(context.config.monitor) or error('Monitor is not attached') -local jobList = UI.Page { +local jobMonitor = UI.Page { parent = UI.Device { device = mon, textScale = .5, @@ -28,13 +28,13 @@ local jobList = UI.Page { }, } -function jobList:showError(msg) +function jobMonitor:showError(msg) self.grid:clear() self.grid:centeredWrite(math.ceil(self.grid.height / 2), msg) self:sync() end -function jobList:updateList(craftList) +function jobMonitor:updateList(craftList) if not Milo:isCraftingPaused() then local t = { } for _,v in pairs(craftList) do @@ -58,7 +58,7 @@ function jobList:updateList(craftList) end end -function jobList.grid:getDisplayValues(row) +function jobMonitor.grid:getDisplayValues(row) row = Util.shallowCopy(row) if row.showRemaining then row.remaining = math.max(0, row.count - row.crafted) @@ -69,7 +69,7 @@ function jobList.grid:getDisplayValues(row) return row end -function jobList.grid:getRowTextColor(row, selected) +function jobMonitor.grid:getRowTextColor(row, selected) local statusColor = { [ Craft.STATUS_ERROR ] = colors.red, [ Craft.STATUS_WARNING ] = colors.orange, @@ -80,18 +80,18 @@ function jobList.grid:getRowTextColor(row, selected) UI.Grid:getRowTextColor(row, selected) end -jobList:enable() -jobList:draw() -jobList:sync() +jobMonitor:enable() +jobMonitor:draw() +jobMonitor:sync() -local JobListTask = { +local jobMonitorTask = { name = 'job status', priority = 80, } -function JobListTask:cycle() - jobList:updateList(context.craftingQueue) +function jobMonitorTask:cycle() + jobMonitor:updateList(context.craftingQueue) end -Milo:registerTask(JobListTask) -context.jobList = jobList +Milo:registerTask(jobMonitorTask) +context.jobMonitor = jobMonitor diff --git a/milo/plugins/listing.lua b/milo/plugins/listing.lua index 5a5699a..fd04423 100644 --- a/milo/plugins/listing.lua +++ b/milo/plugins/listing.lua @@ -10,6 +10,7 @@ local os = _G.os local context = Milo:getContext() +-- TODO: fix local function queue(fn) while Milo:isCraftingPaused() do os.sleep(1) From ff892dfac2b10d68c2903eaa370a2f6383e03331 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Mon, 29 Oct 2018 02:45:13 -0400 Subject: [PATCH 047/165] milo wip --- milo/MiloRemote.lua | 84 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 66 insertions(+), 18 deletions(-) diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index d35fef3..13cd1e4 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -43,12 +43,17 @@ if (options.slot.value or options.shield.value) and end local page = UI.Page { - menuBar = UI.MenuBar { - buttons = { - { text = 'Craft', event = 'craft' }, - { text = 'Refresh', event = 'refresh', x = -9 }, + dummy = UI.Window { + x = 1, ex = -10, y = 1, height = 1, + infoBar = UI.StatusBar { + backgroundColor = colors.lightGray, }, }, + refresh = UI.Button { + y = 1, x = -9, + event = 'refresh', + text = 'Refresh', + }, grid = UI.Grid { y = 2, ey = -2, columns = { @@ -56,34 +61,49 @@ local page = UI.Page { { heading = 'Name', key = 'displayName' }, }, sortColumn = 'displayName', + help = '^(s)tack, ^(a)ll' }, - statusBar = UI.StatusBar { + statusBar = UI.Window { + y = -1, filter = UI.TextEntry { - x = 1, ex = -4, + x = 1, ex = -9, limit = 50, shadowText = 'filter', - shadowTextColor = colors.gray, backgroundColor = colors.cyan, backgroundFocusColor = colors.cyan, accelerators = { - [ 'enter' ] = 'craft', + [ 'enter' ] = 'eject', }, }, + amount = UI.TextEntry { + x = -8, ex = -4, + limit = 3, + shadowText = '1', + shadowTextColor = colors.gray, + backgroundColor = colors.black, + backgroundFocusColor = colors.black, + accelerators = { + [ 'enter' ] = 'eject_specified', + }, + help = 'Specify an amount to send', + }, display = UI.Button { x = -3, event = 'toggle_display', value = 0, text = 'A', + help = 'Toggle display mode', }, }, - notification = UI.Notification(), accelerators = { r = 'refresh', - q = 'quit', - [ 'control-e' ] = 'eject', [ 'control-r' ] = 'refresh', + [ 'control-e' ] = 'eject', [ 'control-s' ] = 'eject_stack', [ 'control-a' ] = 'eject_all', + + q = 'quit', + [ 'control-1' ] = 'eject_1', [ 'control-2' ] = 'eject_1', [ 'control-3' ] = 'eject_1', @@ -94,8 +114,6 @@ local page = UI.Page { [ 'control-8' ] = 'eject_1', [ 'control-9' ] = 'eject_1', [ 'control-0' ] = 'eject_1', - [ 'control-m' ] = 'machines', - [ 'control-l' ] = 'resume', }, displayMode = 0, } @@ -121,14 +139,21 @@ local function filterItems(t, filter, displayMode) return t end +function page:setStatus(status) + self.dummy.infoBar:setStatus(status) + self:sync() +end + function page:sendRequest(data) local response debug(data) sync(self, function() + self:sync() local msg for _ = 1, 2 do if not socket or not socket.connected then + self:setStatus('connecting ...') socket, msg = Socket.connect(options.server.value, 4242) if socket then socket:write(options.user.value) @@ -138,13 +163,16 @@ debug(data) if socket:write(data) then response = socket:read(2) if response then + Event.onTimeout(2, function() + self:setStatus('') + end) return end end socket:close() end end - self.notification:error(msg or 'Failed to connect') + self:setStatus(msg or 'Failed to connect') end) debug('got response') return response @@ -180,9 +208,13 @@ function page:eventHandler(event) if event.type == 'quit' then UI:exitPullEvents() - elseif event.type == 'eject' then + elseif event.type == 'focus_change' then + self.dummy.infoBar:setStatus(event.focused.help) + + elseif event.type == 'eject' or event.type == 'grid_select' then local item = self.grid:getSelected() if item then + self:setStatus('requesting 1 ...') local response = self:sendRequest({ request = 'transfer', item = item, count = 1 }) item.count = response.count self.grid:draw() @@ -191,6 +223,7 @@ function page:eventHandler(event) elseif event.type == 'eject_stack' then local item = self.grid:getSelected() if item then + self:setStatus('requesting stack ...') local response = self:sendRequest({ request = 'transfer', item = item, count = 64 }) item.count = response.count self.grid:draw() @@ -199,15 +232,31 @@ function page:eventHandler(event) elseif event.type == 'eject_all' then local item = self.grid:getSelected() if item then + self:setStatus('requesting all ...') local response = self:sendRequest({ request = 'transfer', item = item, count = item.count }) item.count = response.count self.grid:draw() end + elseif event.type == 'eject_specified' then + local item = self.grid:getSelected() + local count = tonumber(self.statusBar.amount.value) + if item and count then + self.statusBar.amount:reset() + self:setFocus(self.statusBar.filter) + self:setStatus('requesting ' .. count .. ' ...') + local response = self:sendRequest({ request = 'transfer', item = item, count = count }) + item.count = response.count + self.grid:draw() + else + self:setStatus('nope ...') + end + elseif event.type == 'refresh' then + self:setStatus('updating ...') self:refresh() self.grid:draw() - self.statusBar.filter:focus() + self:setFocus(self.statusBar.filter) elseif event.type == 'toggle_display' then local values = { @@ -223,14 +272,13 @@ function page:eventHandler(event) self:applyFilter() self.grid:draw() - elseif event.type == 'text_change' then + elseif event.type == 'text_change' and event.element == self.statusBar.filter then self.filter = event.text if #self.filter == 0 then self.filter = nil end self:applyFilter() self.grid:draw() - self.statusBar.filter:focus() else UI.Page.eventHandler(self, event) From 46ed01630d109d2a166185f88f23b51c5dc326cf Mon Sep 17 00:00:00 2001 From: kepler155c Date: Mon, 29 Oct 2018 16:04:41 -0400 Subject: [PATCH 048/165] milo perf --- milo/Milo.lua | 2 +- milo/MiloRemote.lua | 31 ++++---------- milo/apis/storage.lua | 75 ++++++++++++++++++++++++++-------- milo/plugins/autocraftTask.lua | 1 + milo/plugins/item.lua | 2 + milo/plugins/listing.lua | 14 ++----- milo/plugins/remote.lua | 2 +- 7 files changed, 75 insertions(+), 52 deletions(-) diff --git a/milo/Milo.lua b/milo/Milo.lua index 6e2f61f..c394fa1 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -150,7 +150,7 @@ UI:setPage(page) Event.onInterval(5, function() if not Milo:isCraftingPaused() and context.storage:isOnline() then Milo:resetCraftingStatus() - Milo:refreshItems() + --Milo:refreshItems() for _, task in ipairs(context.tasks) do local s, m = pcall(function() task:cycle(context) end) diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index 13cd1e4..da8fc79 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -60,6 +60,7 @@ local page = UI.Page { { heading = ' Qty', key = 'count' , width = 4, justify = 'right' }, { heading = 'Name', key = 'displayName' }, }, + values = { }, sortColumn = 'displayName', help = '^(s)tack, ^(a)ll' }, @@ -103,17 +104,6 @@ local page = UI.Page { [ 'control-a' ] = 'eject_all', q = 'quit', - - [ 'control-1' ] = 'eject_1', - [ 'control-2' ] = 'eject_1', - [ 'control-3' ] = 'eject_1', - [ 'control-4' ] = 'eject_1', - [ 'control-5' ] = 'eject_1', - [ 'control-6' ] = 'eject_1', - [ 'control-7' ] = 'eject_1', - [ 'control-8' ] = 'eject_1', - [ 'control-9' ] = 'eject_1', - [ 'control-0' ] = 'eject_1', }, displayMode = 0, } @@ -178,10 +168,6 @@ debug('got response') return response end -function page.statusBar:draw() - return UI.Window.draw(self) -end - function page.grid:getRowTextColor(row, selected) if row.is_craftable then return colors.yellow @@ -195,12 +181,6 @@ end function page.grid:getDisplayValues(row) row = Util.shallowCopy(row) row.count = row.count > 0 and Util.toBytes(row.count) or '' - if row.low then - row.low = Util.toBytes(row.low) - end - if row.limit then - row.limit = Util.toBytes(row.limit) - end return row end @@ -224,6 +204,7 @@ function page:eventHandler(event) local item = self.grid:getSelected() if item then self:setStatus('requesting stack ...') + -- TODO: send a stack request - have server figure out stack size local response = self:sendRequest({ request = 'transfer', item = item, count = 64 }) item.count = response.count self.grid:draw() @@ -233,6 +214,7 @@ function page:eventHandler(event) local item = self.grid:getSelected() if item then self:setStatus('requesting all ...') + -- TODO: let server figure out count local response = self:sendRequest({ request = 'transfer', item = item, count = item.count }) item.count = response.count self.grid:draw() @@ -264,7 +246,6 @@ function page:eventHandler(event) [1] = 'I', [2] = 'C', } - event.button.value = (event.button.value + 1) % 3 self.displayMode = event.button.value event.button.text = values[event.button.value] @@ -306,13 +287,17 @@ function page:applyFilter() self.grid:setValues(t) end +_G.p4 = Event + debug(options.slot) if options.slot.value then debug('Transfer items initialized') Event.addRoutine(function() while true do +debug('sleeping') os.sleep(1.5) local neural = device.neuralInterface +debug(neural) if neural and neural.getInventory then local item = neural.getInventory().getItem(options.slot.value) if item then @@ -321,6 +306,8 @@ if options.slot.value then -- local item = -- TODO: update count for this one item -- page.grid:draw() page:sync() + else +debug('empty') end else debug('missing Introspection module') diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 048c86e..e18a752 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -16,6 +16,8 @@ function Storage:init(args) listCount = 0, activity = { }, storageOnline = true, + hits = 0, + misses = 0, } Util.merge(self, defaults) Util.merge(self, args) @@ -29,6 +31,7 @@ debug('%s: %s', e, tostring(dev)) end) Event.onInterval(15, function() self:showStorage() + debug('STORAGE: cache: %d/%d', self.hits, self.misses) end) end @@ -43,7 +46,7 @@ function Storage:showStorage() if #t > 0 then debug('Storage:') for _, k in pairs(t) do - debug(' %s: %s', online and ' online' or 'offline', k) + debug(' offline: ' .. k) end debug('') end @@ -71,6 +74,7 @@ function Storage:initStorage() elseif device[k] and device[k].list and device[k].size and device[k].pullItems then v.adapter = InventoryAdapter.wrap({ side = k }) v.adapter.online = true + v.adapter.dirty = true end if v.mtype == 'storage' then online = online and not not (v.adapter and v.adapter.online) @@ -132,6 +136,10 @@ end function Storage:refresh(throttle) self.dirty = true +debug('STORAGE: Forcing full refresh') + for _, adapter in self:onlineAdapters() do + adapter.dirty = true + end return self:listItems(throttle) end @@ -144,33 +152,35 @@ self.listCount = self.listCount + 1 --debug(self.listCount) -- todo: only listItems from dirty remotes - +local ct = os.clock() local cache = { } local items = { } throttle = throttle or Util.throttle() for _, adapter in self:onlineAdapters() do - adapter:listItems(throttle) + if adapter.dirty then +debug('STORAGE: refresh: ' .. adapter.name) + adapter:listItems(throttle) + adapter.dirty = false + end local rcache = adapter.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 - entry.key = key - cache[key] = entry - table.insert(items, entry) - else - entry.count = entry.count + v.count - end - - throttle() + local entry = cache[key] + if not entry then + entry = Util.shallowCopy(v) + entry.count = v.count + entry.key = key + cache[key] = entry + table.insert(items, entry) + else + entry.count = entry.count + v.count end + + throttle() end end +debug('STORAGE: refresh in ' .. (os.clock() - ct)) self.dirty = false self.cache = cache @@ -185,6 +195,29 @@ end function Storage:provide(item, qty, slot, direction) local total = 0 + local key = item.key or table.concat({ item.name, item.damage, item.nbtHash }, ':') + for _, adapter in self:onlineAdapters() do + if adapter.cache and adapter.cache[key] then + local amount = adapter:provide(item, qty, slot, direction or self.localName) + if amount > 0 then + self.hits = self.hits + 1 + debug('EXT: %s(%d): %s -> %s%s', + item.name, amount, adapter.name, direction or self.localName, + slot and string.format('[%d]', slot) or '') + self.dirty = true + adapter.dirty = true + end + qty = qty - amount + total = total + amount + if qty <= 0 then + return total + end + end + end + + debug('miss: %s - %d', key, qty) + self.misses = self.misses + 1 + for _, adapter in self:onlineAdapters() do local amount = adapter:provide(item, qty, slot, direction or self.localName) if amount > 0 then @@ -241,6 +274,14 @@ debug('INS: %s(%d): %s[%d] -> %s', adapter.dirty = true local entry = self.activity[key] or 0 self.activity[key] = entry + amount + +--[[ + local cached = adapter.cache[key] + if cached then + cached.count = cached.count + amount + else + end +]] end qty = qty - amount total = total + amount diff --git a/milo/plugins/autocraftTask.lua b/milo/plugins/autocraftTask.lua index fd59616..50d2f9f 100644 --- a/milo/plugins/autocraftTask.lua +++ b/milo/plugins/autocraftTask.lua @@ -6,6 +6,7 @@ local Autocraft = { priority = 100, } +-- TODO: fix/test function Autocraft:cycle(context) local list = { } diff --git a/milo/plugins/item.lua b/milo/plugins/item.lua index 3ef6200..f7e0879 100644 --- a/milo/plugins/item.lua +++ b/milo/plugins/item.lua @@ -8,6 +8,8 @@ local device = _G.device local context = Milo:getContext() +-- TODO: allow change of machine + local itemPage = UI.Page { titleBar = UI.TitleBar { title = 'Limit Resource', diff --git a/milo/plugins/listing.lua b/milo/plugins/listing.lua index fd04423..aab75c3 100644 --- a/milo/plugins/listing.lua +++ b/milo/plugins/listing.lua @@ -89,23 +89,15 @@ local listingPage = UI.Page { accelerators = { r = 'refresh', [ 'control-r' ] = 'refresh', - q = 'quit', + [ 'control-e' ] = 'eject', [ 'control-s' ] = 'eject_stack', [ 'control-a' ] = 'eject_all', - [ 'control-1' ] = 'eject_1', - [ 'control-2' ] = 'eject_1', - [ 'control-3' ] = 'eject_1', - [ 'control-4' ] = 'eject_1', - [ 'control-5' ] = 'eject_1', - [ 'control-6' ] = 'eject_1', - [ 'control-7' ] = 'eject_1', - [ 'control-8' ] = 'eject_1', - [ 'control-9' ] = 'eject_1', - [ 'control-0' ] = 'eject_1', [ 'control-m' ] = 'machines', [ 'control-l' ] = 'resume', + + q = 'quit', }, displayMode = 0, } diff --git a/milo/plugins/remote.lua b/milo/plugins/remote.lua index e386a17..c30a651 100644 --- a/milo/plugins/remote.lua +++ b/milo/plugins/remote.lua @@ -37,7 +37,7 @@ local function client(socket) end debug('remote: ' .. data.request) if data.request == 'list' then - local items = Milo:refreshItems() + local items = Milo:listItems() Milo:mergeResources(items) socket:write(items) From f24aeb135dfb4339963649ba1c8c897a40048a0a Mon Sep 17 00:00:00 2001 From: kepler155c Date: Mon, 29 Oct 2018 17:15:07 -0400 Subject: [PATCH 049/165] milo wip --- milo/MiloRemote.lua | 40 ++++++++++++++++++++++++++-------------- milo/apis/milo.lua | 4 ++++ milo/plugins/remote.lua | 14 ++++++++++++-- 3 files changed, 42 insertions(+), 16 deletions(-) diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index da8fc79..e7245fa 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -106,6 +106,7 @@ local page = UI.Page { q = 'quit', }, displayMode = 0, + items = { }, } local function filterItems(t, filter, displayMode) @@ -137,7 +138,6 @@ end function page:sendRequest(data) local response -debug(data) sync(self, function() self:sync() local msg @@ -146,6 +146,7 @@ debug(data) self:setStatus('connecting ...') socket, msg = Socket.connect(options.server.value, 4242) if socket then + self:setStatus('connected ...') socket:write(options.user.value) end end @@ -163,8 +164,11 @@ debug(data) end end self:setStatus(msg or 'Failed to connect') + Event.onTimeout(2, function() + self:setStatus('') + end) end) -debug('got response') + return response end @@ -196,28 +200,32 @@ function page:eventHandler(event) if item then self:setStatus('requesting 1 ...') local response = self:sendRequest({ request = 'transfer', item = item, count = 1 }) - item.count = response.count - self.grid:draw() + if response then + item.count = response.count + self.grid:draw() + end end elseif event.type == 'eject_stack' then local item = self.grid:getSelected() if item then self:setStatus('requesting stack ...') - -- TODO: send a stack request - have server figure out stack size - local response = self:sendRequest({ request = 'transfer', item = item, count = 64 }) - item.count = response.count - self.grid:draw() + local response = self:sendRequest({ request = 'transfer', item = item, count = 'stack' }) + if response then + item.count = response.count + self.grid:draw() + end end elseif event.type == 'eject_all' then local item = self.grid:getSelected() if item then self:setStatus('requesting all ...') - -- TODO: let server figure out count - local response = self:sendRequest({ request = 'transfer', item = item, count = item.count }) - item.count = response.count - self.grid:draw() + local response = self:sendRequest({ request = 'transfer', item = item, count = 'all' }) + if response then + item.count = response.count + self.grid:draw() + end end elseif event.type == 'eject_specified' then @@ -235,10 +243,10 @@ function page:eventHandler(event) end elseif event.type == 'refresh' then + self:setFocus(self.statusBar.filter) self:setStatus('updating ...') self:refresh() self.grid:draw() - self:setFocus(self.statusBar.filter) elseif event.type == 'toggle_display' then local values = { @@ -268,9 +276,13 @@ function page:eventHandler(event) end function page:enable() - self:refresh() self:setFocus(self.statusBar.filter) UI.Page.enable(self) + Event.onTimeout(.1, function() + self:refresh() + self.grid:draw() + self:sync() + end) end function page:refresh() diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index a8b9e9f..be44c09 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -151,6 +151,10 @@ function Milo:xxx(item, count) end function Milo:provideItem(item, count, callback) + if count <= 0 then + return 0 + end + local current = Milo:getItem(Milo:listItems(), item) or { count = 0 } local toCraft = count - math.min(current.count, count) diff --git a/milo/plugins/remote.lua b/milo/plugins/remote.lua index c30a651..d15df2b 100644 --- a/milo/plugins/remote.lua +++ b/milo/plugins/remote.lua @@ -1,4 +1,5 @@ local Event = require('event') +local itemDB = require('itemDB') local Milo = require('milo') local Socket = require('socket') @@ -59,7 +60,16 @@ debug('remote: ' .. data.request) Milo:clearGrid() elseif data.request == 'transfer' then - local count = Milo:provideItem(data.item, data.count, function(amount, currentCount) + local count = data.count + + if count == 'stack' then + count = itemDB:getMaxCount(data.item) + elseif count == 'all' then + local item = Milo:getItem(Milo:listItems(), data.item) + count = item and item.count or 0 + end + + local provided = Milo:provideItem(data.item, count, function(amount, currentCount) amount = context.storage:export( context.localName, nil, @@ -75,7 +85,7 @@ debug('remote: ' .. data.request) return currentCount - amount end) - socket:write({ count = count }) + socket:write({ count = provided }) end until not socket.connected From 871f44a170805093bbe09608e652036c0b356b71 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Mon, 29 Oct 2018 22:00:59 -0400 Subject: [PATCH 050/165] milo wip --- milo/apis/milo.lua | 18 ++++++++--- milo/apis/storage.lua | 8 ++--- milo/core/machines.lua | 9 ++++-- milo/plugins/brewingStandView.lua | 35 +++++++++++++++++++++ milo/plugins/exportView.lua | 1 - milo/plugins/importView.lua | 1 - milo/plugins/item.lua | 52 ++++++++++++++++++++++++++++--- milo/plugins/listing.lua | 10 ++++-- milo/plugins/machineLearn.lua | 16 +++++++--- milo/plugins/storageView.lua | 4 +-- 10 files changed, 129 insertions(+), 25 deletions(-) create mode 100644 milo/plugins/brewingStandView.lua diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index be44c09..be0dc36 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -132,10 +132,11 @@ end function Milo:getTurtleInventory() local list = { } for i = 1,16 do - -- TODO: update item db local item = self.context.introspectionModule.getInventory().getItemMeta(i) if item then - itemDB:add(item) + if not itemDB:get(item) then + itemDB:add(item) + end list[i] = item end end @@ -211,7 +212,8 @@ function Milo:mergeResources(t) item = Util.shallowCopy(v) item.count = 0 item.key = self:uniqueKey(v) - table.insert(t, item) +-- table.insert(t, item) + t[item.key] = item end end @@ -222,11 +224,19 @@ function Milo:mergeResources(t) item = Util.shallowCopy(v) item.count = 0 item.key = self:uniqueKey(v) - table.insert(t, item) + t[item.key] = item +-- table.insert(t, item) end item.has_recipe = true end + for key in pairs(Craft.machineLookup) do + local item = t[key] + if item then + item.is_craftable = true + end + end + for _,v in pairs(t) do if not v.displayName then v.displayName = itemDB:getName(v) diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index e18a752..6d7d6d5 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -44,7 +44,7 @@ function Storage:showStorage() end end if #t > 0 then - debug('Storage:') + debug('Adapter:') for _, k in pairs(t) do debug(' offline: ' .. k) end @@ -164,7 +164,6 @@ debug('STORAGE: refresh: ' .. adapter.name) adapter.dirty = false end local rcache = adapter.cache or { } --- TODO: add a method in each adapter that only updates a passed cache for key,v in pairs(rcache) do local entry = cache[key] if not entry then @@ -172,7 +171,8 @@ debug('STORAGE: refresh: ' .. adapter.name) entry.count = v.count entry.key = key cache[key] = entry - table.insert(items, entry) + items[key] = entry +-- table.insert(items, entry) else entry.count = entry.count + v.count end @@ -215,7 +215,7 @@ function Storage:provide(item, qty, slot, direction) end end - debug('miss: %s - %d', key, qty) + debug('STORAGE: MISS: %s - %d', key, qty) self.misses = self.misses + 1 for _, adapter in self:onlineAdapters() do diff --git a/milo/core/machines.lua b/milo/core/machines.lua index 208f110..cb162c0 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -22,6 +22,7 @@ local function saveConfig() for k,v in pairs(t) do context.config.remoteDefaults[k].adapter = v end + context.storage:initStorage() end local machinesPage = UI.Page { @@ -49,6 +50,7 @@ local machinesPage = UI.Page { ex = -7, backgroundColor = colors.cyan, }, + notification = UI.Notification { }, } function machinesPage.grid:getDisplayValues(row) @@ -68,7 +70,6 @@ function machinesPage.grid:getRowTextColor(row, selected) end function machinesPage:getList() - -- TODO: remove dedupe naming in perf code ? for _, v in pairs(device) do if v.pullItems then if not context.config.remoteDefaults[v.name] then @@ -100,7 +101,11 @@ end function machinesPage:eventHandler(event) if event.type == 'grid_select' then - UI:setPage('machineWizard', event.selected) + if not device[event.selected.name] then + self.notification:error('Unable to edit while disconnected') + else + UI:setPage('machineWizard', event.selected) + end elseif event.type == 'remove_machine' then local machine = self.grid:getSelected() diff --git a/milo/plugins/brewingStandView.lua b/milo/plugins/brewingStandView.lua new file mode 100644 index 0000000..e4471ba --- /dev/null +++ b/milo/plugins/brewingStandView.lua @@ -0,0 +1,35 @@ +local Ansi = require('ansi') +local UI = require('ui') + +local colors = _G.colors +local device = _G.device + +local template = +[[%sBrewing stands have the ability to automatically learn recipes.%s + +Simply craft potions in the brewing stand as normal except for these conditions. +1. Place item in top slot first. +2. At least 1 bottle must be placed in the first slot. + +When finished brewing, the recipe will be available upon refreshing. + +Note that you do not need to import items from the brewing stand, this will be done automatically.]] + +local brewingStandView = UI.Window { + title = 'Storage Options', + index = 2, + backgroundColor = colors.cyan, + [1] = UI.TextArea { + x = 2, ex = -2, y = 2, ey = -2, + value = string.format(template, Ansi.yellow, Ansi.reset), + }, +} + +function brewingStandView:isValidFor(machine) + if machine.mtype == 'machine' then + local m = device[machine.name] + return m and m.type == 'minecraft:brewing_stand' + end +end + +UI:getPage('machineWizard').wizard:add({ brewingStand = brewingStandView }) diff --git a/milo/plugins/exportView.lua b/milo/plugins/exportView.lua index d231e27..0bbb679 100644 --- a/milo/plugins/exportView.lua +++ b/milo/plugins/exportView.lua @@ -51,7 +51,6 @@ function exportView:setMachine(machine) { name = 'All', value = '*' } } - -- TODO: what if device is dettached ? local m = device[machine.name] for k = 1, m.size() do table.insert(self.slots.choices, { name = k, value = k }) diff --git a/milo/plugins/importView.lua b/milo/plugins/importView.lua index 38a4dac..af6af79 100644 --- a/milo/plugins/importView.lua +++ b/milo/plugins/importView.lua @@ -51,7 +51,6 @@ function importView:setMachine(machine) { name = 'All', value = '*' } } - -- TODO: what if device is dettached ? local m = device[machine.name] for k = 1, m.size() do table.insert(self.slots.choices, { name = k, value = k }) diff --git a/milo/plugins/item.lua b/milo/plugins/item.lua index f7e0879..4c98361 100644 --- a/milo/plugins/item.lua +++ b/milo/plugins/item.lua @@ -46,14 +46,12 @@ local itemPage = UI.Page { formLabel = 'Ignore NBT', formKey = 'ignoreNbtHash', help = 'Ignore NBT of item' }, ---[[ [6] = UI.Button { x = 2, y = -2, width = 10, - formLabel = 'Redstone', - event = 'show_rs', + formLabel = 'Machine', + event = 'select_machine', text = 'Configure', }, -]] infoButton = UI.Button { x = 2, y = -2, event = 'show_info', @@ -99,6 +97,30 @@ local itemPage = UI.Page { }, }, }, + machines = UI.SlideOut { + backgroundColor = colors.cyan, + titleBar = UI.TitleBar { + title = 'Select Machine', + previousPage = true, + }, + grid = UI.ScrollingGrid { + y = 2, ey = -4, + disableHeader = true, + values = context.config.remoteDefaults, + columns = { + { heading = 'Name', key = 'displayName'}, + }, + sortColumn = 'displayName', + }, + button1 = UI.Button { + x = -14, y = -2, + text = 'Ok', event = 'set_machine', + }, + button2 = UI.Button { + x = -9, y = -2, + text = 'Cancel', event = 'cancel_machine', + }, + }, info = UI.SlideOut { titleBar = UI.TitleBar { title = "Information", @@ -126,6 +148,16 @@ function itemPage:enable(item) self:focusFirst() end +function itemPage.machines.grid:isRowValid(_, value) + return value.mtype == 'machine' +end + +function itemPage.machines.grid:getDisplayValues(row) + row = Util.shallowCopy(row) + row.displayName = row.displayName or row.name + return row +end + function itemPage.rsControl:enable() local devices = self.form[1].choices Util.clear(devices) @@ -160,6 +192,18 @@ function itemPage:eventHandler(event) elseif event.type == 'show_rs' then self.rsControl:show() + elseif event.type == 'select_machine' then + self.machines.grid:update() + self.machines.grid:setIndex(1) + self.machines:show() + + elseif event.type == 'set_machine' then + --TODO save machine + self.machines:hide() + + elseif event.type == 'cancel_machine' then + self.machines:hide() + elseif event.type == 'show_info' then local value = string.format('%s%s%s\n%s\n', diff --git a/milo/plugins/listing.lua b/milo/plugins/listing.lua index aab75c3..1c8ed15 100644 --- a/milo/plugins/listing.lua +++ b/milo/plugins/listing.lua @@ -172,7 +172,7 @@ function listingPage:eventHandler(event) end elseif event.type == 'refresh' then - self:refresh() + self:refresh(true) self.grid:draw() self.statusBar.filter:focus() @@ -257,8 +257,12 @@ function listingPage:disable() UI.Page.disable(self) end -function listingPage:refresh() - self.allItems = Milo:refreshItems() +function listingPage:refresh(force) + if force then + self.allItems = Milo:refreshItems() + else + self.allItems = Milo:listItems() + end Milo:mergeResources(self.allItems) self:applyFilter() end diff --git a/milo/plugins/machineLearn.lua b/milo/plugins/machineLearn.lua index d1197e2..1a3bddd 100644 --- a/milo/plugins/machineLearn.lua +++ b/milo/plugins/machineLearn.lua @@ -14,7 +14,7 @@ local machineLearnWizard = UI.Page { wizard = UI.Wizard { y = 2, ey = -2, pages = { - machine = UI.Window { + machines = UI.Window { index = 1, grid = UI.ScrollingGrid { y = 2, ey = -2, @@ -46,15 +46,23 @@ Example: Slot 1 is the top slot in a furnace.]], local pages = machineLearnWizard.wizard.pages local machine -function pages.machine.grid:getDisplayValues(row) +function pages.machines.grid:isRowValid(_, value) + return value.mtype == 'machine' and value.adapter and value.adapter.online +end + +function pages.machines.grid:getDisplayValues(row) row = Util.shallowCopy(row) row.displayName = row.displayName or row.name return row end -function pages.machine:validate() +function pages.machines:enable() + self.grid:update() + UI.Window.enable(self) +end + +function pages.machines:validate() --- TODO: validation should only be invoked when moving forward (i think) -- TODO: index number validation in wizard local selected = self.grid:getSelected() diff --git a/milo/plugins/storageView.lua b/milo/plugins/storageView.lua index 82f51a8..99856ca 100644 --- a/milo/plugins/storageView.lua +++ b/milo/plugins/storageView.lua @@ -24,7 +24,7 @@ local storageView = UI.Window { }, [3] = UI.Text { x = 16, ex = -2, y = 3, - value = 'minecraft:xxxxx:0' + value = '', }, [4] = UI.Checkbox { formLabel = 'Void', formKey = 'voidExcess', @@ -32,7 +32,7 @@ local storageView = UI.Window { pruneEmpty = true, }, [5] = UI.Checkbox { - formLabel = 'Partition', formKey = 'voidExcess', + formLabel = 'Partition', formKey = 'partition', help = 'TODO', pruneEmpty = true, }, From 4d3a896d6b55005d9ac8dcfdcd33959488932a77 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Wed, 31 Oct 2018 00:05:56 -0400 Subject: [PATCH 051/165] rename debug function + milo wip --- apis/chestAdapter.lua | 2 +- apis/meAdapter.lua | 2 +- apis/refinedAdapter.lua | 2 +- apps/chestManager.lua | 4 +- apps/debug.lua | 6 +-- milo/Milo.lua | 52 ++++---------------------- milo/MiloRemote.lua | 20 +++++----- milo/apis/milo.lua | 46 ++++++++++++++++++----- milo/apis/storage.lua | 37 +++++++++--------- milo/apis/turtle/craft.lua | 19 ++++++++-- milo/core/machines.lua | 20 ++++++---- milo/plugins/autocraftTask.lua | 26 ------------- milo/plugins/exportTask.lua | 68 ++++++++++++++++++++++++---------- milo/plugins/importTask.lua | 2 + milo/plugins/item.lua | 2 +- milo/plugins/listing.lua | 4 +- milo/plugins/machineLearn.lua | 3 -- milo/plugins/redstoneTask.lua | 2 +- milo/plugins/remote.lua | 8 ++-- milo/plugins/turtleLearn.lua | 4 +- 20 files changed, 167 insertions(+), 162 deletions(-) delete mode 100644 milo/plugins/autocraftTask.lua diff --git a/apis/chestAdapter.lua b/apis/chestAdapter.lua index 71bffc9..ce879ba 100644 --- a/apis/chestAdapter.lua +++ b/apis/chestAdapter.lua @@ -110,7 +110,7 @@ function ChestAdapter:listItems(throttle) return items end else - debug(m) + _debug(m) end end diff --git a/apis/meAdapter.lua b/apis/meAdapter.lua index af7d1af..41d12bd 100644 --- a/apis/meAdapter.lua +++ b/apis/meAdapter.lua @@ -102,7 +102,7 @@ function MEAdapter:refresh() itemDB:flush() if not s and m then - debug(m) + _debug(m) end if s and not failed and hasItems and self.items and not Util.empty(self.items) then diff --git a/apis/refinedAdapter.lua b/apis/refinedAdapter.lua index 6739a74..b0d84c1 100644 --- a/apis/refinedAdapter.lua +++ b/apis/refinedAdapter.lua @@ -70,7 +70,7 @@ function RefinedAdapter:listItems(throttle) end) if not s and m then - debug(m) + _debug(m) end itemDB:flush() diff --git a/apps/chestManager.lua b/apps/chestManager.lua index a220956..384a4e9 100644 --- a/apps/chestManager.lua +++ b/apps/chestManager.lua @@ -1210,8 +1210,8 @@ local function learnRecipe(page) end if not recipe then - debug(results) - debug(newRecipe) + _debug(results) + _debug(newRecipe) error('Failed - view system log') end diff --git a/apps/debug.lua b/apps/debug.lua index 78b032e..6b9b34f 100644 --- a/apps/debug.lua +++ b/apps/debug.lua @@ -13,9 +13,9 @@ mon.clear() mon.setTextScale(.5) mon.setCursorPos(1, 1) -local oldDebug = _G.debug +local oldDebug = _G._debug -_G.debug = function(...) +_G._debug = function(...) local oldTerm = term.redirect(mon) Util.print(...) term.redirect(oldTerm) @@ -30,4 +30,4 @@ repeat end until e == 'terminate' -_G.debug = oldDebug +_G._debug = oldDebug diff --git a/milo/Milo.lua b/milo/Milo.lua index c394fa1..31601ed 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -4,54 +4,17 @@ Using a turtle allows for crafting of items eliminating the need for AE/RS molecular assemblers / crafters. - Inventory setup: - Turtle/computer must be touching at least one type of inventory - - Generic inventory block such as: - Vanilla chest - RFTools modular storage - Storage drawers controller - and many others... - - Applied energistics - AE cable or interface (depending upon AE/MC version) - - Refined storage - TODO: add required block - Turtle crafting: 1. The turtle must have a crafting table equipped. 2. Equip the turtle with an introspection module. - Controller (optional): - Provides the ability to request crafting from AE / RS - - Applied Energistics - In versions 1.7x, AE can be used for both inventory access and crafting - requests. - - In versions 1.8+, AE can only be used to request crafting. - - Refined Storage - In versions 1.8x, inventory access works depending upon version. - - Turtle/computer must be touching an interface for inventory access. If only - requesting crafting, the controller must be either be touching or connected - via CC cables. - Configuration: Configuration file is usr/config/milo - monitor : valid options include: - type/monitor - will use the first monitor found - side/north - specify a direction (top/bottom/east/etc) - name/monitor_1 - specify the exact name of the peripheral - - - - -- Internal - Imports are at < 20 - + monitor : valid options include: + type/monitor - will use the first monitor found + side/north - specify a direction (top/bottom/east/etc) + name/monitor_1 - specify the exact name of the peripheral ]]-- _G.requireInjector(_ENV) @@ -137,9 +100,9 @@ table.sort(context.tasks, function(a, b) return a.priority < b.priority end) -debug('Tasks\n-----') +_debug('Tasks\n-----') for _, task in ipairs(context.tasks) do - debug('%d: %s', task.priority, task.name) + _debug('%d: %s', task.priority, task.name) end Milo:clearGrid() @@ -155,7 +118,8 @@ Event.onInterval(5, function() 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.name) + _debug(task.name .. ' crashed') + Util.print(task.name .. ' crashed') error(m) end end diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index e7245fa..ebfaec0 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -301,48 +301,48 @@ end _G.p4 = Event -debug(options.slot) +_debug(options.slot) if options.slot.value then - debug('Transfer items initialized') + _debug('Transfer items initialized') Event.addRoutine(function() while true do -debug('sleeping') +_debug('sleeping') os.sleep(1.5) local neural = device.neuralInterface -debug(neural) +_debug(neural) if neural and neural.getInventory then local item = neural.getInventory().getItem(options.slot.value) if item then - debug('depositing') + _debug('depositing') page:sendRequest({ request = 'deposit', slot = options.slot.value }) -- local item = -- TODO: update count for this one item -- page.grid:draw() page:sync() else -debug('empty') +_debug('empty') end else - debug('missing Introspection module') + _debug('missing Introspection module') end end end) end if options.shield.value then - debug('Transfer items initialized') + _debug('Transfer items initialized') Event.onInterval(2, function() local neural = device.neuralInterface if neural and neural.getEquipment then local item = neural.getEquipment().getItem(SHIELD_SLOT) if item then - debug('depositing') + _debug('depositing') page:sendRequest({ request = 'deposit', slot = 'shield' }) -- local item = -- TODO: update count for this one item -- page.grid:draw() page:sync() end else - debug('missing Introspection module') + _debug('missing Introspection module') end end) end diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index be0dc36..2acc0fb 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -113,24 +113,50 @@ function Milo:getItemWithQty(res, ignoreDamage, ignoreNbtHash) return item end -function Milo:clearGrid() - local function clear() - turtle.eachFilledSlot(function(slot) - self.context.storage:import(self.context.localName, slot.index, slot.count, slot) - end) +function Milo:getMatches(items, item, ignoreDamage, ignoreNbtHash) + local t = { } - for i = 1, 16 do - if turtle.getItemCount(i) ~= 0 then - return false + if not ignoreDamage and not ignoreNbtHash then + local key = item.key or Milo:uniqueKey(item) + local e = items[key] + if e then + t[key] = Util.shallowCopy(e) + end + + else + for k,v in pairs(items) do + if item.name == v.name and + (ignoreDamage or item.damage == v.damage) and + (ignoreNbtHash or item.nbtHash == v.nbtHash) then + local e = t[k] + if not e then + t[k] = Util.shallowCopy(v) + else + e.count = e.count + item.count + end end end - return true end - return clear() or clear() + + return t +end + +function Milo:clearGrid() + turtle.eachFilledSlot(function(slot) + self.context.storage:import(self.context.localName, slot.index, slot.count, slot) + end) + + for i = 1, 16 do + if turtle.getItemCount(i) ~= 0 then + return false + end + end + return true end function Milo:getTurtleInventory() local list = { } + for i = 1,16 do local item = self.context.introspectionModule.getInventory().getItemMeta(i) if item then diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 6d7d6d5..2a47fd0 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -26,12 +26,12 @@ listCount = 0, self.localName = modem.getNameLocal() Event.on({ 'device_attach', 'device_detach' }, function(e, dev) -debug('%s: %s', e, tostring(dev)) +_debug('%s: %s', e, tostring(dev)) self:initStorage() end) Event.onInterval(15, function() self:showStorage() - debug('STORAGE: cache: %d/%d', self.hits, self.misses) + _debug('STORAGE: cache: %d/%d', self.hits, self.misses) end) end @@ -44,11 +44,11 @@ function Storage:showStorage() end end if #t > 0 then - debug('Adapter:') + _debug('Adapter:') for _, k in pairs(t) do - debug(' offline: ' .. k) + _debug(' offline: ' .. k) end - debug('') + _debug('') end end @@ -56,7 +56,7 @@ function Storage:setOnline(online) if online ~= self.storageOnline then self.storageOnline = online os.queueEvent(self.storageOnline and 'storage_online' or 'storage_offline', online) - debug('Storage: %s', self.storageOnline and 'online' or 'offline') + _debug('Storage: %s', self.storageOnline and 'online' or 'offline') end end @@ -67,7 +67,7 @@ end function Storage:initStorage() local online = true - debug('Initializing storage') + _debug('Initializing storage') for k,v in pairs(self.remoteDefaults) do if v.adapter then v.adapter.online = not not device[k] @@ -136,7 +136,7 @@ end function Storage:refresh(throttle) self.dirty = true -debug('STORAGE: Forcing full refresh') +_debug('STORAGE: Forcing full refresh') for _, adapter in self:onlineAdapters() do adapter.dirty = true end @@ -149,9 +149,8 @@ function Storage:listItems(throttle) return self.items end self.listCount = self.listCount + 1 ---debug(self.listCount) +--_debug(self.listCount) - -- todo: only listItems from dirty remotes local ct = os.clock() local cache = { } local items = { } @@ -159,7 +158,7 @@ local ct = os.clock() for _, adapter in self:onlineAdapters() do if adapter.dirty then -debug('STORAGE: refresh: ' .. adapter.name) +_debug('STORAGE: refresh: ' .. adapter.name) adapter:listItems(throttle) adapter.dirty = false end @@ -180,7 +179,7 @@ debug('STORAGE: refresh: ' .. adapter.name) throttle() end end -debug('STORAGE: refresh in ' .. (os.clock() - ct)) +_debug('STORAGE: refresh in ' .. (os.clock() - ct)) self.dirty = false self.cache = cache @@ -201,7 +200,7 @@ function Storage:provide(item, qty, slot, direction) local amount = adapter:provide(item, qty, slot, direction or self.localName) if amount > 0 then self.hits = self.hits + 1 - debug('EXT: %s(%d): %s -> %s%s', + _debug('EXT: %s(%d): %s -> %s%s', item.name, amount, adapter.name, direction or self.localName, slot and string.format('[%d]', slot) or '') self.dirty = true @@ -215,13 +214,13 @@ function Storage:provide(item, qty, slot, direction) end end - debug('STORAGE: MISS: %s - %d', key, qty) + _debug('STORAGE: MISS: %s - %d', key, qty) self.misses = self.misses + 1 for _, adapter in self:onlineAdapters() do local amount = adapter:provide(item, qty, slot, direction or self.localName) if amount > 0 then -debug('EXT: %s(%d): %s -> %s%s', +_debug('EXT: %s(%d): %s -> %s%s', item.name, amount, adapter.name, direction or self.localName, slot and string.format('[%d]', slot) or '') self.dirty = true @@ -240,7 +239,7 @@ end function Storage:trash(source, slot, count) local trashcan = Util.find(self.remoteDefaults, 'mtype', 'trashcan') if trashcan and trashcan.adapter and trashcan.adapter.online then -debug('TRA: %s[%d] (%d)', source or self.localName, slot, count or 64) +_debug('TRA: %s[%d] (%d)', source or self.localName, slot, count or 64) return trashcan.adapter.pullItems(source or self.localName, slot, count) end return 0 @@ -267,7 +266,8 @@ function Storage:insert(slot, qty, toSlot, item, source) local function insert(adapter) local amount = adapter:insert(slot, qty, toSlot, source or self.localName) if amount > 0 then -debug('INS: %s(%d): %s[%d] -> %s', +-- TODO: change debug to _debug +_debug('INS: %s(%d): %s[%d] -> %s', item.name, amount, source or self.localName, slot, adapter.name) self.dirty = true @@ -300,8 +300,7 @@ debug('INS: %s(%d): %s[%d] -> %s', end if self.cache[key] then -- is this item in some chest - -- low to high priority if the chest already contains that item - for _, adapter in self:onlineAdapters(true --[[ reversed ]]) do + for _, adapter in self:onlineAdapters() do if qty <= 0 then break end diff --git a/milo/apis/turtle/craft.lua b/milo/apis/turtle/craft.lua index c531c7d..d031b86 100644 --- a/milo/apis/turtle/craft.lua +++ b/milo/apis/turtle/craft.lua @@ -105,10 +105,21 @@ local function machineCraft(recipe, inventoryAdapter, machineName, request, coun end end + local xferred = { } for k,v in pairs(recipe.ingredients) do - if inventoryAdapter:provide(splitKey(v), count, k, machineName) ~= count then - -- TODO: suck em back out - request.status = 'unknown error' + local provided = inventoryAdapter:provide(splitKey(v), count, k, machineName) + xferred[k] = { + key = v, + count = provided, + } + if provided ~= count then + -- take back out whatever we put in + for k2,v2 in pairs(xferred) do + if v2.count > 0 then + inventoryAdapter:import(machineName, k2, v2.count, splitKey(v2.key)) + end + end + request.status = 'Invalid recipe' request.statusCode = Craft.STATUS_ERROR return end @@ -175,11 +186,11 @@ function Craft.craftRecipe(recipe, count, inventoryAdapter, origItem) --end for _, request in pairs(origItem.ingredients) do + if request.crafted >= request.count then if request.pending then debug('??') debug(request) end - if request.crafted >= request.count then request.status = nil request.statusCode = Craft.STATUS_SUCCESS end diff --git a/milo/core/machines.lua b/milo/core/machines.lua index cb162c0..cf1e14f 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -7,9 +7,12 @@ local Util = require('util') local colors = _G.colors local device = _G.device +local turtle = _G.turtle local context = Milo:getContext() +-- TODO: no blacklist for export + local function saveConfig() local t = { } for k,v in pairs(context.config.remoteDefaults) do @@ -266,7 +269,14 @@ function machineWizard.filter:show(entry, callback, whitelistOnly) self.form[3].inactive = whitelistOnly UI.SlideOut.show(self) --- self:setFocus(self.filter) + self:setFocus(self.form.scan) + + Milo:pauseCrafting() +end + +function machineWizard.filter:hide() + UI.SlideOut.hide(self) + Milo:resumeCrafting() end function machineWizard.filter:resetGrid() @@ -295,6 +305,7 @@ function machineWizard.filter:eventHandler(event) self:resetGrid() self.grid:update() self.grid:draw() + turtle.emptyInventory() elseif event.type == 'remove_entry' then local row = self.grid:getSelected() @@ -411,13 +422,6 @@ function machineWizard:eventHandler(event) UI:setPreviousPage() elseif event.type == 'accept' then - -- todo: no need for calling this function - use validate instead - for _, v in pairs(self.wizard.pages) do - if v.save and v.index then -- only save if the page was valid for this mtype - v:save(self.machine) - end - end - Util.prune(self.machine, function(v) if type(v) == 'boolean' then return v diff --git a/milo/plugins/autocraftTask.lua b/milo/plugins/autocraftTask.lua deleted file mode 100644 index 50d2f9f..0000000 --- a/milo/plugins/autocraftTask.lua +++ /dev/null @@ -1,26 +0,0 @@ -local Milo = require('milo') -local Util = require('util') - -local Autocraft = { - name = 'autocraft', - priority = 100, -} - --- TODO: fix/test -function Autocraft:cycle(context) - local list = { } - - for _,res in pairs(context.resources) do - if res.auto then - res = Util.shallowCopy(res) - res.count = 256 - list[Milo:uniqueKey(res)] = res - end - end - - if not Util.empty(list) then - Milo:craftItems(list) - end -end - -Milo:registerTask(Autocraft) diff --git a/milo/plugins/exportTask.lua b/milo/plugins/exportTask.lua index 5d864d9..0f0bc80 100644 --- a/milo/plugins/exportTask.lua +++ b/milo/plugins/exportTask.lua @@ -13,29 +13,57 @@ end function ExportTask:cycle(context) for machine in context.storage:filterActive('machine', filter) do for _, entry in pairs(machine.exports) do - local slotNo = type(entry.slot) == 'number' and entry.slot or nil -- '*' indicates any slot - local slot = (slotNo and machine.adapter.getItemMeta(slotNo)) or { count = 0 } - for key in pairs(entry.filter or { }) do - local item = itemDB:splitKey(key) + local function exportSlot(list, slotNo, item, count) + local slot = list[slotNo] or { count = 0 } + + if slot.count == 0 or + (slot.name == item.name and + slot.damage == item.damage and + slot.nbtHash == item.nbtHash) then + + local maxCount = itemDB:getMaxCount(item) + count = math.min(maxCount - slot.count, count) - -- is something else is in this slot - if not slot.name or slot.name == item.name then - local maxCount = slot.maxCount or itemDB:getMaxCount(item) - local count = maxCount - slot.count - if not slotNo then - -- TODO: should we just execute export - - -- or scan all slots for space ?? - count = machine.adapter.size() * maxCount - slot.count - end if count > 0 then - item = Milo:getItemWithQty(item) - if item and count > 0 then - context.storage:export( - machine.name, - slotNo, - math.min(count, item.count), - item) +-- _debug('attempting to export %s %d into slot %d', item.name, count, slotNo) + count = context.storage:export(machine.name, slotNo, count, item) + + if count > 0 then + item.count = item.count - count + list[slotNo] = { + name = item.name, + damage = item.damage, + nbtHash = item.nbtHash, + count = count + slot.count, + } + return true + end + end + end + end + + local list + local function getLazyList() + if not list then + list = machine.adapter.list() + end + return list + end + + for key in pairs(entry.filter or { }) do + -- bad for perf to do listItems each time + local items = Milo:getMatches(Milo:listItems(), itemDB:splitKey(key), entry.ignoreDamage, entry.ignoreNbtHash) + for _,item in pairs(items) do + if item and item.count > 0 then + if type(entry.slot) == 'number' then + if exportSlot(getLazyList(), entry.slot, item, item.count) then + break + end + else +-- _debug('attempting to export %s %d', item.name, item.count) +-- TODO: always going to try and export even if the chest is full + context.storage:export(machine.name, nil, item.count, item) end end end diff --git a/milo/plugins/importTask.lua b/milo/plugins/importTask.lua index 0b27c52..4ef61b8 100644 --- a/milo/plugins/importTask.lua +++ b/milo/plugins/importTask.lua @@ -9,6 +9,8 @@ local function filter(a) return a.imports end +-- TODO: ignore damage/nbt + function ImportTask:cycle(context) for inventory in context.storage:filterActive('machine', filter) do for _, entry in pairs(inventory.imports) do diff --git a/milo/plugins/item.lua b/milo/plugins/item.lua index 4c98361..abb897c 100644 --- a/milo/plugins/item.lua +++ b/milo/plugins/item.lua @@ -270,7 +270,7 @@ function itemPage:eventHandler(event) filtered.count = nil Milo:saveResources() - +-- TODO: min not updated upon save until refresh UI:setPreviousPage() else diff --git a/milo/plugins/listing.lua b/milo/plugins/listing.lua index 1c8ed15..5b9b262 100644 --- a/milo/plugins/listing.lua +++ b/milo/plugins/listing.lua @@ -211,7 +211,7 @@ function listingPage:eventHandler(event) Util.writeTable(Craft.USER_RECIPES, context.userRecipes) Craft.loadRecipes() end - +--TODO: remove machine assoc if context.resources[key] then context.resources[key] = nil Milo:saveResources() @@ -242,7 +242,6 @@ function listingPage:enable() self:setFocus(self.statusBar.filter) self.timer = Event.onInterval(5, function() for _,v in pairs(self.allItems) do - if not v.key then debug(v) error('') end local c = context.storage.cache[v.key] v.count = c and c.count or 0 end @@ -273,6 +272,7 @@ function listingPage:applyFilter() end Event.on({ 'storage_offline', 'storage_online' }, function(e, isOnline) + -- TODO: Fix button listingPage.statusBar.storageStatus.text = isOnline and 'online' or 'offline' listingPage.statusBar.storageStatus.textColor = diff --git a/milo/plugins/machineLearn.lua b/milo/plugins/machineLearn.lua index 1a3bddd..2c31f5e 100644 --- a/milo/plugins/machineLearn.lua +++ b/milo/plugins/machineLearn.lua @@ -62,9 +62,6 @@ function pages.machines:enable() end function pages.machines:validate() - --- TODO: index number validation in wizard - local selected = self.grid:getSelected() if not selected then machineLearnWizard.notification:error('No machines configured') diff --git a/milo/plugins/redstoneTask.lua b/milo/plugins/redstoneTask.lua index f1f286f..6d4e841 100644 --- a/milo/plugins/redstoneTask.lua +++ b/milo/plugins/redstoneTask.lua @@ -13,7 +13,7 @@ function RedstoneTask:cycle(context) if v.redstone then local ri = device[v.redstone.integrator] if not ri or not v.adapter then - debug(v.redstone) + _debug(v.redstone) else local function conditionsSatisfied() return not not next(v.adapter.list()) diff --git a/milo/plugins/remote.lua b/milo/plugins/remote.lua index d15df2b..51e5a42 100644 --- a/milo/plugins/remote.lua +++ b/milo/plugins/remote.lua @@ -19,7 +19,7 @@ local function getManipulatorForUser(user) end local function client(socket) - debug('connection from ' .. socket.dhost) + _debug('connection from ' .. socket.dhost) local user = socket:read(2) if not user then @@ -36,7 +36,7 @@ local function client(socket) if not data then break end -debug('remote: ' .. data.request) +_debug('remote: ' .. data.request) if data.request == 'list' then local items = Milo:listItems() Milo:mergeResources(items) @@ -89,12 +89,12 @@ debug('remote: ' .. data.request) end until not socket.connected - debug('disconnected from ' .. socket.dhost) + _debug('disconnected from ' .. socket.dhost) end if device.wireless_modem then Event.addRoutine(function() - debug('Milo: listening on port 4242') + _debug('Milo: listening on port 4242') while true do local socket = Socket.server(4242) Event.addRoutine(function() diff --git a/milo/plugins/turtleLearn.lua b/milo/plugins/turtleLearn.lua index 0d0e49c..fb6df3e 100644 --- a/milo/plugins/turtleLearn.lua +++ b/milo/plugins/turtleLearn.lua @@ -77,8 +77,8 @@ local function learnRecipe() end if not recipe then - debug(results) - debug(newRecipe) + _debug(results) + _debug(newRecipe) error('Failed - view system log') end From 865d642e5caa91918dd62ab46ece2805dd3dd01a Mon Sep 17 00:00:00 2001 From: kepler155c Date: Wed, 31 Oct 2018 19:38:54 -0400 Subject: [PATCH 052/165] milo wip --- milo/Milo.lua | 11 +-- milo/MiloRemote.lua | 103 ++++++++++++++-------------- milo/apis/milo.lua | 120 +++++++++++++++++---------------- milo/apis/storage.lua | 3 +- milo/core/machines.lua | 5 +- milo/plugins/craftTask.lua | 8 ++- milo/plugins/item.lua | 81 +++++++++++----------- milo/plugins/jobMonitor.lua | 2 + milo/plugins/learn.lua | 7 ++ milo/plugins/limitTask.lua | 9 +-- milo/plugins/listing.lua | 13 ++-- milo/plugins/machineLearn.lua | 7 +- milo/plugins/refreshTask.lua | 16 +++++ milo/plugins/remote.lua | 92 +++++++++++++++---------- milo/plugins/replenishTask.lua | 26 +++---- milo/plugins/turtleLearn.lua | 7 +- 16 files changed, 276 insertions(+), 234 deletions(-) create mode 100644 milo/plugins/refreshTask.lua diff --git a/milo/Milo.lua b/milo/Milo.lua index 31601ed..62d5093 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -51,18 +51,9 @@ end local introspectionModule = Peripheral.get('plethora:introspection') or error('Introspection module not found') -local function loadResources() - local resources = Util.readTable(Milo.RESOURCE_FILE) or { } - for k,v in pairs(resources) do - Util.merge(v, itemDB:splitKey(k)) - end - - return resources -end - local context = { config = config, - resources = loadResources(), + resources = Util.readTable(Milo.RESOURCE_FILE) or { }, craftingQueue = { }, diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index ebfaec0..0df71c8 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -2,12 +2,13 @@ _G.requireInjector(_ENV) local Event = require('event') local Socket = require('socket') -local sync = require('sync') +local sync = require('sync').sync local UI = require('ui') local Util = require('util') local colors = _G.colors local device = _G.device +local os = _G.os local socket local SHIELD_SLOT = 2 @@ -139,7 +140,6 @@ function page:sendRequest(data) local response sync(self, function() - self:sync() local msg for _ = 1, 2 do if not socket or not socket.connected then @@ -154,6 +154,10 @@ function page:sendRequest(data) if socket:write(data) then response = socket:read(2) if response then + if response.msg then + self:setStatus(response.msg) + response = nil + end Event.onTimeout(2, function() self:setStatus('') end) @@ -188,6 +192,20 @@ function page.grid:getDisplayValues(row) return row end +function page:transfer(item, count) + local response = self:sendRequest({ request = 'transfer', item = item, count = count }) + debug(response) + if response then + item.count = response.current - response.transferred + self.grid:draw() + if response.craft > 0 then + self:setStatus(response.craft .. ' crafting ...') + elseif response.craft + response.available < response.requested then + self:setStatus((response.craft + response.available) .. ' available ...') + end + end +end + function page:eventHandler(event) if event.type == 'quit' then UI:exitPullEvents() @@ -199,33 +217,21 @@ function page:eventHandler(event) local item = self.grid:getSelected() if item then self:setStatus('requesting 1 ...') - local response = self:sendRequest({ request = 'transfer', item = item, count = 1 }) - if response then - item.count = response.count - self.grid:draw() - end + self:transfer(item, 1) end elseif event.type == 'eject_stack' then local item = self.grid:getSelected() if item then self:setStatus('requesting stack ...') - local response = self:sendRequest({ request = 'transfer', item = item, count = 'stack' }) - if response then - item.count = response.count - self.grid:draw() - end + self:transfer(item, 'stack') end elseif event.type == 'eject_all' then local item = self.grid:getSelected() if item then self:setStatus('requesting all ...') - local response = self:sendRequest({ request = 'transfer', item = item, count = 'all' }) - if response then - item.count = response.count - self.grid:draw() - end + self:transfer(item, 'all') end elseif event.type == 'eject_specified' then @@ -235,9 +241,7 @@ function page:eventHandler(event) self.statusBar.amount:reset() self:setFocus(self.statusBar.filter) self:setStatus('requesting ' .. count .. ' ...') - local response = self:sendRequest({ request = 'transfer', item = item, count = count }) - item.count = response.count - self.grid:draw() + self:transfer(item, count) else self:setStatus('nope ...') end @@ -299,50 +303,43 @@ function page:applyFilter() self.grid:setValues(t) end -_G.p4 = Event +if options.slot.value or options.shield.value then + local inv = 'getInventory' + local slotNo = options.slot.value + local slotValue = options.slot.value + + if options.shield.value then + slotNo = SHIELD_SLOT + slotValue = 'shield' + inv = 'getEquipment' + end -_debug(options.slot) -if options.slot.value then - _debug('Transfer items initialized') Event.addRoutine(function() while true do -_debug('sleeping') os.sleep(1.5) local neural = device.neuralInterface -_debug(neural) - if neural and neural.getInventory then - local item = neural.getInventory().getItem(options.slot.value) - if item then - _debug('depositing') - page:sendRequest({ request = 'deposit', slot = options.slot.value }) - -- local item = - -- TODO: update count for this one item - -- page.grid:draw() page:sync() - else -_debug('empty') - end - else + if not neural or not neural[inv] then _debug('missing Introspection module') end - end - end) -end -if options.shield.value then - _debug('Transfer items initialized') - Event.onInterval(2, function() - local neural = device.neuralInterface - if neural and neural.getEquipment then - local item = neural.getEquipment().getItem(SHIELD_SLOT) + local method = neural and neural[inv] + local item = method and method().getItemMeta(slotNo) if item then _debug('depositing') - page:sendRequest({ request = 'deposit', slot = 'shield' }) - -- local item = - -- TODO: update count for this one item - -- page.grid:draw() page:sync() + local response = page:sendRequest({ + request = 'deposit', + slot = slotValue, + key = table.concat({ item.name, item.damage, item.nbtHash }, ':') + }) + if response then + local ritem = page.items[response.key] + if ritem then + ritem.count = response.current + end + page.grid:draw() + page:sync() + end end - else - _debug('missing Introspection module') end end) end diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index 2acc0fb..c1c2687 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -64,6 +64,10 @@ function Milo:uniqueKey(item) return table.concat({ item.name, item.damage, item.nbtHash }, ':') end +function Milo:splitKey(key) + return itemDB:splitKey(key) +end + function Milo:resetCraftingStatus() self.context.storage.activity = { } @@ -84,6 +88,10 @@ function Milo:showError(msg) end function Milo:getItem(items, inItem, ignoreDamage, ignoreNbtHash) + if not ignoreDamage and not ignoreNbtHash then + return items[inItem.key or self:uniqueKey(inItem)] + end + for _,item in pairs(items) do if item.name == inItem.name and (ignoreDamage or item.damage == inItem.damage) and @@ -96,10 +104,9 @@ end function Milo:getItemWithQty(res, ignoreDamage, ignoreNbtHash) local items = self:listItems() local item = self:getItem(items, res, ignoreDamage, ignoreNbtHash) + local count = 0 if item and (ignoreDamage or ignoreNbtHash) then - local count = 0 - for _,v in pairs(items) do if item.name == v.name and (ignoreDamage or item.damage == v.damage) and @@ -107,10 +114,11 @@ function Milo:getItemWithQty(res, ignoreDamage, ignoreNbtHash) count = count + v.count end end - item.count = count + elseif item then + count = item.count end - return item + return item, count end function Milo:getMatches(items, item, ignoreDamage, ignoreNbtHash) @@ -170,21 +178,29 @@ function Milo:getTurtleInventory() return list end -function Milo:xxx(item, count) - return self:provideItem(item, count, function(providable, currentCount) - -- return the current amount in the system - return currentCount - self:eject(item, providable) +function Milo:craftAndEject(item, count) + local provided = self:provideItem(item, count, function(amount) + -- eject rest when finished crafted + return self:eject(item, amount) end) + + -- eject what we currently have + return item.count - self:eject(item, provided.available) end function Milo:provideItem(item, count, callback) + local current = Milo:getItem(Milo:listItems(), item) or { count = 0 } + if count <= 0 then - return 0 + return { + requested = 0, + craft = 0, + available = 0, + current = current.count, + } end - local current = Milo:getItem(Milo:listItems(), item) or { count = 0 } local toCraft = count - math.min(current.count, count) - if toCraft > 0 then local recipe = Craft.findRecipe(self:uniqueKey(item)) if not recipe then @@ -195,18 +211,20 @@ function Milo:provideItem(item, count, callback) end end - if toCraft == 0 then - return callback(math.min(count, current.count), current.count) --- return current.count - self:eject(item, math.min(count, current.count)) + if toCraft > 0 then + item = Util.shallowCopy(item) + item.count = toCraft + item.eject = callback + self:requestCrafting(item) + item.crafted = 0 end - item = Util.shallowCopy(item) - item.count = current.count + toCraft - item.eject = callback - self:requestCrafting(item) - item.crafted = current.count - - return current.count + return { + requested = count, + craft = toCraft, + available = math.min(count, current.count), + current = current.count, + } end function Milo:eject(item, count) @@ -230,36 +248,43 @@ function Milo:saveMachineRecipe(recipe, result, machine) end function Milo:mergeResources(t) - for _,v in pairs(self.context.resources) do - local item = self:getItem(t, v) + t = Util.shallowCopy(t) + + for k,v in pairs(self.context.resources) do + local key = itemDB:splitKey(k) + local item = self:getItem(t, key) if item then - Util.merge(item, v) + item = Util.shallowCopy(item) else - item = Util.shallowCopy(v) + item = key item.count = 0 - item.key = self:uniqueKey(v) --- table.insert(t, item) - t[item.key] = item + item.key = k end + Util.merge(item, v) + item.resource = v + t[item.key] = item end for k in pairs(Craft.recipes) do local v = itemDB:splitKey(k) local item = self:getItem(t, v) if not item then - item = Util.shallowCopy(v) + item = v item.count = 0 - item.key = self:uniqueKey(v) - t[item.key] = item --- table.insert(t, item) + item.key = k + else + item = Util.shallowCopy(item) end + t[item.key] = item item.has_recipe = true end for key in pairs(Craft.machineLookup) do local item = t[key] if item then + item = Util.shallowCopy(item) item.is_craftable = true + t[item.key] = item end end @@ -269,38 +294,17 @@ function Milo:mergeResources(t) end v.lname = v.displayName:lower() end + + return t end function Milo:saveResources() - local t = { } - - for k,v in pairs(self.context.resources) do - v = Util.shallowCopy(v) - local keys = Util.transpose({ 'auto', 'low', 'limit', - 'ignoreDamage', 'ignoreNbtHash', - 'rsControl', 'rsDevice', 'rsSide' }) - - for _,key in pairs(Util.keys(v)) do - if not keys[key] then - v[key] = nil - end - end - if not Util.empty(v) then - t[k] = v - end - end - - Util.writeTable(self.RESOURCE_FILE, t) + Util.writeTable(self.RESOURCE_FILE, self.context.resources) end -- Return a list of everything in the system -function Milo:listItems() - return self.context.storage:listItems() -end - --- force a full rescan of chests -function Milo:refreshItems() - return self.context.storage:refresh() +function Milo:listItems(forceRefresh) + return forceRefresh and self.context.storage:refresh() or self.context.storage:listItems() end return Milo diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 2a47fd0..e3b64e3 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -18,6 +18,7 @@ listCount = 0, storageOnline = true, hits = 0, misses = 0, + lastRefresh = os.clock(), } Util.merge(self, defaults) Util.merge(self, args) @@ -136,6 +137,7 @@ end function Storage:refresh(throttle) self.dirty = true + self.lastRefresh = os.clock() _debug('STORAGE: Forcing full refresh') for _, adapter in self:onlineAdapters() do adapter.dirty = true @@ -266,7 +268,6 @@ function Storage:insert(slot, qty, toSlot, item, source) local function insert(adapter) local amount = adapter:insert(slot, qty, toSlot, source or self.localName) if amount > 0 then --- TODO: change debug to _debug _debug('INS: %s(%d): %s[%d] -> %s', item.name, amount, source or self.localName, slot, adapter.name) diff --git a/milo/core/machines.lua b/milo/core/machines.lua index cf1e14f..1ba57ff 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -2,6 +2,7 @@ local Config = require('config') local Event = require('event') local itemDB = require('itemDB') local Milo = require('milo') +local sync = require('sync') local UI = require('ui') local Util = require('util') @@ -11,8 +12,6 @@ local turtle = _G.turtle local context = Milo:getContext() --- TODO: no blacklist for export - local function saveConfig() local t = { } for k,v in pairs(context.config.remoteDefaults) do @@ -272,11 +271,13 @@ function machineWizard.filter:show(entry, callback, whitelistOnly) self:setFocus(self.form.scan) Milo:pauseCrafting() + sync.lock(turtle) end function machineWizard.filter:hide() UI.SlideOut.hide(self) Milo:resumeCrafting() + sync.release(turtle) end function machineWizard.filter:resetGrid() diff --git a/milo/plugins/craftTask.lua b/milo/plugins/craftTask.lua index ca87d97..d79e67d 100644 --- a/milo/plugins/craftTask.lua +++ b/milo/plugins/craftTask.lua @@ -1,9 +1,11 @@ local Craft = require('turtle.craft') local itemDB = require('itemDB') local Milo = require('milo') +local sync = require('sync').sync local Util = require('util') local context = Milo:getContext() +local turtle = _G.turtle local craftTask = { name = 'crafting', @@ -101,12 +103,14 @@ function craftTask:cycle() if item.count - item.crafted > 0 then local recipe = Craft.findRecipe(key) if recipe then - self:craft(recipe, item) + sync(turtle, function() + self:craft(recipe, item) + end) if item.eject and item.crafted >= item.count then if type(item.eject) == 'boolean' then Milo:eject(item, item.count) else - item.eject(item.count, 0) -- unknown amount in system + item.eject(item.count) -- invoke callback end end elseif not context.controllerAdapter then diff --git a/milo/plugins/item.lua b/milo/plugins/item.lua index abb897c..137dbd9 100644 --- a/milo/plugins/item.lua +++ b/milo/plugins/item.lua @@ -20,11 +20,13 @@ local itemPage = UI.Page { x = 1, y = 2, height = 10, ex = -1, [1] = UI.TextEntry { width = 7, - formLabel = 'Min', formKey = 'low', help = 'Craft if below min' + formLabel = 'Min', formKey = 'low', help = 'Craft if below min', + validate = 'numeric', }, [2] = UI.TextEntry { width = 7, - formLabel = 'Max', formKey = 'limit', help = 'Eject if above max' + formLabel = 'Max', formKey = 'limit', help = 'Send to trash if above max', + validate = 'numeric', }, --[[ [3] = UI.Chooser { @@ -40,11 +42,11 @@ local itemPage = UI.Page { ]] [4] = UI.Checkbox { formLabel = 'Ignore Dmg', formKey = 'ignoreDamage', - help = 'Ignore damage of item' + help = 'Ignore damage of item', }, [5] = UI.Checkbox { formLabel = 'Ignore NBT', formKey = 'ignoreNbtHash', - help = 'Ignore NBT of item' + help = 'Ignore NBT of item', }, [6] = UI.Button { x = 2, y = -2, width = 10, @@ -135,13 +137,15 @@ local itemPage = UI.Page { event = 'hide_info', }, }, - statusBar = UI.StatusBar { } + statusBar = UI.StatusBar { }, + notification = UI.Notification { }, } function itemPage:enable(item) self.item = Util.shallowCopy(item) + self.res = item.resource or { } - self.form:setValues(self.item) + self.form:setValues(self.res) self.titleBar.title = item.displayName or item.name UI.Page.enable(self) @@ -232,47 +236,46 @@ function itemPage:eventHandler(event) elseif event.type == 'hide_info' then self.info:hide() + elseif event.type == 'form_invalid' then + self.notification:error(event.message) + elseif event.type == 'focus_change' then self.statusBar:setStatus(event.focused.help) self.statusBar:draw() elseif event.type == 'form_complete' then - local values = self.form.values - local originalKey = Milo:uniqueKey(self.item) + local item = self.item - local filtered = Util.shallowCopy(values) - filtered.low = tonumber(filtered.low) - filtered.limit = tonumber(filtered.limit) + if self.form:save() then + Util.prune(self.res, function(v) + if type(v) == 'boolean' then + return v + elseif type(v) == 'string' then + return #v > 0 + end + return true + end) - if filtered.auto ~= true then - filtered.auto = nil + local newKey = { + name = item.name, + damage = self.res.ignoreDamage and 0 or item.damage, + nbtHash = not self.res.ignoreNbtHash and item.nbtHash or nil, + } + + for k,v in pairs(context.resources) do + if v == self.res then + context.resources[k] = nil + break + end + end + + if not Util.empty(self.res) then + context.resources[Milo:uniqueKey(newKey)] = self.res + end + + Milo:saveResources() + UI:setPreviousPage() end - - if filtered.rsControl ~= true then - filtered.rsControl = nil - filtered.rsSide = nil - filtered.rsDevice = nil - end - - if filtered.ignoreDamage == true then - filtered.damage = 0 - else - filtered.ignoreDamage = nil - end - - if filtered.ignoreNbtHash == true then - filtered.nbtHash = nil - else - filtered.ignoreNbtHash = nil - end - context.resources[originalKey] = nil - context.resources[Milo:uniqueKey(filtered)] = filtered - - filtered.count = nil - Milo:saveResources() --- TODO: min not updated upon save until refresh - UI:setPreviousPage() - else return UI.Page.eventHandler(self, event) end diff --git a/milo/plugins/jobMonitor.lua b/milo/plugins/jobMonitor.lua index 8d4b535..9d1a99e 100644 --- a/milo/plugins/jobMonitor.lua +++ b/milo/plugins/jobMonitor.lua @@ -11,6 +11,8 @@ local context = Milo:getContext() local mon = Peripheral.lookup(context.config.monitor) or error('Monitor is not attached') +-- TODO: some way to cancel a job + local jobMonitor = UI.Page { parent = UI.Device { device = mon, diff --git a/milo/plugins/learn.lua b/milo/plugins/learn.lua index fdbe940..efb92c0 100644 --- a/milo/plugins/learn.lua +++ b/milo/plugins/learn.lua @@ -1,7 +1,9 @@ local Milo = require('milo') +local sync = require('sync') local UI = require('ui') local context = Milo:getContext() +local turtle = _G.turtle local learnPage = UI.Dialog { height = 6, width = UI.term.width - 6, @@ -34,6 +36,9 @@ function learnPage:enable() Milo:getState('learnType') or self.chooser.choices[1].value + Milo:pauseCrafting() + sync.lock(turtle) + self:focusFirst() UI.Dialog.enable(self) end @@ -44,6 +49,8 @@ end function learnPage:eventHandler(event) if event.type == 'cancel' then + sync.release(turtle) + Milo:resumeCrafting() UI:setPreviousPage() elseif event.type == 'accept' then diff --git a/milo/plugins/limitTask.lua b/milo/plugins/limitTask.lua index 9f66a2b..30a5270 100644 --- a/milo/plugins/limitTask.lua +++ b/milo/plugins/limitTask.lua @@ -9,14 +9,15 @@ function LimitTask:cycle(context) local trashcan = context.storage:filterActive('trashcan')() if trashcan then - for _,res in pairs(context.resources) do + for k,res in pairs(context.resources) do if res.limit then - local item = Milo:getItemWithQty(res, res.ignoreDamage, res.ignoreNbtHash) - if item and item.count > res.limit then +-- TODO: change to export method of finding items (maybe) + local item, count = Milo:getItemWithQty(Milo:splitKey(k), res.ignoreDamage, res.ignoreNbtHash) + if item and count > res.limit then context.storage:export( trashcan.name, nil, - item.count - res.limit, + count - res.limit, item) end end diff --git a/milo/plugins/listing.lua b/milo/plugins/listing.lua index 5b9b262..d37e6ca 100644 --- a/milo/plugins/listing.lua +++ b/milo/plugins/listing.lua @@ -118,7 +118,7 @@ end function listingPage.grid:getDisplayValues(row) row = Util.shallowCopy(row) - row.count = row.count > 0 and Util.toBytes(row.count) or '' + row.count = row.count > 0 and Util.toBytes(row.count) if row.low then row.low = Util.toBytes(row.low) end @@ -139,7 +139,7 @@ function listingPage:eventHandler(event) local item = self.grid:getSelected() if item then queue(function() - item.count = Milo:xxx(item, 1) + item.count = Milo:craftAndEject(item, 1) self.grid:draw() end) end @@ -148,7 +148,7 @@ function listingPage:eventHandler(event) local item = self.grid:getSelected() if item then queue(function() - item.count = Milo:xxx(item, itemDB:getMaxCount(item)) + item.count = Milo:craftAndEject(item, itemDB:getMaxCount(item)) self.grid:draw() end) end @@ -257,12 +257,7 @@ function listingPage:disable() end function listingPage:refresh(force) - if force then - self.allItems = Milo:refreshItems() - else - self.allItems = Milo:listItems() - end - Milo:mergeResources(self.allItems) + self.allItems = Milo:mergeResources(Milo:listItems(force)) self:applyFilter() end diff --git a/milo/plugins/machineLearn.lua b/milo/plugins/machineLearn.lua index 2c31f5e..b85a2e9 100644 --- a/milo/plugins/machineLearn.lua +++ b/milo/plugins/machineLearn.lua @@ -1,5 +1,6 @@ local itemDB = require('itemDB') local Milo = require('milo') +local sync = require('sync') local UI = require('ui') local Util = require('util') @@ -133,13 +134,9 @@ function pages.confirmation:validate() return true end -function machineLearnWizard:enable() - Milo:pauseCrafting() - UI.Page.enable(self) -end - function machineLearnWizard:disable() Milo:resumeCrafting() + sync.release(turtle) UI.Page.disable(self) end diff --git a/milo/plugins/refreshTask.lua b/milo/plugins/refreshTask.lua new file mode 100644 index 0000000..d3dcb05 --- /dev/null +++ b/milo/plugins/refreshTask.lua @@ -0,0 +1,16 @@ +local Milo = require('milo') + +-- Do a full scan of inventories every minute + +local RefreshTask = { + name = 'refresher', + priority = 0, +} + +function RefreshTask:cycle(context) + if os.clock() - context.storage.lastRefresh > 60 then + context.storage:refresh() + end +end + +Milo:registerTask(RefreshTask) diff --git a/milo/plugins/remote.lua b/milo/plugins/remote.lua index 51e5a42..323be4b 100644 --- a/milo/plugins/remote.lua +++ b/milo/plugins/remote.lua @@ -2,6 +2,7 @@ local Event = require('event') local itemDB = require('itemDB') local Milo = require('milo') local Socket = require('socket') +local Sync = require('sync') local device = _G.device local turtle = _G.turtle @@ -19,7 +20,7 @@ local function getManipulatorForUser(user) end local function client(socket) - _debug('connection from ' .. socket.dhost) + _G._debug('connection from ' .. socket.dhost) local user = socket:read(2) if not user then @@ -36,28 +37,41 @@ local function client(socket) if not data then break end -_debug('remote: ' .. data.request) + if data.request == 'list' then - local items = Milo:listItems() - Milo:mergeResources(items) + local items = Milo:mergeResources(Milo:listItems()) socket:write(items) elseif data.request == 'deposit' then - local count - - if data.slot == 'shield' then - count = manipulator.getEquipment().pushItems( - context.localName, - SHIELD_SLOT, - 64) + if Sync.isLocked(turtle) then + socket:write({ msg = '' }) else - count = manipulator.getInventory().pushItems( - context.localName, - data.slot, - 64) + local count + + Sync.sync(turtle, function() + if data.slot == 'shield' then + count = manipulator.getEquipment().pushItems( + context.localName, + SHIELD_SLOT, + 64) + else + count = manipulator.getInventory().pushItems( + context.localName, + data.slot, + 64) + end + Milo:clearGrid() + end) + + local list = Milo:listItems() + local current = list[data.key] and list[data.key].count or 0 + + socket:write({ + key = data.key, + count = count, + current = current + count, + }) end - socket:write({ count = count }) - Milo:clearGrid() elseif data.request == 'transfer' then local count = data.count @@ -69,32 +83,42 @@ _debug('remote: ' .. data.request) count = item and item.count or 0 end - local provided = Milo:provideItem(data.item, count, function(amount, currentCount) - amount = context.storage:export( - context.localName, - nil, - amount, - data.item) - - turtle.eachFilledSlot(function(slot) - manipulator.getInventory().pullItems( + local function transfer(amount) + Sync.sync(turtle, function() + amount = context.storage:export( context.localName, - slot.index, - slot.count) - end) - return currentCount - amount - end) + nil, + amount, + data.item) - socket:write({ count = provided }) + turtle.eachFilledSlot(function(slot) + manipulator.getInventory().pullItems( + context.localName, + slot.index, + slot.count) + end) + end) + + return amount + end + + if Sync.isLocked(turtle) then + socket:write({ msg = 'Turtle in use. please wait...' }) + end + + local provided = Milo:provideItem(data.item, count, transfer) + provided.transferred = provided.available > 0 and transfer(provided.available) or 0 + + socket:write(provided) end until not socket.connected - _debug('disconnected from ' .. socket.dhost) + _G._debug('disconnected from ' .. socket.dhost) end if device.wireless_modem then Event.addRoutine(function() - _debug('Milo: listening on port 4242') + _G._debug('Milo: listening on port 4242') while true do local socket = Socket.server(4242) Event.addRoutine(function() diff --git a/milo/plugins/replenishTask.lua b/milo/plugins/replenishTask.lua index bed7987..792043e 100644 --- a/milo/plugins/replenishTask.lua +++ b/milo/plugins/replenishTask.lua @@ -7,27 +7,29 @@ local ReplenishTask = { } function ReplenishTask:cycle(context) - for _,res in pairs(context.resources) do + for k,res in pairs(context.resources) do if res.low then - local item = Milo:getItemWithQty(res, res.ignoreDamage, res.ignoreNbtHash) + local key = Milo:splitKey(k) + local item, count = Milo:getItemWithQty(key, res.ignoreDamage, res.ignoreNbtHash) if not item then item = { - damage = res.damage, - nbtHash = res.nbtHash, - name = res.name, - displayName = itemDB:getName(res), + damage = key.damage, + nbtHash = key.nbtHash, + name = key.name, + displayName = itemDB:getName(key), count = 0 } end - if item.count < res.low then - if res.ignoreDamage then - item.damage = 0 + if count < res.low then + local nbtHash = item.nbtHash + if res.ignoreNbtHash then + nbtHash = nil end Milo:requestCrafting({ - damage = item.damage, - nbtHash = item.nbtHash, - count = res.low - item.count, + damage = res.ignoreDamage and 0 or item.damage, + nbtHash = nbtHash, + count = res.low - count, name = item.name, displayName = item.displayName, replenish = true, diff --git a/milo/plugins/turtleLearn.lua b/milo/plugins/turtleLearn.lua index fb6df3e..ac5d8ec 100644 --- a/milo/plugins/turtleLearn.lua +++ b/milo/plugins/turtleLearn.lua @@ -1,6 +1,7 @@ local Craft = require('turtle.craft') local itemDB = require('itemDB') local Milo = require('milo') +local sync = require('sync') local UI = require('ui') local Util = require('util') @@ -122,13 +123,9 @@ local turtleLearnWizard = UI.Page { notification = UI.Notification { }, } -function turtleLearnWizard:enable() - Milo:pauseCrafting() - UI.Page.enable(self) -end - function turtleLearnWizard:disable() Milo:resumeCrafting() + sync.release(turtle) UI.Page.disable(self) end From 44f8e61f2fa8cdf9e4202beece1de66b8c7fd785 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Wed, 31 Oct 2018 20:10:27 -0400 Subject: [PATCH 053/165] milo wip --- apps/scanningMiner.lua | 2 +- milo/MiloRemote.lua | 1 - milo/apis/turtle/craft.lua | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/scanningMiner.lua b/apps/scanningMiner.lua index 01f894c..76d6cac 100644 --- a/apps/scanningMiner.lua +++ b/apps/scanningMiner.lua @@ -523,7 +523,7 @@ end local success, msg if not fs.exists(DICTIONARY_FILE) or options.setTrash.value then - print('Place blocks into the turtles inventor to ignore, such as cobble, stone, gravel, etc.') + print('Place blocks into the turtles inventory to ignore, such as cobble, stone, gravel, etc.') print('\nPress enter when ready') read() addTrash() diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index 0df71c8..84c7c5a 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -194,7 +194,6 @@ end function page:transfer(item, count) local response = self:sendRequest({ request = 'transfer', item = item, count = count }) - debug(response) if response then item.count = response.current - response.transferred self.grid:draw() diff --git a/milo/apis/turtle/craft.lua b/milo/apis/turtle/craft.lua index d031b86..22b9481 100644 --- a/milo/apis/turtle/craft.lua +++ b/milo/apis/turtle/craft.lua @@ -188,8 +188,8 @@ function Craft.craftRecipe(recipe, count, inventoryAdapter, origItem) for _, request in pairs(origItem.ingredients) do if request.crafted >= request.count then if request.pending then - debug('??') - debug(request) + _debug('??') + _debug(request) end request.status = nil request.statusCode = Craft.STATUS_SUCCESS From 93a31994b775b576f3b0422b76ef09c30d6890ae Mon Sep 17 00:00:00 2001 From: kepler155c Date: Thu, 1 Nov 2018 15:28:43 -0400 Subject: [PATCH 054/165] scanning miner tweaks --- apps/scanningMiner.lua | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/apps/scanningMiner.lua b/apps/scanningMiner.lua index 76d6cac..7d94413 100644 --- a/apps/scanningMiner.lua +++ b/apps/scanningMiner.lua @@ -413,10 +413,13 @@ local function scan() s, m = turtle.digAt(b) end if not s then - page.statusBar:setValue('status', m) + page.statusBar:setValue('status', b.name .. ' ' .. m) page.statusBar:draw() page:sync() - os.sleep(3) + if debug and type(debug) == 'function' then + debug(b.name .. ' ' .. m) + end +-- os.sleep(3) else page.statusBar:setValue('mining', m) end @@ -452,7 +455,10 @@ local function mineChunk() if turtle.isAborted() then error('aborted') end - status('scanning ' .. pt.y + mining.home.y - 8 .. '-' .. pt.y + mining.home.y + 8) + status('scanning %d %d-%d', + mining.chunkIndex, + pt.y + mining.home.y - 8, + pt.y + mining.home.y + 8) turtle.select(1) safeGoto(pt.x, pt.z, pt.y) From fa109c40ed6fea0f596f79bc13450a58f3269770 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Thu, 1 Nov 2018 17:13:27 -0400 Subject: [PATCH 055/165] scanning miner tweaks --- apps/scanningMiner.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/scanningMiner.lua b/apps/scanningMiner.lua index 7d94413..a48d7fb 100644 --- a/apps/scanningMiner.lua +++ b/apps/scanningMiner.lua @@ -455,10 +455,10 @@ local function mineChunk() if turtle.isAborted() then error('aborted') end - status('scanning %d %d-%d', + status(string.format('scanning %d %d-%d', mining.chunkIndex, pt.y + mining.home.y - 8, - pt.y + mining.home.y + 8) + pt.y + mining.home.y + 8)) turtle.select(1) safeGoto(pt.x, pt.z, pt.y) From 10bbb5ebf46f7b8fc2d78724727c0bc371eb3844 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Thu, 1 Nov 2018 21:15:18 -0400 Subject: [PATCH 056/165] scanning miner tweak --- apps/scanningMiner.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/scanningMiner.lua b/apps/scanningMiner.lua index a48d7fb..64ff752 100644 --- a/apps/scanningMiner.lua +++ b/apps/scanningMiner.lua @@ -455,8 +455,9 @@ local function mineChunk() if turtle.isAborted() then error('aborted') end + local chunks = math.pow(mining.diameter-2, 2) + mining.chunkIndex status(string.format('scanning %d %d-%d', - mining.chunkIndex, + chunks, pt.y + mining.home.y - 8, pt.y + mining.home.y + 8)) From 047c2cdddbe925586de219286d241d00673b6886 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Fri, 2 Nov 2018 21:42:47 -0400 Subject: [PATCH 057/165] milo wip --- apps/farm.lua | 2 +- milo/Milo.lua | 31 +++++++++++++++++-- milo/MiloRemote.lua | 13 ++++---- milo/apis/milo.lua | 59 ++++++++++++++++++++++++------------ milo/plugins/craftTask.lua | 8 ++--- milo/plugins/demandCraft.lua | 6 +++- milo/plugins/remote.lua | 52 +++++++++++++------------------ 7 files changed, 103 insertions(+), 68 deletions(-) diff --git a/apps/farm.lua b/apps/farm.lua index fcbcfdc..901398f 100644 --- a/apps/farm.lua +++ b/apps/farm.lua @@ -109,7 +109,7 @@ turtle.run(function() local facing = scanner.getBlockMeta(0, 0, 0).state.facing turtle.point.heading = Point.facings[facing].heading - turtle.setPolicy('digOnly') + --turtle.setPolicy('digOnly') turtle.setMovementStrategy('goto') repeat local blocks, harvestCount = scan() diff --git a/milo/Milo.lua b/milo/Milo.lua index 62d5093..706381f 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -59,6 +59,7 @@ local context = { learnTypes = { }, tasks = { }, + queue = { }, localName = modem.getNameLocal(), storage = Storage(config), @@ -101,10 +102,10 @@ Milo:clearGrid() local page = UI:getPage('listing') UI:setPage(page) -Event.onInterval(5, function() - if not Milo:isCraftingPaused() and context.storage:isOnline() then +Event.on('milo_cycle', function() + if not context.turtleBusy then + context.turtleBusy = true Milo:resetCraftingStatus() - --Milo:refreshItems() for _, task in ipairs(context.tasks) do local s, m = pcall(function() task:cycle(context) end) @@ -114,6 +115,30 @@ Event.onInterval(5, function() error(m) end end + context.turtleBusy = false + if not Util.empty(context.queue) then + os.queueEvent('milo_queue') + end + end +end) + +Event.on('milo_queue', function() + if not context.turtleBusy then + context.turtleBusy = true + + for _, key in pairs(Util.keys(context.queue)) do + local entry = context.queue[key] + entry.callback(entry.request) + context.queue[key] = nil + end + + context.turtleBusy = false + end +end) + +Event.onInterval(5, function() + if not Milo:isCraftingPaused() and context.storage:isOnline() then + os.queueEvent('milo_cycle') end end) diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index 84c7c5a..e24d650 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -195,12 +195,13 @@ end function page:transfer(item, count) local response = self:sendRequest({ request = 'transfer', item = item, count = count }) if response then - item.count = response.current - response.transferred + _debug(response) + item.count = response.current - response.count self.grid:draw() if response.craft > 0 then self:setStatus(response.craft .. ' crafting ...') - elseif response.craft + response.available < response.requested then - self:setStatus((response.craft + response.available) .. ' available ...') + elseif response.craft + response.count < response.requested then + self:setStatus((response.craft + response.count) .. ' available ...') end end end @@ -318,22 +319,22 @@ if options.slot.value or options.shield.value then os.sleep(1.5) local neural = device.neuralInterface if not neural or not neural[inv] then - _debug('missing Introspection module') + _G._debug('missing Introspection module') end local method = neural and neural[inv] local item = method and method().getItemMeta(slotNo) if item then - _debug('depositing') local response = page:sendRequest({ request = 'deposit', slot = slotValue, + count = item.count, key = table.concat({ item.name, item.damage, item.nbtHash }, ':') }) if response then local ritem = page.items[response.key] if ritem then - ritem.count = response.current + ritem.count = response.current + item.count end page.grid:draw() page:sync() diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index c1c2687..87ae054 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -3,6 +3,7 @@ local Craft = require('turtle.craft') local itemDB = require('itemDB') local Util = require('util') +local os = _G.os local turtle = _G.turtle local Milo = { @@ -178,25 +179,37 @@ function Milo:getTurtleInventory() return list end -function Milo:craftAndEject(item, count) - local provided = self:provideItem(item, count, function(amount) - -- eject rest when finished crafted - return self:eject(item, amount) - end) - - -- eject what we currently have - return item.count - self:eject(item, provided.available) +-- queue up an action that reliees on the crafting grid +function Milo:queueRequest(request, callback) + if Util.empty(self.context.queue) then + os.queueEvent('milo_queue') + end + table.insert(self.context.queue, { + request = request, + callback = callback + }) end -function Milo:provideItem(item, count, callback) +function Milo:craftAndEject(item, count) + local request = self:makeRequest(item, count, function(request) + -- eject rest when finished crafted + return self:eject(item, request.count) + end) + + -- predict that we will eject that amount + return request.current - request.count +end + +function Milo:makeRequest(item, count, callback) local current = Milo:getItem(Milo:listItems(), item) or { count = 0 } if count <= 0 then return { requested = 0, craft = 0, - available = 0, + count = 0, current = current.count, + item = item, } end @@ -211,20 +224,26 @@ function Milo:provideItem(item, count, callback) end end - if toCraft > 0 then - item = Util.shallowCopy(item) - item.count = toCraft - item.eject = callback - self:requestCrafting(item) - item.crafted = 0 - end - - return { + local request = { requested = count, craft = toCraft, - available = math.min(count, current.count), + count = math.min(count, current.count), current = current.count, + item = item, } + + if request.count > 0 then + Milo:queueRequest(request, callback) + end + + if request.craft > 0 then + item = Util.shallowCopy(item) + item.count = request.craft + item.callback = callback + self:requestCrafting(item) + end + + return request end function Milo:eject(item, count) diff --git a/milo/plugins/craftTask.lua b/milo/plugins/craftTask.lua index d79e67d..f0e59ee 100644 --- a/milo/plugins/craftTask.lua +++ b/milo/plugins/craftTask.lua @@ -106,12 +106,8 @@ function craftTask:cycle() sync(turtle, function() self:craft(recipe, item) end) - if item.eject and item.crafted >= item.count then - if type(item.eject) == 'boolean' then - Milo:eject(item, item.count) - else - item.eject(item.count) -- invoke callback - end + if item.callback and item.crafted >= item.count then + item.callback(item) -- invoke callback end elseif not context.controllerAdapter then item.status = '(no recipe)' diff --git a/milo/plugins/demandCraft.lua b/milo/plugins/demandCraft.lua index 8bde995..a1afa66 100644 --- a/milo/plugins/demandCraft.lua +++ b/milo/plugins/demandCraft.lua @@ -119,7 +119,11 @@ function craftPage:eventHandler(event) local item = Util.shallowCopy(self.item) item.count = tonumber(self.wizard.pages.quantity.count.value) item.forceCrafting = true - item.eject = self.wizard.pages.quantity.eject.value == true + if self.wizard.pages.quantity.eject.value then + item.callback = function(request) + Milo:eject(item, request.count) + end + end Milo:requestCrafting(item) UI:setPreviousPage() else diff --git a/milo/plugins/remote.lua b/milo/plugins/remote.lua index 323be4b..956e198 100644 --- a/milo/plugins/remote.lua +++ b/milo/plugins/remote.lua @@ -43,36 +43,33 @@ local function client(socket) socket:write(items) elseif data.request == 'deposit' then - if Sync.isLocked(turtle) then - socket:write({ msg = '' }) - else - local count - + local function deposit() Sync.sync(turtle, function() if data.slot == 'shield' then - count = manipulator.getEquipment().pushItems( + manipulator.getEquipment().pushItems( context.localName, SHIELD_SLOT, - 64) + data.count) else - count = manipulator.getInventory().pushItems( + manipulator.getInventory().pushItems( context.localName, data.slot, - 64) + data.count) end Milo:clearGrid() end) - - local list = Milo:listItems() - local current = list[data.key] and list[data.key].count or 0 - - socket:write({ - key = data.key, - count = count, - current = current + count, - }) end + local list = Milo:listItems() + local current = list[data.key] and list[data.key].count or 0 + + socket:write({ + key = data.key, + current = current, + }) + + Milo:queueRequest({ }, deposit) + elseif data.request == 'transfer' then local count = data.count @@ -83,33 +80,26 @@ local function client(socket) count = item and item.count or 0 end - local function transfer(amount) + local function transfer(request) Sync.sync(turtle, function() - amount = context.storage:export( + local transferred = context.storage:export( context.localName, nil, - amount, + request.count, data.item) turtle.eachFilledSlot(function(slot) manipulator.getInventory().pullItems( context.localName, slot.index, - slot.count) + transferred) end) end) - - return amount end - if Sync.isLocked(turtle) then - socket:write({ msg = 'Turtle in use. please wait...' }) - end + local request = Milo:makeRequest(data.item, count, transfer) - local provided = Milo:provideItem(data.item, count, transfer) - provided.transferred = provided.available > 0 and transfer(provided.available) or 0 - - socket:write(provided) + socket:write(request) end until not socket.connected From aa66b1c663487db700e63b5dd26f5c6636ce6ac5 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sat, 3 Nov 2018 04:47:52 -0400 Subject: [PATCH 058/165] tweaks and shatter --- apis/shatter.lua | 482 +++++++++++++++++++++++++++++++++++++++++ apps/cows.lua | 50 ++++- apps/glassesDriver.lua | 31 +++ apps/mwm.lua | 5 +- apps/pickup.lua | 22 +- apps/pickupRemote.lua | 1 + 6 files changed, 573 insertions(+), 18 deletions(-) create mode 100644 apis/shatter.lua create mode 100644 apps/glassesDriver.lua diff --git a/apis/shatter.lua b/apis/shatter.lua new file mode 100644 index 0000000..ed741dc --- /dev/null +++ b/apis/shatter.lua @@ -0,0 +1,482 @@ +local os = _G.os +local parallel = _G.parallel +local peripheral = _G.peripheral + +local mods = peripheral.wrap("back") +--ensure glasses are present +if not mods.canvas then + error("Shatter requires Overlay Glasses", 2) +end + +--###TERMINAL API CODE###-- +--colors, for reference use +local colors = { + white = 0xf0f0f000, + orange = 0xf2b23300, + magenta = 0xe57fd800, + lightBlue = 0x99b2f200, + yellow = 0xdede6c00, + lime = 0x7fcc1900, + pink = 0xf2b2cc00, + gray = 0x4c4c4c00, + lightGray = 0x99999900, + cyan = 0x4c99b200, + purple = 0xb266e500, + blue = 0x3366cc00, + brown = 0x7f664c00, + green = 0x57a64e00, + red = 0xcc4c4c00, + black = 0x19191900 +} +--colors by number + --colors by number +local cbn = { + colors.white, + colors.orange, + colors.magenta, + colors.lightBlue, + colors.yellow, + colors.lime, + colors.pink, + colors.gray, + colors.lightGray, + colors.cyan, + colors.purple, + colors.blue, + colors.brown, + colors.green, + colors.red, + colors.black +} +--scaled character factor +local ox, oy = 6, 9 +-- character size +local sx, sy = 6, 9 +--term bg and fg colors and alpha values +local bg, fg, bgbn, fgbn, fga, bga, fgabn, bgabn = colors.black, colors.white, 2^(#cbn-1), 2^0, 255, 255, 1, 1 +--cursor, pos, and blink +local csr, cx, cy, cb = nil, 1, 1, true +local textScale = 1 +local can = mods.canvas() + +--term size +local tx, ty = can.getSize() +tx, ty = math.floor(tx/ox), math.floor(ty/oy) +--screen rendering in a table +local screen = {} +--populate that table +local function tPop() + local x, y = can.getSize() + for i = 1, math.floor(x/ox) do + screen[i] = {} + for j = 1, math.floor(y/oy) do + screen[i][j] = {bg = {}, fg = {}} + end + end +end +tPop() +--write text in grid fashion and add to table +local function write(x, y, char, color) + x, y = math.floor(x), math.floor(y) + if x > 0 and y > 0 and x <= tx and y <= ty then + if not screen[x][y].fg.getColor then + screen[x][y].fg = can.addText({((x-1)*ox)+1, ((y-1)*oy)+1}, char, color, ox/sx) + else + screen[x][y].fg.setColor(color) + screen[x][y].fg.setText(char) + end + end +end +--draw pixel in grid fashion and add to table +local function draw(x, y, color) + x, y = math.floor(x), math.floor(y) + if x > 0 and y > 0 and x <= tx and y <= ty then + if not screen[x][y].bg.getColor then + screen[x][y].bg = can.addRectangle((x-1)*ox, (y-1)*oy, ox, oy, color) + else + screen[x][y].bg.setColor(color) + end + end +end +--get the data of a particular pixel +local function getData(pixel) + if pixel then + return {bgc = bit32.band(pixel.bg.getColor(), 2^32-1), -- Credit to MC:Anavrins for bit32 ingenuity + fgc = bit32.band(pixel.fg.getColor(), 2^32-1), + txt = pixel.fg.getText()} + end +end +--set the data of a particular pixel +local function setData(pixel, data) + if pixel and data then + if pixel.bg.getPosition then + local x, y = pixel.bg.getPosition() + draw(math.floor(x/ox)+1, math.floor(y/oy)+1, data.bgc) + write(math.floor(x/ox)+1, math.floor(y/oy)+1, data.txt, data.fgc) + end + end +end +--move a row to an entirely different line +local function move(line, to) + if line > 0 and line <= ty and to > 0 and to <= ty then + for i = 1, tx do + setData(screen[i][to], getData(screen[i][line])) + end + end +end +--populate term with default bg and fg colors. +local function repopulate() + local x, y = can.getSize() + for i = 1, math.floor(x/ox) do + for j = 1, math.floor(y/oy) do + draw(i, j, bg) + end + end + for i = 1, math.floor(x/ox) do + for j = 1, math.floor(y/oy) do + write(i, j, "", fg) + end + end +end + +local function resize(x, y) + ox, oy = math.ceil(textScale*sx), math.ceil(textScale*sy) + tx, ty = x, y + csr.remove() + local oldscr = screen -- replicate the screen + screen = {} -- remove it for repopulation of table w/ new scale + tPop() -- repopulate table + repopulate() -- add objects + csr = can.addText({cx*ox, (cy*oy)+1}, "", 0xffffffff, textScale) --recreate cursor + for i = 1, #oldscr do --rerender screen in new scale + for j = 1, #oldscr[i] do + if oldscr[i][j].bg.getColor ~= nil then + --if screen[i] and screen[i][j] then + -- setData(screen[i][j], getData(oldscr[i][j])) + --end + oldscr[i][j].bg.remove() + oldscr[i][j].fg.remove() + end + end + os.sleep(0) + end + os.queueEvent("monitor_resize", "glasses") +end + +local out = {} +out.write = function(str) +--term.write + str = tostring(str) + for i = 1, #str do + write(cx+i-1, cy, str:sub(i, i), fg+fga) + draw(cx+i-1, cy, bg+bga) + end + cx = cx+#str +end +out.blit = function(str, tfg, tbg) +--term.blit + if type(str) ~= "string" then + error("bad argument #1 (expected string, got "..type(str)..")", 2) + elseif type(tfg) ~= "string" then + error("bad argument #2 (expected string, got "..type(tfg)..")", 2) + elseif type(tbg) ~= "string" then + error("bad argument #3 (expected string, got "..type(tbg)..")", 2) + end + for i = 1, #str do + local nfg = cbn[tonumber(tfg:sub(i,i), 16)+1] + local nbg = cbn[tonumber(tbg:sub(i,i), 16)+1] + draw(cx+i-1, cy, nbg+bga) + write(cx+i-1, cy, str:sub(i,i), nfg+fga) + end + cx = cx+#str +end +out.clear = function() +--term.clear + for i = 1, tx do + for j = 1, ty do + write(i, j, "", fg+fga) + draw(i, j, bg+bga) + end + end +end +out.clearLine = function() +--term.clearLine + if cy > 0 and cy <= ty then + for i = 1, tx do + draw(i, cy, bg+bga) + write(i, cy, "", fg+fga) + end + end +end +out.getCursorPos = function() +--term.getCursorPos + return cx, cy +end +out.setCursorPos = function(x, y) +--term.setCursorPos + if type(x) ~= "number" then + error("bad argument #1 (expected number, got "..type(x)..")", 2) + elseif type(y) ~= "number" then + error("bad argument #2 (expected number, got "..type(y)..")", 2) + end + csr.setPosition((x-1)*ox, ((y-1)*oy)+1) + cx, cy = x, y +end + +out.setCursorBlink = function(b) + if type(b) ~= "boolean" then + error("bad argument #1 (expected boolean, got "..type(b)..")", 2) + end + cb = b +end + +out.isColor = function() + return true, "now with more alpha!" +end + +out.isColour = out.isColor + +out.getSize = function() + return tx, ty +end + +out.setSize = function(x, y) + resize(x, y) +end + +out.scroll = function(amount) + local _, tcy = out.getCursorPos() + if type(amount) ~= "number" then + error("bad argument #1 (expected number, got "..type(amount)..")", 2) + end + if amount > 0 then + for i = 1, tx do + move(i, i-amount) + end + elseif amount < 0 then + for i = tx, 1, -1 do + move(i, i-amount) + end + end + out.setCursorPos(1, tcy-amount-1) +end +local function invCol(col) +--A simple error message I am too lazy to type twice +--used in the following few functions + error("invalid color (got "..col..")", 2) +end +local function lb2(num) +--very basic implementation of base 2 logarithm + return math.log(num)/math.log(2) +end +out.setTextColor = function(col) +--term.setTextColor + if type(col) ~= "number" then + error("bad argument #1 (number expected, got "..type(col)..")", 2) + end + if lb2(col) > #cbn or lb2(col) ~= math.ceil(lb2(col)) then + invCol(col) + else + fg = cbn[lb2(col)+1] + fgbn = col + end +end +out.setBackgroundColor = function(col) +--term.setBackgroundColor + if type(col) ~= "number" then + error("bad argument #1 (expected number, got "..type(col)..")", 2) + end + if lb2(col) > #cbn or lb2(col) ~= math.ceil(lb2(col)) then + invCol(col) + else + bg = cbn[lb2(col)+1] + bgbn = col + end +end +-- Text & BG Alpha innovated by MC:Ale32bit +out.setTextAlpha = function(val) +-- set the alpha value of the text + if type(val) ~= "number" then + error("bad argument #1 (expected number, got "..type(val)..")", 2) + end + if val > 1 then val = 1 elseif val < 0 then val = 0 end + fga = math.floor(val*255) + fgabn = val +end +out.setBackgroundAlpha = function(val) +-- set the alpha value of the background + if type(val) ~= "number" then + error("bad argument #1 (expected number, got "..type(val)..")", 2) + end + if val > 1 then val = 1 elseif val < 0 then val = 0 end + bga = math.floor(val*255) + bgabn = val +end +out.setTextHex = function(hex) +-- set the hex color value of the text + if type(tonumber(hex, 16)) ~= "number" then + error("bad argument #1 (expected number, got "..type(hex)..")", 2) + end + fg = hex + fgbn = 1 +end +out.setBackgroundHex = function(hex) +-- set the hex color value of the background + if type(tonumber(hex, 16)) ~= "number" then + error("bad argument #1 (expected number, got "..type(hex)..")", 2) + end + bg = hex + bgbn = 1 +end +out.getTextColor = function() +--term.getTextColor + return fgbn +end +out.getBackgroundColor = function() +--term.getBackgroundColor + return bgbn +end +out.getTextAlpha = function() +-- get the alpha value of the text + return fgabn +end +out.getBackgroundAlpha = function() +--get the alpha value of the background + return bgabn +end +out.getTextHex = function() +-- get the hex color value of the text + return fg +end +out.getBackgroundHex = function() +-- get the hex color value of the background + return bg +end +local function torgba(hex) +-- Converts a hex value into 3 seperate r, g, and b values +-- Technically also gets a value, but it thrown out due to what this is needed for +-- Credit to MC:valithor2 for this algorithm + local vals = {} + for i = 1, 4 do + vals[i] = hex%256 + hex = (hex-vals[i])/256 + end + return vals[4]/255, vals[3]/255, vals[2]/255 +end +local function refreshColor(oc, nc) +-- refreshes terminal when palette values are manipulated + for i = 1, #screen do + for j = 1, #screen[i] do + local op, changed = getData(screen[i][j]), false + if op.bgc == oc then + op.bgc = nc + changed = true + end + if op.fgc == oc then + op.fgc = nc + changed = true + end + if changed then + setData(screen[i][j], op) + end + end + end +end +out.getPaletteColor = function(col) +--term.getPaletteColor + if type(col) ~= "number" then + error("bad argument #1 (number expected, got "..type(col)..")", 2) + end + if lb2(col) > #cbn or lb2(col) ~= math.ceil(lb2(col)) then + invCol(col) + end + return torgba(cbn[lb2(col)+1]) +end +out.setPaletteColor = function(cnum, r, g, b) +--term.setPaletteColor + local oc = cbn[lb2(cnum)+1] + if type(cnum) ~= "number" then + error("bad argument #1 (number expected, got "..type(cnum)..")", 2) + end + if type(r) ~= "number" then + error("bad argument #2 (number expected, got "..type(r)..")", 2) + end + if g then + if type(g) ~= "number" then + error("bad argument #3 (number expected, got "..type(g)..")", 2) + elseif type(b) ~= "number" then + error("bad argument #4 (number expected, got "..type(b)..")", 2) + end + if r > 1 then r = 1 elseif r < 0 then r = 0 end + if g > 1 then g = 1 elseif g < 0 then g = 0 end + if b > 1 then b = 1 elseif b < 0 then b = 0 end + cbn[lb2(cnum)+1] = (((r*255)*(16^6))+((g*255)*(16^4))+((b*255)*(16^2))) + else + cbn[lb2(cnum)+1] = (r*256) + end + if bgbn == cnum then + out.setBackgroundColor(bgbn) + end + if fgbn == cnum then + out.setTextColor(fgbn) + end + --refreshColor(oc, cbn[lb2(cnum)+1]) -- errors +end +--compat for all those UK'ers +out.setTextColour = out.setTextColor +out.setBackgroundColour = out.setBackgroundColor +out.setPaletteColour = out.setPaletteColor +out.getTextColour = out.getTextColor +out.getBackgroundColour = out.getBackgroundColor +out.getPaletteColour = out.getPaletteColor + +out.getTextScale = function() return textScale end +out.setTextScale = function(scale) + if type(scale) ~= "number" then + error("bad argument #1 (number expected, got "..type(scale)..")", 2) + end + if 0.4 >= scale or scale > 10 then + error("Expected number in range 0.5-10", 2) + end + if textScale ~= scale then + local factor = textScale/scale +debug({ scale, textScale, factor, tx*factor, ty*factor }) + textScale = scale + resize(tx*factor, ty*factor) + end +end + +--###TERMINAL CREATION CODE###-- +csr = can.addText({cx*ox, (cy*oy)+1}, "", 0xffffffff, ox/sx) +repopulate() +out.cursorRoutine = function() + parallel.waitForAll(function() + --cursor flicker + while true do + if not cb then + csr.setText(" ") + os.sleep() + else + csr.setText("_") + os.sleep(.4) + csr.setText(" ") + os.sleep(.4) + end + end + end, + function() + --glasses event handler conversion + while true do + local e = {os.pullEvent()} + if e[1]:find("glasses") then + local _, b = e[1]:find("glasses") + e[1] = "mouse"..e[1]:sub(b+1, -1) + if e[1] ~= "mouse_scroll" then + e[3], e[4] = math.ceil(e[3]/ox), math.ceil(e[4]/oy) + end + os.queueEvent(unpack(e)) + end + end + end) +end +return out diff --git a/apps/cows.lua b/apps/cows.lua index 784e595..2196c92 100644 --- a/apps/cows.lua +++ b/apps/cows.lua @@ -2,6 +2,7 @@ _G.requireInjector(_ENV) local Config = require('config') local Util = require('util') +local InventoryAdapter = require('chestAdapter18') local device = _G.device local os = _G.os @@ -16,23 +17,51 @@ local sensor = device['plethora:sensor'] or turtle.equip('right', 'plethora:module:3') and device['plethora:sensor'] or error('Plethora sensor required') +local dispenser = device['minecraft:dispenser_89'] or + error('Dispenser not found') +local integrator = device['redstone_integrator_131'] or + error('Integrator not found') + +local function pulse() + integrator.setOutput('north', true) + os.sleep(.25) + integrator.setOutput('north', false) +end + +local function turnOffWater() + local list = dispenser.list() + if list[1].name == 'minecraft:bucket' then + pulse() + os.sleep(2) + end +end + +local function turnOnWater() + if dispenser.list()[1].name == 'minecraft:water_bucket' then + pulse() + end +end + local function getCowCount() local blocks = sensor.sense() local grown = 0 local babies = 0 + local xpCount = 0 Util.filterInplace(blocks, function(v) if v.name == 'Cow' then if v.y > -.5 then grown = grown + 1 end if v.y < -.5 then babies = babies + 1 end return v.y > -.5 + elseif v.name == 'XPOrb' then + xpCount = xpCount + 1 end end) - Util.print('%d grown, %d babies', grown, babies) + Util.print('%d grown, %d babies, %d xp', grown, babies, xpCount) - return #blocks + return #blocks, xpCount end local function butcher() @@ -60,17 +89,30 @@ local function breed() end end +local chest = InventoryAdapter({ side = 'top', direction = 'down' }) or + error('missing chest above') + turtle.run(function() + turnOffWater() + repeat - if getCowCount() > config.max_cows then + local cowCount, xpCount = getCowCount() + if cowCount > config.max_cows then turtle.setStatus('Butchering') butcher() elseif turtle.getItemCount('minecraft:wheat') == 0 then - turtle.setStatus('Out of wheat') + if chest:provide({ name = 'minecraft:wheat' }, 64) == 0 then + turtle.setStatus('Out of wheat') + end else turtle.setStatus('Breeding') breed() end + if xpCount > 2 then + turnOnWater() + os.sleep(8) + turnOffWater() + end os.sleep(5) until turtle.isAborted() end) diff --git a/apps/glassesDriver.lua b/apps/glassesDriver.lua new file mode 100644 index 0000000..9b17688 --- /dev/null +++ b/apps/glassesDriver.lua @@ -0,0 +1,31 @@ +_G.requireInjector(_ENV) + +local device = _G.device +local kernel = _G.kernel +local os = _G.os + +local glasses = require('shatter') +glasses.name = 'glasses' +glasses.type = 'rayban' +glasses.size = 'face' +device.glasses = glasses + +glasses.setTextScale(.5) +glasses.setSize(100, 40) + +kernel.hook({ 'glasses_click', 'glasses_up', 'glasses_drag' }, function(event, eventData) + local sx, sy = 6, 9 + local scale = glasses.getTextScale() + local ox, oy = math.ceil(scale*sx), math.ceil(scale*sy) + + local lookup = { + glasses_click = 'monitor_touch', + glasses_up = 'monitor_up', + glasses_drag = 'monitor_drag', + } + local x, y = math.floor(eventData[2]/ox) + 1, math.floor(eventData[3]/oy) + 1 + os.queueEvent(lookup[event], 'glasses', x, y) + + glasses.setCursorPos(x, y) + glasses.write('X ' .. eventData[3]) +end) diff --git a/apps/mwm.lua b/apps/mwm.lua index 590879e..2858942 100644 --- a/apps/mwm.lua +++ b/apps/mwm.lua @@ -28,8 +28,9 @@ local monitor local defaultEnv = Util.shallowCopy(_ENV) defaultEnv.multishell = multishell - -if args[2] then +if args[3] then + monitor = _G.device[args[3]] +elseif args[2] then monitor = peripheral.wrap(args[2]) or syntax() else monitor = peripheral.find('monitor') or syntax() diff --git a/apps/pickup.lua b/apps/pickup.lua index f0aa9bb..140c326 100644 --- a/apps/pickup.lua +++ b/apps/pickup.lua @@ -12,10 +12,6 @@ local peripheral = _G.peripheral local printError = _G.printError local turtle = _G.turtle -if not device.wireless_modem then - error('Modem is required') -end - if not turtle then error('Can only be run on a turtle') end @@ -236,16 +232,18 @@ local function pickupHost(socket) end end -Event.addRoutine(function() - while true do - print('waiting for connection on port 5222') - local socket = Socket.server(5222) +if device.wireless_modem then + Event.addRoutine(function() + while true do + print('waiting for connection on port 5222') + local socket = Socket.server(5222) - print('pickup: connection from ' .. socket.dhost) + print('pickup: connection from ' .. socket.dhost) - Event.addRoutine(function() pickupHost(socket) end) - end -end) + Event.addRoutine(function() pickupHost(socket) end) + end + end) +end local function eachEntry(t, fn) diff --git a/apps/pickupRemote.lua b/apps/pickupRemote.lua index 3e99ec5..a8162bd 100644 --- a/apps/pickupRemote.lua +++ b/apps/pickupRemote.lua @@ -94,6 +94,7 @@ local function getPoint() if not gpt then mainPage.statusBar:timedStatus('Unable to get location', 3) end + gpt.y = gpt.y - 1 return gpt end From 1f7ef4a4831483c03fbcf0ab288c9019ffcdec12 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sat, 3 Nov 2018 18:14:11 -0400 Subject: [PATCH 059/165] package management --- builder/.package | 9 + {apis => builder/apis}/base64.lua | 0 {apis => builder/apis}/builder/blocks.lua | 0 {apis => builder/apis}/builder/builder.lua | 0 {apis => builder/apis}/builder/commands.lua | 0 {apis => builder/apis}/builder/schematic.lua | 0 {apis => builder/apis}/builder/turtle.lua | 0 {apis => builder/apis}/deflatelua.lua | 0 {apps => builder}/base64dl.lua | 0 {apps => builder}/builder.lua | 0 {apps => builder}/supplier.lua | 0 core/.package | 9 + {apps => core}/Appstore.lua | 0 {apps => core}/Devices.lua | 0 {apps => core}/Events.lua | 0 {apps => core}/Music.lua | 0 {apps => core}/Script.lua | 0 {apps => core}/Turtles.lua | 0 {apis => core/apis}/chestAdapter.lua | 0 {apis => core/apis}/chestAdapter18.lua | 0 {apis => core/apis}/controllerAdapter.lua | 0 {apis => core/apis}/inventoryAdapter.lua | 0 {apis => core/apis}/itemDB.lua | 0 {apis => core/apis}/meAdapter.lua | 0 {apis => core/apis}/meAdapter18.lua | 0 {apis => core/apis}/message.lua | 0 {apis => core/apis}/nameDB.lua | 2 +- {apis => core/apis}/refinedAdapter.lua | 0 {apis => core/apis}/tableDB.lua | 0 {apis => core/apis}/turtle/craft.lua | 2 +- {apis => core/apis}/turtle/crafting.lua | 0 {apis => core/apis}/turtle/home.lua | 0 {apis => core/apis}/turtle/level.lua | 0 {apps => core}/debug.lua | 0 {apps => core}/edit.lua | 0 {etc => core/etc}/apps/opus-apps.db | 0 .../etc}/names/appliedenergistics2.json | 0 {etc => core/etc}/names/computercraft.json | 0 {etc => core/etc}/names/enderio.json | 0 .../etc}/names/exnihiloadscensio.json | 0 {etc => core/etc}/names/extrautils2.json | 0 {etc => core/etc}/names/ironchest.json | 0 {etc => core/etc}/names/minecraft.json | 0 {etc => core/etc}/names/rftools.json | 0 {etc => core/etc}/names/storagedrawers.json | 0 {etc => core/etc}/names/tconstruct.json | 0 {etc => core/etc}/recipes/minecraft.db | 0 {etc => core/etc}/scripts/abort | 0 {etc => core/etc}/scripts/follow | 0 {etc => core/etc}/scripts/goHome | 0 {etc => core/etc}/scripts/moveTo | 0 {etc => core/etc}/scripts/obsidian | 0 {etc => core/etc}/scripts/reboot | 0 {etc => core/etc}/scripts/setHome | 0 {etc => core/etc}/scripts/shutdown | 0 {etc => core/etc}/scripts/summon | 0 {apps => core}/logMonitor.lua | 0 {apps => core}/mirror.lua | 0 {apps => core}/mirrorClient.lua | 0 {apps => core}/mirrorHost.lua | 0 {apps => core}/mwm.lua | 0 {apps => core}/namedb.lua | 0 {apps => core}/persist.lua | 0 {apps => core}/recorder.lua | 0 {apps => core}/shapes.lua | 0 {apps => core}/t.lua | 0 {apps => core}/termShare.lua | 0 {apps => core}/trace.lua | 0 farms/.package | 9 + {apps => farms}/cows.lua | 0 {apps => farms}/farm.lua | 0 {apps => farms}/treefarm.lua | 0 forestry/.package | 9 + forestry/alveary.lua | 106 +++++++++++ forestry/beeInfo.lua | 176 ++++++++++++++++++ forestry/filing.lua | 34 ++++ forestry/serums.lua | 34 ++++ glasses/.package | 9 + {apis => glasses/apis}/shatter.lua | 1 - {apps => glasses}/glassesDriver.lua | 0 milo/.package | 7 +- milo/apis/turtle/craft.lua | 2 +- miners/.package | 9 + {apps => miners}/scanningMiner.lua | 0 {apps => miners}/simpleMiner.lua | 0 neural/.package | 9 + neural/apis/neural/angle.lua | 11 ++ neural/apis/neural/interface.lua | 139 ++++++++++++++ neural/apis/neural/mobs.lua | 22 +++ neural/autorun/6.neural.lua | 24 +++ neural/etc/apps/neural-apps.db | 22 +++ neural/mobfollow.lua | 49 +++++ neural/neuralFight.lua | 98 ++++++++++ neural/neuralFly.lua | 78 ++++++++ neural/neuralRecorder.lua | 88 +++++++++ neural/neuralRemote.lua | 56 ++++++ neural/neuralReplay.lua | 52 ++++++ neural/neuralSword.lua | 28 +++ neural/robotWars.lua | 83 +++++++++ neural/shootMob.lua | 31 +++ neural/shootingGallery.lua | 48 +++++ pickup/.package | 9 + {apps => pickup}/pickup.lua | 0 {apps => pickup}/pickupRemote.lua | 0 recipeBook/.package | 9 + .../etc}/recipes/appliedenergistics2.db | 0 {etc => recipeBook/etc}/recipes/botania.db | 0 .../etc}/recipes/computercraft.db | 0 {etc => recipeBook/etc}/recipes/enderio.db | 0 {etc => recipeBook/etc}/recipes/exnihilo.db | 0 .../etc}/recipes/extrautils2.db | 0 {etc => recipeBook/etc}/recipes/ironchest.db | 0 {etc => recipeBook/etc}/recipes/mekanism.db | 0 .../etc}/recipes/mysticalagriculture.db | 0 {etc => recipeBook/etc}/recipes/rftools.db | 0 .../etc}/recipes/skyblockinfinity.db | 0 .../etc}/recipes/storagedrawers.db | 0 {etc => recipeBook/etc}/recipes/tconstruct.db | 0 {apps => recipeBook}/recipeBook.lua | 0 storageManager/.package | 9 + {apps => storageManager}/Crafter.lua | 0 {apps => storageManager}/chestManager.lua | 0 {apps => storageManager}/levelEmitter.lua | 0 {apps => storageManager}/storageActivity.lua | 0 124 files changed, 1274 insertions(+), 9 deletions(-) create mode 100644 builder/.package rename {apis => builder/apis}/base64.lua (100%) rename {apis => builder/apis}/builder/blocks.lua (100%) rename {apis => builder/apis}/builder/builder.lua (100%) rename {apis => builder/apis}/builder/commands.lua (100%) rename {apis => builder/apis}/builder/schematic.lua (100%) rename {apis => builder/apis}/builder/turtle.lua (100%) rename {apis => builder/apis}/deflatelua.lua (100%) rename {apps => builder}/base64dl.lua (100%) rename {apps => builder}/builder.lua (100%) rename {apps => builder}/supplier.lua (100%) create mode 100644 core/.package rename {apps => core}/Appstore.lua (100%) rename {apps => core}/Devices.lua (100%) rename {apps => core}/Events.lua (100%) rename {apps => core}/Music.lua (100%) rename {apps => core}/Script.lua (100%) rename {apps => core}/Turtles.lua (100%) rename {apis => core/apis}/chestAdapter.lua (100%) rename {apis => core/apis}/chestAdapter18.lua (100%) rename {apis => core/apis}/controllerAdapter.lua (100%) rename {apis => core/apis}/inventoryAdapter.lua (100%) rename {apis => core/apis}/itemDB.lua (100%) rename {apis => core/apis}/meAdapter.lua (100%) rename {apis => core/apis}/meAdapter18.lua (100%) rename {apis => core/apis}/message.lua (100%) rename {apis => core/apis}/nameDB.lua (95%) rename {apis => core/apis}/refinedAdapter.lua (100%) rename {apis => core/apis}/tableDB.lua (100%) rename {apis => core/apis}/turtle/craft.lua (99%) rename {apis => core/apis}/turtle/crafting.lua (100%) rename {apis => core/apis}/turtle/home.lua (100%) rename {apis => core/apis}/turtle/level.lua (100%) rename {apps => core}/debug.lua (100%) rename {apps => core}/edit.lua (100%) rename {etc => core/etc}/apps/opus-apps.db (100%) rename {etc => core/etc}/names/appliedenergistics2.json (100%) rename {etc => core/etc}/names/computercraft.json (100%) rename {etc => core/etc}/names/enderio.json (100%) rename {etc => core/etc}/names/exnihiloadscensio.json (100%) rename {etc => core/etc}/names/extrautils2.json (100%) rename {etc => core/etc}/names/ironchest.json (100%) rename {etc => core/etc}/names/minecraft.json (100%) rename {etc => core/etc}/names/rftools.json (100%) rename {etc => core/etc}/names/storagedrawers.json (100%) rename {etc => core/etc}/names/tconstruct.json (100%) rename {etc => core/etc}/recipes/minecraft.db (100%) rename {etc => core/etc}/scripts/abort (100%) rename {etc => core/etc}/scripts/follow (100%) rename {etc => core/etc}/scripts/goHome (100%) rename {etc => core/etc}/scripts/moveTo (100%) rename {etc => core/etc}/scripts/obsidian (100%) rename {etc => core/etc}/scripts/reboot (100%) rename {etc => core/etc}/scripts/setHome (100%) rename {etc => core/etc}/scripts/shutdown (100%) rename {etc => core/etc}/scripts/summon (100%) rename {apps => core}/logMonitor.lua (100%) rename {apps => core}/mirror.lua (100%) rename {apps => core}/mirrorClient.lua (100%) rename {apps => core}/mirrorHost.lua (100%) rename {apps => core}/mwm.lua (100%) rename {apps => core}/namedb.lua (100%) rename {apps => core}/persist.lua (100%) rename {apps => core}/recorder.lua (100%) rename {apps => core}/shapes.lua (100%) rename {apps => core}/t.lua (100%) rename {apps => core}/termShare.lua (100%) rename {apps => core}/trace.lua (100%) create mode 100644 farms/.package rename {apps => farms}/cows.lua (100%) rename {apps => farms}/farm.lua (100%) rename {apps => farms}/treefarm.lua (100%) create mode 100644 forestry/.package create mode 100644 forestry/alveary.lua create mode 100644 forestry/beeInfo.lua create mode 100644 forestry/filing.lua create mode 100644 forestry/serums.lua create mode 100644 glasses/.package rename {apis => glasses/apis}/shatter.lua (99%) rename {apps => glasses}/glassesDriver.lua (100%) create mode 100644 miners/.package rename {apps => miners}/scanningMiner.lua (100%) rename {apps => miners}/simpleMiner.lua (100%) create mode 100644 neural/.package create mode 100644 neural/apis/neural/angle.lua create mode 100644 neural/apis/neural/interface.lua create mode 100644 neural/apis/neural/mobs.lua create mode 100644 neural/autorun/6.neural.lua create mode 100644 neural/etc/apps/neural-apps.db create mode 100644 neural/mobfollow.lua create mode 100644 neural/neuralFight.lua create mode 100644 neural/neuralFly.lua create mode 100644 neural/neuralRecorder.lua create mode 100644 neural/neuralRemote.lua create mode 100644 neural/neuralReplay.lua create mode 100644 neural/neuralSword.lua create mode 100644 neural/robotWars.lua create mode 100644 neural/shootMob.lua create mode 100644 neural/shootingGallery.lua create mode 100644 pickup/.package rename {apps => pickup}/pickup.lua (100%) rename {apps => pickup}/pickupRemote.lua (100%) create mode 100644 recipeBook/.package rename {etc => recipeBook/etc}/recipes/appliedenergistics2.db (100%) rename {etc => recipeBook/etc}/recipes/botania.db (100%) rename {etc => recipeBook/etc}/recipes/computercraft.db (100%) rename {etc => recipeBook/etc}/recipes/enderio.db (100%) rename {etc => recipeBook/etc}/recipes/exnihilo.db (100%) rename {etc => recipeBook/etc}/recipes/extrautils2.db (100%) rename {etc => recipeBook/etc}/recipes/ironchest.db (100%) rename {etc => recipeBook/etc}/recipes/mekanism.db (100%) rename {etc => recipeBook/etc}/recipes/mysticalagriculture.db (100%) rename {etc => recipeBook/etc}/recipes/rftools.db (100%) rename {etc => recipeBook/etc}/recipes/skyblockinfinity.db (100%) rename {etc => recipeBook/etc}/recipes/storagedrawers.db (100%) rename {etc => recipeBook/etc}/recipes/tconstruct.db (100%) rename {apps => recipeBook}/recipeBook.lua (100%) create mode 100644 storageManager/.package rename {apps => storageManager}/Crafter.lua (100%) rename {apps => storageManager}/chestManager.lua (100%) rename {apps => storageManager}/levelEmitter.lua (100%) rename {apps => storageManager}/storageActivity.lua (100%) diff --git a/builder/.package b/builder/.package new file mode 100644 index 0000000..308669a --- /dev/null +++ b/builder/.package @@ -0,0 +1,9 @@ +{ + required = { + 'opus-develop-1.8', + }, + title = 'Schematic Builder', + repository = 'kepler155c/opus-apps/develop1.8/builder', + description = [[ ... ]], + licence = 'MIT', +} diff --git a/apis/base64.lua b/builder/apis/base64.lua similarity index 100% rename from apis/base64.lua rename to builder/apis/base64.lua diff --git a/apis/builder/blocks.lua b/builder/apis/builder/blocks.lua similarity index 100% rename from apis/builder/blocks.lua rename to builder/apis/builder/blocks.lua diff --git a/apis/builder/builder.lua b/builder/apis/builder/builder.lua similarity index 100% rename from apis/builder/builder.lua rename to builder/apis/builder/builder.lua diff --git a/apis/builder/commands.lua b/builder/apis/builder/commands.lua similarity index 100% rename from apis/builder/commands.lua rename to builder/apis/builder/commands.lua diff --git a/apis/builder/schematic.lua b/builder/apis/builder/schematic.lua similarity index 100% rename from apis/builder/schematic.lua rename to builder/apis/builder/schematic.lua diff --git a/apis/builder/turtle.lua b/builder/apis/builder/turtle.lua similarity index 100% rename from apis/builder/turtle.lua rename to builder/apis/builder/turtle.lua diff --git a/apis/deflatelua.lua b/builder/apis/deflatelua.lua similarity index 100% rename from apis/deflatelua.lua rename to builder/apis/deflatelua.lua diff --git a/apps/base64dl.lua b/builder/base64dl.lua similarity index 100% rename from apps/base64dl.lua rename to builder/base64dl.lua diff --git a/apps/builder.lua b/builder/builder.lua similarity index 100% rename from apps/builder.lua rename to builder/builder.lua diff --git a/apps/supplier.lua b/builder/supplier.lua similarity index 100% rename from apps/supplier.lua rename to builder/supplier.lua diff --git a/core/.package b/core/.package new file mode 100644 index 0000000..5ad65a1 --- /dev/null +++ b/core/.package @@ -0,0 +1,9 @@ +{ + required = { + 'opus-develop-1.8', + }, + title = 'Core apps and apis', + repository = 'kepler155c/opus-apps/develop1.8/core', + description = [[ ... ]], + licence = 'MIT', +} diff --git a/apps/Appstore.lua b/core/Appstore.lua similarity index 100% rename from apps/Appstore.lua rename to core/Appstore.lua diff --git a/apps/Devices.lua b/core/Devices.lua similarity index 100% rename from apps/Devices.lua rename to core/Devices.lua diff --git a/apps/Events.lua b/core/Events.lua similarity index 100% rename from apps/Events.lua rename to core/Events.lua diff --git a/apps/Music.lua b/core/Music.lua similarity index 100% rename from apps/Music.lua rename to core/Music.lua diff --git a/apps/Script.lua b/core/Script.lua similarity index 100% rename from apps/Script.lua rename to core/Script.lua diff --git a/apps/Turtles.lua b/core/Turtles.lua similarity index 100% rename from apps/Turtles.lua rename to core/Turtles.lua diff --git a/apis/chestAdapter.lua b/core/apis/chestAdapter.lua similarity index 100% rename from apis/chestAdapter.lua rename to core/apis/chestAdapter.lua diff --git a/apis/chestAdapter18.lua b/core/apis/chestAdapter18.lua similarity index 100% rename from apis/chestAdapter18.lua rename to core/apis/chestAdapter18.lua diff --git a/apis/controllerAdapter.lua b/core/apis/controllerAdapter.lua similarity index 100% rename from apis/controllerAdapter.lua rename to core/apis/controllerAdapter.lua diff --git a/apis/inventoryAdapter.lua b/core/apis/inventoryAdapter.lua similarity index 100% rename from apis/inventoryAdapter.lua rename to core/apis/inventoryAdapter.lua diff --git a/apis/itemDB.lua b/core/apis/itemDB.lua similarity index 100% rename from apis/itemDB.lua rename to core/apis/itemDB.lua diff --git a/apis/meAdapter.lua b/core/apis/meAdapter.lua similarity index 100% rename from apis/meAdapter.lua rename to core/apis/meAdapter.lua diff --git a/apis/meAdapter18.lua b/core/apis/meAdapter18.lua similarity index 100% rename from apis/meAdapter18.lua rename to core/apis/meAdapter18.lua diff --git a/apis/message.lua b/core/apis/message.lua similarity index 100% rename from apis/message.lua rename to core/apis/message.lua diff --git a/apis/nameDB.lua b/core/apis/nameDB.lua similarity index 95% rename from apis/nameDB.lua rename to core/apis/nameDB.lua index f21851e..c068c14 100644 --- a/apis/nameDB.lua +++ b/core/apis/nameDB.lua @@ -3,7 +3,7 @@ local TableDB = require('tableDB') local fs = _G.fs -local NAME_DIR = '/usr/etc/names' +local NAME_DIR = '/packages/core/etc/names' local nameDB = TableDB() diff --git a/apis/refinedAdapter.lua b/core/apis/refinedAdapter.lua similarity index 100% rename from apis/refinedAdapter.lua rename to core/apis/refinedAdapter.lua diff --git a/apis/tableDB.lua b/core/apis/tableDB.lua similarity index 100% rename from apis/tableDB.lua rename to core/apis/tableDB.lua diff --git a/apis/turtle/craft.lua b/core/apis/turtle/craft.lua similarity index 99% rename from apis/turtle/craft.lua rename to core/apis/turtle/craft.lua index 1b7b676..0d5c017 100644 --- a/apis/turtle/craft.lua +++ b/core/apis/turtle/craft.lua @@ -4,7 +4,7 @@ local Util = require('util') local fs = _G.fs local turtle = _G.turtle -local RECIPES_DIR = 'usr/etc/recipes' +local RECIPES_DIR = 'packages/core/etc/recipes' local USER_RECIPES = 'usr/config/recipes.db' local Craft = { } diff --git a/apis/turtle/crafting.lua b/core/apis/turtle/crafting.lua similarity index 100% rename from apis/turtle/crafting.lua rename to core/apis/turtle/crafting.lua diff --git a/apis/turtle/home.lua b/core/apis/turtle/home.lua similarity index 100% rename from apis/turtle/home.lua rename to core/apis/turtle/home.lua diff --git a/apis/turtle/level.lua b/core/apis/turtle/level.lua similarity index 100% rename from apis/turtle/level.lua rename to core/apis/turtle/level.lua diff --git a/apps/debug.lua b/core/debug.lua similarity index 100% rename from apps/debug.lua rename to core/debug.lua diff --git a/apps/edit.lua b/core/edit.lua similarity index 100% rename from apps/edit.lua rename to core/edit.lua diff --git a/etc/apps/opus-apps.db b/core/etc/apps/opus-apps.db similarity index 100% rename from etc/apps/opus-apps.db rename to core/etc/apps/opus-apps.db diff --git a/etc/names/appliedenergistics2.json b/core/etc/names/appliedenergistics2.json similarity index 100% rename from etc/names/appliedenergistics2.json rename to core/etc/names/appliedenergistics2.json diff --git a/etc/names/computercraft.json b/core/etc/names/computercraft.json similarity index 100% rename from etc/names/computercraft.json rename to core/etc/names/computercraft.json diff --git a/etc/names/enderio.json b/core/etc/names/enderio.json similarity index 100% rename from etc/names/enderio.json rename to core/etc/names/enderio.json diff --git a/etc/names/exnihiloadscensio.json b/core/etc/names/exnihiloadscensio.json similarity index 100% rename from etc/names/exnihiloadscensio.json rename to core/etc/names/exnihiloadscensio.json diff --git a/etc/names/extrautils2.json b/core/etc/names/extrautils2.json similarity index 100% rename from etc/names/extrautils2.json rename to core/etc/names/extrautils2.json diff --git a/etc/names/ironchest.json b/core/etc/names/ironchest.json similarity index 100% rename from etc/names/ironchest.json rename to core/etc/names/ironchest.json diff --git a/etc/names/minecraft.json b/core/etc/names/minecraft.json similarity index 100% rename from etc/names/minecraft.json rename to core/etc/names/minecraft.json diff --git a/etc/names/rftools.json b/core/etc/names/rftools.json similarity index 100% rename from etc/names/rftools.json rename to core/etc/names/rftools.json diff --git a/etc/names/storagedrawers.json b/core/etc/names/storagedrawers.json similarity index 100% rename from etc/names/storagedrawers.json rename to core/etc/names/storagedrawers.json diff --git a/etc/names/tconstruct.json b/core/etc/names/tconstruct.json similarity index 100% rename from etc/names/tconstruct.json rename to core/etc/names/tconstruct.json diff --git a/etc/recipes/minecraft.db b/core/etc/recipes/minecraft.db similarity index 100% rename from etc/recipes/minecraft.db rename to core/etc/recipes/minecraft.db diff --git a/etc/scripts/abort b/core/etc/scripts/abort similarity index 100% rename from etc/scripts/abort rename to core/etc/scripts/abort diff --git a/etc/scripts/follow b/core/etc/scripts/follow similarity index 100% rename from etc/scripts/follow rename to core/etc/scripts/follow diff --git a/etc/scripts/goHome b/core/etc/scripts/goHome similarity index 100% rename from etc/scripts/goHome rename to core/etc/scripts/goHome diff --git a/etc/scripts/moveTo b/core/etc/scripts/moveTo similarity index 100% rename from etc/scripts/moveTo rename to core/etc/scripts/moveTo diff --git a/etc/scripts/obsidian b/core/etc/scripts/obsidian similarity index 100% rename from etc/scripts/obsidian rename to core/etc/scripts/obsidian diff --git a/etc/scripts/reboot b/core/etc/scripts/reboot similarity index 100% rename from etc/scripts/reboot rename to core/etc/scripts/reboot diff --git a/etc/scripts/setHome b/core/etc/scripts/setHome similarity index 100% rename from etc/scripts/setHome rename to core/etc/scripts/setHome diff --git a/etc/scripts/shutdown b/core/etc/scripts/shutdown similarity index 100% rename from etc/scripts/shutdown rename to core/etc/scripts/shutdown diff --git a/etc/scripts/summon b/core/etc/scripts/summon similarity index 100% rename from etc/scripts/summon rename to core/etc/scripts/summon diff --git a/apps/logMonitor.lua b/core/logMonitor.lua similarity index 100% rename from apps/logMonitor.lua rename to core/logMonitor.lua diff --git a/apps/mirror.lua b/core/mirror.lua similarity index 100% rename from apps/mirror.lua rename to core/mirror.lua diff --git a/apps/mirrorClient.lua b/core/mirrorClient.lua similarity index 100% rename from apps/mirrorClient.lua rename to core/mirrorClient.lua diff --git a/apps/mirrorHost.lua b/core/mirrorHost.lua similarity index 100% rename from apps/mirrorHost.lua rename to core/mirrorHost.lua diff --git a/apps/mwm.lua b/core/mwm.lua similarity index 100% rename from apps/mwm.lua rename to core/mwm.lua diff --git a/apps/namedb.lua b/core/namedb.lua similarity index 100% rename from apps/namedb.lua rename to core/namedb.lua diff --git a/apps/persist.lua b/core/persist.lua similarity index 100% rename from apps/persist.lua rename to core/persist.lua diff --git a/apps/recorder.lua b/core/recorder.lua similarity index 100% rename from apps/recorder.lua rename to core/recorder.lua diff --git a/apps/shapes.lua b/core/shapes.lua similarity index 100% rename from apps/shapes.lua rename to core/shapes.lua diff --git a/apps/t.lua b/core/t.lua similarity index 100% rename from apps/t.lua rename to core/t.lua diff --git a/apps/termShare.lua b/core/termShare.lua similarity index 100% rename from apps/termShare.lua rename to core/termShare.lua diff --git a/apps/trace.lua b/core/trace.lua similarity index 100% rename from apps/trace.lua rename to core/trace.lua diff --git a/farms/.package b/farms/.package new file mode 100644 index 0000000..8fd8d66 --- /dev/null +++ b/farms/.package @@ -0,0 +1,9 @@ +{ + required = { + 'opus-develop-1.8', + }, + title = 'Programs for farming resources', + repository = 'kepler155c/opus-apps/develop1.8/farms', + description = [[ ... ]], + licence = 'MIT', +} diff --git a/apps/cows.lua b/farms/cows.lua similarity index 100% rename from apps/cows.lua rename to farms/cows.lua diff --git a/apps/farm.lua b/farms/farm.lua similarity index 100% rename from apps/farm.lua rename to farms/farm.lua diff --git a/apps/treefarm.lua b/farms/treefarm.lua similarity index 100% rename from apps/treefarm.lua rename to farms/treefarm.lua diff --git a/forestry/.package b/forestry/.package new file mode 100644 index 0000000..efff75c --- /dev/null +++ b/forestry/.package @@ -0,0 +1,9 @@ +{ + required = { + 'opus-develop-1.8', + }, + title = 'Forestry mod applications', + repository = 'kepler155c/opus-apps/develop1.8/forestry', + description = [[ ... ]], + licence = 'MIT', +} diff --git a/forestry/alveary.lua b/forestry/alveary.lua new file mode 100644 index 0000000..b25a701 --- /dev/null +++ b/forestry/alveary.lua @@ -0,0 +1,106 @@ +_G.requireInjector(_ENV) + +local Event = require('event') +local UI = require('ui') + +redstone.setBundledOutput('bottom', 0) + +local function regulate(humidity, heat) + local heater = heat == 'Up 1' or heat == 'Both 1' + local lava = heat == 'Both 1' + local water = humidity == 'Up 1' + + local c = colors.combine( + lava and colors.green or 0, + heater and colors.red or 0, + water and colors.blue or 0) + + redstone.setBundledOutput('bottom', c) +end + +function create(alveary, terminal) + local window = UI.Window({ + alveary = alveary, + parent = UI.Device({ + device = terminal, + textScale = 0.5, + backgroundColor = colors.green + }), + progressBar = UI.ProgressBar({ + y = 3, + x = 2, ex = -2, + }), +--[[ + heater = UI.Button { + x = 2, y = -2, width = 7, + text = 'heater', + }, + humidifier = UI.Button { + x = 2, y = -4, + text = 'Humidify', + }, + dehumidifier = UI.Button { + x = 2, y = -6, + text = 'Dehumidify', + }, +--]] + }) + + function window:draw() + + local queen = self.alveary.getQueen() + if not queen then + self:clear(colors.black) + regulate() + else + self.backgroundColor = self.alveary.canBreed() and colors.green or colors.red + self:clear() + local percDone = 100 - math.floor(queen.health * 100 / queen.maxHealth) + if not queen.canSpawn then + percDone = 0 + end + self.progressBar.value = percDone + --self.progressBar:draw() + for _,c in pairs(self.children) do + c:draw() + end + + self:centeredWrite(2, queen.displayName) + self:centeredWrite(4, percDone .. '%') + self:write(1, 6, 'Generation: ' .. queen.generation) + self:setCursorPos(1, 7) + if queen.active then + regulate( + queen.active.humidityTolerance, + queen.active.temperatureTolerance) + + if queen.active.flowerProvider ~= 'Flowers' then + self:print(queen.active.flowerProvider .. '\n') + end + if queen.active.effect ~= 'None' then + self:print('Effect: ' .. queen.active.effect) + end + else + self:print('(pure)') + end + end + end + + return window +end + +local pages = { + create(device.items, device.monitor), + --create(device.items_6, device.monitor_22), + --create(device.items_5, device.monitor_21), +} + +Event.onInterval(5, function() + for _,v in pairs(pages) do + v:draw() + v:sync() + end +end) + +UI:pullEvents() + diff --git a/forestry/beeInfo.lua b/forestry/beeInfo.lua new file mode 100644 index 0000000..7a4fdfd --- /dev/null +++ b/forestry/beeInfo.lua @@ -0,0 +1,176 @@ +_G.requireInjector(_ENV) + +local Event = require('event') +local UI = require('ui') +local Util = require('util') + +local chest = peripheral.wrap('bottom') + +local data +local monitor = UI.Device({ + deviceType = 'monitor', + textScale = .5 +}) + +UI:setDefaultDevice(monitor) + +local breedingPage = UI.Page({ + titleBar = UI.TitleBar(), + grid = UI.Grid({ + columns = { + { heading = ' ', key = 'chance' }, + { heading = 'Princess', key = 'princess', }, + { heading = 'Drone', key = 'drone' }, + { heading = 'Result', key = 'result', }, + }, + y = 2, ey = -8, + sortColumn = 'result', + autospace = true + }), + specialConditions = UI.Window({ + backgroundColor = colors.red, + y = -7, + height = 2 + }), + buttons = UI.Window({ + y = monitor.height - 4, + width = monitor.width, + height = 5, + backgroundColor = colors.gray, + prevButton = UI.Button({ + event = 'previous', + x = 2, + y = 2, + height = 3, + width = 5, + text = ' < ' + }), + resetButton = UI.Button({ + event = 'clear', + x = 8, + y = 2, + height = 3, + width = monitor.width - 14, + text = 'Clear' + }), + nextButton = UI.Button({ + event = 'next', + x = monitor.width - 5, + y = 2, + height = 3, + width = 5, + text = ' > ' + }) + }) +}) + +function breedingPage:getBreedingData() + self.grid.values = { } + local stacks = chest.getAllStacks(false) + local stack = stacks[1] + self.titleBar.title = stack.individual.displayName + if stack.individual.active then + end + for _,d in pairs(data) do + if d.allele1 == stack.individual.displayName or + d.allele2 == stack.individual.displayName then + local ind = '' + if d.specialConditions then + ind = '*' + end + table.insert(self.grid.values, { + princess = d.allele1 .. ind, + drone = d.allele2, + result = d.result, + chance = d.chance .. '%', + specialConditions = d.specialConditions + }) + end + end + self.grid.index = 1 + self.grid:adjustWidth() + self.grid:update() + self:draw() + self:sync() +end + +function breedingPage.specialConditions:draw() + local selected = self.parent.grid:getSelected() + if selected and selected.specialConditions then + local sc = '' + if selected.specialConditions then + for _,v in ipairs(selected.specialConditions) do + if sc ~= '' then + sc = sc .. ', ' + end + sc = sc .. v + end + end + self:clear() + self:setCursorPos(2, 1) + self:print(sc) + else + self:clear(colors.red) + end +end + +function breedingPage.grid:draw() + UI.Grid.draw(self) + self.parent.specialConditions:draw() +end + +function breedingPage:eventHandler(event) + if event.type == 'next' then + self.grid:setPage(self.grid:getPage() + 1) + elseif event.type == 'previous' then + self.grid:setPage(self.grid:getPage() - 1) + elseif event.type == 'clear' then + self.grid:setTable({}) + self.grid:draw() + elseif event.type == 'grid_focus_row' then + self.specialConditions:draw() + else + return UI.Page.eventHandler(self, event) + end + return false +end + +Event.on('turtle_inventory', function() + local slot = turtle.selectSlotWithQuantity(1) + + if slot then + turtle.dropDown() + breedingPage:getBreedingData() + turtle.suckDown() + turtle.drop() + end + +end) + +if not fs.exists('.bee.data') then + local p = peripheral.wrap("back") + local data = p.getBeeBreedingData() + local t = { } + for _,d in pairs(data) do + d = Util.shallowCopy(d) + if type(d.specialConditions) == 'string' then + if d.specialConditions == '[]' then + d.specialConditions = '' + end + end + if #d.specialConditions == 0 then + d.specialConditions = nil + else + d.specialConditions = Util.shallowCopy(d.specialConditions) + end + table.insert(t, d) + end + Util.writeTable('.bee.data', t) +else + data = Util.readTable('.bee.data') +end + +UI:setPage(breedingPage) + +UI:pullEvents() + diff --git a/forestry/filing.lua b/forestry/filing.lua new file mode 100644 index 0000000..17423fd --- /dev/null +++ b/forestry/filing.lua @@ -0,0 +1,34 @@ +_G.requireInjector(_ENV) + +local Event = require('event') +local Util = require('util') + +local chest = peripheral.wrap('top') + +function getOpenChestSlot(stacks) + for i = 1, chest.getInventorySize() do + if not stacks[i] then + return i + end + end +end + +Event.on('turtle_inventory', function() + for i = 1, 16 do + if turtle.getItemCount(i) > 0 then + chest.pullItem('down', i, 1) + os.sleep(.5) + local stacks = chest.getAllStacks(false) + local _,slot = Util.find(stacks, 'qty', 2) + if slot then + print('Duplicate') + chest.pushItem('north', slot, 1) + else + print('New Serum') + end + end + end +end) + +Event.pullEvents() + diff --git a/forestry/serums.lua b/forestry/serums.lua new file mode 100644 index 0000000..7eb3e1e --- /dev/null +++ b/forestry/serums.lua @@ -0,0 +1,34 @@ +_G.requireInjector(_ENV) + +local Event = require('event') +local Util = require('util') + +local chest = peripheral.wrap('top') + +function getOpenChestSlot(stacks) + for i = 1, chest.getInventorySize() do + if not stacks[i] then + return i + end + end +end + +Event.on('turtle_inventory', function() + for i = 1, 16 do + if turtle.getItemCount(i) > 0 then + local stacks = chest.getAllStacks(false) + local slot = getOpenChestSlot(stacks) + chest.pullItemIntoSlot('down', i, 1, slot) + local serum = chest.getStackInSlot(slot) + if Util.find(stacks, 'nbt_hash', serum.nbt_hash) then + print('Duplicate') + chest.pushItem('north', slot, 1) + else + print('New Serum') + end + end + end + +end) + +Event.pullEvents() diff --git a/glasses/.package b/glasses/.package new file mode 100644 index 0000000..00581da --- /dev/null +++ b/glasses/.package @@ -0,0 +1,9 @@ +{ + required = { + 'opus-develop-1.8', + }, + title = 'Plethora overlay glasses support', + repository = 'kepler155c/opus-apps/develop1.8/glasses', + description = [[ ... ]], + licence = 'MIT', +} diff --git a/apis/shatter.lua b/glasses/apis/shatter.lua similarity index 99% rename from apis/shatter.lua rename to glasses/apis/shatter.lua index ed741dc..6c58810 100644 --- a/apis/shatter.lua +++ b/glasses/apis/shatter.lua @@ -440,7 +440,6 @@ out.setTextScale = function(scale) end if textScale ~= scale then local factor = textScale/scale -debug({ scale, textScale, factor, tx*factor, ty*factor }) textScale = scale resize(tx*factor, ty*factor) end diff --git a/apps/glassesDriver.lua b/glasses/glassesDriver.lua similarity index 100% rename from apps/glassesDriver.lua rename to glasses/glassesDriver.lua diff --git a/milo/.package b/milo/.package index 737e1e0..62e9262 100644 --- a/milo/.package +++ b/milo/.package @@ -3,10 +3,7 @@ 'opus-develop-1.8', }, title = 'Inventory manager for Opus OS', - description = [[ -Unstable Branch -]], + repository = 'kepler155c/opus-apps/develop1.8/milo', + description = [[ ... ]], licence = 'MIT', - --location = '', - --mount = 'packages/opus-neural gitfs kepler155c/opus-neural/master', } diff --git a/milo/apis/turtle/craft.lua b/milo/apis/turtle/craft.lua index 22b9481..d05dd4a 100644 --- a/milo/apis/turtle/craft.lua +++ b/milo/apis/turtle/craft.lua @@ -11,7 +11,7 @@ local Craft = { STATUS_ERROR = 'error', STATUS_SUCCESS = 'success', - RECIPES_DIR = 'usr/etc/recipes', + RECIPES_DIR = 'packages/core/etc/recipes', USER_RECIPES = 'usr/config/recipes.db', MACHINE_LOOKUP = 'usr/config/machine_crafting.db', } diff --git a/miners/.package b/miners/.package new file mode 100644 index 0000000..b872f56 --- /dev/null +++ b/miners/.package @@ -0,0 +1,9 @@ +{ + required = { + 'opus-develop-1.8', + }, + title = 'Turtle mining programs', + repository = 'kepler155c/opus-apps/develop1.8/miners', + description = [[ ... ]], + licence = 'MIT', +} diff --git a/apps/scanningMiner.lua b/miners/scanningMiner.lua similarity index 100% rename from apps/scanningMiner.lua rename to miners/scanningMiner.lua diff --git a/apps/simpleMiner.lua b/miners/simpleMiner.lua similarity index 100% rename from apps/simpleMiner.lua rename to miners/simpleMiner.lua diff --git a/neural/.package b/neural/.package new file mode 100644 index 0000000..6af5b15 --- /dev/null +++ b/neural/.package @@ -0,0 +1,9 @@ +{ + required = { + 'opus-develop-1.8', + }, + title = 'Programs for the neural interface', + repository = 'kepler155c/opus-apps/develop1.8/neural', + description = [[ ... ]], + licence = 'MIT', +} diff --git a/neural/apis/neural/angle.lua b/neural/apis/neural/angle.lua new file mode 100644 index 0000000..f378a31 --- /dev/null +++ b/neural/apis/neural/angle.lua @@ -0,0 +1,11 @@ +local Angle = { } + +function Angle.towards(x, y, z) + return math.deg(math.atan2(-x, z)), 0 +end + +function Angle.away(x, y, z) + return math.deg(math.atan2(x, -z)), 0 +end + +return Angle diff --git a/neural/apis/neural/interface.lua b/neural/apis/neural/interface.lua new file mode 100644 index 0000000..ea36793 --- /dev/null +++ b/neural/apis/neural/interface.lua @@ -0,0 +1,139 @@ +local Interface = { } + +local Angle = require('neural.angle') +local Util = require('util') + +local device = _G.device +local os = _G.os + +local ni = device.neuralInterface or { } +for k,v in pairs(ni) do + Interface[k] = v +end + +local function yap(pt) + local x, y, z = pt.x, pt.y + 1, pt.z + local pitch = -math.atan2(y, math.atan2(-(x - .5), z - .5)) + local yaw = math.deg(math.atan2(-(x - .5), z - .5)) + + return math.deg(yaw), math.deg(pitch) +end + +function Interface.launchTo(pt, strength) + local yaw = math.deg(math.atan2(pt.x, -pt.z)) + if not strength then + local dist = math.sqrt( + math.pow(pt.x, 2) + + math.pow(pt.z, 2)) + strength = math.sqrt(math.max(32, dist) / 3) + debug(strength) + end + Interface.launch(yaw, 225, strength or 1) +end + +function Interface.dropArmor() + for i = 3, 5 do + Interface.unequip(i) + end +end + +function Interface.walkTo(pt) + local s, m = ni.walk(pt.x, pt.y, pt.z) + if not s then + _G.printError(m) + end + os.sleep(.05) + while ni.isWalking() do + os.sleep(0) + end +end + +-- flatten equipment functions +function Interface.getEquipmentList() + local l = Interface.getEquipment and Interface.getEquipment().list() or { } + + for k, v in pairs(l) do + v.slot = k + end + + return l +end + +function Interface.equip(slot) + return Interface.getEquipment and Interface.getEquipment().suck(slot) or 0 +end + +function Interface.unequip(slot) + return Interface.getEquipment and Interface.getEquipment().drop(slot) +end + +function Interface.getUniqueNames() + local t = { } + for _,v in pairs(Interface.sense()) do + t[v.name] = v.name + end + return Util.transpose(t) +end + +function Interface.lookAt(pt) + local yaw, pitch = Angle.towards(pt.x - .5, pt.y + 1, pt.z - .5) + return Interface.look(yaw, pitch) +end + +function Interface.shootAt(entity, strength) + Interface.lookAt(entity) + return Interface.shoot(strength or 1) +end + +function Interface.shootAt2(entity, strength) + local x, z = entity.x - .5, entity.z - .5 + + local function quad(a, b, c) + if math.abs(a) < 1e-6 then + if math.abs(b) < 1e-6 then + return math.abs(c) < 1e-6 and 0, 0 + else + return -c/b, -c/b + end + else + local disc = b*b - 4*a*c + if disc >= 0 then + disc = math.sqrt(disc) + a = 2*a + return (-b-disc)/a, (-b+disc)/a + end + end + end + + local v = .025 -- velocity of arrow + + local tvx = entity.motionX + local tvz = entity.motionZ + local a = tvx*tvx + tvz*tvz - v*v + local b = 2 * (tvx * x + tvz * z) + local c = x * x + z * z + local t0, t1 = quad(a, b, c) + if t0 then + local t = math.min(t0, t1) + if t < 0 then + t = math.max(t0, t1) + end + if t > 0 then + --Util.print({ x, t, tvx, x + tvx * t }) + x = x + tvx * t + z = z + tvz * t + end + end + + local yaw = math.deg(math.atan2(-(x - .5), z - .5)) + local pitch = -math.deg(math.atan2(entity.y, math.sqrt(x * x + z * z))) + + Interface.look(yaw, pitch) -- pitch is broken + return Interface.shoot(strength or 1) +end + +function Interface.setStatus(s) + ni.status = s +end + +return Interface diff --git a/neural/apis/neural/mobs.lua b/neural/apis/neural/mobs.lua new file mode 100644 index 0000000..88b6cf8 --- /dev/null +++ b/neural/apis/neural/mobs.lua @@ -0,0 +1,22 @@ +local Mobs = { } + +local hostiles = { + ancient_golem = true, + BabySkeleton = true, + BabyZombie = true, + Bat = true, + Creeper = true, + Husk = true, + Skeleton = true, + Slime = true, + Spider = true, + Witch = true, + Zombie = true, + ZombieVillager = true, +} + +function Mobs.getNames() + return hostiles +end + +return Mobs diff --git a/neural/autorun/6.neural.lua b/neural/autorun/6.neural.lua new file mode 100644 index 0000000..10ab439 --- /dev/null +++ b/neural/autorun/6.neural.lua @@ -0,0 +1,24 @@ +_G.requireInjector(_ENV) + +local GPS = require('gps') + +local kernel = _G.kernel + +kernel.onDeviceAttach('neuralInterface', function(dev) + dev.goTo = function(x, _, z) + local pt = GPS.locate(2) + if pt then + return pcall(function() + local gpt = { + x = x - pt.x, + y = 0, + z = z - pt.z, + } + gpt.x = math.min(math.max(gpt.x, -15), 15) + gpt.z = math.min(math.max(gpt.z, -15), 15) + return dev.walk(gpt.x, gpt.y, gpt.z) + end) + end + return false, 'No GPS' + end +end) diff --git a/neural/etc/apps/neural-apps.db b/neural/etc/apps/neural-apps.db new file mode 100644 index 0000000..3004314 --- /dev/null +++ b/neural/etc/apps/neural-apps.db @@ -0,0 +1,22 @@ +{ + [ "shootingGallery" ] = { + title = "Gallery", + category = "Neural", + run = "shootingGallery.lua", + }, + [ "neuralFight" ] = { + title = "Fight", + category = "Neural", + run = "neuralFight.lua", + }, + [ "neuralFly" ] = { + title = "Fly", + category = "Neural", + run = "neuralFly.lua", + }, + [ "neuralRemote" ] = { + title = "Remote", + category = "Neural", + run = "neuralRemote.lua", + }, +} diff --git a/neural/mobfollow.lua b/neural/mobfollow.lua new file mode 100644 index 0000000..5c819fc --- /dev/null +++ b/neural/mobfollow.lua @@ -0,0 +1,49 @@ +_G.requireInjector(_ENV) + +local GPS = require('gps') +local Util = require('util') +local Peripheral = require('peripheral') +local Point = require('point') + +local os = _G.os + +local args = { ... } +local remoteId = args[1] or error('mobFollow ') +local ni = Peripheral.lookup(remoteId .. '://name/neuralInterface') + +if not ni then + error('failed to connect') +end + +local lpt = nil + +while true do + local pt = GPS.locate(2) + + if not pt then + print('No GPS') + else + local gpt = Util.shallowCopy(pt) + if pt and lpt and Point.same(pt, lpt) then + -- havent moved + print('no move') + else + if not lpt then + gpt.x = gpt.x - 2 + else + local dx = lpt.x - pt.x + local dz = lpt.z - pt.z + local angle = math.atan2(dx, dz) + gpt.x = pt.x + 2.5 * math.sin(angle) + gpt.z = pt.z + 2.5 * math.cos(angle) + end + lpt = pt + local s, m = ni.goTo(gpt.x, gpt.y + 1, gpt.z) + if not s then + print(m) + end + end + end + + os.sleep(.5) +end \ No newline at end of file diff --git a/neural/neuralFight.lua b/neural/neuralFight.lua new file mode 100644 index 0000000..0fb4d77 --- /dev/null +++ b/neural/neuralFight.lua @@ -0,0 +1,98 @@ +_G.requireInjector() + +local Angle = require('neural.angle') +local GPS = require('gps') +local Mobs = require('neural.mobs') +local ni = require('neural.interface') +local Point = require('point') +local Util = require('util') + +local os = _G.os + +local RADIUS = 13 +local ROTATION = math.pi / 16 + +local uid = ni.getID and ni.getID() or error('Introspection module is required') +local pos = { x = 0, y = 0, z = 0 } + +local function findTargets() + local l = ni.sense() + table.sort(l, function(e1, e2) + return Point.distance(e1, pos) < Point.distance(e2, pos) + end) + + local targets = { } + for _,v in ipairs(l) do + if v.id ~= uid and Mobs.getNames()[v.name] then + if math.abs(v.y) < 2 and Point.distance(v, pos) < 16 then -- pitch is broken + table.insert(targets, v) + end + end + end + return #targets > 0 and targets +end + +local function shootAt(targets) + for _,target in ipairs(targets) do + target = ni.getMetaByID(target.id) + if target and target.isAlive and Point.distance(target, pos) < 14 then + ni.shootAt(target) + end + end +end + +local potions = Util.filter( + ni.getEquipmentList(), + function(a) + return a.name == 'minecraft:splash_potion' + end) + +local function heal(target) + local hands = { 'main', 'off' } + + if #potions > 0 and ni.getMetaOwner().health < 10 then + local yaw, pitch = Angle.away(target.x - .5, 0, target.z - .5) + ni.look(yaw, pitch) + ni.use(.01, hands[potions[1].slot]) + ni.launch(yaw, pitch, 1) + table.remove(potions, 1) + end +end + +local pt = GPS.locate() + +while true do + local targets = findTargets() + if not targets then + local cpt = GPS.locate() + if Point.distance(pt, cpt) > 2 then + print('walking to starting point') + local s, m = ni.goTo(pt.x, pt.y, pt.z) + Util.print({ s, m }) + os.sleep(.05) + while ni.isWalking() do + os.sleep(0) + end + Util.print('done walking') + end + os.sleep(1) + else + local target = targets[1] + local angle = math.atan2(-target.x, -target.z) + ROTATION + + ni.launchTo({ + x = target.x + RADIUS * math.sin(angle), + y = 0, + z = target.z + RADIUS * math.cos(angle) + }, 1) + os.sleep(.2) + + shootAt(targets) + + heal(target) + + if math.random(1, 3) == 3 then + ROTATION = -ROTATION + end + end +end diff --git a/neural/neuralFly.lua b/neural/neuralFly.lua new file mode 100644 index 0000000..35ae482 --- /dev/null +++ b/neural/neuralFly.lua @@ -0,0 +1,78 @@ +_G.requireInjector(_ENV) + +local ni = require('neural.interface') +local GPS = require('gps') + +local strength = .315 +local delay = .1 + +while ni.getMetaOwner().health < 26 do + print('health: ' .. ni.getMetaOwner().health) + os.sleep(1) +end + +ni.launch(0, 270, 1.5) +os.sleep(.25) + +local pt + +local function fly() + for i = 1, 100 do + os.sleep(1) + if pt then + print(pt.y) + print(strength) + end + end +end + +local function gps() + local lastY = 12 + while true do + pt = GPS.locate() + if pt then + local d = math.abs(lastY - pt.y) + + -- force required to get to lvl 12 + + local motionY = ni.getMetaOwner().motionY +-- print('y: ' .. pt.y) + if pt.y < 12 then + if pt.y > lastY then + --strength = strength + .001 + else + strength = strength + .02 * d + end + elseif pt.y > 12 then + if pt.y > lastY then + strength = strength - .02 * d + else + --strength = strength - .001 + end + end + lastY = pt.y + + -- force required to offset motion + local om = (motionY - 0.138) / 0.8 + + ni.launch(0, 270, strength-motionY) +-- print('strength: ' .. strength) + os.sleep(delay) + end + end +end + +parallel.waitForAny(fly, gps) + +repeat + ni.launch(0, 270, .25) + os.sleep(.1) +until not ni.getMetaOwner().isAirborne + +print('descending') +for i = 1, 50 do + ni.launch(0, 270, .2) + os.sleep(.1) +end + +ni.look(180, 0) diff --git a/neural/neuralRecorder.lua b/neural/neuralRecorder.lua new file mode 100644 index 0000000..eac4da5 --- /dev/null +++ b/neural/neuralRecorder.lua @@ -0,0 +1,88 @@ +_G.requireInjector(_ENV) + +local GPS = require('gps') +local Point = require('point') +local Util = require('util') + +local os = _G.os +local parallel = _G.parallel + +local t = { } +local ni = _G.device.neuralInterface or error('Neural Interface not found') + +if not ni.getID then + error('Missing Introspection Module') +end + +local uid = ni.getID() +local c = os.clock() + +local pt = GPS.locate(3) or error('GPS failed') +local lpt +local me = Util.find(ni.sense(), 'id', uid) + +local function gps() + while true do + me = Util.find(ni.sense(), 'id', uid) + pt = GPS.locate(3) or error('GPS failed') + os.sleep(.3) + print('got gps') + end +end + +local function record() + local timerId = os.startTimer(.1) + repeat + local event, ch = os.pullEvent() + local v + local delay = os.clock() - c + c = os.clock() + --print(event .. ' ' .. tostring(ch)) + if event == 'char' then + print('char ' .. ch) + if ch == ' ' then + v = { + action = 'walk', + x = pt.x, + y = pt.y, + z = pt.z, + pitch = me.pitch, + yaw = me.yaw, + delay = delay, + } + elseif ch == 'u' then + v = { + action = 'use', + x = pt.x, + y = pt.y, + z = pt.z, + pitch = me.pitch, + yaw = me.yaw, + delay = delay, + } + end + elseif event == 'timer' and ch == timerId then + if not lpt or not Point.same(pt, lpt) then + v = { + action = 'walk', + x = pt.x, + y = pt.y, + z = pt.z, + pitch = me.pitch, + yaw = me.yaw, + delay = delay, + } + lpt = pt + end + timerId = os.startTimer(.2) + end + + if v then + Util.print(v) + table.insert(t, v) + end + until event == 'char' and ch == 'q' +end + +parallel.waitForAny(gps, record) +Util.writeTable('neural.tbl', t) diff --git a/neural/neuralRemote.lua b/neural/neuralRemote.lua new file mode 100644 index 0000000..2833681 --- /dev/null +++ b/neural/neuralRemote.lua @@ -0,0 +1,56 @@ +rednet.open("right") +local sensor = peripheral.wrap("back") +local modules = peripheral.wrap("back") +local Ka = peripheral.find("neuralInterface") +local function fire(entity) + local x, y, z = entity.x, entity.y, entity.z + local pitch = -math.atan2(y, math.sqrt(x * x + z * z)) + local yaw = math.atan2(-x, z) + + Ka.look(math.deg(yaw), math.deg(pitch), 5) + Ka.shoot(1) + sleep(0.2) +end +local mobNames = {"Skeleton"} +local mobLookup = {} +for i = 1, #mobNames do + mobLookup[mobNames[i]] = true +end + +function SkeletonShoot() + local mobs = sensor.sense() + local candidates = {} + for i = 1, #mobs do + local mob = mobs[i] + if mobLookup[mob.name] then + candidates[#candidates + 1] = mob + end + end + if #candidates > 0 then + local mob = candidates[math.random(1, #candidates)] + fire(mob) + else + sleep(.1) + end +end + + while true do + local id,message = rednet.receive() + print(tostring(id)..message) + if id == 582 then + if message == "forward" then --W + Ka.walk(1,0,0) + elseif message == "back" then --S + Ka.walk(-1,0,0) + elseif message == "turnLeft" then--A + Ka.walk(0,0,-1) + elseif message == "turnRight" then--D + Ka.walk(0,0,1) + elseif message == "shoot" then--Starts fell program + SkeletonShoot() + end + else + write(" Denied!") + end + end + diff --git a/neural/neuralReplay.lua b/neural/neuralReplay.lua new file mode 100644 index 0000000..b2264ca --- /dev/null +++ b/neural/neuralReplay.lua @@ -0,0 +1,52 @@ +_G.requireInjector(_ENV) + +local GPS = require('gps') +local Util = require('util') + +local os = _G.os +local shell = _ENV.shell + +local args = { ... } +local fileName = args[1] or 'neural.tbl' + +local t = Util.readTable(shell.resolve(fileName)) or error('Unable to read ' .. fileName) +local ni = _G.device.neuralInterface + +local function walkTo(x, y, z) + local pt = GPS.locate(2) + if pt then + local s, m, m2 = pcall(function() + local gpt = { + x = x - pt.x, + y = math.floor(y) - math.floor(pt.y), + z = z - pt.z, + } + gpt.x = math.min(math.max(gpt.x, -30), 30) + gpt.z = math.min(math.max(gpt.z, -30), 30) + return ni.walk(gpt.x, gpt.y, gpt.z) + end) + if not s or not m then + _G.printError(m2 or m) + end + end + os.sleep(.5) + while ni.isWalking() do + os.sleep(0) + end +end + +for _,v in pairs(t) do + Util.print(v) + + --if v.action == 'walk' then + walkTo(v.x, v.y, v.z) + --end + ni.look(v.yaw, v.pitch) + if v.action == 'use' then + ni.use() + os.sleep(2) + end +-- os.sleep(v.delay) + -- os.sleep(2) + -- read() +end diff --git a/neural/neuralSword.lua b/neural/neuralSword.lua new file mode 100644 index 0000000..c269c62 --- /dev/null +++ b/neural/neuralSword.lua @@ -0,0 +1,28 @@ +_G.requireInjector(_ENV) + +local ni = require('neural.interface') +local Util = require('util') + +local os = _G.os + +while true do + local target = Util.find(ni.sense(), 'name', 'joebodo') + if target then + if math.abs(target.x) < 2 and + math.abs(target.z) < 2 then + ni.lookAt(target) + ni.swing() + os.sleep(.5) + else + local angle = math.atan2(-(target.x - .5), target.z - .5) + ni.walkTo({ + x = target.x + 1.5 * math.sin(angle), + y = 0, + z = target.z - 1.5 * math.cos(angle) + }, 1) + end + else + print('no target') + os.sleep(1) + end +end \ No newline at end of file diff --git a/neural/robotWars.lua b/neural/robotWars.lua new file mode 100644 index 0000000..4dc4604 --- /dev/null +++ b/neural/robotWars.lua @@ -0,0 +1,83 @@ +_G.requireInjector() + +local Angle = require('neural.angle') +local ni = require('neural.interface') +local Util = require('util') + +local os = _G.os + +local RADIUS = 13 +local ROTATION = math.pi / 16 + +local args = { ... } +local TARGET = args[1] or error('Syntax: robotWars ') +local uid = ni.getID and ni.getID() or error('Introspection module is required') + +local function findTarget(name) + for _, v in pairs(ni.sense()) do + if v.name == name and v.id ~= uid then + return v + end + end +end + +local function shootAt(entity) + local target = ni.getMetaByID(entity.id) + if target then + ni.shootAt(target) + end +end + +local enemy = findTarget(TARGET) +local potions = Util.filter( + ni.getEquipmentList(), + function(a) + return a.name == 'minecraft:splash_potion' + end) + +if not enemy then + print('Current enemies:') + for _,v in pairs(ni.getUniqueNames()) do + print(v) + end + print() + error('Invalid enemy') +end + +local function heal(target) + local hands = { 'main', 'off' } + + if #potions > 0 and ni.getMetaOwner().health < 10 then + local yaw, pitch = Angle.away({ x = target.x, y = 0, z = target.z }) + ni.look(yaw, pitch) + ni.use(.01, hands[potions[1].slot]) + ni.launch(yaw, pitch, 1) + table.remove(potions, 1) + end +end + +repeat + local target = ni.getMetaByID(enemy.id) + if not target then + print('lost target') + break + end + local angle = math.atan2(-target.x, -target.z) + ROTATION + + ni.launchTo({ + x = target.x + RADIUS * math.sin(angle), + y = 0, + z = target.z + RADIUS * math.cos(angle) + }, 1) + os.sleep(.2) + + shootAt(enemy) + + heal(enemy) + + if math.random(1, 3) == 3 then + ROTATION = -ROTATION + end +until not target.isAlive + +print('Won !') \ No newline at end of file diff --git a/neural/shootMob.lua b/neural/shootMob.lua new file mode 100644 index 0000000..02b4815 --- /dev/null +++ b/neural/shootMob.lua @@ -0,0 +1,31 @@ +_G.requireInjector(_ENV) + +local ni = require('neural.interface') +local uid = ni.getID and ni.getID() or error('Introspection module is required') + +local os = _G.os + +local args = { ... } + +local function findEntity(name) + for _,v in pairs(ni.sense()) do + if v.id ~= uid and v.name == name then + return v + end + end +end + +print('Targets:') +for _,v in pairs(ni.sense()) do + print(v.name) +end + +local target = args[1] or error('specify target name') + +repeat + local entity = findEntity(target) + if entity then + ni.shootAt(entity, 1) + end + os.sleep(.5) +until not entity diff --git a/neural/shootingGallery.lua b/neural/shootingGallery.lua new file mode 100644 index 0000000..6344508 --- /dev/null +++ b/neural/shootingGallery.lua @@ -0,0 +1,48 @@ +_G.requireInjector(_ENV) + +local Mobs = require('neural.mobs') +local ni = require('neural.interface') +local Point = require('point') +local Util = require('util') + +local os = _G.os + +if not ni.look then + error('neuralInterface required') +end + +local uid = ni.getID and ni.getID() or error('Introspection module is required') + +local function findTargets() + local pos = { x = 0, y = 0, z = 0 } + local l = ni.sense() + table.sort(l, function(e1, e2) + return Point.distance(e1, pos) < Point.distance(e2, pos) + end) + + local targets = { } + for _,v in ipairs(l) do + if v.id ~= uid and Mobs.getNames()[v.name] then + if math.abs(v.y) < 2 then -- pitch is broken + table.insert(targets, v) + end + end + end + return #targets > 0 and targets +end + +print('Targets:') +for _,v in pairs(ni.sense()) do + print(v.name) +end + +while true do + local targets = findTargets() + if targets then + for _, entity in ipairs(targets) do +Util.print(entity) + ni.shootAt(entity, 1) + end + end + os.sleep(.5) +end diff --git a/pickup/.package b/pickup/.package new file mode 100644 index 0000000..7174c0b --- /dev/null +++ b/pickup/.package @@ -0,0 +1,9 @@ +{ + required = { + 'opus-develop-1.8', + }, + title = 'Move resources around with turtles', + repository = 'kepler155c/opus-apps/develop1.8/pickup', + description = [[ ... ]], + licence = 'MIT', +} diff --git a/apps/pickup.lua b/pickup/pickup.lua similarity index 100% rename from apps/pickup.lua rename to pickup/pickup.lua diff --git a/apps/pickupRemote.lua b/pickup/pickupRemote.lua similarity index 100% rename from apps/pickupRemote.lua rename to pickup/pickupRemote.lua diff --git a/recipeBook/.package b/recipeBook/.package new file mode 100644 index 0000000..86f1361 --- /dev/null +++ b/recipeBook/.package @@ -0,0 +1,9 @@ +{ + required = { + 'opus-develop-1.8', + }, + title = 'Recipe books for crafting programs', + repository = 'kepler155c/opus-apps/develop1.8/recipeBook', + description = [[ ... ]], + licence = 'MIT', +} diff --git a/etc/recipes/appliedenergistics2.db b/recipeBook/etc/recipes/appliedenergistics2.db similarity index 100% rename from etc/recipes/appliedenergistics2.db rename to recipeBook/etc/recipes/appliedenergistics2.db diff --git a/etc/recipes/botania.db b/recipeBook/etc/recipes/botania.db similarity index 100% rename from etc/recipes/botania.db rename to recipeBook/etc/recipes/botania.db diff --git a/etc/recipes/computercraft.db b/recipeBook/etc/recipes/computercraft.db similarity index 100% rename from etc/recipes/computercraft.db rename to recipeBook/etc/recipes/computercraft.db diff --git a/etc/recipes/enderio.db b/recipeBook/etc/recipes/enderio.db similarity index 100% rename from etc/recipes/enderio.db rename to recipeBook/etc/recipes/enderio.db diff --git a/etc/recipes/exnihilo.db b/recipeBook/etc/recipes/exnihilo.db similarity index 100% rename from etc/recipes/exnihilo.db rename to recipeBook/etc/recipes/exnihilo.db diff --git a/etc/recipes/extrautils2.db b/recipeBook/etc/recipes/extrautils2.db similarity index 100% rename from etc/recipes/extrautils2.db rename to recipeBook/etc/recipes/extrautils2.db diff --git a/etc/recipes/ironchest.db b/recipeBook/etc/recipes/ironchest.db similarity index 100% rename from etc/recipes/ironchest.db rename to recipeBook/etc/recipes/ironchest.db diff --git a/etc/recipes/mekanism.db b/recipeBook/etc/recipes/mekanism.db similarity index 100% rename from etc/recipes/mekanism.db rename to recipeBook/etc/recipes/mekanism.db diff --git a/etc/recipes/mysticalagriculture.db b/recipeBook/etc/recipes/mysticalagriculture.db similarity index 100% rename from etc/recipes/mysticalagriculture.db rename to recipeBook/etc/recipes/mysticalagriculture.db diff --git a/etc/recipes/rftools.db b/recipeBook/etc/recipes/rftools.db similarity index 100% rename from etc/recipes/rftools.db rename to recipeBook/etc/recipes/rftools.db diff --git a/etc/recipes/skyblockinfinity.db b/recipeBook/etc/recipes/skyblockinfinity.db similarity index 100% rename from etc/recipes/skyblockinfinity.db rename to recipeBook/etc/recipes/skyblockinfinity.db diff --git a/etc/recipes/storagedrawers.db b/recipeBook/etc/recipes/storagedrawers.db similarity index 100% rename from etc/recipes/storagedrawers.db rename to recipeBook/etc/recipes/storagedrawers.db diff --git a/etc/recipes/tconstruct.db b/recipeBook/etc/recipes/tconstruct.db similarity index 100% rename from etc/recipes/tconstruct.db rename to recipeBook/etc/recipes/tconstruct.db diff --git a/apps/recipeBook.lua b/recipeBook/recipeBook.lua similarity index 100% rename from apps/recipeBook.lua rename to recipeBook/recipeBook.lua diff --git a/storageManager/.package b/storageManager/.package new file mode 100644 index 0000000..f7481b5 --- /dev/null +++ b/storageManager/.package @@ -0,0 +1,9 @@ +{ + required = { + 'opus-develop-1.8', + }, + title = 'Manage inventory', + repository = 'kepler155c/opus-apps/develop1.8/storageManager', + description = [[ ... ]], + licence = 'MIT', +} diff --git a/apps/Crafter.lua b/storageManager/Crafter.lua similarity index 100% rename from apps/Crafter.lua rename to storageManager/Crafter.lua diff --git a/apps/chestManager.lua b/storageManager/chestManager.lua similarity index 100% rename from apps/chestManager.lua rename to storageManager/chestManager.lua diff --git a/apps/levelEmitter.lua b/storageManager/levelEmitter.lua similarity index 100% rename from apps/levelEmitter.lua rename to storageManager/levelEmitter.lua diff --git a/apps/storageActivity.lua b/storageManager/storageActivity.lua similarity index 100% rename from apps/storageActivity.lua rename to storageManager/storageActivity.lua From 4d65ebe24cc85679e05bc1cbd82c5f464798ba37 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sat, 3 Nov 2018 19:45:30 -0400 Subject: [PATCH 060/165] remove hardcoded devices from cows.lua --- farms/cows.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/farms/cows.lua b/farms/cows.lua index 2196c92..43f3711 100644 --- a/farms/cows.lua +++ b/farms/cows.lua @@ -3,6 +3,7 @@ _G.requireInjector(_ENV) local Config = require('config') local Util = require('util') local InventoryAdapter = require('chestAdapter18') +local Peripheral = require('peripheral') local device = _G.device local os = _G.os @@ -17,9 +18,9 @@ local sensor = device['plethora:sensor'] or turtle.equip('right', 'plethora:module:3') and device['plethora:sensor'] or error('Plethora sensor required') -local dispenser = device['minecraft:dispenser_89'] or +local dispenser = Peripheral.lookup('type/minecraft:dispenser') or error('Dispenser not found') -local integrator = device['redstone_integrator_131'] or +local integrator = Peripheral.lookup('type/redstone_integrator') or error('Integrator not found') local function pulse() From ed6f95ef943a615a13c674f30786300ddf2e8807 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sun, 4 Nov 2018 13:01:10 -0500 Subject: [PATCH 061/165] package manager --- builder/.package | 2 +- builder/apis/builder/blocks.lua | 2 +- core/.package | 2 +- farms/.package | 2 +- forestry/.package | 2 +- glasses/.package | 2 +- milo/.package | 2 +- milo/Milo.lua | 1 - miners/.package | 2 +- neural/.package | 2 +- pickup/.package | 2 +- recipeBook/.package | 2 +- storageManager/.package | 2 +- 13 files changed, 12 insertions(+), 13 deletions(-) diff --git a/builder/.package b/builder/.package index 308669a..e50854f 100644 --- a/builder/.package +++ b/builder/.package @@ -3,7 +3,7 @@ 'opus-develop-1.8', }, title = 'Schematic Builder', - repository = 'kepler155c/opus-apps/develop1.8/builder', + repository = 'kepler155c/opus-apps/develop-1.8/builder', description = [[ ... ]], licence = 'MIT', } diff --git a/builder/apis/builder/blocks.lua b/builder/apis/builder/blocks.lua index 0ab6fc8..a69f031 100644 --- a/builder/apis/builder/blocks.lua +++ b/builder/apis/builder/blocks.lua @@ -10,7 +10,7 @@ local JSON = require('json') local blockDB = TableDB() function blockDB:load() - local blocks = JSON.decodeFromFile('usr/etc/names/minecraft.json') + local blocks = JSON.decodeFromFile('packages/core/etc/names/minecraft.json') if not blocks then error('Unable to read blocks.json') diff --git a/core/.package b/core/.package index 5ad65a1..cc59d0b 100644 --- a/core/.package +++ b/core/.package @@ -3,7 +3,7 @@ 'opus-develop-1.8', }, title = 'Core apps and apis', - repository = 'kepler155c/opus-apps/develop1.8/core', + repository = 'kepler155c/opus-apps/develop-1.8/core', description = [[ ... ]], licence = 'MIT', } diff --git a/farms/.package b/farms/.package index 8fd8d66..929deff 100644 --- a/farms/.package +++ b/farms/.package @@ -3,7 +3,7 @@ 'opus-develop-1.8', }, title = 'Programs for farming resources', - repository = 'kepler155c/opus-apps/develop1.8/farms', + repository = 'kepler155c/opus-apps/develop-1.8/farms', description = [[ ... ]], licence = 'MIT', } diff --git a/forestry/.package b/forestry/.package index efff75c..fa438d9 100644 --- a/forestry/.package +++ b/forestry/.package @@ -3,7 +3,7 @@ 'opus-develop-1.8', }, title = 'Forestry mod applications', - repository = 'kepler155c/opus-apps/develop1.8/forestry', + repository = 'kepler155c/opus-apps/develop-1.8/forestry', description = [[ ... ]], licence = 'MIT', } diff --git a/glasses/.package b/glasses/.package index 00581da..0c7c9c1 100644 --- a/glasses/.package +++ b/glasses/.package @@ -3,7 +3,7 @@ 'opus-develop-1.8', }, title = 'Plethora overlay glasses support', - repository = 'kepler155c/opus-apps/develop1.8/glasses', + repository = 'kepler155c/opus-apps/develop-1.8/glasses', description = [[ ... ]], licence = 'MIT', } diff --git a/milo/.package b/milo/.package index 62e9262..3479fc3 100644 --- a/milo/.package +++ b/milo/.package @@ -3,7 +3,7 @@ 'opus-develop-1.8', }, title = 'Inventory manager for Opus OS', - repository = 'kepler155c/opus-apps/develop1.8/milo', + repository = 'kepler155c/opus-apps/develop-1.8/milo', description = [[ ... ]], licence = 'MIT', } diff --git a/milo/Milo.lua b/milo/Milo.lua index 706381f..2460867 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -21,7 +21,6 @@ _G.requireInjector(_ENV) local Config = require('config') local Event = require('event') -local itemDB = require('itemDB') local Milo = require('milo') local Peripheral = require('peripheral') local Storage = require('storage') diff --git a/miners/.package b/miners/.package index b872f56..bc9a002 100644 --- a/miners/.package +++ b/miners/.package @@ -3,7 +3,7 @@ 'opus-develop-1.8', }, title = 'Turtle mining programs', - repository = 'kepler155c/opus-apps/develop1.8/miners', + repository = 'kepler155c/opus-apps/develop-1.8/miners', description = [[ ... ]], licence = 'MIT', } diff --git a/neural/.package b/neural/.package index 6af5b15..0d6be54 100644 --- a/neural/.package +++ b/neural/.package @@ -3,7 +3,7 @@ 'opus-develop-1.8', }, title = 'Programs for the neural interface', - repository = 'kepler155c/opus-apps/develop1.8/neural', + repository = 'kepler155c/opus-apps/develop-1.8/neural', description = [[ ... ]], licence = 'MIT', } diff --git a/pickup/.package b/pickup/.package index 7174c0b..546c4f9 100644 --- a/pickup/.package +++ b/pickup/.package @@ -3,7 +3,7 @@ 'opus-develop-1.8', }, title = 'Move resources around with turtles', - repository = 'kepler155c/opus-apps/develop1.8/pickup', + repository = 'kepler155c/opus-apps/develop-1.8/pickup', description = [[ ... ]], licence = 'MIT', } diff --git a/recipeBook/.package b/recipeBook/.package index 86f1361..1323a51 100644 --- a/recipeBook/.package +++ b/recipeBook/.package @@ -3,7 +3,7 @@ 'opus-develop-1.8', }, title = 'Recipe books for crafting programs', - repository = 'kepler155c/opus-apps/develop1.8/recipeBook', + repository = 'kepler155c/opus-apps/develop-1.8/recipeBook', description = [[ ... ]], licence = 'MIT', } diff --git a/storageManager/.package b/storageManager/.package index f7481b5..7114d5f 100644 --- a/storageManager/.package +++ b/storageManager/.package @@ -3,7 +3,7 @@ 'opus-develop-1.8', }, title = 'Manage inventory', - repository = 'kepler155c/opus-apps/develop1.8/storageManager', + repository = 'kepler155c/opus-apps/develop-1.8/storageManager', description = [[ ... ]], licence = 'MIT', } From 6de154dc48cc82da89fa431417a4369c8d43e00b Mon Sep 17 00:00:00 2001 From: kepler155c Date: Mon, 5 Nov 2018 00:26:28 -0500 Subject: [PATCH 062/165] milo wip --- farms/farm.lua | 1 + milo/Milo.lua | 7 ++++--- milo/MiloRemote.lua | 8 +++++++- milo/core/machines.lua | 12 +++++++++--- milo/plugins/remote.lua | 7 +++++++ 5 files changed, 28 insertions(+), 7 deletions(-) diff --git a/farms/farm.lua b/farms/farm.lua index 901398f..297e54e 100644 --- a/farms/farm.lua +++ b/farms/farm.lua @@ -91,6 +91,7 @@ local function harvest(blocks) end end dropped = true + turtle.condense() turtle.select(1) end elseif b.action == 'smash' then diff --git a/milo/Milo.lua b/milo/Milo.lua index 2460867..722c725 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -109,9 +109,10 @@ Event.on('milo_cycle', function() for _, task in ipairs(context.tasks) do local s, m = pcall(function() task:cycle(context) end) if not s and m then - _debug(task.name .. ' crashed') - Util.print(task.name .. ' crashed') - error(m) + _G._debug(task.name .. ' crashed') + _G._debug(m) + -- _G.printError(task.name .. ' crashed') + -- _G.printError(m) end end context.turtleBusy = false diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index e24d650..7c763af 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -146,8 +146,14 @@ function page:sendRequest(data) self:setStatus('connecting ...') socket, msg = Socket.connect(options.server.value, 4242) if socket then - self:setStatus('connected ...') socket:write(options.user.value) + local r = socket:read(2) + if r and not r.msg then + self:setStatus('connected ...') + else + socket = nil + self:setStatus(r.msg) + end end end if socket then diff --git a/milo/core/machines.lua b/milo/core/machines.lua index 1ba57ff..144cf31 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -36,9 +36,10 @@ local machinesPage = UI.Page { y = 2, ey = -2, values = context.config.remoteDefaults, columns = { - { key = 'priority', width = 3 }, - { heading = 'Name', key = 'displayName' }, - { heading = 'Type', key = 'mtype', width = 5 }, + { key = 'suffix', width = 4, justify = 'right' }, + { heading = 'Name', key = 'displayName' }, + { heading = 'Type', key = 'mtype', width = 4 }, + { heading = 'Pri', key = 'priority', width = 3 }, }, sortColumn = 'displayName', help = 'Select Machine', @@ -57,6 +58,11 @@ local machinesPage = UI.Page { function machinesPage.grid:getDisplayValues(row) row = Util.shallowCopy(row) + local t = { row.name:match(':(.+)_(%d+)') } + if t and #t == 2 then + row.name, row.suffix = table.unpack(t) + row.name = row.name .. '_' .. row.suffix + end row.displayName = row.displayName or row.name return row end diff --git a/milo/plugins/remote.lua b/milo/plugins/remote.lua index 956e198..ead9956 100644 --- a/milo/plugins/remote.lua +++ b/milo/plugins/remote.lua @@ -29,9 +29,16 @@ local function client(socket) local manipulator = getManipulatorForUser(user) if not manipulator then + socket:write({ + msg = 'Manipulator not found' + }) return end + socket:write({ + data = 'ok', + }) + repeat local data = socket:read() if not data then From d113b1da1a1994fc99e4a806c7095908a8607966 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Mon, 5 Nov 2018 00:46:07 -0500 Subject: [PATCH 063/165] milo wip --- milo/Milo.lua | 1 + milo/MiloRemote.lua | 4 +++- milo/plugins/remote.lua | 9 ++++++--- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/milo/Milo.lua b/milo/Milo.lua index 722c725..f78c468 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -38,6 +38,7 @@ end local config = { monitor = 'type/monitor', + activityMonitor = 'none', remoteDefaults = { }, } Config.load('milo', config) diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index 7c763af..fa16b57 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -151,8 +151,10 @@ function page:sendRequest(data) if r and not r.msg then self:setStatus('connected ...') else + msg = r and r.msg or 'Timed out' + socket:close() socket = nil - self:setStatus(r.msg) + break end end end diff --git a/milo/plugins/remote.lua b/milo/plugins/remote.lua index ead9956..e7100dc 100644 --- a/milo/plugins/remote.lua +++ b/milo/plugins/remote.lua @@ -20,7 +20,7 @@ local function getManipulatorForUser(user) end local function client(socket) - _G._debug('connection from ' .. socket.dhost) + _G._debug('REMOTE: connection from ' .. socket.dhost) local user = socket:read(2) if not user then @@ -29,12 +29,15 @@ local function client(socket) local manipulator = getManipulatorForUser(user) if not manipulator then + _debug('REMOTE: Manipulator with introspection module bound with user not found. Closing connection.') socket:write({ msg = 'Manipulator not found' }) + socket:close() return end + _debug('REMOTE: all good') socket:write({ data = 'ok', }) @@ -110,12 +113,12 @@ local function client(socket) end until not socket.connected - _G._debug('disconnected from ' .. socket.dhost) + _G._debug('REMOTE: disconnected from ' .. socket.dhost) end if device.wireless_modem then Event.addRoutine(function() - _G._debug('Milo: listening on port 4242') + _G._debug('REMOTE: listening on port 4242') while true do local socket = Socket.server(4242) Event.addRoutine(function() From 01452ced726544cb012fe8c19362c4b17885e88e Mon Sep 17 00:00:00 2001 From: kepler155c Date: Tue, 6 Nov 2018 16:21:31 -0500 Subject: [PATCH 064/165] package manager + tweaks to milo --- core/etc/apps/opus-apps.db | 71 ++++++++------------------------- farms/cows.lua | 6 ++- farms/etc/apps/apps.db | 11 +++++ farms/farm.lua | 6 ++- milo/.package | 6 ++- milo/Milo.lua | 5 +-- milo/apps/furni.lua | 54 +++++++++++++++++++++++++ milo/core/machines.lua | 2 +- storageManager/etc/apps/apps.db | 19 +++++++++ 9 files changed, 117 insertions(+), 63 deletions(-) create mode 100644 farms/etc/apps/apps.db create mode 100644 milo/apps/furni.lua create mode 100644 storageManager/etc/apps/apps.db diff --git a/core/etc/apps/opus-apps.db b/core/etc/apps/opus-apps.db index e2a8d90..443d9a8 100644 --- a/core/etc/apps/opus-apps.db +++ b/core/etc/apps/opus-apps.db @@ -32,15 +32,6 @@ \030 \031 \030f\0317\130\030 \031 \030f\0317\130", run = "Appstore.lua", }, - [ "4486006f811b88cacd5f211fd579717e29b600cd" ] = { - title = "Miner", - category = "Apps", - icon = " \0315\\\030 \031 \ - \0304\031f _ \030 \031c/\0315\\\ - \0304 ", - run = "simpleMiner.lua", - requires = 'turtle', - }, [ "131260cbfbb0c821f8eae5e7c3c296c7aa4d50b9" ] = { title = "Music", category = "Apps", @@ -52,13 +43,14 @@ requires = 'turtle', }, --[[ - [ "81c0d915fa6d82fd30661c5e66e204cea52bb2b5" ] = { - title = "Activity", + [ "4486006f811b88cacd5f211fd579717e29b600cd" ] = { + title = "Miner", category = "Apps", - icon = "\0318/\030f\031 \030 \0318\\\ -\030f \0308\0319o\030f\031 \ -\0318\\\030f\031 \030 \0318/", - run = "storageActivity.lua", + icon = " \0315\\\030 \031 \ + \0304\031f _ \030 \031c/\0315\\\ + \0304 ", + run = "simpleMiner.lua", + requires = 'turtle', }, [ "89307d419a2fe4fbb69af92b3d3af27b6ec14d3e" ] = { title = "Telnet", @@ -76,6 +68,15 @@ \031e\\/\031 \0319c", run = "vnc.lua", }, + [ "131f0b008f44298812221d120d982940609be781" ] = { + title = "Builder", + category = "Apps", + icon = "\0317_____\ +\030e\031c###\0308\0317=\030e\031c#\ +\030e\031c#\0307\031f.\030e\031c###", + run = "usr/apps/builder.lua", + requires = "turtle", + }, --]] [ "a0365977708b7387ee9ce2c13e5820e6e11732cb" ] = { title = "Pain", @@ -85,15 +86,6 @@ \030 \031f\030f\0318\143\133\0312\136\0302\031f\159\159\143\131\030f\0312\132", run = "http://pastebin.com/raw/wJQ7jav0", }, - [ "9e092dda4f0e27d0c7686ddd00272079e678b6e6" ] = { - title = "Storage", - category = "Apps", - icon = "\0307 \ -\0307 \0308\0311 \0305 \0308\031 \0307 \0308 \0301 \ -\0307 ", - run = "chestManager.lua", - requires = 'turtle', - }, [ "114edfc04a1ab03541bdc80ce064f66a7cfcedbb" ] = { title = "Recorder", category = "Apps", @@ -105,15 +97,6 @@ \030 \031f\030f\031e\139\030e\128\030f\159\129\0314\130\131\131\129", run = "recorder.lua", }, - [ "131f0b008f44298812221d120d982940609be781" ] = { - title = "Builder", - category = "Apps", - icon = "\0317_____\ -\030e\031c###\0308\0317=\030e\031c#\ -\030e\031c#\0307\031f.\030e\031c###", - run = "usr/apps/builder.lua", - requires = "turtle", - }, [ "d8c298dd41e4a4ec20e8307901797b64688b3b77" ] = { title = "GPS Deploy", category = "Apps", @@ -150,19 +133,6 @@ \030 \031f\030f\0310\136\140\140\030 ", run = "https://gist.github.com/LDDestroier/c7528d95bc0103545c2a/raw", }, ---[[ - [ "d78f28759f255a0db76604ee560b87c4715a0da5" ] = { - title = "Sketch", - category = "Apps", - icon = " \031bskch\ -\0303\031f \030d \ -\030d\031f ", - iconExt = "\030 \031f\0308\031f\151\0313\140\140\140\030f\0318\148\ -\030 \031f\030b\031f\149\0318\130\131\129\030f\031b\149\ -\030 \031f\030f\0318\138\0308\031b\130\131\129\030f\0318\133", - run = "http://pastebin.com/raw/Mm5hd97E", - }, -]] [ "58ec8d6e36e346d9f42eb43935652e3e58e2c829" ] = { title = "Mwm", category = "Apps", @@ -174,15 +144,6 @@ \030 \031f\0304\031f\131\131\131\131\131\131\131\030e\0314\131", run = "mwm.lua usr/config/mwm", }, - [ "8d1b0a73bedc0dc492377c2f6ab880940b97ec6e" ] = { - title = "Treefarm", - icon = "\030 \031f \0305 \030 \030d \030 \ -\0305\031f \030d \030 \030d \0305 \030d \ -\030 \031f \030c \030 \0304 \030 \030c \030 ", - category = "Apps", - run = "treefarm.lua", - requires = "turtle", - }, [ "01c933b2a36ad8ed2d54089cb2903039046c1216" ] = { title = "Enchat", icon = "\030e\031f\151\030f\031e\156\0311\140\0314\140\0315\140\031d\140\031b\140\031a\132\ diff --git a/farms/cows.lua b/farms/cows.lua index 43f3711..b5efaf0 100644 --- a/farms/cows.lua +++ b/farms/cows.lua @@ -93,7 +93,7 @@ end local chest = InventoryAdapter({ side = 'top', direction = 'down' }) or error('missing chest above') -turtle.run(function() +local s, m = turtle.run(function() turnOffWater() repeat @@ -117,3 +117,7 @@ turtle.run(function() os.sleep(5) until turtle.isAborted() end) + +if not s and m then + error(m) +end diff --git a/farms/etc/apps/apps.db b/farms/etc/apps/apps.db new file mode 100644 index 0000000..c9f8d4b --- /dev/null +++ b/farms/etc/apps/apps.db @@ -0,0 +1,11 @@ +{ + [ "8d1b0a73bedc0dc492377c2f6ab880940b97ec6e" ] = { + title = "Treefarm", + icon = "\030 \031f \0305 \030 \030d \030 \ +\0305\031f \030d \030 \030d \0305 \030d \ +\030 \031f \030c \030 \0304 \030 \030c \030 ", + category = "Apps", + run = "treefarm.lua", + requires = "turtle", + }, +} \ No newline at end of file diff --git a/farms/farm.lua b/farms/farm.lua index 297e54e..a68a41c 100644 --- a/farms/farm.lua +++ b/farms/farm.lua @@ -106,7 +106,7 @@ local function harvest(blocks) turtle.equip('right', 'plethora:module:2') end -turtle.run(function() +local s, m = turtle.run(function() local facing = scanner.getBlockMeta(0, 0, 0).state.facing turtle.point.heading = Point.facings[facing].heading @@ -125,3 +125,7 @@ turtle.run(function() end until turtle.isAborted() end) + +if not s and m then + error(m) +end diff --git a/milo/.package b/milo/.package index 3479fc3..b9690c4 100644 --- a/milo/.package +++ b/milo/.package @@ -2,8 +2,10 @@ required = { 'opus-develop-1.8', }, - title = 'Inventory manager for Opus OS', + title = 'Milo: Advanced inventory management', repository = 'kepler155c/opus-apps/develop-1.8/milo', - description = [[ ... ]], + description = [[Provides AE style crafting in computercraft. + Includes Importing, exporting, autocrafting, replenish and limits, and much more. + Includes over 200 standard Minecraft recipes.]], licence = 'MIT', } diff --git a/milo/Milo.lua b/milo/Milo.lua index f78c468..df1cbe9 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -69,7 +69,7 @@ local context = { _G._p = context --debug Event.on('storage_offline', function() - Milo:showError('A storage chest has gone offline, ctrl-l to continue') + Milo:showError('A storage chest has gone offline - See configuration screen') end) Milo:init(context) @@ -99,8 +99,7 @@ end Milo:clearGrid() -local page = UI:getPage('listing') -UI:setPage(page) +UI:setPage(UI:getPage('listing')) Event.on('milo_cycle', function() if not context.turtleBusy then diff --git a/milo/apps/furni.lua b/milo/apps/furni.lua new file mode 100644 index 0000000..8eb5294 --- /dev/null +++ b/milo/apps/furni.lua @@ -0,0 +1,54 @@ +--[[ +Use 4 furnaces at once to smelt items. + +Set up a turtle with a hopper on top and surrounded by furnaces. + +Set the hopper as a machine used for crafting. +Export coal to slot 2 of each furnace and import from slot 3. +--]] + +_G.requireInjector(_ENV) + +local Event = require('event') +local Util = require('util') + +local device = _G.device +local os = _G.os +local turtle = _G.turtle + +local intro = + device['plethora:introspection'] +local inv = intro.getInventory() + +Event.on('turtle_inventory', function() + while true do + local list = inv.list() + local sleepTime = 10 + if Util.empty(list) then + break + end + for k,slot in pairs(list) do + for _ = 1, 4 do + local count = 0 + local s, m = pcall(function() + count = inv.pushItems('front', k, 8, 1) + end) + if not s then + _G.printError(m) + end + if count > 0 then + sleepTime = 0 + end + turtle.turnRight() + slot.count = slot.count - count + if slot.count <= 0 then + break + end + end + end + os.sleep(sleepTime) + end +end) + +os.queueEvent('turtle_inventory') +Event.pullEvents() \ No newline at end of file diff --git a/milo/core/machines.lua b/milo/core/machines.lua index 144cf31..bc7b621 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -58,7 +58,7 @@ local machinesPage = UI.Page { function machinesPage.grid:getDisplayValues(row) row = Util.shallowCopy(row) - local t = { row.name:match(':(.+)_(%d+)') } + local t = { row.name:match(':(.+)_(%d+)$') } if t and #t == 2 then row.name, row.suffix = table.unpack(t) row.name = row.name .. '_' .. row.suffix diff --git a/storageManager/etc/apps/apps.db b/storageManager/etc/apps/apps.db new file mode 100644 index 0000000..603a26b --- /dev/null +++ b/storageManager/etc/apps/apps.db @@ -0,0 +1,19 @@ +{ + [ "81c0d915fa6d82fd30661c5e66e204cea52bb2b5" ] = { + title = "Activity", + category = "Apps", + icon = "\0318/\030f\031 \030 \0318\\\ +\030f \0308\0319o\030f\031 \ +\0318\\\030f\031 \030 \0318/", + run = "storageActivity.lua", + }, + [ "9e092dda4f0e27d0c7686ddd00272079e678b6e6" ] = { + title = "Storage", + category = "Apps", + icon = "\0307 \ +\0307 \0308\0311 \0305 \0308\031 \0307 \0308 \0301 \ +\0307 ", + run = "chestManager.lua", + requires = 'turtle', + }, +} From 3098cd86bf44ce307dd5060f5ab1c0352da8b9c6 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Tue, 6 Nov 2018 17:28:41 -0500 Subject: [PATCH 065/165] fix neural autorun --- milo/apis/storage.lua | 2 +- neural/autorun/{6.neural.lua => interface.lua} | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) rename neural/autorun/{6.neural.lua => interface.lua} (65%) diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index e3b64e3..2b22130 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -160,7 +160,7 @@ local ct = os.clock() for _, adapter in self:onlineAdapters() do if adapter.dirty then -_debug('STORAGE: refresh: ' .. adapter.name) +--_debug('STORAGE: refresh: ' .. adapter.name) adapter:listItems(throttle) adapter.dirty = false end diff --git a/neural/autorun/6.neural.lua b/neural/autorun/interface.lua similarity index 65% rename from neural/autorun/6.neural.lua rename to neural/autorun/interface.lua index 10ab439..ab63796 100644 --- a/neural/autorun/6.neural.lua +++ b/neural/autorun/interface.lua @@ -2,10 +2,10 @@ _G.requireInjector(_ENV) local GPS = require('gps') -local kernel = _G.kernel +local device = _G.device -kernel.onDeviceAttach('neuralInterface', function(dev) - dev.goTo = function(x, _, z) +if device.neuralInterface and device.wireless_modem then + device.neuralInterface.goTo = function(x, _, z) local pt = GPS.locate(2) if pt then return pcall(function() @@ -16,9 +16,9 @@ kernel.onDeviceAttach('neuralInterface', function(dev) } gpt.x = math.min(math.max(gpt.x, -15), 15) gpt.z = math.min(math.max(gpt.z, -15), 15) - return dev.walk(gpt.x, gpt.y, gpt.z) + return device.neuralInterface.walk(gpt.x, gpt.y, gpt.z) end) end return false, 'No GPS' end -end) +end From 6db34bc4f9fb6cdb6a7c4e4dcec29610b92d6500 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Thu, 8 Nov 2018 10:26:20 -0500 Subject: [PATCH 066/165] scanning miner fixes --- miners/scanningMiner.lua | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/miners/scanningMiner.lua b/miners/scanningMiner.lua index 64ff752..59175a3 100644 --- a/miners/scanningMiner.lua +++ b/miners/scanningMiner.lua @@ -314,7 +314,13 @@ end local function equip(side, item) if not turtle.equip(side, item) then - turtle.selectSlotWithQuantity(0) + if not turtle.selectSlotWithQuantity(0) then + ejectTrash() + end + if not turtle.selectSlotWithQuantity(0) then + turtle.select(16) + turtle.drop() + end turtle.equip(side) if not turtle.equip(side, item) then error('Unable to equip ' .. item) @@ -350,6 +356,9 @@ local function scan() (b.action == 'liquid_fuel' and b.y <= bedrock) then return false + elseif b.action == 'liquid_fuel' and b.damage > 0 then + return false + elseif b.y >= bedrock then b.action = dictionary:get(b.name, b.metadata) or 'mine' From a0dd3a7b6d08fe6ad057217f75ee3ae8bf5c17e4 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Thu, 8 Nov 2018 11:50:58 -0500 Subject: [PATCH 067/165] farm cocoa beans --- farms/farm.lua | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/farms/farm.lua b/farms/farm.lua index a68a41c..0db7ac1 100644 --- a/farms/farm.lua +++ b/farms/farm.lua @@ -23,6 +23,8 @@ local crops = Util.readTable(CONFIG_FILE) or { { seed = 'minecraft:potato', mature = 7, action = 'plant' }, ['minecraft:beetroots'] = { seed = 'minecraft:beetroot_seeds', mature = 3, action = 'plant' }, + ['minecraft:cocoa'] = + { seed = 'minecraft:dye:3', mature = 8, action = 'pick' }, ['minecraft:reeds'] = { action = 'bash' }, ['minecraft:melon_block'] = { action = 'smash' }, ['minecraft:pumpkin'] = { action = 'smash' }, @@ -57,6 +59,9 @@ local function scan() if v.action == 'smash' then return v.y == -1 end + if v.action == 'pick' then + return v.y == 0 and v.state.age == 2 + end return v.action == 'plant' and v.metadata == crops[v.name].mature and v.y == -1 @@ -101,6 +106,17 @@ local function harvest(blocks) turtle.placeDown(crops[b.name].seed) turtle.select(1) end + elseif b.action == 'pick' then + local h = Point.facings[b.state.facing].heading + local hi = Point.headings[(h + 2) % 4] -- opposite heading + + -- without pathfinding, will be unable to circle log + if turtle._goto({ x = b.x + hi.xd, z = b.z + hi.zd, heading = h }) then + if turtle.dig() then + turtle.place(crops[b.name].seed) + turtle.select(1) + end + end end end) turtle.equip('right', 'plethora:module:2') From 089316486819dc20a3d12e9506804a69605a789b Mon Sep 17 00:00:00 2001 From: kepler155c Date: Thu, 8 Nov 2018 12:43:00 -0500 Subject: [PATCH 068/165] refuel with lava program --- farms/lavaRefuel.lua | 56 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 farms/lavaRefuel.lua diff --git a/farms/lavaRefuel.lua b/farms/lavaRefuel.lua new file mode 100644 index 0000000..e91a926 --- /dev/null +++ b/farms/lavaRefuel.lua @@ -0,0 +1,56 @@ +_G.requireInjector(_ENV) + +local Point = require('point') + +local device = _G.device +local turtle = _G.turtle + +local MAX_FUEL = turtle.getFuelLimit() + +local scanner = device['plethora:scanner'] or + turtle.equip('right', 'plethora:module:2') and device['plethora:scanner'] or + error('Plethora scanner required') + +if not turtle.select('minecraft:bucket') then + error('bucket required') +end + +local s, m = turtle.run(function() + local facing = scanner.getBlockMeta(0, 0, 0).state.facing + turtle.setPoint({ x = 0, y = 0, z = 0, heading = Point.facings[facing].heading }) + + local blocks = scanner.scan() + local first, last = blocks[#blocks].y, blocks[1].y + + for y = first, last, -1 do + if turtle.getFuelLevel() >= (MAX_FUEL - 1000) then + print('I am full') + break + end + local t = { } + for _,v in pairs(blocks) do + if v.y == y then + if (v.name == 'minecraft:lava' and v.metadata == 0) or + (v.name == 'minecraft:flowing_lava' and v.metadata == 0) then + table.insert(t, v) + end + end + end + print(y .. ': ' .. #t) + Point.eachClosest(turtle.point, t, function(b) + if turtle.getFuelLevel() >= (MAX_FUEL - 1000) then + return true + end + turtle.placeDownAt(b) + turtle.refuel() + print(turtle.getFuelLevel()) + end) + end +end) + +turtle.gotoY(0) +turtle._goto({ x = 0, y = 0, z = 0 }) + +if not s and m then + error(m) +end From 85b1d1d52e541a8ce2c9fe6cc9eb623986c5a9c3 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Thu, 8 Nov 2018 15:21:57 -0500 Subject: [PATCH 069/165] app tweaks --- {farms => core}/lavaRefuel.lua | 2 ++ farms/cows.lua | 23 ++++++++++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) rename {farms => core}/lavaRefuel.lua (97%) diff --git a/farms/lavaRefuel.lua b/core/lavaRefuel.lua similarity index 97% rename from farms/lavaRefuel.lua rename to core/lavaRefuel.lua index e91a926..3cb0653 100644 --- a/farms/lavaRefuel.lua +++ b/core/lavaRefuel.lua @@ -16,6 +16,8 @@ if not turtle.select('minecraft:bucket') then end local s, m = turtle.run(function() + turtle.setMovementStrategy('goto') + local facing = scanner.getBlockMeta(0, 0, 0).state.facing turtle.setPoint({ x = 0, y = 0, z = 0, heading = Point.facings[facing].heading }) diff --git a/farms/cows.lua b/farms/cows.lua index b5efaf0..118abcb 100644 --- a/farms/cows.lua +++ b/farms/cows.lua @@ -9,7 +9,14 @@ local device = _G.device local os = _G.os local turtle = _G.turtle +local retain = Util.transpose { + 'minecraft:shears', + 'minecraft_wheat', + 'minecraft:diamond_sword', + 'plethora:module:3', +} local config = { + animal = 'Cow', max_cows = 15, } Config.load('cows', config) @@ -51,7 +58,7 @@ local function getCowCount() local xpCount = 0 Util.filterInplace(blocks, function(v) - if v.name == 'Cow' then + if v.name == config.animal then if v.y > -.5 then grown = grown + 1 end if v.y < -.5 then babies = babies + 1 end return v.y > -.5 @@ -76,16 +83,26 @@ local function butcher() end turtle.equip('right', 'plethora:module:3') - turtle.dropUp('minecraft:beef') - turtle.dropUp('minecraft:leather') + turtle.eachFilledSlot(function(slot) + if not retain[slot.name] then + turtle.select(slot.index) + turtle.dropUp() + end + end) end local function breed() turtle.select(1) + if config.animal == 'Sheep' then + turtle.place('minecraft:shears') + end turtle.place('minecraft:wheat') for _ = 1, 3 do turtle.turnRight() + if config.animal == 'Sheep' then + turtle.place('minecraft:shears') + end turtle.place('minecraft:wheat') end end From 5e744be2520c23000c09bd3a80597587bdf97813 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Thu, 8 Nov 2018 22:38:04 -0500 Subject: [PATCH 070/165] app tweaks --- farms/farm.lua | 98 +++++++++++++++++++++++++-------- farms/{cows.lua => rancher.lua} | 10 ++-- pickup/pickupRemote.lua | 19 ++++--- 3 files changed, 91 insertions(+), 36 deletions(-) rename farms/{cows.lua => rancher.lua} (94%) diff --git a/farms/farm.lua b/farms/farm.lua index 0db7ac1..f5d6082 100644 --- a/farms/farm.lua +++ b/farms/farm.lua @@ -1,12 +1,13 @@ _G.requireInjector(_ENV) -local Point = require('point') -local Util = require('util') +local Point = require('point') +local Util = require('util') -local device = _G.device -local fs = _G.fs -local os = _G.os -local turtle = _G.turtle +local device = _G.device +local fs = _G.fs +local os = _G.os +local peripheral = _G.peripheral +local turtle = _G.turtle local CONFIG_FILE = 'usr/config/farm' @@ -29,12 +30,25 @@ local crops = Util.readTable(CONFIG_FILE) or { ['minecraft:melon_block'] = { action = 'smash' }, ['minecraft:pumpkin'] = { action = 'smash' }, ['minecraft:chest'] = { action = 'drop' }, + ['minecraft:cactus'] = { action = 'bump' }, } if not fs.exists(CONFIG_FILE) then Util.writeTable(CONFIG_FILE, crops) end +local retain = Util.transpose { + "minecraft:diamond_pickaxe", + "plethora:module:2", + "plethora:module:3", +} + +for _, v in pairs(crops) do + if v.seed then + retain[v.seed] = true + end +end + local function scan() local blocks = scanner.scan() local summed = turtle.getSummedInventory() @@ -47,29 +61,35 @@ local function scan() end end - Util.filterInplace(blocks, function(v) - v.action = crops[v.name] and crops[v.name].action + Util.filterInplace(blocks, function(b) + b.action = crops[b.name] and crops[b.name].action - if v.action == 'bash' then - return v.y == 0 + if b.action == 'bash' then + return b.y == 0 end - if v.action == 'drop' then - return doDropOff and v.y == -1 + if b.action == 'drop' then + return doDropOff and b.y == -1 end - if v.action == 'smash' then - return v.y == -1 + if b.action == 'smash' then + return b.y == -1 end - if v.action == 'pick' then - return v.y == 0 and v.state.age == 2 + if b.action == 'pick' then + return b.y == 0 and b.state.age == 2 end - return v.action == 'plant' and - v.metadata == crops[v.name].mature and - v.y == -1 + if b.action == 'bump' then + return b.y == 0 + end + return b.action == 'plant' and + b.metadata == crops[b.name].mature and + b.y == -1 end) local harvestCount = 0 - for _,v in pairs(blocks) do - if v.action ~= 'drop' then + for _,b in pairs(blocks) do + b.x = b.x + turtle.point.x + b.y = b.y + turtle.point.y + b.z = b.z + turtle.point.z + if b.action ~= 'drop' then harvestCount = harvestCount + 1 end end @@ -79,7 +99,6 @@ end local function harvest(blocks) turtle.equip('right', 'minecraft:diamond_pickaxe') - turtle.setPoint({ x = 0, y = 0, z = 0, heading = turtle.point.heading }) turtle.select(1) local dropped @@ -87,25 +106,58 @@ local function harvest(blocks) Point.eachClosest(turtle.point, blocks, function(b) if b.action == 'bash' then turtle.digForwardAt(b) + elseif b.action == 'drop' and not dropped then if turtle._goto(Point.above(b)) then + turtle.eachFilledSlot(function(slot) + if not retain[slot.name] and not retain[slot.key] then + turtle.select(slot.index) + turtle.dropDown() + end + end) local summed = turtle.getSummedInventory() for k,v in pairs(summed) do if v.count > 16 then turtle.dropDown(k, v.count - 16) end end + dropped = true turtle.condense() turtle.select(1) end + elseif b.action == 'smash' then turtle.digDownAt(b) + elseif b.action == 'plant' then if turtle.digDownAt(b) then turtle.placeDown(crops[b.name].seed) turtle.select(1) end + + elseif b.action == 'bump' then + if turtle.faceAgainst(b) then + turtle.equip('right', 'plethora:module:3') + local sensed = peripheral.call('right', 'sense') + turtle.equip('right', 'minecraft:diamond_pickaxe') + os.sleep(.25) + Util.filterInplace(sensed, function(s) + if s.displayName == 'item.tile.cactus' and Point.distance(b, s) < 6 then + s.ex = s.x + s.ez = s.z + s.x = Util.round(s.x) + turtle.point.x + s.z = Util.round(s.z) + turtle.point.z + s.y = -1 + return true + end + end) + Point.eachClosest(turtle.point, sensed, function(s) + turtle.suckDownAt(s) + end) + turtle.select(1) + end + elseif b.action == 'pick' then local h = Point.facings[b.state.facing].heading local hi = Point.headings[(h + 2) % 4] -- opposite heading @@ -126,6 +178,8 @@ local s, m = turtle.run(function() local facing = scanner.getBlockMeta(0, 0, 0).state.facing turtle.point.heading = Point.facings[facing].heading + print('Fuel: ' .. turtle.getFuelLevel()) + --turtle.setPolicy('digOnly') turtle.setMovementStrategy('goto') repeat diff --git a/farms/cows.lua b/farms/rancher.lua similarity index 94% rename from farms/cows.lua rename to farms/rancher.lua index 118abcb..5a862d8 100644 --- a/farms/cows.lua +++ b/farms/rancher.lua @@ -17,9 +17,9 @@ local retain = Util.transpose { } local config = { animal = 'Cow', - max_cows = 15, + max_animals = 15, } -Config.load('cows', config) +Config.load('rancher', config) local sensor = device['plethora:sensor'] or turtle.equip('right', 'plethora:module:3') and device['plethora:sensor'] or @@ -50,7 +50,7 @@ local function turnOnWater() end end -local function getCowCount() +local function getAnimalCount() local blocks = sensor.sense() local grown = 0 @@ -114,8 +114,8 @@ local s, m = turtle.run(function() turnOffWater() repeat - local cowCount, xpCount = getCowCount() - if cowCount > config.max_cows then + local animalCount, xpCount = getAnimalCount() + if animalCount > config.max_animals then turtle.setStatus('Butchering') butcher() elseif turtle.getItemCount('minecraft:wheat') == 0 then diff --git a/pickup/pickupRemote.lua b/pickup/pickupRemote.lua index a8162bd..e6b3320 100644 --- a/pickup/pickupRemote.lua +++ b/pickup/pickupRemote.lua @@ -1,9 +1,12 @@ +_G.requireInjector(_ENV) + +local device = _G.device +local multishell = _G.multishell + if not device.wireless_modem then error('Wireless modem is required') end -requireInjector(getfenv(1)) - local Event = require('event') local GPS = require('gps') local Socket = require('socket') @@ -20,11 +23,10 @@ local mainPage = UI.Page({ y = 2, height = 8, menuItems = { + { prompt = 'Add', event = 'add_location', help = 'Add a new location' }, { prompt = 'Pickup', event = 'pickup', help = 'Pickup items from this location' }, - { prompt = 'Charge cell', event = 'charge', help = 'Recharge this cell' }, { prompt = 'Refill', event = 'refill', help = 'Recharge this cell' }, { prompt = 'Set drop off location', event = 'setPickup', help = 'Recharge this cell' }, - { prompt = 'Set recharge location', event = 'setRecharge', help = 'Recharge this cell' }, { prompt = 'Clear', event = 'clear', help = 'Remove this location' }, }, }), @@ -115,7 +117,7 @@ function refillPage:eventHandler(event) text = UI.Text({ x = 3, y = 3, value = 'Quantity' }), textEntry = UI.TextEntry({ x = 14, y = 3 }) }) - + dialog.eventHandler = function(self, event) if event.type == 'accept' then local l = tonumber(self.textEntry.value) @@ -129,10 +131,10 @@ function refillPage:eventHandler(event) end return true end - + return UI.Dialog.eventHandler(self, event) end - + dialog.titleBar.title = item.name dialog:setFocus(dialog.textEntry) UI:setPage(dialog) @@ -202,8 +204,7 @@ function mainPage:eventHandler(event) UI:setPage(refillPage) end - elseif event.type == 'pickup' or event.type == 'setPickup' or - event.type == 'setRecharge' or event.type == 'charge' or + elseif event.type == 'pickup' or event.type == 'setPickup' or event.type == 'clear' then local pt = getPoint() if pt then From e59a7a59a453e07fa707eb9b41d2e905434edb73 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Fri, 9 Nov 2018 11:35:27 -0500 Subject: [PATCH 071/165] app tweaks --- farms/farm.lua | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/farms/farm.lua b/farms/farm.lua index f5d6082..2a5a65d 100644 --- a/farms/farm.lua +++ b/farms/farm.lua @@ -139,13 +139,12 @@ local function harvest(blocks) elseif b.action == 'bump' then if turtle.faceAgainst(b) then turtle.equip('right', 'plethora:module:3') + os.sleep(.5) + -- search the ground for the dropped cactus local sensed = peripheral.call('right', 'sense') turtle.equip('right', 'minecraft:diamond_pickaxe') - os.sleep(.25) Util.filterInplace(sensed, function(s) if s.displayName == 'item.tile.cactus' and Point.distance(b, s) < 6 then - s.ex = s.x - s.ez = s.z s.x = Util.round(s.x) + turtle.point.x s.z = Util.round(s.z) + turtle.point.z s.y = -1 From 7d09073703d735da58ef54ae4930ee35710af415 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Fri, 9 Nov 2018 15:07:12 -0500 Subject: [PATCH 072/165] app tweaks --- farms/{farm.lua => farmer.lua} | 0 .../{activityMonitor.lua => activityView.lua} | 45 ++++++++++++------- 2 files changed, 30 insertions(+), 15 deletions(-) rename farms/{farm.lua => farmer.lua} (100%) rename milo/plugins/{activityMonitor.lua => activityView.lua} (78%) diff --git a/farms/farm.lua b/farms/farmer.lua similarity index 100% rename from farms/farm.lua rename to farms/farmer.lua diff --git a/milo/plugins/activityMonitor.lua b/milo/plugins/activityView.lua similarity index 78% rename from milo/plugins/activityMonitor.lua rename to milo/plugins/activityView.lua index 684599d..259877e 100644 --- a/milo/plugins/activityMonitor.lua +++ b/milo/plugins/activityView.lua @@ -13,7 +13,7 @@ if not mon then return end -local changedPage = UI.Window { +local page = UI.Window { parent = UI.Device { device = mon, textScale = .5, @@ -55,7 +55,14 @@ local changedPage = UI.Window { } } -function changedPage.grid:getDisplayValues(row) +function page.grid:getRowTextColor(row, selected) + if row.lastCount and row.lastCount ~= row.count then + return row.count > row.lastCount and colors.yellow or colors.lightGray + end + return UI.Grid:getRowTextColor(row, selected) +end + +function page.grid:getDisplayValues(row) row = Util.shallowCopy(row) local ind = '+' @@ -70,7 +77,7 @@ function changedPage.grid:getDisplayValues(row) return row end -function changedPage:eventHandler(event) +function page:eventHandler(event) if event.type == 'reset' then self.lastItems = nil self.grid:setValues({ }) @@ -93,7 +100,7 @@ function changedPage:eventHandler(event) return true end -function changedPage:refresh() +function page:refresh() local t = context.storage.cache if not self.lastItems then @@ -101,7 +108,7 @@ function changedPage:refresh() for k,v in pairs(t) do self.lastItems[k] = { displayName = v.displayName, - lastCount = v.count, + initialCount = v.count, } end self.timestamp = os.clock() @@ -109,6 +116,7 @@ function changedPage:refresh() else for _,v in pairs(self.lastItems) do + v.lastCount = v.count v.count = nil end @@ -122,7 +130,7 @@ function changedPage:refresh() self.lastItems[k] = { displayName = v.displayName, count = v.count, - lastCount = 0, + initialCount = 0, } end end @@ -132,8 +140,8 @@ function changedPage:refresh() if not v.count then v.count = 0 end - if v.count ~= v.lastCount then - v.change = v.count - v.lastCount + if v.count ~= v.initialCount then + v.change = v.count - v.initialCount v.rate = Util.round(60 / self.elapsed * v.change, 1) changedItems[k] = v end @@ -146,14 +154,21 @@ end Event.onInterval(5, function() if context.storage:isOnline() then - changedPage:refresh() - changedPage:sync() + page:refresh() + page:sync() else - changedPage.grid:clear() - changedPage.grid:centeredWrite(math.ceil(changedPage.height / 2), 'Storage Offline') - changedPage:sync() + page.grid:clear() + page.grid:centeredWrite(math.ceil(page.height / 2), 'Storage Offline') + page:sync() end end) -changedPage:draw() -changedPage:sync() +Event.on('monitor_touch', function(_, side) + if side == mon.side then + page:emit({ type = 'reset' }) + page:sync() + end +end) + +page:draw() +page:sync() From aff05f8587e1373a103056038260e4a9a6f5e641 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sat, 10 Nov 2018 13:04:23 -0500 Subject: [PATCH 073/165] rework milo crafting --- milo/apis/milo.lua | 6 +- milo/apis/storage.lua | 20 ++--- milo/apis/turtle/craft.lua | 146 ++++++++++++++-------------------- milo/plugins/activityView.lua | 54 ++----------- milo/plugins/craftTask.lua | 96 +++++----------------- milo/plugins/demandCraft.lua | 2 +- milo/plugins/jobMonitor.lua | 9 ++- miners/scanningMiner.lua | 4 - 8 files changed, 103 insertions(+), 234 deletions(-) diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index 87ae054..1025709 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -23,11 +23,11 @@ function Milo:requestCrafting(item) local key = Milo:uniqueKey(item) if not self.context.craftingQueue[key] then - item.ingredients = { - [ key ] = item - } item.crafted = 0 + item.pending = { } + item.key = key self.context.craftingQueue[key] = item + os.queueEvent('milo_cycle') end end diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 2b22130..ce05b5d 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -27,12 +27,12 @@ listCount = 0, self.localName = modem.getNameLocal() Event.on({ 'device_attach', 'device_detach' }, function(e, dev) -_debug('%s: %s', e, tostring(dev)) +--_debug('%s: %s', e, tostring(dev)) self:initStorage() end) Event.onInterval(15, function() self:showStorage() - _debug('STORAGE: cache: %d/%d', self.hits, self.misses) +-- _debug('STORAGE: cache: %d/%d', self.hits, self.misses) end) end @@ -202,9 +202,9 @@ function Storage:provide(item, qty, slot, direction) local amount = adapter:provide(item, qty, slot, direction or self.localName) if amount > 0 then self.hits = self.hits + 1 - _debug('EXT: %s(%d): %s -> %s%s', - item.name, amount, adapter.name, direction or self.localName, - slot and string.format('[%d]', slot) or '') +-- _debug('EXT: %s(%d): %s -> %s%s', +-- item.name, amount, adapter.name, direction or self.localName, +-- slot and string.format('[%d]', slot) or '') self.dirty = true adapter.dirty = true end @@ -222,9 +222,9 @@ function Storage:provide(item, qty, slot, direction) for _, adapter in self:onlineAdapters() do local amount = adapter:provide(item, qty, slot, direction or self.localName) if amount > 0 then -_debug('EXT: %s(%d): %s -> %s%s', - item.name, amount, adapter.name, direction or self.localName, - slot and string.format('[%d]', slot) or '') +--_debug('EXT: %s(%d): %s -> %s%s', +-- item.name, amount, adapter.name, direction or self.localName, +-- slot and string.format('[%d]', slot) or '') self.dirty = true adapter.dirty = true end @@ -241,8 +241,8 @@ end function Storage:trash(source, slot, count) local trashcan = Util.find(self.remoteDefaults, 'mtype', 'trashcan') if trashcan and trashcan.adapter and trashcan.adapter.online then -_debug('TRA: %s[%d] (%d)', source or self.localName, slot, count or 64) - return trashcan.adapter.pullItems(source or self.localName, slot, count) +--_debug('TRA: %s[%d] (%d)', source or self.localName, slot, count or 64) +-- return trashcan.adapter.pullItems(source or self.localName, slot, count) end return 0 end diff --git a/milo/apis/turtle/craft.lua b/milo/apis/turtle/craft.lua index d05dd4a..8c2dea9 100644 --- a/milo/apis/turtle/craft.lua +++ b/milo/apis/turtle/craft.lua @@ -73,23 +73,10 @@ function Craft.sumIngredients(recipe) for _,item in pairs(recipe.ingredients) do t[item] = (t[item] or 0) + 1 end --- need a check for crafting tool return t end -local function machineCraft(recipe, inventoryAdapter, machineName, request, count) - if request.pending then - local imported = (inventoryAdapter.activity[recipe.result] or 0) - request.pending = request.pending - imported - request.crafted = request.crafted + imported - if request.pending <= 0 then - request.pending = nil - request.statusCode = nil - request.status = nil - end - return - end - +local function machineCraft(recipe, inventoryAdapter, machineName, request, count, item) local machine = device[machineName] if not machine then request.status = 'machine not found' @@ -126,7 +113,7 @@ local function machineCraft(recipe, inventoryAdapter, machineName, request, coun end request.status = 'processing' request.statusCode = Craft.STATUS_INFO - request.pending = count * recipe.count + item.pending[recipe.result] = count * recipe.count end local function turtleCraft(recipe, inventoryAdapter, request, count) @@ -149,12 +136,28 @@ local function turtleCraft(recipe, inventoryAdapter, request, count) turtle.select(1) if turtle.craft() then request.crafted = request.crafted + count * recipe.count + request.status = 'crafted' + request.statusCode = Craft.STATUS_INFO return true end request.status = 'Failed to craft' request.statusCode = Craft.STATUS_ERROR end +function Craft.processPending(item, inventoryAdapter) + for key, count in pairs(item.pending) do + local imported = inventoryAdapter.activity[key] + if imported then + local amount = math.min(imported, count) + inventoryAdapter.activity[key] = imported - amount + item.pending[key] = count - amount + if item.pending[key] <= 0 then + item.pending[key] = nil + end + end + end +end + function Craft.craftRecipe(recipe, count, inventoryAdapter, origItem) if type(recipe) == 'string' then recipe = Craft.recipes[recipe] @@ -163,89 +166,43 @@ function Craft.craftRecipe(recipe, count, inventoryAdapter, origItem) end end - -- wait til all requests have been completed - local isPending = false - for key, request in pairs(origItem.ingredients) do - if request.pending then - local irecipe = Craft.findRecipe(key) - machineCraft(irecipe, inventoryAdapter, - Craft.machineLookup[irecipe.result], request) - isPending = request.pending or isPending - end - end + local crafted = Craft.craftRecipeInternal(recipe, count, inventoryAdapter, origItem) - local crafted = 0 - --if not isPending then - for _,key in pairs(Util.keys(origItem.ingredients)) do - local e = origItem.ingredients[key] - if e and e.transient then - origItem.ingredients[key] = nil - end - end - crafted = Craft.craftRecipeInternal(recipe, count, inventoryAdapter, origItem) - --end - - for _, request in pairs(origItem.ingredients) do - if request.crafted >= request.count then -if request.pending then - _debug('??') - _debug(request) -end - request.status = nil - request.statusCode = Craft.STATUS_SUCCESS - end - end + origItem.crafted = math.min(origItem.count, origItem.crafted + crafted) return crafted end +local function adjustCounts(recipe, count, ingredients) + -- decrement ingredients used + for key,icount in pairs(Craft.sumIngredients(recipe)) do + ingredients[key].count = ingredients[key].count - (icount * count) + end + + -- increment crafted + local result = ingredients[recipe.result] + result.count = result.count + (count * recipe.count) +end + function Craft.craftRecipeInternal(recipe, count, inventoryAdapter, origItem) - local items = inventoryAdapter:listItems() - if not items then - return 0, 'Inventory changed' - end - local request = origItem.ingredients[recipe.result] - if not request then - request = { - crafted = 0, - count = count, - } - origItem.ingredients[recipe.result] = request - end - if request.pending then - --machineCraft(recipe, inventoryAdapter, - -- Craft.machineLookup[recipe.result], request) + local canCraft = Craft.getCraftableAmount(recipe, count, origItem.ingredients) + if not origItem.forceCrafting and canCraft == 0 then return 0 end - local canCraft = Craft.getCraftableAmount(recipe, count, items, { }) - if canCraft == 0 then - - local resourceList = Craft.getResourceList(recipe, items, count) -_G._p2 = resourceList - for k,v in pairs(resourceList) do - if v.need > 0 then - if not origItem.ingredients[k] then - origItem.ingredients[k] = { - status = 'No recipe', - statusCode = Craft.STATUS_ERROR, - count = v.need, - crafted = 0, - transient = true, - } - end - end - end - return 0 + canCraft = math.ceil(canCraft / recipe.count) + if origItem.forceCrafting then + count = math.ceil(count / recipe.count) + else + count = canCraft end - count = math.ceil(canCraft / recipe.count) local maxCount = recipe.maxCount or math.floor(64 / recipe.count) for key,icount in pairs(Craft.sumIngredients(recipe)) do - local itemCount = Craft.getItemCount(items, key) + local itemCount = Craft.getItemCount(origItem.ingredients, key) local need = icount * count if recipe.craftingTools and recipe.craftingTools[key] then need = 1 @@ -258,27 +215,40 @@ _G._p2 = resourceList end local iqty = need - itemCount local crafted = Craft.craftRecipeInternal(irecipe, iqty, inventoryAdapter, origItem) - if crafted ~= iqty then + if not origItem.forceCrafting and crafted < iqty then return 0 end + if origItem.forceCrafting and crafted < iqty then + canCraft = math.floor((itemCount + crafted) / icount) + end end end + local crafted = 0 - repeat - local batch = math.min(count, maxCount) + while canCraft > 0 do + if origItem.pending[recipe.result] then + request.status = 'processing' + request.statusCode = Craft.STATUS_INFO + break + end + + local batch = math.min(canCraft, maxCount) + if Craft.machineLookup[recipe.result] then if not machineCraft(recipe, inventoryAdapter, - Craft.machineLookup[recipe.result], request, batch) then + Craft.machineLookup[recipe.result], request, batch, origItem) then break end elseif not turtleCraft(recipe, inventoryAdapter, request, batch) then break end + adjustCounts(recipe, batch, origItem.ingredients) + crafted = crafted + batch - count = count - maxCount - until count <= 0 + canCraft = canCraft - maxCount + end return crafted * recipe.count end diff --git a/milo/plugins/activityView.lua b/milo/plugins/activityView.lua index 259877e..44b420f 100644 --- a/milo/plugins/activityView.lua +++ b/milo/plugins/activityView.lua @@ -19,7 +19,6 @@ local page = UI.Window { textScale = .5, }, grid = UI.Grid { - ey = -6, columns = { { heading = 'Qty', key = 'count', width = 6 }, { heading = 'Change', key = 'change', width = 6 }, @@ -28,31 +27,6 @@ local page = UI.Window { }, sortColumn = 'displayName', }, - buttons = UI.Window { - y = -5, height = 5, - backgroundColor = colors.gray, - prevButton = UI.Button { - x = 2, y = 2, height = 3, width = 5, - event = 'previous', - backgroundColor = colors.lightGray, - text = ' < ' - }, - resetButton = UI.Button { - x = 8, y = 2, height = 3, ex = -8, - event = 'reset', - backgroundColor = colors.lightGray, - text = 'Reset' - }, - nextButton = UI.Button { - x = -6, y = 2, height = 3, width = 5, - event = 'next', - backgroundColor = colors.lightGray, - text = ' > ' - }, - }, - accelerators = { - q = 'quit', - } } function page.grid:getRowTextColor(row, selected) @@ -77,27 +51,11 @@ function page.grid:getDisplayValues(row) return row end -function page:eventHandler(event) - if event.type == 'reset' then - self.lastItems = nil - self.grid:setValues({ }) - self.grid:clear() - self.grid:draw() - - elseif event.type == 'next' then - self.grid:nextPage() - - elseif event.type == 'previous' then - self.grid:previousPage() - - elseif event.type == 'quit' then - Event.exitPullEvents() - - else - return UI.Window.eventHandler(self, event) - end - - return true +function page:reset() + self.lastItems = nil + self.grid:setValues({ }) + self.grid:clear() + self.grid:draw() end function page:refresh() @@ -165,7 +123,7 @@ end) Event.on('monitor_touch', function(_, side) if side == mon.side then - page:emit({ type = 'reset' }) + page:reset() page:sync() end end) diff --git a/milo/plugins/craftTask.lua b/milo/plugins/craftTask.lua index f0e59ee..ea01318 100644 --- a/milo/plugins/craftTask.lua +++ b/milo/plugins/craftTask.lua @@ -1,5 +1,4 @@ local Craft = require('turtle.craft') -local itemDB = require('itemDB') local Milo = require('milo') local sync = require('sync').sync local Util = require('util') @@ -12,89 +11,30 @@ local craftTask = { priority = 70, } -function craftTask:craftItem(recipe, item, count) - Craft.craftRecipe(recipe, count, context.storage, item) - Milo:clearGrid() -end - --- Craft as much as possible regardless if all ingredients are available -function craftTask:forceCraftItem(inRecipe, originalItem, inCount) - local summed = { } - local items = Milo:listItems() - local throttle = Util.throttle() - - local function sumItems(recipe, count) - count = math.ceil(count / recipe.count) - local craftable = count - - for key,iqty in pairs(Craft.sumIngredients(recipe)) do - throttle() - local item = itemDB:splitKey(key) - local summedItem = summed[key] - if not summedItem then - summedItem = Util.shallowCopy(item) - summedItem.recipe = Craft.findRecipe(item) - summedItem.count = Craft.getItemCount(items, key) - summedItem.need = 0 - summedItem.used = 0 - summedItem.craftable = 0 - summed[key] = summedItem - end - - local total = count * iqty -- 4 * 2 - local used = math.min(summedItem.count, total) -- 5 - local need = total - used -- 3 - - if recipe.craftingTools and recipe.craftingTools[key] then - if summedItem.count > 0 then - summedItem.used = 1 - summedItem.need = 0 - need = 0 - elseif not summedItem.recipe then - summedItem.need = 1 - need = 1 - else - need = 1 - end - else - summedItem.count = summedItem.count - used - summedItem.used = summedItem.used + used - end - - if need > 0 then - if not summedItem.recipe then - craftable = math.min(craftable, math.floor(used / iqty)) - summedItem.need = summedItem.need + need - else - local c = sumItems(summedItem.recipe, need) -- 4 - craftable = math.min(craftable, math.floor((used + c) / iqty)) - summedItem.craftable = summedItem.craftable + c - end - end - end - - if craftable > 0 then - craftable = Craft.craftRecipe(recipe, craftable * recipe.count, - context.storage, originalItem) / recipe.count - Milo:clearGrid() - end - - return craftable * recipe.count - end - - return sumItems(inRecipe, inCount) -end - function craftTask:craft(recipe, item) if Milo:isCraftingPaused() then return end - if item.forceCrafting then - self:forceCraftItem(recipe, item, item.count - item.crafted) - else - self:craftItem(recipe, item, item.count - item.crafted) + Craft.processPending(item, context.storage) + + item.ingredients = Craft.getResourceList(recipe, Milo:listItems(), item.count - item.crafted) + + for k, v in pairs(item.ingredients) do + v.crafted = v.used + v.count = v.used + v.key = k + if v.need > 0 then + v.status = 'No recipe' + v.statusCode = Craft.STATUS_ERROR + end end + item.ingredients[recipe.result] = Util.shallowCopy(item) + item.ingredients[recipe.result].total = item.count + item.ingredients[recipe.result].crafted = item.crafted + + Craft.craftRecipe(recipe, item.count - item.crafted, context.storage, item) + Milo:clearGrid() end function craftTask:cycle() diff --git a/milo/plugins/demandCraft.lua b/milo/plugins/demandCraft.lua index a1afa66..bc059dd 100644 --- a/milo/plugins/demandCraft.lua +++ b/milo/plugins/demandCraft.lua @@ -100,7 +100,7 @@ function craftPage.wizard.pages.resources:enable() local count = tonumber(self.parent.quantity.count.value) local recipe = Craft.findRecipe(craftPage.item) if recipe then - local ingredients = Craft.getResourceList4(recipe, items, count) + local ingredients = Craft.getResourceList(recipe, items, count) for _,v in pairs(ingredients) do v.displayName = itemDB:getName(v) end diff --git a/milo/plugins/jobMonitor.lua b/milo/plugins/jobMonitor.lua index 9d1a99e..da4fc30 100644 --- a/milo/plugins/jobMonitor.lua +++ b/milo/plugins/jobMonitor.lua @@ -25,7 +25,12 @@ local jobMonitor = UI.Page { { heading = 'Qty', key = 'remaining', width = 4 }, { heading = 'Crafting', key = 'displayName', }, { heading = 'Status', key = 'status', }, - { heading = 'Progress', key = 'progress', width = 8 }, + { heading = 'need', key = 'need', width = 4 }, + { heading = 'total', key = 'total', width = 4 }, + { heading = 'used', key = 'used', width = 4 }, + { heading = 'count', key = 'count', width = 4 }, + { heading = 'crafted', key = 'crafted', width = 4 }, +-- { heading = 'Progress', key = 'progress', width = 8 }, }, }, } @@ -44,7 +49,7 @@ function jobMonitor:updateList(craftList) v.index = #t v.showRemaining = true for k2,v2 in pairs(v.ingredients) do - if v2 ~= v then + if v2.key ~= v.key and v2.statusCode then table.insert(t, v2) if not v2.displayName then v2.displayName = itemDB:getName(k2) diff --git a/miners/scanningMiner.lua b/miners/scanningMiner.lua index 59175a3..1c3cbe4 100644 --- a/miners/scanningMiner.lua +++ b/miners/scanningMiner.lua @@ -425,10 +425,6 @@ local function scan() page.statusBar:setValue('status', b.name .. ' ' .. m) page.statusBar:draw() page:sync() - if debug and type(debug) == 'function' then - debug(b.name .. ' ' .. m) - end --- os.sleep(3) else page.statusBar:setValue('mining', m) end From bc9028f0c7777dd6148a015ebba09cda9850985a Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sat, 10 Nov 2018 21:01:53 -0500 Subject: [PATCH 074/165] rework milo crafting --- milo/apis/milo.lua | 6 +-- milo/apis/storage.lua | 27 ++++------ milo/apis/turtle/craft.lua | 90 +++++++++++++++++++--------------- milo/apps/water.lua | 17 +++++++ milo/plugins/craftTask.lua | 30 +++++++++--- milo/plugins/demandCraft.lua | 6 +-- milo/plugins/importTask.lua | 23 +++++++-- milo/plugins/jobMonitor.lua | 11 ++--- milo/plugins/remote.lua | 2 +- milo/plugins/replenishTask.lua | 4 +- 10 files changed, 131 insertions(+), 85 deletions(-) create mode 100644 milo/apps/water.lua diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index 1025709..6c5668e 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -74,7 +74,7 @@ function Milo:resetCraftingStatus() for _,key in pairs(Util.keys(self.context.craftingQueue)) do local item = self.context.craftingQueue[key] - if item.crafted >= item.count then + if item.crafted >= item.requested then self.context.craftingQueue[key] = nil end end @@ -193,7 +193,7 @@ end function Milo:craftAndEject(item, count) local request = self:makeRequest(item, count, function(request) -- eject rest when finished crafted - return self:eject(item, request.count) + return self:eject(item, request.requested) end) -- predict that we will eject that amount @@ -238,7 +238,7 @@ function Milo:makeRequest(item, count, callback) if request.craft > 0 then item = Util.shallowCopy(item) - item.count = request.craft + item.requested = request.craft item.callback = callback self:requestCrafting(item) end diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index ce05b5d..3cd3631 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -27,12 +27,12 @@ listCount = 0, self.localName = modem.getNameLocal() Event.on({ 'device_attach', 'device_detach' }, function(e, dev) ---_debug('%s: %s', e, tostring(dev)) +_debug('%s: %s', e, tostring(dev)) self:initStorage() end) Event.onInterval(15, function() self:showStorage() --- _debug('STORAGE: cache: %d/%d', self.hits, self.misses) + _debug('STORAGE: cache: %d/%d', self.hits, self.misses) end) end @@ -248,17 +248,8 @@ function Storage:trash(source, slot, count) end function Storage:import(source, slot, count, item) - return self:insert(slot, count, nil, item, source) -end - -function Storage:insert(slot, qty, toSlot, item, source) local total = 0 - -- toSlot is not really valid with this adapter - if toSlot then - error('Storage: toSlot is not valid') - end - local key = table.concat({ item.name, item.damage, item.nbtHash }, ':') if not self.cache then @@ -266,9 +257,9 @@ function Storage:insert(slot, qty, toSlot, item, source) end local function insert(adapter) - local amount = adapter:insert(slot, qty, toSlot, source or self.localName) + local amount = adapter:insert(slot, count, nil, source or self.localName) if amount > 0 then -_debug('INS: %s(%d): %s[%d] -> %s', +_G._debug('INS: %s(%d): %s[%d] -> %s', item.name, amount, source or self.localName, slot, adapter.name) self.dirty = true @@ -284,7 +275,7 @@ _debug('INS: %s(%d): %s[%d] -> %s', end ]] end - qty = qty - amount + count = count - amount total = total + amount end @@ -293,8 +284,8 @@ _debug('INS: %s(%d): %s[%d] -> %s', -- TODO: proper checking using ignore dmg/nbt if remote.lock == key or remote.lock == item.name then insert(remote.adapter) - if qty > 0 then -- TODO: only if void flag set - total = total + self:trash(source, slot, qty) + if count > 0 then -- TODO: only if void flag set + total = total + self:trash(source, slot, count) end return total end @@ -302,7 +293,7 @@ _debug('INS: %s(%d): %s[%d] -> %s', if self.cache[key] then -- is this item in some chest for _, adapter in self:onlineAdapters() do - if qty <= 0 then + if count <= 0 then break end if adapter.cache and adapter.cache[key] and not adapter.lock then @@ -313,7 +304,7 @@ _debug('INS: %s(%d): %s[%d] -> %s', -- high to low priority for remote in self:onlineAdapters() do - if qty <= 0 then + if count <= 0 then break end if not remote.lock then diff --git a/milo/apis/turtle/craft.lua b/milo/apis/turtle/craft.lua index 8c2dea9..c3e25ee 100644 --- a/milo/apis/turtle/craft.lua +++ b/milo/apis/turtle/craft.lua @@ -16,9 +16,9 @@ local Craft = { MACHINE_LOOKUP = 'usr/config/machine_crafting.db', } -local function clearGrid(inventoryAdapter) +local function clearGrid(storage) turtle.eachFilledSlot(function(slot) - inventoryAdapter:insert(slot.index, slot.count, nil, slot) + storage:import(storage.localName, slot.index, slot.count, slot) end) for i = 1, 16 do @@ -76,7 +76,7 @@ function Craft.sumIngredients(recipe) return t end -local function machineCraft(recipe, inventoryAdapter, machineName, request, count, item) +local function machineCraft(recipe, storage, machineName, request, count, item) local machine = device[machineName] if not machine then request.status = 'machine not found' @@ -94,7 +94,7 @@ local function machineCraft(recipe, inventoryAdapter, machineName, request, coun local xferred = { } for k,v in pairs(recipe.ingredients) do - local provided = inventoryAdapter:provide(splitKey(v), count, k, machineName) + local provided = storage:provide(splitKey(v), count, k, machineName) xferred[k] = { key = v, count = provided, @@ -103,7 +103,7 @@ local function machineCraft(recipe, inventoryAdapter, machineName, request, coun -- take back out whatever we put in for k2,v2 in pairs(xferred) do if v2.count > 0 then - inventoryAdapter:import(machineName, k2, v2.count, splitKey(v2.key)) + storage:import(machineName, k2, v2.count, splitKey(v2.key)) end end request.status = 'Invalid recipe' @@ -116,8 +116,8 @@ local function machineCraft(recipe, inventoryAdapter, machineName, request, coun item.pending[recipe.result] = count * recipe.count end -local function turtleCraft(recipe, inventoryAdapter, request, count) - if not clearGrid(inventoryAdapter) then +local function turtleCraft(recipe, storage, request, count) + if not clearGrid(storage) then request.status = 'grid in use' request.statusCode = Craft.STATUS_ERROR return @@ -125,7 +125,7 @@ local function turtleCraft(recipe, inventoryAdapter, request, count) for k,v in pairs(recipe.ingredients) do local item = splitKey(v) - if inventoryAdapter:provide(item, count, k) ~= count then + if storage:provide(item, count, k) ~= count then -- FIX: ingredients cannot be stacked request.status = 'unknown error' request.statusCode = Craft.STATUS_ERROR @@ -137,20 +137,23 @@ local function turtleCraft(recipe, inventoryAdapter, request, count) if turtle.craft() then request.crafted = request.crafted + count * recipe.count request.status = 'crafted' - request.statusCode = Craft.STATUS_INFO - return true + request.statusCode = Craft.STATUS_SUCCESS + else + request.status = 'Failed to craft' + request.statusCode = Craft.STATUS_ERROR end - request.status = 'Failed to craft' - request.statusCode = Craft.STATUS_ERROR + clearGrid(storage) + return request.statusCode == Craft.STATUS_SUCCESS end -function Craft.processPending(item, inventoryAdapter) +function Craft.processPending(item, storage) for key, count in pairs(item.pending) do - local imported = inventoryAdapter.activity[key] + local imported = storage.activity[key] if imported then local amount = math.min(imported, count) - inventoryAdapter.activity[key] = imported - amount + storage.activity[key] = imported - amount item.pending[key] = count - amount + item.ingredients[key].crafted = item.ingredients[key].crafted + amount if item.pending[key] <= 0 then item.pending[key] = nil end @@ -158,7 +161,7 @@ function Craft.processPending(item, inventoryAdapter) end end -function Craft.craftRecipe(recipe, count, inventoryAdapter, origItem) +function Craft.craftRecipe(recipe, count, storage, origItem) if type(recipe) == 'string' then recipe = Craft.recipes[recipe] if not recipe then @@ -166,11 +169,7 @@ function Craft.craftRecipe(recipe, count, inventoryAdapter, origItem) end end - local crafted = Craft.craftRecipeInternal(recipe, count, inventoryAdapter, origItem) - - origItem.crafted = math.min(origItem.count, origItem.crafted + crafted) - - return crafted + return Craft.craftRecipeInternal(recipe, count, storage, origItem) end local function adjustCounts(recipe, count, ingredients) @@ -184,9 +183,15 @@ local function adjustCounts(recipe, count, ingredients) result.count = result.count + (count * recipe.count) end -function Craft.craftRecipeInternal(recipe, count, inventoryAdapter, origItem) +function Craft.craftRecipeInternal(recipe, count, storage, origItem) local request = origItem.ingredients[recipe.result] + if origItem.pending[recipe.result] then + request.status = 'processing' + request.statusCode = Craft.STATUS_INFO + return 0 + end + local canCraft = Craft.getCraftableAmount(recipe, count, origItem.ingredients) if not origItem.forceCrafting and canCraft == 0 then return 0 @@ -199,6 +204,8 @@ function Craft.craftRecipeInternal(recipe, count, inventoryAdapter, origItem) count = canCraft end +_G._debug({'eval', recipe.result, count }) + local maxCount = recipe.maxCount or math.floor(64 / recipe.count) for key,icount in pairs(Craft.sumIngredients(recipe)) do @@ -213,8 +220,9 @@ function Craft.craftRecipeInternal(recipe, count, inventoryAdapter, origItem) if not irecipe then return 0 end + local iqty = need - itemCount - local crafted = Craft.craftRecipeInternal(irecipe, iqty, inventoryAdapter, origItem) + local crafted = Craft.craftRecipeInternal(irecipe, iqty, storage, origItem) if not origItem.forceCrafting and crafted < iqty then return 0 end @@ -224,23 +232,16 @@ function Craft.craftRecipeInternal(recipe, count, inventoryAdapter, origItem) end end - local crafted = 0 while canCraft > 0 do - if origItem.pending[recipe.result] then - request.status = 'processing' - request.statusCode = Craft.STATUS_INFO - break - end - local batch = math.min(canCraft, maxCount) - - if Craft.machineLookup[recipe.result] then - if not machineCraft(recipe, inventoryAdapter, - Craft.machineLookup[recipe.result], request, batch, origItem) then + local machine = Craft.machineLookup[recipe.result] +_G._debug({ 'crafting', recipe.result, batch }) + if machine then + if not machineCraft(recipe, storage, machine, request, batch, origItem) then break end - elseif not turtleCraft(recipe, inventoryAdapter, request, batch) then + elseif not turtleCraft(recipe, storage, request, batch) then break end @@ -275,7 +276,7 @@ end -- determine the full list of ingredients needed to craft -- a quantity of a recipe. -function Craft.getResourceList(inRecipe, items, inCount) +function Craft.getResourceList(inRecipe, items, inCount, pending) local summed = { } local function sumItems(recipe, key, count) @@ -295,6 +296,10 @@ function Craft.getResourceList(inRecipe, items, inCount) local used = math.min(summedItem.count, total) local need = total - used + if pending and pending[key] then + need = need - pending[key] + end + if recipe.craftingTools and recipe.craftingTools[key] then summedItem.total = 1 if summedItem.count > 0 then @@ -325,8 +330,13 @@ function Craft.getResourceList(inRecipe, items, inCount) end inCount = math.ceil(inCount / inRecipe.count) - for ikey,iqty in pairs(Craft.sumIngredients(inRecipe)) do - sumItems(inRecipe, ikey, math.ceil(inCount * iqty)) + if pending and pending[inRecipe.result] then + inCount = inCount - pending[inRecipe.result] + end + if inCount > 0 then + for ikey,iqty in pairs(Craft.sumIngredients(inRecipe)) do + sumItems(inRecipe, ikey, math.ceil(inCount * iqty)) + end end return summed @@ -339,7 +349,7 @@ function Craft.getResourceList4(inRecipe, items, count) end -- given a certain quantity, return how many of those can be crafted -function Craft.getCraftableAmount(inRecipe, count, items, missing) +function Craft.getCraftableAmount(inRecipe, inCount, items, missing) local function sumItems(recipe, summedItems, count) local canCraft = 0 @@ -367,7 +377,7 @@ function Craft.getCraftableAmount(inRecipe, count, items, missing) return canCraft end - return sumItems(inRecipe, { }, math.ceil(count / inRecipe.count)) + return sumItems(inRecipe, { }, math.ceil(inCount / inRecipe.count)) end function Craft.loadRecipes() diff --git a/milo/apps/water.lua b/milo/apps/water.lua new file mode 100644 index 0000000..15fd48a --- /dev/null +++ b/milo/apps/water.lua @@ -0,0 +1,17 @@ +local os = _G.os +local turtle = _G.turtle + +while true do + turtle.placeDown('minecraft:bucket:0') + turtle.placeDown('minecraft:glass_bottle:0') + for k,v in pairs(turtle.getInventory()) do + if v.name == 'minecraft:concrete_powder' then + turtle.select(k) + for _ = 1, v.count do + turtle.placeDown() + turtle.digDown() + end + end + end + os.pullEvent('turtle_inventory') +end diff --git a/milo/plugins/craftTask.lua b/milo/plugins/craftTask.lua index ea01318..75711e0 100644 --- a/milo/plugins/craftTask.lua +++ b/milo/plugins/craftTask.lua @@ -15,10 +15,11 @@ function craftTask:craft(recipe, item) if Milo:isCraftingPaused() then return end - Craft.processPending(item, context.storage) - item.ingredients = Craft.getResourceList(recipe, Milo:listItems(), item.count - item.crafted) +--TODO: this needs to take into account what is pending + item.ingredients = Craft.getResourceList( + recipe, Milo:listItems(), item.requested - item.crafted, item.pending) for k, v in pairs(item.ingredients) do v.crafted = v.used @@ -29,24 +30,39 @@ function craftTask:craft(recipe, item) v.statusCode = Craft.STATUS_ERROR end end - item.ingredients[recipe.result] = Util.shallowCopy(item) + item.ingredients[recipe.result] = item item.ingredients[recipe.result].total = item.count item.ingredients[recipe.result].crafted = item.crafted - Craft.craftRecipe(recipe, item.count - item.crafted, context.storage, item) - Milo:clearGrid() +_G._p2 = item +if not item.history then + item.history = { } +end +local t = Util.shallowCopy(item) +t.history = { input = { }, output = { } } +for k,v in pairs(item.ingredients) do + t.history.input[k] = Util.shallowCopy(v) +end +table.insert(item.history, t) + + Craft.craftRecipe(recipe, item.requested - item.crafted, context.storage, item) + +for k,v in pairs(item.ingredients) do + t.history.output[k] = Util.shallowCopy(v) +end + end function craftTask:cycle() for _,key in pairs(Util.keys(context.craftingQueue)) do local item = context.craftingQueue[key] - if item.count - item.crafted > 0 then + if item.requested - item.crafted > 0 then local recipe = Craft.findRecipe(key) if recipe then sync(turtle, function() self:craft(recipe, item) end) - if item.callback and item.crafted >= item.count then + if item.callback and item.crafted >= item.requested then item.callback(item) -- invoke callback end elseif not context.controllerAdapter then diff --git a/milo/plugins/demandCraft.lua b/milo/plugins/demandCraft.lua index bc059dd..be09056 100644 --- a/milo/plugins/demandCraft.lua +++ b/milo/plugins/demandCraft.lua @@ -100,7 +100,7 @@ function craftPage.wizard.pages.resources:enable() local count = tonumber(self.parent.quantity.count.value) local recipe = Craft.findRecipe(craftPage.item) if recipe then - local ingredients = Craft.getResourceList(recipe, items, count) + local ingredients = Craft.getResourceList4(recipe, items, count) for _,v in pairs(ingredients) do v.displayName = itemDB:getName(v) end @@ -117,11 +117,11 @@ function craftPage:eventHandler(event) elseif event.type == 'accept' then local item = Util.shallowCopy(self.item) - item.count = tonumber(self.wizard.pages.quantity.count.value) + item.requested = tonumber(self.wizard.pages.quantity.count.value) item.forceCrafting = true if self.wizard.pages.quantity.eject.value then item.callback = function(request) - Milo:eject(item, request.count) + Milo:eject(item, request.requested) end end Milo:requestCrafting(item) diff --git a/milo/plugins/importTask.lua b/milo/plugins/importTask.lua index 4ef61b8..059ba89 100644 --- a/milo/plugins/importTask.lua +++ b/milo/plugins/importTask.lua @@ -9,22 +9,35 @@ local function filter(a) return a.imports end --- TODO: ignore damage/nbt - function ImportTask:cycle(context) for inventory in context.storage:filterActive('machine', filter) do for _, entry in pairs(inventory.imports) do + local function itemMatchesFilter(item) + if not entry.ignoreDamage and not entry.ignoreNbtHash then + return entry.filter[item.key] + end + + for key in pairs(entry.filter) do + local v = Milo:splitKey(key) + if item.name == v.name and + (entry.ignoreDamage or item.damage == v.damage) and + (entry.ignoreNbtHash or item.nbtHash == v.nbtHash) then + return true + end + end + end + local function matchesFilter(item) if not entry.filter then return true end - local key = Milo:uniqueKey(item) if entry.blacklist then - return not entry.filter[key] + return not itemMatchesFilter(item) end - return entry.filter[key] + + return itemMatchesFilter(item) end local function importSlot(slotNo) diff --git a/milo/plugins/jobMonitor.lua b/milo/plugins/jobMonitor.lua index da4fc30..53411fe 100644 --- a/milo/plugins/jobMonitor.lua +++ b/milo/plugins/jobMonitor.lua @@ -47,9 +47,8 @@ function jobMonitor:updateList(craftList) for _,v in pairs(craftList) do table.insert(t, v) v.index = #t - v.showRemaining = true - for k2,v2 in pairs(v.ingredients) do - if v2.key ~= v.key and v2.statusCode then + for k2,v2 in pairs(v.ingredients or { }) do + if v2.key ~= v.key --[[and v2.statusCode ]] then table.insert(t, v2) if not v2.displayName then v2.displayName = itemDB:getName(k2) @@ -67,12 +66,12 @@ end function jobMonitor.grid:getDisplayValues(row) row = Util.shallowCopy(row) - if row.showRemaining then - row.remaining = math.max(0, row.count - row.crafted) + if row.requested then + row.remaining = math.max(0, row.requested - row.crafted) else row.displayName = ' ' .. row.displayName end - row.progress = string.format('%d/%d', row.crafted, row.count) + --row.progress = string.format('%d/%d', row.crafted, row.count) return row end diff --git a/milo/plugins/remote.lua b/milo/plugins/remote.lua index e7100dc..d01b5b4 100644 --- a/milo/plugins/remote.lua +++ b/milo/plugins/remote.lua @@ -95,7 +95,7 @@ local function client(socket) local transferred = context.storage:export( context.localName, nil, - request.count, + request.requested, data.item) turtle.eachFilledSlot(function(slot) diff --git a/milo/plugins/replenishTask.lua b/milo/plugins/replenishTask.lua index 792043e..eb4605e 100644 --- a/milo/plugins/replenishTask.lua +++ b/milo/plugins/replenishTask.lua @@ -29,7 +29,7 @@ function ReplenishTask:cycle(context) Milo:requestCrafting({ damage = res.ignoreDamage and 0 or item.damage, nbtHash = nbtHash, - count = res.low - count, + requested = res.low - count, name = item.name, displayName = item.displayName, replenish = true, @@ -37,7 +37,7 @@ function ReplenishTask:cycle(context) else local request = context.craftingQueue[Milo:uniqueKey(item)] if request and request.replenish then - request.count = request.crafted + --request.count = request.crafted end end end From a00a66f4e626acd0c2b7a0bd4c9429913816bb15 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sun, 11 Nov 2018 01:04:24 -0500 Subject: [PATCH 075/165] milo cleanup --- milo/Milo.lua | 2 +- milo/apis/milo.lua | 2 +- milo/apis/storage.lua | 178 +++++++++++++++++++------------------ milo/apis/turtle/craft.lua | 40 +-------- milo/plugins/craftTask.lua | 4 +- milo/plugins/listing.lua | 68 +++++++------- 6 files changed, 136 insertions(+), 158 deletions(-) diff --git a/milo/Milo.lua b/milo/Milo.lua index df1cbe9..cdb16af 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -69,7 +69,7 @@ local context = { _G._p = context --debug Event.on('storage_offline', function() - Milo:showError('A storage chest has gone offline - See configuration screen') + Milo:showError('A storage chest has gone offline - Review configuration') end) Milo:init(context) diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index 6c5668e..b00749e 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -247,7 +247,7 @@ function Milo:makeRequest(item, count, callback) end function Milo:eject(item, count) - count = self.context.storage:provide(item, count) + count = self.context.storage:export(self.context.storage.localName, nil, count, item) turtle.emptyInventory() return count end diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 3cd3631..e0f3528 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -1,6 +1,7 @@ local class = require('class') local Event = require('event') local InventoryAdapter = require('inventoryAdapter') +local itemDB = require('itemDB') local Peripheral = require('peripheral') local Util = require('util') @@ -13,11 +14,8 @@ function Storage:init(args) local defaults = { remoteDefaults = { }, dirty = true, -listCount = 0, activity = { }, storageOnline = true, - hits = 0, - misses = 0, lastRefresh = os.clock(), } Util.merge(self, defaults) @@ -27,12 +25,11 @@ listCount = 0, self.localName = modem.getNameLocal() Event.on({ 'device_attach', 'device_detach' }, function(e, dev) -_debug('%s: %s', e, tostring(dev)) +_G._debug('%s: %s', e, tostring(dev)) self:initStorage() end) Event.onInterval(15, function() self:showStorage() - _debug('STORAGE: cache: %d/%d', self.hits, self.misses) end) end @@ -45,19 +42,11 @@ function Storage:showStorage() end end if #t > 0 then - _debug('Adapter:') + _G._debug('Adapter:') for _, k in pairs(t) do - _debug(' offline: ' .. k) + _G._debug(' offline: ' .. k) end - _debug('') - end -end - -function Storage:setOnline(online) - if online ~= self.storageOnline then - self.storageOnline = online - os.queueEvent(self.storageOnline and 'storage_online' or 'storage_offline', online) - _debug('Storage: %s', self.storageOnline and 'online' or 'offline') + _G._debug('') end end @@ -68,7 +57,7 @@ end function Storage:initStorage() local online = true - _debug('Initializing storage') + _G._debug('Initializing storage') for k,v in pairs(self.remoteDefaults) do if v.adapter then v.adapter.online = not not device[k] @@ -82,7 +71,11 @@ function Storage:initStorage() end end - self:setOnline(online) + if online ~= self.storageOnline then + self.storageOnline = online + os.queueEvent(self.storageOnline and 'storage_online' or 'storage_offline', online) + _G._debug('Storage: %s', self.storageOnline and 'online' or 'offline') + end end function Storage:filterActive(mtype, filter) @@ -138,29 +131,33 @@ end function Storage:refresh(throttle) self.dirty = true self.lastRefresh = os.clock() -_debug('STORAGE: Forcing full refresh') +_G._debug('STORAGE: Forcing full refresh') for _, adapter in self:onlineAdapters() do adapter.dirty = true end return self:listItems(throttle) end +local function Timer() + local ct = os.clock() + return function() + return os.clock() - ct + end +end + -- provide a consolidated list of items function Storage:listItems(throttle) if not self.dirty then - return self.items + return self.cache end -self.listCount = self.listCount + 1 ---_debug(self.listCount) -local ct = os.clock() +-- TODO: is there any reason now to maintain 2 lists local cache = { } - local items = { } throttle = throttle or Util.throttle() + local timer = Timer() for _, adapter in self:onlineAdapters() do if adapter.dirty then ---_debug('STORAGE: refresh: ' .. adapter.name) adapter:listItems(throttle) adapter.dirty = false end @@ -172,8 +169,6 @@ local ct = os.clock() entry.count = v.count entry.key = key cache[key] = entry - items[key] = entry --- table.insert(items, entry) else entry.count = entry.count + v.count end @@ -181,76 +176,80 @@ local ct = os.clock() throttle() end end -_debug('STORAGE: refresh in ' .. (os.clock() - ct)) +_G._debug('STORAGE: refresh in ' .. timer()) self.dirty = false self.cache = cache - self.items = items - return items + return cache +end + +function Storage:updateCache(adapter, key, count) + local entry = adapter.cache[key] + + if not entry then + if count < 0 then + adapter.dirty = true + self.dirty = true + else + entry = Util.shallowCopy(itemDB:get(key)) + entry.count = count + entry.key = key + adapter.cache[key] = entry + end + else + entry.count = entry.count + count + if entry.count <= 0 then + adapter.cache[key] = nil + end + end end function Storage:export(target, slot, count, item) - return self:provide(item, count, slot, target) -end - -function Storage:provide(item, qty, slot, direction) local total = 0 - local key = item.key or table.concat({ item.name, item.damage, item.nbtHash }, ':') - for _, adapter in self:onlineAdapters() do - if adapter.cache and adapter.cache[key] then - local amount = adapter:provide(item, qty, slot, direction or self.localName) - if amount > 0 then - self.hits = self.hits + 1 + + local function provide(adapter) + local amount = adapter:provide(item, count, slot, target or self.localName) + if amount > 0 then -- _debug('EXT: %s(%d): %s -> %s%s', -- item.name, amount, adapter.name, direction or self.localName, -- slot and string.format('[%d]', slot) or '') - self.dirty = true - adapter.dirty = true - end - qty = qty - amount - total = total + amount - if qty <= 0 then + self:updateCache(adapter, key, -amount) + self:updateCache(self, key, -amount) + end + count = count - amount + total = total + amount + end + + -- request from adapters with this item + for _, adapter in self:onlineAdapters() do + if adapter.cache and adapter.cache[key] then + provide(adapter) + if count <= 0 then return total end end end - _debug('STORAGE: MISS: %s - %d', key, qty) - self.misses = self.misses + 1 + _G._debug('STORAGE: MISS: %s - %d', key, count) + -- not found - scan all others for _, adapter in self:onlineAdapters() do - local amount = adapter:provide(item, qty, slot, direction or self.localName) - if amount > 0 then ---_debug('EXT: %s(%d): %s -> %s%s', --- item.name, amount, adapter.name, direction or self.localName, --- slot and string.format('[%d]', slot) or '') - self.dirty = true - adapter.dirty = true - end - qty = qty - amount - total = total + amount - if qty <= 0 then - break + if not adapter.cache or not adapter.cache[key] then + provide(adapter) + if count <= 0 then + _G._debug('STORAGE: FOUND: %s - %d', key, count) + break + end end end return total end -function Storage:trash(source, slot, count) - local trashcan = Util.find(self.remoteDefaults, 'mtype', 'trashcan') - if trashcan and trashcan.adapter and trashcan.adapter.online then ---_debug('TRA: %s[%d] (%d)', source or self.localName, slot, count or 64) --- return trashcan.adapter.pullItems(source or self.localName, slot, count) - end - return 0 -end - function Storage:import(source, slot, count, item) local total = 0 - - local key = table.concat({ item.name, item.damage, item.nbtHash }, ':') + local key = item.key or table.concat({ item.name, item.damage, item.nbtHash }, ':') if not self.cache then self:listItems() @@ -259,21 +258,16 @@ function Storage:import(source, slot, count, item) local function insert(adapter) local amount = adapter:insert(slot, count, nil, source or self.localName) if amount > 0 then + _G._debug('INS: %s(%d): %s[%d] -> %s', item.name, amount, source or self.localName, slot, adapter.name) - self.dirty = true - adapter.dirty = true - local entry = self.activity[key] or 0 - self.activity[key] = entry + amount ---[[ - local cached = adapter.cache[key] - if cached then - cached.count = cached.count + amount - else - end -]] + self:updateCache(adapter, key, amount) + self:updateCache(self, key, amount) + + -- record that we have imported this item into storage during this cycle + self.activity[key] = (self.activity[key] or 0) + amount end count = count - amount total = total + amount @@ -281,8 +275,7 @@ _G._debug('INS: %s(%d): %s[%d] -> %s', -- find a chest locked with this item for remote in self:onlineAdapters() do - -- TODO: proper checking using ignore dmg/nbt - if remote.lock == key or remote.lock == item.name then + if remote.lock == key then insert(remote.adapter) if count > 0 then -- TODO: only if void flag set total = total + self:trash(source, slot, count) @@ -291,10 +284,11 @@ _G._debug('INS: %s(%d): %s[%d] -> %s', end end - if self.cache[key] then -- is this item in some chest + -- is this item in some chest + if self.cache[key] then for _, adapter in self:onlineAdapters() do if count <= 0 then - break + return total end if adapter.cache and adapter.cache[key] and not adapter.lock then insert(adapter) @@ -315,4 +309,16 @@ _G._debug('INS: %s(%d): %s[%d] -> %s', return total end +-- When importing items into a locked chest, trash any remaining items if full +function Storage:trash(source, slot, count) + local trashcan = Util.find(self.remoteDefaults, 'mtype', 'trashcan') + if trashcan and trashcan.adapter and trashcan.adapter.online then + +_G._debug('TRA: %s[%d] (%d)', source or self.localName, slot, count or 64) + + return trashcan.adapter.pullItems(source or self.localName, slot, count) + end + return 0 +end + return Storage diff --git a/milo/apis/turtle/craft.lua b/milo/apis/turtle/craft.lua index c3e25ee..d27bc1b 100644 --- a/milo/apis/turtle/craft.lua +++ b/milo/apis/turtle/craft.lua @@ -11,7 +11,7 @@ local Craft = { STATUS_ERROR = 'error', STATUS_SUCCESS = 'success', - RECIPES_DIR = 'packages/core/etc/recipes', + RECIPES_DIR = 'packages/core/etc/recipes', USER_RECIPES = 'usr/config/recipes.db', MACHINE_LOOKUP = 'usr/config/machine_crafting.db', } @@ -94,7 +94,7 @@ local function machineCraft(recipe, storage, machineName, request, count, item) local xferred = { } for k,v in pairs(recipe.ingredients) do - local provided = storage:provide(splitKey(v), count, k, machineName) + local provided = storage:export(machineName, k, count, splitKey(v)) xferred[k] = { key = v, count = provided, @@ -125,8 +125,8 @@ local function turtleCraft(recipe, storage, request, count) for k,v in pairs(recipe.ingredients) do local item = splitKey(v) - if storage:provide(item, count, k) ~= count then - -- FIX: ingredients cannot be stacked + if storage:export(storage.localName, k, count, item) ~= count then + -- TODO: FIX: ingredients cannot be stacked request.status = 'unknown error' request.statusCode = Craft.STATUS_ERROR return @@ -405,38 +405,6 @@ function Craft.canCraft(item, count, items) return Craft.getCraftableAmount(Craft.recipes[item], count, items) == count end -function Craft.setRecipes(recipes) - Craft.recipes = recipes -end - -function Craft.getCraftableAmountTest() - local results = { } - Craft.setRecipes(Util.readTable('usr/etc/recipes.db')) - - local items = { - { name = 'minecraft:planks', damage = 0, count = 5 }, - { name = 'minecraft:log', damage = 0, count = 2 }, - } - results[1] = { item = 'chest', expected = 1, - got = Craft.getCraftableAmount(Craft.recipes['minecraft:chest:0'], 2, items) } - - items = { - { name = 'minecraft:log', damage = 0, count = 1 }, - { name = 'minecraft:coal', damage = 1, count = 1 }, - } - results[2] = { item = 'torch', expected = 4, - got = Craft.getCraftableAmount(Craft.recipes['minecraft:torch:0'], 4, items) } - - return results -end - -function Craft.craftRecipeTest(name, count) - local ChestAdapter = require('chestAdapter18') - local chestAdapter = ChestAdapter({ wrapSide = 'top', direction = 'down' }) - Craft.setRecipes(Util.readTable('usr/etc/recipes.db')) - return { Craft.craftRecipe(Craft.recipes[name], count, chestAdapter) } -end - Craft.loadRecipes() return Craft diff --git a/milo/plugins/craftTask.lua b/milo/plugins/craftTask.lua index 75711e0..2ccb73d 100644 --- a/milo/plugins/craftTask.lua +++ b/milo/plugins/craftTask.lua @@ -15,9 +15,11 @@ function craftTask:craft(recipe, item) if Milo:isCraftingPaused() then return end + + -- TODO: refactor into craft.lua Craft.processPending(item, context.storage) ---TODO: this needs to take into account what is pending + -- create a mini-list of items that are required for this recipe item.ingredients = Craft.getResourceList( recipe, Milo:listItems(), item.requested - item.crafted, item.pending) diff --git a/milo/plugins/listing.lua b/milo/plugins/listing.lua index d37e6ca..4f50832 100644 --- a/milo/plugins/listing.lua +++ b/milo/plugins/listing.lua @@ -6,18 +6,8 @@ local UI = require('ui') local Util = require('util') local colors = _G.colors -local os = _G.os - local context = Milo:getContext() --- TODO: fix -local function queue(fn) - while Milo:isCraftingPaused() do - os.sleep(1) - end - fn() -end - local function filterItems(t, filter, displayMode) if filter or displayMode > 0 then local r = { } @@ -61,7 +51,7 @@ local listingPage = UI.Page { }, statusBar = UI.StatusBar { filter = UI.TextEntry { - x = 1, ex = -13, + x = 1, ex = -17, limit = 50, shadowText = 'filter', shadowTextColor = colors.gray, @@ -71,12 +61,23 @@ local listingPage = UI.Page { [ 'enter' ] = 'craft', }, }, - storageStatus = UI.Button { - x = -12, ex = -4, - event = 'toggle_online', - backgroundColor = colors.cyan, + storageStatus = UI.Text { + x = -16, ex = -9, textColor = colors.lime, - text = '', + backgroundColor = colors.cyan, + value = '', + }, + amount = UI.TextEntry { + x = -8, ex = -4, + limit = 3, + shadowText = '1', + shadowTextColor = colors.gray, + backgroundColor = colors.black, + backgroundFocusColor = colors.black, + accelerators = { + [ 'enter' ] = 'eject_specified', + }, + help = 'Specify an amount to send', }, display = UI.Button { x = -3, @@ -95,7 +96,6 @@ local listingPage = UI.Page { [ 'control-a' ] = 'eject_all', [ 'control-m' ] = 'machines', - [ 'control-l' ] = 'resume', q = 'quit', }, @@ -132,25 +132,18 @@ function listingPage:eventHandler(event) if event.type == 'quit' then UI:exitPullEvents() - elseif event.type == 'resume' then - context.storage:setOnline(true) - elseif event.type == 'eject' then local item = self.grid:getSelected() if item then - queue(function() - item.count = Milo:craftAndEject(item, 1) - self.grid:draw() - end) + item.count = Milo:craftAndEject(item, 1) + self.grid:draw() end elseif event.type == 'eject_stack' then local item = self.grid:getSelected() if item then - queue(function() - item.count = Milo:craftAndEject(item, itemDB:getMaxCount(item)) - self.grid:draw() - end) + item.count = Milo:craftAndEject(item, itemDB:getMaxCount(item)) + self.grid:draw() end elseif event.type == 'eject_all' then @@ -158,10 +151,19 @@ function listingPage:eventHandler(event) if item then local updated = Milo:getItem(Milo:listItems(), item) if updated then - queue(function() Milo:eject(item, updated.count) end) + Milo:craftAndEject(item, updated.count) end end + elseif event.type == 'eject_specified' then + local item = self.grid:getSelected() + local count = tonumber(self.statusBar.amount.value) + if item and count then + self.statusBar.amount:reset() + self:setFocus(self.statusBar.filter) + Milo:craftAndEject(item, count) + end + elseif event.type == 'machines' then UI:setPage('machines') @@ -222,7 +224,7 @@ function listingPage:eventHandler(event) self.grid:draw() end - elseif event.type == 'text_change' then + elseif event.type == 'text_change' and event.element == self.statusBar.filter then self.filter = event.text if #self.filter == 0 then self.filter = nil @@ -240,7 +242,7 @@ end function listingPage:enable() self:refresh() self:setFocus(self.statusBar.filter) - self.timer = Event.onInterval(5, function() + self.timer = Event.onInterval(3, function() for _,v in pairs(self.allItems) do local c = context.storage.cache[v.key] v.count = c and c.count or 0 @@ -268,8 +270,8 @@ end Event.on({ 'storage_offline', 'storage_online' }, function(e, isOnline) -- TODO: Fix button - listingPage.statusBar.storageStatus.text = - isOnline and 'online' or 'offline' + listingPage.statusBar.storageStatus.value = + isOnline and '' or 'offline' listingPage.statusBar.storageStatus.textColor = isOnline and colors.lime or colors.red listingPage.statusBar.storageStatus:draw() From fe1fc6ea93e27fa9ff3ebfbdf5386166f4571d39 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sun, 11 Nov 2018 14:45:39 -0500 Subject: [PATCH 076/165] milo cleanup --- milo/Milo.lua | 8 +- milo/MiloMonitor.lua | 62 ------------- milo/apis/storage.lua | 17 ++-- milo/core/machines.lua | 149 ++++++++++++++++++------------ milo/plugins/activityView.lua | 20 +++- milo/plugins/brewingStandView.lua | 8 +- milo/plugins/exportView.lua | 14 +-- milo/plugins/importView.lua | 14 +-- milo/plugins/item.lua | 28 +++++- milo/plugins/listing.lua | 46 +++------ milo/plugins/machineLearn.lua | 2 +- milo/plugins/redstoneView.lua | 18 ++-- milo/plugins/storageView.lua | 14 +-- 13 files changed, 200 insertions(+), 200 deletions(-) delete mode 100644 milo/MiloMonitor.lua diff --git a/milo/Milo.lua b/milo/Milo.lua index cdb16af..cd529fa 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -39,10 +39,16 @@ end local config = { monitor = 'type/monitor', activityMonitor = 'none', - remoteDefaults = { }, + nodes = { }, } Config.load('milo', config) +-- TODO: remove - temporary +if config.remoteDefaults then + config.nodes = config.remoteDefaults + config.remoteDefaults = nil +end + local modem = Peripheral.get('wired_modem') if not modem or not modem.getNameLocal then error('Wired modem is not connected') diff --git a/milo/MiloMonitor.lua b/milo/MiloMonitor.lua deleted file mode 100644 index 1e59828..0000000 --- a/milo/MiloMonitor.lua +++ /dev/null @@ -1,62 +0,0 @@ -_G.requireInjector(_ENV) - -local Util = require('util') - -local colors = _G.colors -local device = _G.device - -local args = { ... } -local mon = device[args[1] or 'monitor'] or error('Syntax: MiloMonitor ') -local config = Util.readTable('/usr/config/milo') or error('Milo is not configured') - -local row -local monWidth, monHeight = mon.getSize() -local machines = { } - -local function write(x, y, s, bg, fg) - mon.setCursorPos(x, y) - mon.setBackgroundColor(bg) - if fg then - mon.setTextColor(fg) - end - mon.write(s) -end - -local function progress(y, percent) - local width = math.ceil(percent / 100 * monWidth) - write(2, y, string.rep(' ', monWidth - 2), colors.gray) - write(2, y, string.rep(' ', width), colors.lime) -end - -local function draw(machine, percent) - write(2, row, machine.displayName or machine.name, colors.black, colors.yellow) - progress(row + 1, percent) - row = row + 3 -end - -local function redraw() - row = 1 - mon.setBackgroundColor(colors.black) - mon.clear() - for _,machine in ipairs(machines) do - local dev = device[machine.name] - if dev then - local percent = 50 - if machine.mtype == 'storage' then - percent = Util.size(dev.list()) / dev.size() * 100 - end - draw(machine, percent) - end - end -end - -for _, v in pairs(config.remoteDefaults) do - table.insert(machines, v) -end - -table.sort(machines, function(a, b) - return (a.displayName or a.name) < (b.displayName or b.name) -end) - -mon.setTextScale(.5) -redraw() diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index e0f3528..aa652d4 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -12,7 +12,7 @@ local Storage = class() function Storage:init(args) local defaults = { - remoteDefaults = { }, + nodes = { }, dirty = true, activity = { }, storageOnline = true, @@ -35,9 +35,9 @@ end function Storage:showStorage() local t = { } - for k,v in pairs(self.remoteDefaults) do + for k,v in pairs(self.nodes) do local online = v.adapter and v.adapter.online - if not online then + if not online and v.mtype ~= 'ignore' then table.insert(t, k) end end @@ -58,7 +58,7 @@ function Storage:initStorage() local online = true _G._debug('Initializing storage') - for k,v in pairs(self.remoteDefaults) do + for k,v in pairs(self.nodes) do if v.adapter then v.adapter.online = not not device[k] elseif device[k] and device[k].list and device[k].size and device[k].pullItems then @@ -80,7 +80,7 @@ end function Storage:filterActive(mtype, filter) local iter = { } - for _, v in pairs(self.remoteDefaults) do + for _, v in pairs(self.nodes) do if v.adapter and v.adapter.online and v.mtype == mtype then if not filter or filter(v) then table.insert(iter, v) @@ -97,7 +97,7 @@ end function Storage:onlineAdapters(reversed) local iter = { } - for _, v in pairs(self.remoteDefaults) do + for _, v in pairs(self.nodes) do if v.adapter and v.adapter.online and v.mtype == 'storage' then table.insert(iter, v) end @@ -191,7 +191,8 @@ function Storage:updateCache(adapter, key, count) adapter.dirty = true self.dirty = true else - entry = Util.shallowCopy(itemDB:get(key)) + local item = itemDB:get(key) or itemDB:splitKey(key) + entry = Util.shallowCopy(item) entry.count = count entry.key = key adapter.cache[key] = entry @@ -311,7 +312,7 @@ end -- When importing items into a locked chest, trash any remaining items if full function Storage:trash(source, slot, count) - local trashcan = Util.find(self.remoteDefaults, 'mtype', 'trashcan') + local trashcan = Util.find(self.nodes, 'mtype', 'trashcan') if trashcan and trashcan.adapter and trashcan.adapter.online then _G._debug('TRA: %s[%d] (%d)', source or self.localName, slot, count or 64) diff --git a/milo/core/machines.lua b/milo/core/machines.lua index bc7b621..0dda36f 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -14,7 +14,7 @@ local context = Milo:getContext() local function saveConfig() local t = { } - for k,v in pairs(context.config.remoteDefaults) do + for k,v in pairs(context.config.nodes) do t[k] = v.adapter v.adapter = nil end @@ -22,19 +22,29 @@ local function saveConfig() Config.update('milo', context.config) for k,v in pairs(t) do - context.config.remoteDefaults[k].adapter = v + context.config.nodes[k].adapter = v end context.storage:initStorage() end -local machinesPage = UI.Page { +local networkPage = UI.Page { titleBar = UI.TitleBar { previousPage = true, - title = 'Machines', + title = 'Network', + }, + filter = UI.TextEntry { + y = -2, x = 1, ex = -9, + limit = 50, + shadowText = 'filter', + backgroundColor = colors.cyan, + backgroundFocusColor = colors.cyan, + accelerators = { + [ 'enter' ] = 'eject', + }, }, grid = UI.ScrollingGrid { - y = 2, ey = -2, - values = context.config.remoteDefaults, + y = 2, ey = -3, + values = context.config.nodes, columns = { { key = 'suffix', width = 4, justify = 'right' }, { heading = 'Name', key = 'displayName' }, @@ -42,23 +52,25 @@ local machinesPage = UI.Page { { heading = 'Pri', key = 'priority', width = 3 }, }, sortColumn = 'displayName', - help = 'Select Machine', + help = 'Select Node', }, remove = UI.Button { - y = -1, x = -4, - text = '-', event = 'remove_machine', - help = 'Remove Machine', + y = -2, x = -4, + text = '-', event = 'remove_node', + help = 'Remove Node', }, statusBar = UI.StatusBar { - ex = -7, - backgroundColor = colors.cyan, + backgroundColor = colors.lightGray, }, notification = UI.Notification { }, } -function machinesPage.grid:getDisplayValues(row) +function networkPage.grid:getDisplayValues(row) row = Util.shallowCopy(row) local t = { row.name:match(':(.+)_(%d+)$') } + if #t ~= 2 then + t = { row.name:match('(.+)_(%d+)$') } + end if t and #t == 2 then row.name, row.suffix = table.unpack(t) row.name = row.name .. '_' .. row.suffix @@ -67,7 +79,7 @@ function machinesPage.grid:getDisplayValues(row) return row end -function machinesPage.grid:getRowTextColor(row, selected) +function networkPage.grid:getRowTextColor(row, selected) if not device[row.name] then return colors.red end @@ -77,11 +89,11 @@ function machinesPage.grid:getRowTextColor(row, selected) return UI.Grid:getRowTextColor(row, selected) end -function machinesPage:getList() +function networkPage:getList() for _, v in pairs(device) do if v.pullItems then - if not context.config.remoteDefaults[v.name] then - context.config.remoteDefaults[v.name] = { + if not context.config.nodes[v.name] then + context.config.nodes[v.name] = { name = v.name, mtype = 'ignore', } @@ -90,9 +102,10 @@ function machinesPage:getList() end end -function machinesPage:enable() +function networkPage:enable() self:getList() self.grid:update() + self:setFocus(self.filter) UI.Page.enable(self) self.handler = Event.on({ 'device_attach', 'device_detach'}, function() self:getList() @@ -102,28 +115,50 @@ function machinesPage:enable() end) end -function machinesPage:disable() +function networkPage:disable() UI.Page.disable(self) Event.off(self.handler) end -function machinesPage:eventHandler(event) +function networkPage:applyFilter() + local t = context.config.nodes + local filter = self.filter.value + + if #filter > 0 then + t = { } + filter = filter:lower() + + for _,v in pairs(context.config.nodes) do + if (v.displayName and string.find(string.lower(v.displayName), filter, 1, true)) or + string.find(string.lower(v.name), filter, 1, true) then + table.insert(t, v) + end + end + end + self.grid:setValues(t) +end + +function networkPage:eventHandler(event) if event.type == 'grid_select' then if not device[event.selected.name] then self.notification:error('Unable to edit while disconnected') else - UI:setPage('machineWizard', event.selected) + UI:setPage('nodeWizard', event.selected) end - elseif event.type == 'remove_machine' then - local machine = self.grid:getSelected() - if machine then - context.config.remoteDefaults[machine.name] = nil + elseif event.type == 'remove_node' then + local node = self.grid:getSelected() + if node then + context.config.nodes[node.name] = nil saveConfig() end self.grid:update() self.grid:draw() + elseif event.type == 'text_change' then + self:applyFilter() + self.grid:draw() + elseif event.type == 'focus_change' then self.statusBar:setStatus(event.focused.help) @@ -133,7 +168,7 @@ function machinesPage:eventHandler(event) return true end -local machineWizard = UI.Page { +local nodeWizard = UI.Page { titleBar = UI.TitleBar { title = 'Configure' }, wizard = UI.Wizard { y = 2, ey = -2, @@ -260,7 +295,7 @@ The settings will take effect immediately!]], } --[[ Filter slide out ]] -- -function machineWizard.filter:show(entry, callback, whitelistOnly) +function nodeWizard.filter:show(entry, callback, whitelistOnly) self.entry = entry self.callback = callback @@ -280,13 +315,13 @@ function machineWizard.filter:show(entry, callback, whitelistOnly) sync.lock(turtle) end -function machineWizard.filter:hide() +function nodeWizard.filter:hide() UI.SlideOut.hide(self) Milo:resumeCrafting() sync.release(turtle) end -function machineWizard.filter:resetGrid() +function nodeWizard.filter:resetGrid() local t = { } for k in pairs(self.entry.filter) do table.insert(t, itemDB:splitKey(k)) @@ -294,13 +329,13 @@ function machineWizard.filter:resetGrid() self.grid:setValues(t) end -function machineWizard.filter.grid:getDisplayValues(row) +function nodeWizard.filter.grid:getDisplayValues(row) row = Util.shallowCopy(row) row.displayName = itemDB:getName(row) return row end -function machineWizard.filter:eventHandler(event) +function nodeWizard.filter:eventHandler(event) if event.type == 'focus_change' then self.statusBar:setStatus(event.focused.help) @@ -341,16 +376,16 @@ function machineWizard.filter:eventHandler(event) end --[[ General Page ]] -- -function machineWizard.wizard.pages.general:enable() +function nodeWizard.wizard.pages.general:enable() UI.Window.enable(self) self:focusFirst() end -function machineWizard.wizard.pages.general:setMachine(machine) +function nodeWizard.wizard.pages.general:setNode(node) local inventory - if device[machine.name] and device[machine.name].list then - inventory = device[machine.name].list() + if device[node.name] and device[node.name].list then + inventory = device[node.name].list() for k,v in pairs(inventory) do v.slot = k end @@ -359,18 +394,18 @@ function machineWizard.wizard.pages.general:setMachine(machine) self.grid:setValues(inventory or { }) end -function machineWizard.wizard.pages.general.grid:getDisplayValues(row) +function nodeWizard.wizard.pages.general.grid:getDisplayValues(row) row = Util.shallowCopy(row) row.displayName = itemDB:getName(row) return row end -function machineWizard.wizard.pages.general:validate() +function nodeWizard.wizard.pages.general:validate() return self.form:save() end --[[ Wizard ]] -- -function machineWizard.wizard:eventHandler(event) +function nodeWizard.wizard:eventHandler(event) if event.type == 'nextView' and Util.find(self.pages, 'enabled', true) == self.pages.general then @@ -383,7 +418,7 @@ function machineWizard.wizard:eventHandler(event) self.pages.confirmation.index = 2 for _, page in pairs(self.pages) do - if not page.index and page:isValidFor(self.parent.machine) then + if not page.index and page:isValidFor(self.parent.node) then page.index = index index = index + 1 end @@ -396,16 +431,16 @@ function machineWizard.wizard:eventHandler(event) end end -function machineWizard:enable(machine) - local adapter = machine.adapter - machine.adapter = nil -- don't deep copy the adapter - self.machine = Util.deepCopy(machine) - self.machine.adapter = adapter - machine.adapter = adapter +function nodeWizard:enable(node) + local adapter = node.adapter + node.adapter = nil -- don't deep copy the adapter + self.node = Util.deepCopy(node) + self.node.adapter = adapter + node.adapter = adapter -_G._p2 = self.machine - self.wizard.pages.general.form:setValues(self.machine) - self.wizard.pages.general.form[1].shadowText = self.machine.name +_G._p2 = self.node + self.wizard.pages.general.form:setValues(self.node) + self.wizard.pages.general.form[1].shadowText = self.node.name -- restore indices for _, page in pairs(self.wizard.pages) do @@ -418,18 +453,18 @@ _G._p2 = self.machine UI.Page.enable(self) for _, v in pairs(self.wizard.pages) do - if v.setMachine then - v:setMachine(self.machine) + if v.setNode then + v:setNode(self.node) end end end -function machineWizard:eventHandler(event) +function nodeWizard:eventHandler(event) if event.type == 'cancel' then UI:setPreviousPage() elseif event.type == 'accept' then - Util.prune(self.machine, function(v) + Util.prune(self.node, function(v) if type(v) == 'boolean' then return v elseif type(v) == 'string' then @@ -440,8 +475,8 @@ function machineWizard:eventHandler(event) return true end) - Util.clear(context.config.remoteDefaults[self.machine.name]) - Util.merge(context.config.remoteDefaults[self.machine.name], self.machine) + Util.clear(context.config.nodes[self.node.name]) + Util.merge(context.config.nodes[self.node.name], self.node) saveConfig() UI:setPreviousPage() @@ -451,7 +486,7 @@ function machineWizard:eventHandler(event) elseif event.type == 'enable_view' then local current = event.next or event.prev - self.titleBar.title = current.title or 'Machine' + self.titleBar.title = current.title or 'Node' self.titleBar:draw() elseif event.type == 'focus_change' then @@ -467,5 +502,5 @@ function machineWizard:eventHandler(event) return true end -UI:addPage('machines', machinesPage) -UI:addPage('machineWizard', machineWizard) +UI:addPage('network', networkPage) +UI:addPage('nodeWizard', nodeWizard) diff --git a/milo/plugins/activityView.lua b/milo/plugins/activityView.lua index 44b420f..61ca2f0 100644 --- a/milo/plugins/activityView.lua +++ b/milo/plugins/activityView.lua @@ -4,11 +4,15 @@ local Peripheral = require('peripheral') local UI = require('ui') local Util = require('util') -local colors = _G.colors - +local colors = _G.colors local context = Milo:getContext() local mon = Peripheral.lookup(context.config.activityMonitor) +local ActivityTask = { + name = 'activity', + priority = 30, +} + if not mon then return end @@ -110,7 +114,7 @@ function page:refresh() self.grid:draw() end -Event.onInterval(5, function() +function page:update() if context.storage:isOnline() then page:refresh() page:sync() @@ -119,6 +123,10 @@ Event.onInterval(5, function() page.grid:centeredWrite(math.ceil(page.height / 2), 'Storage Offline') page:sync() end +end + +Event.on({ 'storage_offline', 'storage_online' }, function() + page:update() end) Event.on('monitor_touch', function(_, side) @@ -128,5 +136,11 @@ Event.on('monitor_touch', function(_, side) end end) +function ActivityTask:cycle() + page:update() +end + +Milo:registerTask(ActivityTask) + page:draw() page:sync() diff --git a/milo/plugins/brewingStandView.lua b/milo/plugins/brewingStandView.lua index e4471ba..332aa5e 100644 --- a/milo/plugins/brewingStandView.lua +++ b/milo/plugins/brewingStandView.lua @@ -25,11 +25,11 @@ local brewingStandView = UI.Window { }, } -function brewingStandView:isValidFor(machine) - if machine.mtype == 'machine' then - local m = device[machine.name] +function brewingStandView:isValidFor(node) + if node.mtype == 'machine' then + local m = device[node.name] return m and m.type == 'minecraft:brewing_stand' end end -UI:getPage('machineWizard').wizard:add({ brewingStand = brewingStandView }) +UI:getPage('nodeWizard').wizard:add({ brewingStand = brewingStandView }) diff --git a/milo/plugins/exportView.lua b/milo/plugins/exportView.lua index 0bbb679..98d868d 100644 --- a/milo/plugins/exportView.lua +++ b/milo/plugins/exportView.lua @@ -36,22 +36,22 @@ local exportView = UI.Window { }, } -function exportView:isValidFor(machine) - return machine.mtype == 'machine' +function exportView:isValidFor(node) + return node.mtype == 'machine' end -function exportView:setMachine(machine) - self.machine = machine +function exportView:setNode(node) + self.machine = node if not self.machine.exports then self.machine.exports = { } end - self.grid:setValues(machine.exports) + self.grid:setValues(self.machine.exports) self.slots.choices = { { name = 'All', value = '*' } } - local m = device[machine.name] + local m = device[self.machine.name] for k = 1, m.size() do table.insert(self.slots.choices, { name = k, value = k }) end @@ -101,4 +101,4 @@ function exportView:eventHandler(event) end end -UI:getPage('machineWizard').wizard:add({ export = exportView }) +UI:getPage('nodeWizard').wizard:add({ export = exportView }) diff --git a/milo/plugins/importView.lua b/milo/plugins/importView.lua index af6af79..06a7fcb 100644 --- a/milo/plugins/importView.lua +++ b/milo/plugins/importView.lua @@ -36,22 +36,22 @@ local importView = UI.Window { }, } -function importView:isValidFor(machine) - return machine.mtype == 'machine' +function importView:isValidFor(node) + return node.mtype == 'machine' end -function importView:setMachine(machine) - self.machine = machine +function importView:setNode(node) + self.machine = node if not self.machine.imports then self.machine.imports = { } end - self.grid:setValues(machine.imports) + self.grid:setValues(self.machine.imports) self.slots.choices = { { name = 'All', value = '*' } } - local m = device[machine.name] + local m = device[self.machine.name] for k = 1, m.size() do table.insert(self.slots.choices, { name = k, value = k }) end @@ -100,4 +100,4 @@ function importView:eventHandler(event) end end -UI:getPage('machineWizard').wizard:add({ import = importView }) +UI:getPage('nodeWizard').wizard:add({ import = importView }) diff --git a/milo/plugins/item.lua b/milo/plugins/item.lua index 137dbd9..052d2d0 100644 --- a/milo/plugins/item.lua +++ b/milo/plugins/item.lua @@ -1,4 +1,5 @@ local Ansi = require('ansi') +local Craft = require('turtle.craft') local Milo = require('milo') local UI = require('ui') local Util = require('util') @@ -59,6 +60,12 @@ local itemPage = UI.Page { event = 'show_info', text = 'Info', }, + resetButton = UI.Button { + x = 9, y = -2, + event = 'reset', + text = 'Reset', + help = 'Clear recipe and all settings', + }, }, rsControl = UI.SlideOut { backgroundColor = colors.cyan, @@ -108,7 +115,7 @@ local itemPage = UI.Page { grid = UI.ScrollingGrid { y = 2, ey = -4, disableHeader = true, - values = context.config.remoteDefaults, + values = context.config.nodes, columns = { { heading = 'Name', key = 'displayName'}, }, @@ -201,6 +208,25 @@ function itemPage:eventHandler(event) self.machines.grid:setIndex(1) self.machines:show() + elseif event.type == 'reset' then + if context.userRecipes[self.item.key] then + context.userRecipes[self.item.key] = nil + Util.writeTable(Craft.USER_RECIPES, context.userRecipes) + Craft.loadRecipes() + end + + if context.resources[self.item.key] then + context.resources[self.item.key] = nil + Milo:saveResources() + end + + if Craft.machineLookup[self.item.key] then + Craft.machineLookup[self.item.key] = nil + Util.writeTable(Craft.MACHINE_LOOKUP, Craft.machineLookup) + end + + UI:setPreviousPage() + elseif event.type == 'set_machine' then --TODO save machine self.machines:hide() diff --git a/milo/plugins/listing.lua b/milo/plugins/listing.lua index 4f50832..def0050 100644 --- a/milo/plugins/listing.lua +++ b/milo/plugins/listing.lua @@ -32,20 +32,21 @@ end local listingPage = UI.Page { menuBar = UI.MenuBar { buttons = { - { text = 'Learn', event = 'learn' }, - { text = 'Forget', event = 'forget' }, - { text = 'Craft', event = 'craft' }, - { text = '...', event = 'machines' }, + { text = 'Learn', event = 'learn' }, + --{ text = 'Forget', event = 'forget' }, + { text = 'Craft', event = 'craft' }, + { text = 'Edit', event = 'details' }, + { text = 'Network', event = 'network' }, { text = 'Refresh', event = 'refresh', x = -9 }, }, }, grid = UI.Grid { y = 2, ey = -2, columns = { - { heading = ' Qty', key = 'count' , width = 4, justify = 'right' }, + { heading = ' Qty', key = 'count' , width = 4, justify = 'right' }, { heading = 'Name', key = 'displayName' }, - { heading = 'Min', key = 'low' , width = 4 }, - { heading = 'Max', key = 'limit' , width = 4 }, + { heading = 'Min', key = 'low' , width = 4 }, + { heading = 'Max', key = 'limit' , width = 4 }, }, sortColumn = 'displayName', }, @@ -95,7 +96,7 @@ local listingPage = UI.Page { [ 'control-s' ] = 'eject_stack', [ 'control-a' ] = 'eject_all', - [ 'control-m' ] = 'machines', + [ 'control-m' ] = 'network', q = 'quit', }, @@ -132,7 +133,7 @@ function listingPage:eventHandler(event) if event.type == 'quit' then UI:exitPullEvents() - elseif event.type == 'eject' then + elseif event.type == 'eject' or event.type == 'grid_select' then local item = self.grid:getSelected() if item then item.count = Milo:craftAndEject(item, 1) @@ -164,8 +165,8 @@ function listingPage:eventHandler(event) Milo:craftAndEject(item, count) end - elseif event.type == 'machines' then - UI:setPage('machines') + elseif event.type == 'network' then + UI:setPage('network') elseif event.type == 'details' or event.type == 'grid_select_right' then local item = self.grid:getSelected() @@ -195,7 +196,7 @@ function listingPage:eventHandler(event) elseif event.type == 'learn' then UI:setPage('learn') - elseif event.type == 'craft' or event.type == 'grid_select' then + elseif event.type == 'craft' then local item = self.grid:getSelected() if Craft.findRecipe(item) or true then -- or item.is_craftable then UI:setPage('craft', self.grid:getSelected()) @@ -203,27 +204,6 @@ function listingPage:eventHandler(event) self.notification:error('No recipe defined') end - elseif event.type == 'forget' then - local item = self.grid:getSelected() - if item then - local key = Milo:uniqueKey(item) - - if context.userRecipes[key] then - context.userRecipes[key] = nil - Util.writeTable(Craft.USER_RECIPES, context.userRecipes) - Craft.loadRecipes() - end ---TODO: remove machine assoc - if context.resources[key] then - context.resources[key] = nil - Milo:saveResources() - end - - self.notification:info('Forgot: ' .. item.name) - self:refresh() - self.grid:draw() - end - elseif event.type == 'text_change' and event.element == self.statusBar.filter then self.filter = event.text if #self.filter == 0 then diff --git a/milo/plugins/machineLearn.lua b/milo/plugins/machineLearn.lua index b85a2e9..4d33afc 100644 --- a/milo/plugins/machineLearn.lua +++ b/milo/plugins/machineLearn.lua @@ -19,7 +19,7 @@ local machineLearnWizard = UI.Page { index = 1, grid = UI.ScrollingGrid { y = 2, ey = -2, - values = context.config.remoteDefaults, + values = context.config.nodes, columns = { { heading = 'Name', key = 'displayName' }, }, diff --git a/milo/plugins/redstoneView.lua b/milo/plugins/redstoneView.lua index 0ed3dfc..0ceb93a 100644 --- a/milo/plugins/redstoneView.lua +++ b/milo/plugins/redstoneView.lua @@ -37,10 +37,10 @@ local dispenserView = UI.Window { }, } -function dispenserView:isValidFor(machine) - if machine.mtype == 'machine' then - local m = device[machine.name] - return m and m.type == 'minecraft:dispenser' +function dispenserView:isValidFor(node) + if node.mtype == 'machine' then + local m = device[node.name] + --return m and m.type == 'minecraft:dispenser' end end @@ -63,11 +63,11 @@ function dispenserView:validate() return self.form:save() end -function dispenserView:setMachine(machine) - if not machine.redstone then - machine.redstone = { } +function dispenserView:setNode(node) + if not node.redstone then + node.redstone = { } end - self.form:setValues(machine.redstone) + self.form:setValues(node.redstone) end -UI:getPage('machineWizard').wizard:add({ dispenser = dispenserView }) +UI:getPage('nodeWizard').wizard:add({ dispenser = dispenserView }) diff --git a/milo/plugins/storageView.lua b/milo/plugins/storageView.lua index 99856ca..e792cc3 100644 --- a/milo/plugins/storageView.lua +++ b/milo/plugins/storageView.lua @@ -48,14 +48,14 @@ function storageView:validate() return self.form:save() end -function storageView:isValidFor(machine) - return machine.mtype == 'storage' +function storageView:isValidFor(node) + return node.mtype == 'storage' end -function storageView:setMachine(machine) - self.machine = machine - self.form:setValues(machine) - self.form[3].value = machine.lock and itemDB:getName(machine.lock) or '' +function storageView:setNode(node) + self.machine = node + self.form:setValues(node) + self.form[3].value = node.lock and itemDB:getName(node.lock) or '' end function storageView:eventHandler(event) @@ -83,4 +83,4 @@ function storageView:eventHandler(event) end end -UI:getPage('machineWizard').wizard:add({ storage = storageView }) +UI:getPage('nodeWizard').wizard:add({ storage = storageView }) From 9ee8ce9f33901a9a75e55612cf71c37a3323ba24 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sun, 11 Nov 2018 21:59:13 -0500 Subject: [PATCH 077/165] milo node rework --- milo/apis/storage.lua | 29 +++++++++- milo/core/machines.lua | 93 +++++++++++++++---------------- milo/plugins/activityView.lua | 56 ++++++++++++++----- milo/plugins/brewingStandView.lua | 12 ++-- milo/plugins/exportView.lua | 5 ++ milo/plugins/importView.lua | 5 ++ milo/plugins/inputChestView.lua | 33 +++++++++++ milo/plugins/jobMonitor.lua | 43 ++++++++++++-- milo/plugins/manipulatorView.lua | 64 +++++++++++++++++++++ milo/plugins/redstoneView.lua | 2 +- milo/plugins/remote.lua | 18 +++--- milo/plugins/storageView.lua | 5 ++ milo/plugins/trashcanView.lua | 35 ++++++++++++ 13 files changed, 317 insertions(+), 83 deletions(-) create mode 100644 milo/plugins/inputChestView.lua create mode 100644 milo/plugins/manipulatorView.lua create mode 100644 milo/plugins/trashcanView.lua diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index aa652d4..070e538 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -65,6 +65,9 @@ function Storage:initStorage() v.adapter = InventoryAdapter.wrap({ side = k }) v.adapter.online = true v.adapter.dirty = true + elseif device[k] then + v.adapter = device[k] + v.adapter.online = true end if v.mtype == 'storage' then online = online and not not (v.adapter and v.adapter.online) @@ -73,15 +76,23 @@ function Storage:initStorage() if online ~= self.storageOnline then self.storageOnline = online + -- TODO: if online, then list items os.queueEvent(self.storageOnline and 'storage_online' or 'storage_offline', online) _G._debug('Storage: %s', self.storageOnline and 'online' or 'offline') end end -function Storage:filterActive(mtype, filter) +function Storage:getSingleNode(mtype) + local node = Util.find(self.nodes, 'mtype', mtype) + if node and node.adapter and node.adapter.online then + return node + end +end + +function Storage:filterNodes(mtype, filter) local iter = { } for _, v in pairs(self.nodes) do - if v.adapter and v.adapter.online and v.mtype == mtype then + if v.mtype == mtype then if not filter or filter(v) then table.insert(iter, v) end @@ -95,6 +106,14 @@ function Storage:filterActive(mtype, filter) end end +function Storage:filterActive(mtype, filter) + return self:filterNodes(mtype, function(v) + if v.adapter and v.adapter.online then + return not filter and true or filter(v) + end + end) +end + function Storage:onlineAdapters(reversed) local iter = { } for _, v in pairs(self.nodes) do @@ -184,6 +203,12 @@ _G._debug('STORAGE: refresh in ' .. timer()) end function Storage:updateCache(adapter, key, count) + if not adapter.cache then + adapter.dirty = true + self.dirty = true + return + end + local entry = adapter.cache[key] if not entry then diff --git a/milo/core/machines.lua b/milo/core/machines.lua index 0dda36f..f8cb0cc 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -12,6 +12,8 @@ local turtle = _G.turtle local context = Milo:getContext() +local nodeWizard + local function saveConfig() local t = { } for k,v in pairs(context.config.nodes) do @@ -38,9 +40,6 @@ local networkPage = UI.Page { shadowText = 'filter', backgroundColor = colors.cyan, backgroundFocusColor = colors.cyan, - accelerators = { - [ 'enter' ] = 'eject', - }, }, grid = UI.ScrollingGrid { y = 2, ey = -3, @@ -91,12 +90,16 @@ end function networkPage:getList() for _, v in pairs(device) do - if v.pullItems then - if not context.config.nodes[v.name] then - context.config.nodes[v.name] = { - name = v.name, - mtype = 'ignore', - } + if not context.config.nodes[v.name] then + local node = { + name = v.name, + mtype = 'ignore', + } + for _, page in pairs(nodeWizard.wizard.pages) do + if page.isValidType and page:isValidType(node) then + context.config.nodes[v.name] = node + break + end end end end @@ -168,7 +171,7 @@ function networkPage:eventHandler(event) return true end -local nodeWizard = UI.Page { +nodeWizard = UI.Page { titleBar = UI.TitleBar { title = 'Configure' }, wizard = UI.Wizard { y = 2, ey = -2, @@ -185,16 +188,9 @@ local nodeWizard = UI.Page { limit = 64, pruneEmpty = true, }, [2] = UI.Chooser { - width = 15, + width = 25, formLabel = 'Type', formKey = 'mtype', - nochoice = 'Storage', - choices = { - { name = 'Storage', value = 'storage' }, - { name = 'Trashcan', value = 'trashcan' }, - { name = 'Input chest', value = 'input' }, - { name = 'Ignore', value = 'ignore' }, - { name = 'Machine', value = 'machine' }, - }, + --nochoice = 'Storage', help = 'Select type', }, }, @@ -401,36 +397,30 @@ function nodeWizard.wizard.pages.general.grid:getDisplayValues(row) end function nodeWizard.wizard.pages.general:validate() - return self.form:save() -end - ---[[ Wizard ]] -- -function nodeWizard.wizard:eventHandler(event) - if event.type == 'nextView' and - Util.find(self.pages, 'enabled', true) == self.pages.general then - - if self.pages.general.form:save() then - local index = 2 - for _, page in pairs(self.pages) do - page.index = nil - end - self.pages.general.index = 1 - self.pages.confirmation.index = 2 - - for _, page in pairs(self.pages) do - if not page.index and page:isValidFor(self.parent.node) then + if self.form:save() then + for _, page in pairs(nodeWizard.wizard.pages) do + page.index = nil + end + local index = 2 + nodeWizard.wizard.pages.general.index = 1 + nodeWizard.wizard.pages.confirmation.index = 2 + for _, page in pairs(nodeWizard.wizard.pages) do + if not page.index then + if not page.isValidFor or page:isValidFor(nodeWizard.node) then page.index = index index = index + 1 + if page.setNode then + page:setNode(nodeWizard.node) + end end end - self.pages.confirmation.index = index - return UI.Wizard.eventHandler(self, event) end - else - return UI.Wizard.eventHandler(self, event) + nodeWizard.wizard.pages.confirmation.index = index + return true end end +--[[ Wizard ]] -- function nodeWizard:enable(node) local adapter = node.adapter node.adapter = nil -- don't deep copy the adapter @@ -439,8 +429,21 @@ function nodeWizard:enable(node) node.adapter = adapter _G._p2 = self.node - self.wizard.pages.general.form:setValues(self.node) + + local choices = { + { name = 'Ignore', value = 'ignore' }, + } + for _, page in pairs(self.wizard.pages) do + if page.isValidType then + local choice = page:isValidType(self.node) + if choice then + table.insert(choices, choice) + end + end + end self.wizard.pages.general.form[1].shadowText = self.node.name + self.wizard.pages.general.form[2].choices = choices + self.wizard.pages.general.form:setValues(self.node) -- restore indices for _, page in pairs(self.wizard.pages) do @@ -451,12 +454,6 @@ _G._p2 = self.node end UI.Page.enable(self) - - for _, v in pairs(self.wizard.pages) do - if v.setNode then - v:setNode(self.node) - end - end end function nodeWizard:eventHandler(event) diff --git a/milo/plugins/activityView.lua b/milo/plugins/activityView.lua index 61ca2f0..7b48148 100644 --- a/milo/plugins/activityView.lua +++ b/milo/plugins/activityView.lua @@ -1,25 +1,47 @@ +local Ansi = require('ansi') local Event = require('event') local Milo = require('milo') -local Peripheral = require('peripheral') local UI = require('ui') local Util = require('util') -local colors = _G.colors -local context = Milo:getContext() -local mon = Peripheral.lookup(context.config.activityMonitor) +local colors = _G.colors +local context = Milo:getContext() +local device = _G.device +local monitor = context.storage:getSingleNode('activity') -local ActivityTask = { - name = 'activity', - priority = 30, +--[[ Configuration Page ]]-- +local template = +[[%sDisplays the amount of items entering or leaving storage%s + +Right-clicking on the activity monitor will reset the totals. + +%sMilo must be restarted to activate diplay. +]] + +local activityWizardPage = UI.Window { + title = 'Activity Monitor', + index = 2, + backgroundColor = colors.cyan, + [1] = UI.TextArea { + x = 2, ex = -2, y = 2, ey = -2, + value = string.format(template, Ansi.yellow, Ansi.reset, Ansi.orange), + }, } -if not mon then - return +function activityWizardPage:isValidType(node) + local m = device[node.name] + return m and m.type == 'monitor' and { name = 'Activity Monitor', value = 'activity' } end +function activityWizardPage:isValidFor(node) + return node.mtype == 'activity' +end + +UI:getPage('nodeWizard').wizard:add({ activity = activityWizardPage }) + local page = UI.Window { parent = UI.Device { - device = mon, + device = monitor.adapter, textScale = .5, }, grid = UI.Grid { @@ -130,17 +152,23 @@ Event.on({ 'storage_offline', 'storage_online' }, function() end) Event.on('monitor_touch', function(_, side) - if side == mon.side then + if side == monitor.adapter.side then page:reset() page:sync() end end) +page:draw() +page:sync() + +--[[ Task ]]-- +local ActivityTask = { + name = 'activity', + priority = 30, +} + function ActivityTask:cycle() page:update() end Milo:registerTask(ActivityTask) - -page:draw() -page:sync() diff --git a/milo/plugins/brewingStandView.lua b/milo/plugins/brewingStandView.lua index 332aa5e..018a8c3 100644 --- a/milo/plugins/brewingStandView.lua +++ b/milo/plugins/brewingStandView.lua @@ -16,7 +16,7 @@ When finished brewing, the recipe will be available upon refreshing. Note that you do not need to import items from the brewing stand, this will be done automatically.]] local brewingStandView = UI.Window { - title = 'Storage Options', + title = 'Brewing Stand', index = 2, backgroundColor = colors.cyan, [1] = UI.TextArea { @@ -25,11 +25,13 @@ local brewingStandView = UI.Window { }, } +function brewingStandView:isValidType(node) + local m = device[node.name] + return m and m.type == 'minecraft:brewing_stand'and { name = 'Brewing Stand', value = 'brewingStand' } +end + function brewingStandView:isValidFor(node) - if node.mtype == 'machine' then - local m = device[node.name] - return m and m.type == 'minecraft:brewing_stand' - end + return node.mtype == 'brewingStand' end UI:getPage('nodeWizard').wizard:add({ brewingStand = brewingStandView }) diff --git a/milo/plugins/exportView.lua b/milo/plugins/exportView.lua index 98d868d..201e8be 100644 --- a/milo/plugins/exportView.lua +++ b/milo/plugins/exportView.lua @@ -36,6 +36,11 @@ local exportView = UI.Window { }, } +function exportView:isValidType(node) + local m = device[node.name] + return m and m.pullItems and { name = 'Generic Inventory', value = 'machine' } +end + function exportView:isValidFor(node) return node.mtype == 'machine' end diff --git a/milo/plugins/importView.lua b/milo/plugins/importView.lua index 06a7fcb..996736c 100644 --- a/milo/plugins/importView.lua +++ b/milo/plugins/importView.lua @@ -36,6 +36,11 @@ local importView = UI.Window { }, } +function importView:isValidType(node) + local m = device[node.name] + return m and m.pullItems and { name = 'Generic Inventory', value = 'machine' } +end + function importView:isValidFor(node) return node.mtype == 'machine' end diff --git a/milo/plugins/inputChestView.lua b/milo/plugins/inputChestView.lua new file mode 100644 index 0000000..0177903 --- /dev/null +++ b/milo/plugins/inputChestView.lua @@ -0,0 +1,33 @@ +local Ansi = require('ansi') +local UI = require('ui') + +local colors = _G.colors +local device = _G.device + +--[[ Configuration Screen ]] +local template = +[[%sInput Chest%s + +Any items placed in this chest will be imported into storage. +]] + +local inputChestWizardPage = UI.Window { + title = 'Input Chest', + index = 2, + backgroundColor = colors.cyan, + [1] = UI.TextArea { + x = 2, ex = -2, y = 2, ey = -2, + value = string.format(template, Ansi.yellow, Ansi.reset), + }, +} + +function inputChestWizardPage:isValidType(node) + local m = device[node.name] + return m and m.pullItems and { name = 'Input Chest', value = 'input' } +end + +function inputChestWizardPage:isValidFor(node) + return node.mtype == 'input' +end + +UI:getPage('nodeWizard').wizard:add({ inputChest = inputChestWizardPage }) diff --git a/milo/plugins/jobMonitor.lua b/milo/plugins/jobMonitor.lua index 53411fe..bbd86ce 100644 --- a/milo/plugins/jobMonitor.lua +++ b/milo/plugins/jobMonitor.lua @@ -1,21 +1,53 @@ +local Ansi = require('ansi') local Craft = require('turtle.craft') local itemDB = require('itemDB') local Milo = require('milo') -local Peripheral = require('peripheral') local UI = require('ui') local Util = require('util') local colors = _G.colors +local context = Milo:getContext() +local device = _G.device +local monNode = context.storage:getSingleNode('jobs') -local context = Milo:getContext() -local mon = Peripheral.lookup(context.config.monitor) or - error('Monitor is not attached') +--[[ Configuration Screen ]] +local template = +[[%sDisplays the crafting progress%s + +%sMilo must be restarted to activate diplay. +]] + +local jobsWizardPage = UI.Window { + title = 'Crafting Monitor', + index = 2, + backgroundColor = colors.cyan, + [1] = UI.TextArea { + x = 2, ex = -2, y = 2, ey = -2, + value = string.format(template, Ansi.yellow, Ansi.reset, Ansi.orange), + }, +} + +function jobsWizardPage:isValidType(node) + local m = device[node.name] + return m and m.type == 'monitor' and { name = 'Crafting Monitor', value = 'jobs' } +end + +function jobsWizardPage:isValidFor(node) + return node.mtype == 'jobs' +end + +UI:getPage('nodeWizard').wizard:add({ jobs = jobsWizardPage }) + +--[[ Display ]] +if not monNode then + return +end -- TODO: some way to cancel a job local jobMonitor = UI.Page { parent = UI.Device { - device = mon, + device = monNode.adapter, textScale = .5, }, grid = UI.Grid { @@ -90,6 +122,7 @@ jobMonitor:enable() jobMonitor:draw() jobMonitor:sync() +--[[ Task ]] local jobMonitorTask = { name = 'job status', priority = 80, diff --git a/milo/plugins/manipulatorView.lua b/milo/plugins/manipulatorView.lua new file mode 100644 index 0000000..6fa7f76 --- /dev/null +++ b/milo/plugins/manipulatorView.lua @@ -0,0 +1,64 @@ +local Ansi = require('ansi') +local Milo = require('milo') +local Sync = require('sync') +local UI = require('ui') + +local colors = _G.colors +local device = _G.device +local turtle = _G.turtle + +--[[ Configuration Screen ]] +local template = +[[%sBound Manipulator%s + +Automatically import items into storage from your ender chest. +]] + +local wizardPage = UI.Window { + title = 'Manipulator', + index = 2, + backgroundColor = colors.cyan, + [1] = UI.TextArea { + x = 2, ex = -2, y = 2, ey = -2, + value = string.format(template, Ansi.yellow, Ansi.reset), + }, +} + +function wizardPage:isValidType(node) + local m = device[node.name] + return m and + m.type == 'manipulator' and + m.getEnder and + { name = 'Manipulator', value = 'manipulator' } +end + +function wizardPage:isValidFor(node) + return node.mtype == 'manipulator' +end + +UI:getPage('nodeWizard').wizard:add({ manipulator = wizardPage }) + +local task = { + name = 'manipulator', + priority = 15, +} + +function task:cycle(context) + local function filter(v) + return v.adapter.getEnder + end + + for manipulator in context.storage:filterActive('manipulator', filter) do + for slot, item in pairs(manipulator.adapter.getEnder().list()) do + Sync.sync(turtle, function() + manipulator.adapter.getEnder().pushItems( + context.localName, + slot, + item.count) + Milo:clearGrid() + end) + end + end +end + +Milo:registerTask(task) diff --git a/milo/plugins/redstoneView.lua b/milo/plugins/redstoneView.lua index 0ceb93a..07678a3 100644 --- a/milo/plugins/redstoneView.lua +++ b/milo/plugins/redstoneView.lua @@ -70,4 +70,4 @@ function dispenserView:setNode(node) self.form:setValues(node.redstone) end -UI:getPage('nodeWizard').wizard:add({ dispenser = dispenserView }) +--UI:getPage('nodeWizard').wizard:add({ dispenser = dispenserView }) diff --git a/milo/plugins/remote.lua b/milo/plugins/remote.lua index d01b5b4..0f522f8 100644 --- a/milo/plugins/remote.lua +++ b/milo/plugins/remote.lua @@ -29,7 +29,7 @@ local function client(socket) local manipulator = getManipulatorForUser(user) if not manipulator then - _debug('REMOTE: Manipulator with introspection module bound with user not found. Closing connection.') + _G._debug('REMOTE: Manipulator with introspection module bound with user not found. Closing connection.') socket:write({ msg = 'Manipulator not found' }) @@ -37,7 +37,7 @@ local function client(socket) return end - _debug('REMOTE: all good') + _G._debug('REMOTE: all good') socket:write({ data = 'ok', }) @@ -98,12 +98,14 @@ local function client(socket) request.requested, data.item) - turtle.eachFilledSlot(function(slot) - manipulator.getInventory().pullItems( - context.localName, - slot.index, - transferred) - end) + if transferred > 0 then + turtle.eachFilledSlot(function(slot) + manipulator.getInventory().pullItems( + context.localName, + slot.index, + slot.count) + end) + end end) end diff --git a/milo/plugins/storageView.lua b/milo/plugins/storageView.lua index e792cc3..70449b6 100644 --- a/milo/plugins/storageView.lua +++ b/milo/plugins/storageView.lua @@ -48,6 +48,11 @@ function storageView:validate() return self.form:save() end +function storageView:isValidType(node) + local m = device[node.name] + return m and m.pullItems and { name = 'Storage', value = 'storage' } +end + function storageView:isValidFor(node) return node.mtype == 'storage' end diff --git a/milo/plugins/trashcanView.lua b/milo/plugins/trashcanView.lua new file mode 100644 index 0000000..2b5547e --- /dev/null +++ b/milo/plugins/trashcanView.lua @@ -0,0 +1,35 @@ +local Ansi = require('ansi') +local UI = require('ui') + +local colors = _G.colors +local device = _G.device + +--[[ Configuration Screen ]] +local template = +[[%sUse this inventory as a trashcan%s + +If the number of items exceed the maximum value will be sent to this inventory. + +Any items that cannot fit into a locked chest will automatically be sent to this inventory. +]] + +local trashcanWizardPage = UI.Window { + title = 'Trashcan', + index = 2, + backgroundColor = colors.cyan, + [1] = UI.TextArea { + x = 2, ex = -2, y = 2, ey = -2, + value = string.format(template, Ansi.yellow, Ansi.reset), + }, +} + +function trashcanWizardPage:isValidType(node) + local m = device[node.name] + return m and m.pullItems and { name = 'Trashcan', value = 'trashcan' } +end + +function trashcanWizardPage:isValidFor(node) + return node.mtype == 'trashcan' +end + +UI:getPage('nodeWizard').wizard:add({ trashcan = trashcanWizardPage }) From 3f095ff5224816a691f0a68feb8384091089e9e7 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Mon, 12 Nov 2018 02:03:37 -0500 Subject: [PATCH 078/165] milo logging --- farms/farmer.lua | 6 ++++-- milo/apis/milo.lua | 5 ++++- milo/apis/storage.lua | 34 +++++++++++++++++++++++++--------- milo/core/machines.lua | 1 + milo/plugins/activityView.lua | 4 ++++ milo/plugins/jobMonitor.lua | 6 +++--- 6 files changed, 41 insertions(+), 15 deletions(-) diff --git a/farms/farmer.lua b/farms/farmer.lua index 2a5a65d..877d1a0 100644 --- a/farms/farmer.lua +++ b/farms/farmer.lua @@ -144,11 +144,13 @@ local function harvest(blocks) local sensed = peripheral.call('right', 'sense') turtle.equip('right', 'minecraft:diamond_pickaxe') Util.filterInplace(sensed, function(s) - if s.displayName == 'item.tile.cactus' and Point.distance(b, s) < 6 then + if s.displayName == 'item.tile.cactus' then s.x = Util.round(s.x) + turtle.point.x s.z = Util.round(s.z) + turtle.point.z s.y = -1 - return true + if Point.distance(b, s) < 6 then + return true + end end end) Point.eachClosest(turtle.point, sensed, function(s) diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index b00749e..aadd885 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -85,7 +85,10 @@ function Milo:registerTask(task) end function Milo:showError(msg) - self.context.jobMonitor:showError(msg) + -- TODO: break dependency + if self.context.jobMonitor then + self.context.jobMonitor:showError(msg) + end end function Milo:getItem(items, inItem, ignoreDamage, ignoreNbtHash) diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 070e538..da7cfac 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -216,11 +216,19 @@ function Storage:updateCache(adapter, key, count) adapter.dirty = true self.dirty = true else + -- TODO: all items imported should be updated in itemdb + -- error here if not local item = itemDB:get(key) or itemDB:splitKey(key) - entry = Util.shallowCopy(item) - entry.count = count - entry.key = key - adapter.cache[key] = entry + if item.displayName then + entry = Util.shallowCopy(item) + entry.count = count + entry.key = key + adapter.cache[key] = entry + else + -- TODO: somehow update itemdb with this maybe new item + adapter.dirty = true + self.dirty = true + end end else entry.count = entry.count + count @@ -230,6 +238,14 @@ function Storage:updateCache(adapter, key, count) end end +local function sn(name) + local t = { name:match(':(.+)_(%d+)$') } + if #t ~= 2 then + return name + end + return table.concat(table.unpack(t), '_') +end + function Storage:export(target, slot, count, item) local total = 0 local key = item.key or table.concat({ item.name, item.damage, item.nbtHash }, ':') @@ -237,9 +253,9 @@ function Storage:export(target, slot, count, item) local function provide(adapter) local amount = adapter:provide(item, count, slot, target or self.localName) if amount > 0 then --- _debug('EXT: %s(%d): %s -> %s%s', --- item.name, amount, adapter.name, direction or self.localName, --- slot and string.format('[%d]', slot) or '') + _G._debug('EXT: %s(%d): %s -> %s%s', + item.name, amount, sn(adapter.name), sn(target or self.localName), + slot and string.format('[%d]', slot) or '') self:updateCache(adapter, key, -amount) self:updateCache(self, key, -amount) end @@ -287,7 +303,7 @@ function Storage:import(source, slot, count, item) _G._debug('INS: %s(%d): %s[%d] -> %s', item.name, amount, - source or self.localName, slot, adapter.name) + sn(source or self.localName), slot, sn(adapter.name)) self:updateCache(adapter, key, amount) self:updateCache(self, key, amount) @@ -340,7 +356,7 @@ function Storage:trash(source, slot, count) local trashcan = Util.find(self.nodes, 'mtype', 'trashcan') if trashcan and trashcan.adapter and trashcan.adapter.online then -_G._debug('TRA: %s[%d] (%d)', source or self.localName, slot, count or 64) +_G._debug('TRA: %s[%d] (%d)', sn(source or self.localName), slot, count or 64) return trashcan.adapter.pullItems(source or self.localName, slot, count) end diff --git a/milo/core/machines.lua b/milo/core/machines.lua index f8cb0cc..388a651 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -435,6 +435,7 @@ _G._p2 = self.node } for _, page in pairs(self.wizard.pages) do if page.isValidType then + -- TODO: dedupe list local choice = page:isValidType(self.node) if choice then table.insert(choices, choice) diff --git a/milo/plugins/activityView.lua b/milo/plugins/activityView.lua index 7b48148..d313aa1 100644 --- a/milo/plugins/activityView.lua +++ b/milo/plugins/activityView.lua @@ -39,6 +39,10 @@ end UI:getPage('nodeWizard').wizard:add({ activity = activityWizardPage }) +if not monitor then + return +end + local page = UI.Window { parent = UI.Device { device = monitor.adapter, diff --git a/milo/plugins/jobMonitor.lua b/milo/plugins/jobMonitor.lua index bbd86ce..30ee1f7 100644 --- a/milo/plugins/jobMonitor.lua +++ b/milo/plugins/jobMonitor.lua @@ -8,7 +8,7 @@ local Util = require('util') local colors = _G.colors local context = Milo:getContext() local device = _G.device -local monNode = context.storage:getSingleNode('jobs') +local monitor = context.storage:getSingleNode('jobs') --[[ Configuration Screen ]] local template = @@ -39,7 +39,7 @@ end UI:getPage('nodeWizard').wizard:add({ jobs = jobsWizardPage }) --[[ Display ]] -if not monNode then +if not monitor then return end @@ -47,7 +47,7 @@ end local jobMonitor = UI.Page { parent = UI.Device { - device = monNode.adapter, + device = monitor.adapter, textScale = .5, }, grid = UI.Grid { From 52ea4e039cd54a021f738411bede3204008e17aa Mon Sep 17 00:00:00 2001 From: kepler155c Date: Mon, 12 Nov 2018 11:44:24 -0500 Subject: [PATCH 079/165] milo manipulator + crafting fixes --- farms/farmer.lua | 2 +- milo/apis/milo.lua | 11 +-------- milo/apis/storage.lua | 13 +++++++---- milo/apis/turtle/craft.lua | 40 +++++++++++++++++++------------- milo/plugins/manipulatorView.lua | 39 ++++++++++++++++++++++--------- milo/plugins/replenishTask.lua | 1 + 6 files changed, 63 insertions(+), 43 deletions(-) diff --git a/farms/farmer.lua b/farms/farmer.lua index 877d1a0..ac182f8 100644 --- a/farms/farmer.lua +++ b/farms/farmer.lua @@ -139,7 +139,7 @@ local function harvest(blocks) elseif b.action == 'bump' then if turtle.faceAgainst(b) then turtle.equip('right', 'plethora:module:3') - os.sleep(.5) + os.sleep(.3) -- search the ground for the dropped cactus local sensed = peripheral.call('right', 'sense') turtle.equip('right', 'minecraft:diamond_pickaxe') diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index aadd885..e071c23 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -154,16 +154,7 @@ function Milo:getMatches(items, item, ignoreDamage, ignoreNbtHash) end function Milo:clearGrid() - turtle.eachFilledSlot(function(slot) - self.context.storage:import(self.context.localName, slot.index, slot.count, slot) - end) - - for i = 1, 16 do - if turtle.getItemCount(i) ~= 0 then - return false - end - end - return true + return Craft.clearGrid(self.context.storage) end function Milo:getTurtleInventory() diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index da7cfac..dac5548 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -218,13 +218,14 @@ function Storage:updateCache(adapter, key, count) else -- TODO: all items imported should be updated in itemdb -- error here if not - local item = itemDB:get(key) or itemDB:splitKey(key) - if item.displayName then + local item = itemDB:get(key) + if item then entry = Util.shallowCopy(item) entry.count = count entry.key = key adapter.cache[key] = entry else +debug('STORAGE: item missing details') -- TODO: somehow update itemdb with this maybe new item adapter.dirty = true self.dirty = true @@ -243,7 +244,7 @@ local function sn(name) if #t ~= 2 then return name end - return table.concat(table.unpack(t), '_') + return table.concat(t, '_') end function Storage:export(target, slot, count, item) @@ -253,9 +254,11 @@ function Storage:export(target, slot, count, item) local function provide(adapter) local amount = adapter:provide(item, count, slot, target or self.localName) if amount > 0 then + _G._debug('EXT: %s(%d): %s -> %s%s', - item.name, amount, sn(adapter.name), sn(target or self.localName), + item.displayName or item.name, amount, sn(adapter.name), sn(target or self.localName), slot and string.format('[%d]', slot) or '') + self:updateCache(adapter, key, -amount) self:updateCache(self, key, -amount) end @@ -302,7 +305,7 @@ function Storage:import(source, slot, count, item) if amount > 0 then _G._debug('INS: %s(%d): %s[%d] -> %s', - item.name, amount, + item.displayName or item.name, amount, sn(source or self.localName), slot, sn(adapter.name)) self:updateCache(adapter, key, amount) diff --git a/milo/apis/turtle/craft.lua b/milo/apis/turtle/craft.lua index d27bc1b..819237b 100644 --- a/milo/apis/turtle/craft.lua +++ b/milo/apis/turtle/craft.lua @@ -4,6 +4,7 @@ local Util = require('util') local device = _G.device local fs = _G.fs local turtle = _G.turtle +local intro = device['plethora:introspection'] local Craft = { STATUS_INFO = 'info', @@ -16,20 +17,6 @@ local Craft = { MACHINE_LOOKUP = 'usr/config/machine_crafting.db', } -local function clearGrid(storage) - turtle.eachFilledSlot(function(slot) - storage:import(storage.localName, slot.index, slot.count, slot) - end) - - for i = 1, 16 do - if turtle.getItemCount(i) ~= 0 then - return false - end - end - - return true -end - local function splitKey(key) local t = Util.split(key, '(.-):') local item = { } @@ -48,6 +35,27 @@ local function makeRecipeKey(item) return table.concat({ item.name, item.damage or 0, item.nbtHash }, ':') end +function Craft.clearGrid(storage) + turtle.eachFilledSlot(function(slot) + local item = slot + if intro then + item = intro.getInventory().getItemMeta(slot.index) + if not itemDB:get(item) then + itemDB:add(item) + end + end + storage:import(storage.localName, slot.index, slot.count, item) + end) + + for i = 1, 16 do + if turtle.getItemCount(i) ~= 0 then + return false + end + end + + return true +end + function Craft.getItemCount(items, item) if type(item) == 'string' then item = splitKey(item) @@ -117,7 +125,7 @@ local function machineCraft(recipe, storage, machineName, request, count, item) end local function turtleCraft(recipe, storage, request, count) - if not clearGrid(storage) then + if not Craft.clearGrid(storage) then request.status = 'grid in use' request.statusCode = Craft.STATUS_ERROR return @@ -142,7 +150,7 @@ local function turtleCraft(recipe, storage, request, count) request.status = 'Failed to craft' request.statusCode = Craft.STATUS_ERROR end - clearGrid(storage) + Craft.clearGrid(storage) return request.statusCode == Craft.STATUS_SUCCESS end diff --git a/milo/plugins/manipulatorView.lua b/milo/plugins/manipulatorView.lua index 6fa7f76..66ac15c 100644 --- a/milo/plugins/manipulatorView.lua +++ b/milo/plugins/manipulatorView.lua @@ -7,20 +7,26 @@ local colors = _G.colors local device = _G.device local turtle = _G.turtle ---[[ Configuration Screen ]] -local template = -[[%sBound Manipulator%s - -Automatically import items into storage from your ender chest. -]] - +--[[ Configuration Screen ]]-- local wizardPage = UI.Window { title = 'Manipulator', index = 2, backgroundColor = colors.cyan, - [1] = UI.TextArea { - x = 2, ex = -2, y = 2, ey = -2, - value = string.format(template, Ansi.yellow, Ansi.reset), + form = UI.Form { + x = 1, y = 3, ex = -1, ey = -2, + manualControls = true, + [1] = UI.Checkbox { + formLabel = 'Import', formKey = 'importEnder', + help = 'Locks chest to a single item type', + pruneEmpty = true, + }, + [2] = UI.TextArea { + x = 13, ex = -2, y = 2, + value = 'Automatically import the user\'s ender chest contents', + }, + }, + userInfo = UI.TextArea { + x = 2, ex = -2, y = 2, height = 1, }, } @@ -36,8 +42,19 @@ function wizardPage:isValidFor(node) return node.mtype == 'manipulator' end +function wizardPage:setNode(node) + self.form:setValues(node) + self.userInfo.value = string.format('%sBound to: %s%s', + Ansi.black, Ansi.yellow, node.adapter.getName()) +end + +function wizardPage:validate() + return self.form:save() +end + UI:getPage('nodeWizard').wizard:add({ manipulator = wizardPage }) +--[[ Task ]]-- local task = { name = 'manipulator', priority = 15, @@ -45,7 +62,7 @@ local task = { function task:cycle(context) local function filter(v) - return v.adapter.getEnder + return v.adapter.getEnder and v.importEnder end for manipulator in context.storage:filterActive('manipulator', filter) do diff --git a/milo/plugins/replenishTask.lua b/milo/plugins/replenishTask.lua index eb4605e..3a8c36c 100644 --- a/milo/plugins/replenishTask.lua +++ b/milo/plugins/replenishTask.lua @@ -30,6 +30,7 @@ function ReplenishTask:cycle(context) damage = res.ignoreDamage and 0 or item.damage, nbtHash = nbtHash, requested = res.low - count, + count = count, name = item.name, displayName = item.displayName, replenish = true, From 1eaaca2cc347e76b4314e9e449309b7b4b459781 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Mon, 12 Nov 2018 20:22:14 -0500 Subject: [PATCH 080/165] milo better user experience --- core/apis/itemDB.lua | 1 + farms/farmer.lua | 2 +- milo/MiloRemote.lua | 154 +++++++++++++++++++----------- milo/autorun/milo.lua | 8 ++ milo/core/machines.lua | 28 ++++-- milo/etc/apps/apps.db | 13 +++ milo/plugins/activityView.lua | 6 +- milo/plugins/brewingStandView.lua | 6 +- milo/plugins/exportView.lua | 9 +- milo/plugins/importView.lua | 9 +- milo/plugins/inputChestView.lua | 6 +- milo/plugins/jobMonitor.lua | 6 +- milo/plugins/listing.lua | 16 ++-- milo/plugins/manipulatorView.lua | 6 +- milo/plugins/potionImportTask.lua | 2 +- milo/plugins/storageView.lua | 8 +- milo/plugins/trashcanView.lua | 6 +- 17 files changed, 204 insertions(+), 82 deletions(-) create mode 100644 milo/autorun/milo.lua create mode 100644 milo/etc/apps/apps.db diff --git a/core/apis/itemDB.lua b/core/apis/itemDB.lua index 95bd135..ab014e9 100644 --- a/core/apis/itemDB.lua +++ b/core/apis/itemDB.lua @@ -31,6 +31,7 @@ local function safeString(text) end function itemDB:makeKey(item) + if not item then error('itemDB:makeKey: item is required', 2) end return table.concat({ item.name, item.damage or '*', item.nbtHash }, ':') end diff --git a/farms/farmer.lua b/farms/farmer.lua index ac182f8..877d1a0 100644 --- a/farms/farmer.lua +++ b/farms/farmer.lua @@ -139,7 +139,7 @@ local function harvest(blocks) elseif b.action == 'bump' then if turtle.faceAgainst(b) then turtle.equip('right', 'plethora:module:3') - os.sleep(.3) + os.sleep(.5) -- search the ground for the dropped cactus local sensed = peripheral.call('right', 'sense') turtle.equip('right', 'minecraft:diamond_pickaxe') diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index fa16b57..2ae9ffa 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -1,5 +1,6 @@ _G.requireInjector(_ENV) +local Config = require('config') local Event = require('event') local Socket = require('socket') local sync = require('sync').sync @@ -13,48 +14,26 @@ local socket local SHIELD_SLOT = 2 -local options = { - user = { arg = 'u', type = 'string', - desc = 'User name associated with bound manipulator' }, - slot = { arg = 's', type = 'number', - desc = 'Optional inventory slot to use to transfer to milo' }, - shield = { arg = 'e', type = 'flag', - desc = 'Use shield slot to use to transfer to milo' }, - server = { arg = 'm', type = 'number', - desc = 'ID of Milo server' }, - help = { arg = 'h', type = 'flag', value = false, - desc = 'Displays the options' }, -} - -local args = { ... } -if not Util.getOptions(options, args) then - print() - error('Invalid arguments') -end - -if not options.user.value or not options.server.value then - Util.showOptions(options) - print() - error('Invalid arguments') -end - -if (options.slot.value or options.shield.value) and - not (device.neuralInterface and device.neuralInterface.getInventory) then - error('Introspection module is required for transferring items') -end +local config = Config.load('miloRemote', { }) local page = UI.Page { dummy = UI.Window { - x = 1, ex = -10, y = 1, height = 1, + x = 1, ex = -13, y = 1, height = 1, infoBar = UI.StatusBar { backgroundColor = colors.lightGray, }, }, refresh = UI.Button { - y = 1, x = -9, + y = 1, x = -12, event = 'refresh', text = 'Refresh', }, + setupButton = UI.Button { + y = 1, x = -3, + event = 'setup', + text = '\206', + help = 'Configuration', + }, grid = UI.Grid { y = 2, ey = -2, columns = { @@ -106,6 +85,46 @@ local page = UI.Page { q = 'quit', }, + setup = UI.SlideOut { + backgroundColor = colors.cyan, + titleBar = UI.TitleBar { + title = 'Remote Setup', + }, + form = UI.Form { + x = 2, ex = -2, y = 2, ey = -1, + values = config, + [1] = UI.TextEntry { + formLabel = 'Server', formKey = 'server', + help = 'ID for the server', + shadowText = 'Milo server ID', + limit = 6, + validate = 'numeric', + required = true, + }, + [2] = UI.TextEntry { + formLabel = 'User Name', formKey = 'user', + help = 'User name for bound manipulator', + shadowText = 'User name', + limit = 50, + required = true, + }, + [3] = UI.TextEntry { + formLabel = 'Return Slot', formKey = 'slot', + help = 'Use a slot for sending to storage', + shadowText = 'Inventory slot #', + limit = 5, + validate = 'numeric', + required = true, + }, + [4] = UI.Checkbox { + formLabel = 'Shield Slot', formKey = 'useShield', + help = 'or use the shield slot for sending' + }, + }, + statusBar = UI.StatusBar { + backgroundColor = colors.cyan, + }, + }, displayMode = 0, items = { }, } @@ -139,14 +158,22 @@ end function page:sendRequest(data) local response + if not config.server then + self:setStatus('Invalid configuration') + Event.onTimeout(2, function() + self:setStatus('') + end) + return + end + sync(self, function() local msg for _ = 1, 2 do if not socket or not socket.connected then self:setStatus('connecting ...') - socket, msg = Socket.connect(options.server.value, 4242) + socket, msg = Socket.connect(config.server, 4242) if socket then - socket:write(options.user.value) + socket:write(config.user) local r = socket:read(2) if r and not r.msg then self:setStatus('connected ...') @@ -203,7 +230,6 @@ end function page:transfer(item, count) local response = self:sendRequest({ request = 'transfer', item = item, count = count }) if response then - _debug(response) item.count = response.current - response.count self.grid:draw() if response.craft > 0 then @@ -214,10 +240,29 @@ function page:transfer(item, count) end end +function page.setup:eventHandler(event) + if event.type == 'focus_change' then + self.statusBar:setStatus(event.focused.help) + end + return UI.SlideOut.eventHandler(self, event) +end + function page:eventHandler(event) if event.type == 'quit' then UI:exitPullEvents() + elseif event.type == 'setup' then + self.setup:show() + + elseif event.type == 'form_complete' then + Config.update('miloRemote', config) + self.setup:hide() + self:refresh() + self.grid:draw() + + elseif event.type == 'form_cancel' then + self.setup:hide() + elseif event.type == 'focus_change' then self.dummy.infoBar:setStatus(event.focused.help) @@ -290,6 +335,9 @@ end function page:enable() self:setFocus(self.statusBar.filter) UI.Page.enable(self) + if not config.server then + self.setup:show() + end Event.onTimeout(.1, function() self:refresh() self.grid:draw() @@ -311,31 +359,21 @@ function page:applyFilter() self.grid:setValues(t) end -if options.slot.value or options.shield.value then - local inv = 'getInventory' - local slotNo = options.slot.value - local slotValue = options.slot.value - - if options.shield.value then - slotNo = SHIELD_SLOT - slotValue = 'shield' - inv = 'getEquipment' - end - - Event.addRoutine(function() - while true do - os.sleep(1.5) - local neural = device.neuralInterface - if not neural or not neural[inv] then - _G._debug('missing Introspection module') - end - - local method = neural and neural[inv] - local item = method and method().getItemMeta(slotNo) +Event.addRoutine(function() + while true do + os.sleep(1.5) + local neural = device.neuralInterface + local inv = config.useShield and 'getEquipment' or 'getInventory' + if not neural or not neural[inv] then + _G._debug('missing Introspection module') + elseif config.server then + local method = neural[inv] + local item = method and method().getItemMeta(config.useShield and SHIELD_SLOT or config.slot) if item then + local slotNo = config.useShield and 'shield' or config.slot local response = page:sendRequest({ request = 'deposit', - slot = slotValue, + slot = slotNo, count = item.count, key = table.concat({ item.name, item.damage, item.nbtHash }, ':') }) @@ -349,8 +387,8 @@ if options.slot.value or options.shield.value then end end end - end) -end + end +end) UI:setPage(page) UI:pullEvents() diff --git a/milo/autorun/milo.lua b/milo/autorun/milo.lua new file mode 100644 index 0000000..3304f36 --- /dev/null +++ b/milo/autorun/milo.lua @@ -0,0 +1,8 @@ +local device = _G.device +local shell = _ENV.shell + +if device.workbench then + shell.openForegroundTab('Milo') +elseif device.neuralInterface then + shell.openForegroundTab('MiloRemote') +end diff --git a/milo/core/machines.lua b/milo/core/machines.lua index 388a651..75b594d 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -62,6 +62,9 @@ local networkPage = UI.Page { backgroundColor = colors.lightGray, }, notification = UI.Notification { }, + accelerators = { + delete = 'remove_node', + } } function networkPage.grid:getDisplayValues(row) @@ -236,7 +239,9 @@ The settings will take effect immediately!]], { heading = 'Name', key = 'displayName' }, }, sortColumn = 'displayName', - help = 'Select item to export', + accelerators = { + delete = 'remove_entry', + }, }, remove = UI.Button { x = -4, y = 4, @@ -255,7 +260,7 @@ The settings will take effect immediately!]], { name = 'Yes', value = true }, { name = 'No', value = false }, }, - help = 'Ignore damage of item when exporting' + help = 'Ignore damage of item' }, [2] = UI.Chooser { width = 7, @@ -266,7 +271,7 @@ The settings will take effect immediately!]], { name = 'Yes', value = true }, { name = 'No', value = false }, }, - help = 'Ignore NBT of item when exporting' + help = 'Ignore NBT of item' }, [3] = UI.Chooser { width = 13, @@ -276,7 +281,7 @@ The settings will take effect immediately!]], { name = 'whitelist', value = false }, { name = 'blacklist', value = true }, }, - help = 'Ignore damage of item when exporting' + help = 'Ignore damage of item' }, scan = UI.Button { x = -11, y = 1, @@ -377,7 +382,7 @@ function nodeWizard.wizard.pages.general:enable() self:focusFirst() end -function nodeWizard.wizard.pages.general:setNode(node) +function nodeWizard.wizard.pages.general:showInventory(node) local inventory if device[node.name] and device[node.name].list then @@ -437,7 +442,7 @@ _G._p2 = self.node if page.isValidType then -- TODO: dedupe list local choice = page:isValidType(self.node) - if choice then + if choice and not Util.find(choices, 'value', choice.value) then table.insert(choices, choice) end end @@ -446,6 +451,8 @@ _G._p2 = self.node self.wizard.pages.general.form[2].choices = choices self.wizard.pages.general.form:setValues(self.node) + self.wizard.pages.general:showInventory(self.node) + -- restore indices for _, page in pairs(self.wizard.pages) do if not page.oindex then @@ -479,6 +486,15 @@ function nodeWizard:eventHandler(event) UI:setPreviousPage() + elseif event.type == 'choice_change' then + local help + if event.choice and event.choice.help then -- TODO: new param sent by UI api + help = event.choice.help + else + help = '' + end + self.statusBar:setStatus(help) + elseif event.type == 'edit_filter' then self.filter:show(event.entry, event.callback, event.whitelistOnly) diff --git a/milo/etc/apps/apps.db b/milo/etc/apps/apps.db new file mode 100644 index 0000000..76ad61f --- /dev/null +++ b/milo/etc/apps/apps.db @@ -0,0 +1,13 @@ +{ + [ "9302912a2d9794a47241faefc475335b4e07a581" ] = { + title = "Remote", + category = "Apps", + run = "MiloRemote", + }, + [ "eea426f9baef72a8fcefd091e0cec5ab94a76698" ] = { + title = "Milo", + category = "Apps", + run = "Milo", + requires = 'advancedTurtle', + }, +} diff --git a/milo/plugins/activityView.lua b/milo/plugins/activityView.lua index d313aa1..1a15748 100644 --- a/milo/plugins/activityView.lua +++ b/milo/plugins/activityView.lua @@ -30,7 +30,11 @@ local activityWizardPage = UI.Window { function activityWizardPage:isValidType(node) local m = device[node.name] - return m and m.type == 'monitor' and { name = 'Activity Monitor', value = 'activity' } + return m and m.type == 'monitor' and { + name = 'Activity Monitor', + value = 'activity', + help = 'Display storage activity' + } end function activityWizardPage:isValidFor(node) diff --git a/milo/plugins/brewingStandView.lua b/milo/plugins/brewingStandView.lua index 018a8c3..b3036a4 100644 --- a/milo/plugins/brewingStandView.lua +++ b/milo/plugins/brewingStandView.lua @@ -27,7 +27,11 @@ local brewingStandView = UI.Window { function brewingStandView:isValidType(node) local m = device[node.name] - return m and m.type == 'minecraft:brewing_stand'and { name = 'Brewing Stand', value = 'brewingStand' } + return m and m.type == 'minecraft:brewing_stand'and { + name = 'Brewing Stand', + value = 'brewingStand', + help = 'Auto-learning brewing stand', + } end function brewingStandView:isValidFor(node) diff --git a/milo/plugins/exportView.lua b/milo/plugins/exportView.lua index 201e8be..8bfcbf6 100644 --- a/milo/plugins/exportView.lua +++ b/milo/plugins/exportView.lua @@ -15,6 +15,9 @@ local exportView = UI.Window { }, sortColumn = 'slot', help = 'Edit this entry', + accelerators = { + delete = 'remove_entry', + }, }, text = UI.Text { x = 2, y = -2, @@ -38,7 +41,11 @@ local exportView = UI.Window { function exportView:isValidType(node) local m = device[node.name] - return m and m.pullItems and { name = 'Generic Inventory', value = 'machine' } + return m and m.pullItems and { + name = 'Generic Inventory', + value = 'machine', + help = 'Chest, furnace... (has an inventory)' + } end function exportView:isValidFor(node) diff --git a/milo/plugins/importView.lua b/milo/plugins/importView.lua index 996736c..cd3d6c3 100644 --- a/milo/plugins/importView.lua +++ b/milo/plugins/importView.lua @@ -15,6 +15,9 @@ local importView = UI.Window { }, sortColumn = 'slot', help = 'Edit this entry', + accelerators = { + delete = 'remove_entry', + }, }, text = UI.Text { x = 2, y = -2, @@ -38,7 +41,11 @@ local importView = UI.Window { function importView:isValidType(node) local m = device[node.name] - return m and m.pullItems and { name = 'Generic Inventory', value = 'machine' } + return m and m.pullItems and { + name = 'Generic Inventory', + value = 'machine', + help = 'Chest, furnace... (has an inventory)', + } end function importView:isValidFor(node) diff --git a/milo/plugins/inputChestView.lua b/milo/plugins/inputChestView.lua index 0177903..59d48fe 100644 --- a/milo/plugins/inputChestView.lua +++ b/milo/plugins/inputChestView.lua @@ -23,7 +23,11 @@ local inputChestWizardPage = UI.Window { function inputChestWizardPage:isValidType(node) local m = device[node.name] - return m and m.pullItems and { name = 'Input Chest', value = 'input' } + return m and m.pullItems and { + name = 'Input Chest', + value = 'input', + help = 'Sends all items to storage', + } end function inputChestWizardPage:isValidFor(node) diff --git a/milo/plugins/jobMonitor.lua b/milo/plugins/jobMonitor.lua index 30ee1f7..33d811a 100644 --- a/milo/plugins/jobMonitor.lua +++ b/milo/plugins/jobMonitor.lua @@ -29,7 +29,11 @@ local jobsWizardPage = UI.Window { function jobsWizardPage:isValidType(node) local m = device[node.name] - return m and m.type == 'monitor' and { name = 'Crafting Monitor', value = 'jobs' } + return m and m.type == 'monitor' and { + name = 'Crafting Monitor', + value = 'jobs', + help = 'Display crafting progress / jobs' + } end function jobsWizardPage:isValidFor(node) diff --git a/milo/plugins/listing.lua b/milo/plugins/listing.lua index def0050..aa5a557 100644 --- a/milo/plugins/listing.lua +++ b/milo/plugins/listing.lua @@ -36,8 +36,8 @@ local listingPage = UI.Page { --{ text = 'Forget', event = 'forget' }, { text = 'Craft', event = 'craft' }, { text = 'Edit', event = 'details' }, - { text = 'Network', event = 'network' }, - { text = 'Refresh', event = 'refresh', x = -9 }, + { text = 'Refresh', event = 'refresh', x = -12 }, + { text = '\206', event = 'network', x = -3 }, }, }, grid = UI.Grid { @@ -59,7 +59,7 @@ local listingPage = UI.Page { backgroundColor = colors.cyan, backgroundFocusColor = colors.cyan, accelerators = { - [ 'enter' ] = 'craft', + [ 'enter' ] = 'eject', }, }, storageStatus = UI.Text { @@ -198,10 +198,12 @@ function listingPage:eventHandler(event) elseif event.type == 'craft' then local item = self.grid:getSelected() - if Craft.findRecipe(item) or true then -- or item.is_craftable then - UI:setPage('craft', self.grid:getSelected()) - else - self.notification:error('No recipe defined') + if item then + if Craft.findRecipe(item) then -- or item.is_craftable then + UI:setPage('craft', self.grid:getSelected()) + else + self.notification:error('No recipe defined') + end end elseif event.type == 'text_change' and event.element == self.statusBar.filter then diff --git a/milo/plugins/manipulatorView.lua b/milo/plugins/manipulatorView.lua index 66ac15c..5c2feb4 100644 --- a/milo/plugins/manipulatorView.lua +++ b/milo/plugins/manipulatorView.lua @@ -35,7 +35,11 @@ function wizardPage:isValidType(node) return m and m.type == 'manipulator' and m.getEnder and - { name = 'Manipulator', value = 'manipulator' } + { + name = 'Manipulator', + value = 'manipulator', + help = 'Manipulator w/bound introspection mod' + } end function wizardPage:isValidFor(node) diff --git a/milo/plugins/potionImportTask.lua b/milo/plugins/potionImportTask.lua index 58a6a07..30aa793 100644 --- a/milo/plugins/potionImportTask.lua +++ b/milo/plugins/potionImportTask.lua @@ -12,7 +12,7 @@ local function filter(a) end function PotionImportTask:cycle(context) - for bs in context.storage:filterActive('machine', filter) do + for bs in context.storage:filterActive('brewingStand', filter) do if bs.adapter.getBrewTime() == 0 then local list = bs.adapter.list() diff --git a/milo/plugins/storageView.lua b/milo/plugins/storageView.lua index 70449b6..1ba333d 100644 --- a/milo/plugins/storageView.lua +++ b/milo/plugins/storageView.lua @@ -26,6 +26,7 @@ local storageView = UI.Window { x = 16, ex = -2, y = 3, value = '', }, +--[[ [4] = UI.Checkbox { formLabel = 'Void', formKey = 'voidExcess', help = 'Void excess if locked - TODO', @@ -36,6 +37,7 @@ local storageView = UI.Window { help = 'TODO', pruneEmpty = true, }, +]]-- }, } @@ -50,7 +52,11 @@ end function storageView:isValidType(node) local m = device[node.name] - return m and m.pullItems and { name = 'Storage', value = 'storage' } + return m and m.pullItems and { + name = 'Storage', + value = 'storage', + help = 'Use for item storage', + } end function storageView:isValidFor(node) diff --git a/milo/plugins/trashcanView.lua b/milo/plugins/trashcanView.lua index 2b5547e..e81969f 100644 --- a/milo/plugins/trashcanView.lua +++ b/milo/plugins/trashcanView.lua @@ -25,7 +25,11 @@ local trashcanWizardPage = UI.Window { function trashcanWizardPage:isValidType(node) local m = device[node.name] - return m and m.pullItems and { name = 'Trashcan', value = 'trashcan' } + return m and m.pullItems and { + name = 'Trashcan', + value = 'trashcan', + help = 'An inventory to send unwanted items', + } end function trashcanWizardPage:isValidFor(node) From c50de60f4384058fa9f1b2e773810f6328884fee Mon Sep 17 00:00:00 2001 From: kepler155c Date: Tue, 13 Nov 2018 00:03:36 -0500 Subject: [PATCH 081/165] treefarm bug fixes --- farms/treefarm.lua | 55 +++++++++++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/farms/treefarm.lua b/farms/treefarm.lua index 6f7c146..55e61ec 100644 --- a/farms/treefarm.lua +++ b/farms/treefarm.lua @@ -6,7 +6,7 @@ _G.requireInjector() Area around turtle must be flat and can only be dirt or grass (10 blocks in each direction from turtle) Turtle must have: crafting table, chest - Turtle must have a pick equipped + Turtle must have a pick equipped on the RIGHT side Optional: Add additional sapling types that can grow with a single sapling @@ -140,8 +140,22 @@ local function craftItem(item, qty) return success end +local function emptyFurnace() + if state.cooking then + + print('Emptying furnace') + + turtle.suckDownAt(state.furnace) + turtle.suckForwardAt(state.furnace) + turtle.suckUpAt(state.furnace) + setState('cooking') + end +end + local function cook(item, count, result, fuel, fuelCount) + emptyFurnace() + setState('cooking', true) fuel = fuel or CHARCOAL @@ -154,9 +168,19 @@ local function cook(item, count, result, fuel, fuelCount) count = count + turtle.getItemCount(result) turtle.select(1) turtle.pathfind(Point.below(state.furnace)) + + local lastSuck = os.clock() repeat os.sleep(1) - turtle.suckUp() + if turtle.suckUp() then + lastSuck = os.clock() + end + + if os.clock() - lastSuck > 10 then + -- sponge bug + Util.print('Timed out waiting for furnace') + return + end until turtle.getItemCount(result) >= count setState('cooking') @@ -225,18 +249,6 @@ local function makeCharcoal() return true end -local function emptyFurnace() - if state.cooking then - - print('Emptying furnace') - - turtle.suckDownAt(state.furnace) - turtle.suckForwardAt(state.furnace) - turtle.suckUpAt(state.furnace) - setState('cooking') - end -end - local function getCobblestone(count) local slots = turtle.getSummedInventory() @@ -602,7 +614,20 @@ local function findGround() break end - if b == COBBLESTONE or b == STONE then + if b == COBBLESTONE then + turtle.back() + local s2, b2 = turtle.inspectDown() + if not s2 then + error('lost') + end + if b2.name == COBBLESTONE then + turtle.turnLeft() + turtle.back() + end + break + end + + if b == STONE then error('lost') end From 986fe160c7ae705a7723b39ffaa5eca9aa2d6c57 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Tue, 13 Nov 2018 13:43:30 -0500 Subject: [PATCH 082/165] cleanup + adv brewing --- core/etc/recipes/minecraft.db | 6 ++++++ milo/Milo.lua | 18 +++++------------- milo/apis/milo.lua | 11 +++++------ milo/apis/storage.lua | 24 +++++++++++++++++------- milo/apis/turtle/craft.lua | 10 +--------- milo/plugins/exportTask.lua | 5 +++-- milo/plugins/importView.lua | 2 +- milo/plugins/potionImportTask.lua | 22 ++++++++++++++++++++-- 8 files changed, 58 insertions(+), 40 deletions(-) diff --git a/core/etc/recipes/minecraft.db b/core/etc/recipes/minecraft.db index 241889e..c5614d4 100644 --- a/core/etc/recipes/minecraft.db +++ b/core/etc/recipes/minecraft.db @@ -2223,6 +2223,12 @@ [ 6 ] = "minecraft:stone:0", }, }, + [ "minecraft:blaze_powder:0" ] = { + count = 2, + ingredients = { + "minecraft:blaze_rod:0", + }, + }, [ "minecraft:end_rod:0" ] = { count = 4, ingredients = { diff --git a/milo/Milo.lua b/milo/Milo.lua index cd529fa..fe1e440 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -7,14 +7,6 @@ Turtle crafting: 1. The turtle must have a crafting table equipped. 2. Equip the turtle with an introspection module. - - Configuration: - Configuration file is usr/config/milo - - monitor : valid options include: - type/monitor - will use the first monitor found - side/north - specify a direction (top/bottom/east/etc) - name/monitor_1 - specify the exact name of the peripheral ]]-- _G.requireInjector(_ENV) @@ -27,6 +19,7 @@ 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 os = _G.os @@ -37,8 +30,6 @@ if multishell then end local config = { - monitor = 'type/monitor', - activityMonitor = 'none', nodes = { }, } Config.load('milo', config) @@ -54,7 +45,7 @@ if not modem or not modem.getNameLocal then error('Wired modem is not connected') end -local introspectionModule = Peripheral.get('plethora:introspection') or +local introspection = Peripheral.get('plethora:introspection') or error('Introspection module not found') local context = { @@ -62,16 +53,17 @@ local context = { resources = Util.readTable(Milo.RESOURCE_FILE) or { }, craftingQueue = { }, - learnTypes = { }, tasks = { }, queue = { }, localName = modem.getNameLocal(), storage = Storage(config), - introspectionModule = introspectionModule, + turtleInventory = introspection.getInventory(), } +device[context.localName] = introspection.getInventory() + _G._p = context --debug Event.on('storage_offline', function() diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index e071c23..7a3fe2a 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -161,14 +161,13 @@ function Milo:getTurtleInventory() local list = { } for i = 1,16 do - local item = self.context.introspectionModule.getInventory().getItemMeta(i) - if item then - if not itemDB:get(item) then - itemDB:add(item) - end - list[i] = item + local item = self.context.turtleInventory.getItemMeta(i) + if item and not itemDB:get(item) then + itemDB:add(item) end + list[i] = item end + itemDB:flush() return list end diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index dac5548..20524e8 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -225,7 +225,7 @@ function Storage:updateCache(adapter, key, count) entry.key = key adapter.cache[key] = entry else -debug('STORAGE: item missing details') +_G._debug('STORAGE: item missing details') -- TODO: somehow update itemdb with this maybe new item adapter.dirty = true self.dirty = true @@ -252,11 +252,11 @@ function Storage:export(target, slot, count, item) local key = item.key or table.concat({ item.name, item.damage, item.nbtHash }, ':') local function provide(adapter) - local amount = adapter:provide(item, count, slot, target or self.localName) + local amount = adapter:provide(item, count, slot, target) if amount > 0 then _G._debug('EXT: %s(%d): %s -> %s%s', - item.displayName or item.name, amount, sn(adapter.name), sn(target or self.localName), + item.displayName or item.name, amount, sn(adapter.name), sn(target), slot and string.format('[%d]', slot) or '') self:updateCache(adapter, key, -amount) @@ -301,12 +301,12 @@ function Storage:import(source, slot, count, item) end local function insert(adapter) - local amount = adapter:insert(slot, count, nil, source or self.localName) + local amount = adapter:insert(slot, count, nil, source) if amount > 0 then _G._debug('INS: %s(%d): %s[%d] -> %s', item.displayName or item.name, amount, - sn(source or self.localName), slot, sn(adapter.name)) + sn(source), slot, sn(adapter.name)) self:updateCache(adapter, key, amount) self:updateCache(self, key, amount) @@ -341,6 +341,16 @@ _G._debug('INS: %s(%d): %s[%d] -> %s', end end + if not itemDB:get(item) then + if not slot then + _G._debug("IMPORT: NO SLOT") + elseif not device[source] or not device[source].getItemMeta then + _G._debug("IMPORT: DEVICE? : " .. source) + else + itemDB:add(device[source].getItemMeta(slot)) + end + end + -- high to low priority for remote in self:onlineAdapters() do if count <= 0 then @@ -359,9 +369,9 @@ function Storage:trash(source, slot, count) local trashcan = Util.find(self.nodes, 'mtype', 'trashcan') if trashcan and trashcan.adapter and trashcan.adapter.online then -_G._debug('TRA: %s[%d] (%d)', sn(source or self.localName), slot, count or 64) +_G._debug('TRA: %s[%d] (%d)', sn(source), slot, count or 64) - return trashcan.adapter.pullItems(source or self.localName, slot, count) + return trashcan.adapter.pullItems(source, slot, count) end return 0 end diff --git a/milo/apis/turtle/craft.lua b/milo/apis/turtle/craft.lua index 819237b..ccb419c 100644 --- a/milo/apis/turtle/craft.lua +++ b/milo/apis/turtle/craft.lua @@ -4,7 +4,6 @@ local Util = require('util') local device = _G.device local fs = _G.fs local turtle = _G.turtle -local intro = device['plethora:introspection'] local Craft = { STATUS_INFO = 'info', @@ -37,14 +36,7 @@ end function Craft.clearGrid(storage) turtle.eachFilledSlot(function(slot) - local item = slot - if intro then - item = intro.getInventory().getItemMeta(slot.index) - if not itemDB:get(item) then - itemDB:add(item) - end - end - storage:import(storage.localName, slot.index, slot.count, item) + storage:import(storage.localName, slot.index, slot.count, slot) end) for i = 1, 16 do diff --git a/milo/plugins/exportTask.lua b/milo/plugins/exportTask.lua index 0f0bc80..3fa5958 100644 --- a/milo/plugins/exportTask.lua +++ b/milo/plugins/exportTask.lua @@ -52,7 +52,6 @@ function ExportTask:cycle(context) end for key in pairs(entry.filter or { }) do - -- bad for perf to do listItems each time local items = Milo:getMatches(Milo:listItems(), itemDB:splitKey(key), entry.ignoreDamage, entry.ignoreNbtHash) for _,item in pairs(items) do if item and item.count > 0 then @@ -63,7 +62,9 @@ function ExportTask:cycle(context) else -- _debug('attempting to export %s %d', item.name, item.count) -- TODO: always going to try and export even if the chest is full - context.storage:export(machine.name, nil, item.count, item) + if context.storage:export(machine.name, nil, item.count, item) == 0 then + break + end end end end diff --git a/milo/plugins/importView.lua b/milo/plugins/importView.lua index cd3d6c3..96e3db6 100644 --- a/milo/plugins/importView.lua +++ b/milo/plugins/importView.lua @@ -64,7 +64,7 @@ function importView:setNode(node) } local m = device[self.machine.name] - for k = 1, m.size() do + for k = 1, m.size() do table.insert(self.slots.choices, { name = k, value = k }) end end diff --git a/milo/plugins/potionImportTask.lua b/milo/plugins/potionImportTask.lua index 30aa793..6f5eff4 100644 --- a/milo/plugins/potionImportTask.lua +++ b/milo/plugins/potionImportTask.lua @@ -1,5 +1,8 @@ -local Craft = require('turtle.craft') -local Milo = require('milo') +local Craft = require('turtle.craft') +local itemDB = require('itemDB') +local Milo = require('milo') + +local BLAZE_POWDER = "minecraft:blaze_powder:0" local PotionImportTask = { name = 'potions', @@ -16,6 +19,20 @@ function PotionImportTask:cycle(context) if bs.adapter.getBrewTime() == 0 then local list = bs.adapter.list() + -- refill blaze powder + if not list[5] then + local blazePowder = context.storage.cache[BLAZE_POWDER] + if blazePowder then + context.storage:export(bs.name, 5, 1, blazePowder) + else + local item = itemDB:get(BLAZE_POWDER) + if item then + item.requested = 1 + Milo:requestCrafting(item) + end + end + end + if list[1] and not list[4] then -- brewing has completd @@ -25,6 +42,7 @@ function PotionImportTask:cycle(context) Milo:saveMachineRecipe(self.brewQueue[bs.name], list[1], bs.name) end end + for slot = 1, 3 do if list[slot] then context.storage:import(bs.name, slot, 1, list[slot]) From 2af29c5d934ddc7122a487f31e425cdc149d6745 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Tue, 13 Nov 2018 14:32:37 -0500 Subject: [PATCH 083/165] milo: refresh interval --- milo/apis/storage.lua | 1 + milo/plugins/brewingStandView.lua | 2 +- milo/plugins/refreshTask.lua | 17 +++++++++++++++-- milo/plugins/storageView.lua | 18 +++++++++++++++--- 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 20524e8..a18297f 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -177,6 +177,7 @@ function Storage:listItems(throttle) local timer = Timer() for _, adapter in self:onlineAdapters() do if adapter.dirty then + _G._debug('STORAGE: refreshing ' .. adapter.name) adapter:listItems(throttle) adapter.dirty = false end diff --git a/milo/plugins/brewingStandView.lua b/milo/plugins/brewingStandView.lua index b3036a4..d67a74e 100644 --- a/milo/plugins/brewingStandView.lua +++ b/milo/plugins/brewingStandView.lua @@ -13,7 +13,7 @@ Simply craft potions in the brewing stand as normal except for these conditions. When finished brewing, the recipe will be available upon refreshing. -Note that you do not need to import items from the brewing stand, this will be done automatically.]] +Note that you do not need to import items from the brewing stand or export blaze powder, this will be done automatically.]] local brewingStandView = UI.Window { title = 'Brewing Stand', diff --git a/milo/plugins/refreshTask.lua b/milo/plugins/refreshTask.lua index d3dcb05..9b89f81 100644 --- a/milo/plugins/refreshTask.lua +++ b/milo/plugins/refreshTask.lua @@ -8,9 +8,22 @@ local RefreshTask = { } function RefreshTask:cycle(context) - if os.clock() - context.storage.lastRefresh > 60 then - context.storage:refresh() + local now = os.clock() + + for node, adapter in context.storage:onlineAdapters() do + if node.refreshInterval then + if not adapter.lastRefresh or adapter.lastRefresh + node.refreshInterval < now then + _G._debug('REFRESHER: ' .. adapter.name) + context.storage.dirty = true + adapter.dirty = true + adapter.lastRefresh = now + end + end end + +-- if os.clock() - context.storage.lastRefresh > 60 then +-- context.storage:refresh() +-- end end Milo:registerTask(RefreshTask) diff --git a/milo/plugins/storageView.lua b/milo/plugins/storageView.lua index 1ba333d..b850510 100644 --- a/milo/plugins/storageView.lua +++ b/milo/plugins/storageView.lua @@ -9,23 +9,35 @@ local storageView = UI.Window { index = 2, backgroundColor = colors.cyan, form = UI.Form { - x = 1, y = 2, ex = -1, ey = -2, + x = 1, y = 1, ex = -1, ey = -2, manualControls = true, [1] = UI.TextEntry { formLabel = 'Priority', formKey = 'priority', help = 'Larger values get precedence', limit = 4, - validate = 'numeric', pruneEmpty = true, + validate = 'numeric', + shadowText = 'Numeric priority', }, [2] = UI.Checkbox { formLabel = 'Locked', formKey = 'lockWith', help = 'Locks chest to a single item type', - pruneEmpty = true, }, [3] = UI.Text { x = 16, ex = -2, y = 3, value = '', }, + [4] = UI.TextEntry { + formLabel = 'Refresh', formKey = 'refreshInterval', + help = 'Refresh periodically', + limit = 4, + validate = 'numeric', + shadowText = 'seconds between refresh', + }, + [5] = UI.TextArea { + x = 12, ex = -2, y = 5, + textColor = colors.yellow, + value = 'Only specify if you are manually taking items out of this inventory. Value should be > 10', + }, --[[ [4] = UI.Checkbox { formLabel = 'Void', formKey = 'voidExcess', From 6870378422d7f122ae9aa2ed9be43bcb44f4b149 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Tue, 13 Nov 2018 16:07:28 -0500 Subject: [PATCH 084/165] milo: lock chest with multiple items --- core/apis/itemDB.lua | 1 + milo/apis/storage.lua | 29 +++++++++++++++---- milo/plugins/storageView.lua | 55 +++++++++++++++++++++++++----------- 3 files changed, 62 insertions(+), 23 deletions(-) diff --git a/core/apis/itemDB.lua b/core/apis/itemDB.lua index ab014e9..21d0442 100644 --- a/core/apis/itemDB.lua +++ b/core/apis/itemDB.lua @@ -52,6 +52,7 @@ function itemDB:splitKey(key, item) end function itemDB:get(key) + if not key then error('itemDB:get: key is required', 2) end if type(key) == 'string' then key = self:splitKey(key) end diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index a18297f..021e3b6 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -293,6 +293,19 @@ function Storage:export(target, slot, count, item) return total end +local function isLockedWith(node, key) + if node.lock then + if type(node.lock) == 'string' then + return node.lock == key + end + for k in pairs(node.lock) do + if k == key then + return true + end + end + end +end + function Storage:import(source, slot, count, item) local total = 0 local key = item.key or table.concat({ item.name, item.damage, item.nbtHash }, ':') @@ -320,23 +333,27 @@ _G._debug('INS: %s(%d): %s[%d] -> %s', end -- find a chest locked with this item - for remote in self:onlineAdapters() do - if remote.lock == key then - insert(remote.adapter) - if count > 0 then -- TODO: only if void flag set + for node in self:onlineAdapters() do + if isLockedWith(node, key) then + insert(node.adapter) + if count > 0 and node.void then total = total + self:trash(source, slot, count) + return total end + --return total + end + if count <= 0 then return total end end -- is this item in some chest if self.cache[key] then - for _, adapter in self:onlineAdapters() do + for node, adapter in self:onlineAdapters() do if count <= 0 then return total end - if adapter.cache and adapter.cache[key] and not adapter.lock then + if adapter.cache and adapter.cache[key] and not node.lock then insert(adapter) end end diff --git a/milo/plugins/storageView.lua b/milo/plugins/storageView.lua index b850510..e11a07a 100644 --- a/milo/plugins/storageView.lua +++ b/milo/plugins/storageView.lua @@ -20,21 +20,25 @@ local storageView = UI.Window { }, [2] = UI.Checkbox { formLabel = 'Locked', formKey = 'lockWith', - help = 'Locks chest to a single item type', + help = 'Locks chest to current item types', }, [3] = UI.Text { x = 16, ex = -2, y = 3, value = '', }, - [4] = UI.TextEntry { + [4] = UI.Checkbox { + formLabel = 'Void', formKey = 'void', + help = 'Void items if locked chest is full', + }, + [5] = UI.TextEntry { formLabel = 'Refresh', formKey = 'refreshInterval', - help = 'Refresh periodically', + shadowText = 'seconds between refresh', limit = 4, validate = 'numeric', - shadowText = 'seconds between refresh', + help = 'Refresh periodically', }, - [5] = UI.TextArea { - x = 12, ex = -2, y = 5, + [6] = UI.TextArea { + x = 12, ex = -2, y = 6, textColor = colors.yellow, value = 'Only specify if you are manually taking items out of this inventory. Value should be > 10', }, @@ -53,9 +57,24 @@ local storageView = UI.Window { }, } +function storageView:showLockTypes() + local types = { } + if self.node.lock then + if type(self.node.lock) == 'string' then + self.form[3].value = self.node.lock + return + end + for name in pairs(self.node.lock) do + table.insert(types, itemDB:getName(name)) + end + end + self.form[3].value = table.concat(types, ', ') +end + function storageView:enable() UI.Window.enable(self) self:focusFirst() + self:showLockTypes() end function storageView:validate() @@ -76,32 +95,34 @@ function storageView:isValidFor(node) end function storageView:setNode(node) - self.machine = node + self.node = node self.form:setValues(node) - self.form[3].value = node.lock and itemDB:getName(node.lock) or '' + self:showLockTypes() end function storageView:eventHandler(event) if event.type == 'checkbox_change' and event.element.formKey == 'lockWith' then if event.checked then - if device[self.machine.name] and device[self.machine.name].list then - local _, slot = next(device[self.machine.name].list()) - if slot then - self.machine.lock = itemDB:makeKey(slot) - self.form[3].value = itemDB:getName(slot) - else + if device[self.node.name] and device[self.node.name].list then + local lock = { } + for _, slot in pairs(device[self.node.name].list()) do + lock[itemDB:makeKey(slot)] = true + end + if not next(lock) then self:emit({ type = 'general_error', field = event.element, - message = 'The chest must contain the item to lock' }) + message = 'The chest must contain the item(s) to lock' }) self.form[3].value = false - self.form[3]:draw() + else + self.node.lock = lock end end else - self.machine.lock = nil + self.node.lock = nil self.form[3].value = '' end + self:showLockTypes() self.form[3]:draw() end end From 16c56aa09269e4df918d78003b07df28f7fba918 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Tue, 13 Nov 2018 17:18:55 -0500 Subject: [PATCH 085/165] milo: lock chest with multiple items --- milo/Milo.lua | 9 +++ milo/apis/storage.lua | 15 +--- milo/plugins/storageView.lua | 134 +++++++++++++++++++++-------------- 3 files changed, 90 insertions(+), 68 deletions(-) diff --git a/milo/Milo.lua b/milo/Milo.lua index fe1e440..351c626 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -40,6 +40,15 @@ if config.remoteDefaults then config.remoteDefaults = nil end +-- TODO: remove - temporary +for _, node in pairs(config.nodes) do + if node.lock and type(node.lock) == 'string' then + node.lock = { + [ node.lock ] = true, + } + end +end + local modem = Peripheral.get('wired_modem') if not modem or not modem.getNameLocal then error('Wired modem is not connected') diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 021e3b6..08f7576 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -293,19 +293,6 @@ function Storage:export(target, slot, count, item) return total end -local function isLockedWith(node, key) - if node.lock then - if type(node.lock) == 'string' then - return node.lock == key - end - for k in pairs(node.lock) do - if k == key then - return true - end - end - end -end - function Storage:import(source, slot, count, item) local total = 0 local key = item.key or table.concat({ item.name, item.damage, item.nbtHash }, ':') @@ -334,7 +321,7 @@ _G._debug('INS: %s(%d): %s[%d] -> %s', -- find a chest locked with this item for node in self:onlineAdapters() do - if isLockedWith(node, key) then + if node.lock and node.lock[key] then insert(node.adapter) if count > 0 and node.void then total = total + self:trash(source, slot, count) diff --git a/milo/plugins/storageView.lua b/milo/plugins/storageView.lua index e11a07a..56bc72c 100644 --- a/milo/plugins/storageView.lua +++ b/milo/plugins/storageView.lua @@ -5,7 +5,7 @@ local colors = _G.colors local device = _G.device local storageView = UI.Window { - title = 'Storage Options', + title = 'Storage Options - General', index = 2, backgroundColor = colors.cyan, form = UI.Form { @@ -18,63 +18,24 @@ local storageView = UI.Window { validate = 'numeric', shadowText = 'Numeric priority', }, - [2] = UI.Checkbox { - formLabel = 'Locked', formKey = 'lockWith', - help = 'Locks chest to current item types', - }, - [3] = UI.Text { - x = 16, ex = -2, y = 3, - value = '', - }, - [4] = UI.Checkbox { - formLabel = 'Void', formKey = 'void', - help = 'Void items if locked chest is full', - }, - [5] = UI.TextEntry { + [2] = UI.TextEntry { formLabel = 'Refresh', formKey = 'refreshInterval', shadowText = 'seconds between refresh', limit = 4, validate = 'numeric', help = 'Refresh periodically', }, - [6] = UI.TextArea { - x = 12, ex = -2, y = 6, + [3] = UI.TextArea { + x = 12, ex = -2, y = 4, textColor = colors.yellow, value = 'Only specify if you are manually taking items out of this inventory. Value should be > 10', }, ---[[ - [4] = UI.Checkbox { - formLabel = 'Void', formKey = 'voidExcess', - help = 'Void excess if locked - TODO', - pruneEmpty = true, - }, - [5] = UI.Checkbox { - formLabel = 'Partition', formKey = 'partition', - help = 'TODO', - pruneEmpty = true, - }, -]]-- }, } -function storageView:showLockTypes() - local types = { } - if self.node.lock then - if type(self.node.lock) == 'string' then - self.form[3].value = self.node.lock - return - end - for name in pairs(self.node.lock) do - table.insert(types, itemDB:getName(name)) - end - end - self.form[3].value = table.concat(types, ', ') -end - function storageView:enable() UI.Window.enable(self) self:focusFirst() - self:showLockTypes() end function storageView:validate() @@ -95,36 +56,101 @@ function storageView:isValidFor(node) end function storageView:setNode(node) + self.form:setValues(node) +end + +UI:getPage('nodeWizard').wizard:add({ storageGeneral = storageView }) + +--[[ Locking Page ]]-- +local lockView = UI.Window { + title = 'Storage Options - Locking', + index = 3, + backgroundColor = colors.cyan, + form = UI.Form { + x = 1, y = 1, ex = -1, ey = 3, + manualControls = true, + [1] = UI.Checkbox { + formLabel = 'Locked', formKey = 'lockWith', + help = 'Locks chest to current item types', + }, + [2] = UI.Checkbox { + formLabel = 'Void', formKey = 'void', + help = 'Void items if locked chest is full', + }, + }, + grid = UI.ScrollingGrid { + x = 2, ex = -2, y = 4, ey = -2, + columns = { + { heading = 'Name', key = 'displayName' }, + }, + sortColumn = 'displayName', + }, +} + +function lockView:showLockTypes() + self.grid.values = { } + if self.node.lock then + for key in pairs(self.node.lock) do + table.insert(self.grid.values, { + displayName = itemDB:getName(key), + key = key, + }) + end + end + self.grid:update() + self.grid:draw() +end + +function lockView:enable() + UI.Window.enable(self) + self:focusFirst() +end + +function lockView:validate() + return self.form:save() +end + +function lockView:isValidType(node) + local m = device[node.name] + return m and m.pullItems and { + name = 'Storage', + value = 'storage', + help = 'Use for item storage', + } +end + +function lockView:isValidFor(node) + return node.mtype == 'storage' +end + +function lockView:setNode(node) self.node = node self.form:setValues(node) self:showLockTypes() end -function storageView:eventHandler(event) +function lockView:eventHandler(event) if event.type == 'checkbox_change' and event.element.formKey == 'lockWith' then if event.checked then if device[self.node.name] and device[self.node.name].list then - local lock = { } - for _, slot in pairs(device[self.node.name].list()) do - lock[itemDB:makeKey(slot)] = true - end - if not next(lock) then + local list = device[self.node.name].list() + if not next(list) then self:emit({ type = 'general_error', field = event.element, message = 'The chest must contain the item(s) to lock' }) - self.form[3].value = false else - self.node.lock = lock + self.node.lock = { } + for _, slot in pairs(list) do + self.node.lock[itemDB:makeKey(slot)] = true + end end end else self.node.lock = nil - self.form[3].value = '' end self:showLockTypes() - self.form[3]:draw() end end -UI:getPage('nodeWizard').wizard:add({ storage = storageView }) +UI:getPage('nodeWizard').wizard:add({ storageLock = lockView }) From f6c05dba56c038549179ec6b8f321dbb368f3c07 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Tue, 13 Nov 2018 18:56:29 -0500 Subject: [PATCH 086/165] milo: drop from trashcans --- milo/core/machines.lua | 22 ++-------- milo/plugins/trashcanView.lua | 82 +++++++++++++++++++++++++++-------- 2 files changed, 67 insertions(+), 37 deletions(-) diff --git a/milo/core/machines.lua b/milo/core/machines.lua index 75b594d..734e578 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -251,27 +251,13 @@ The settings will take effect immediately!]], x = 2, y = -4, height = 3, margin = 1, manualControls = true, - [1] = UI.Chooser { - width = 7, + [1] = UI.Checkbox { formLabel = 'Ignore Dmg', formKey = 'ignoreDamage', - pruneEmpty = true, - nochoice = 'No', - choices = { - { name = 'Yes', value = true }, - { name = 'No', value = false }, - }, - help = 'Ignore damage of item' + help = 'Ignore damage of item', }, - [2] = UI.Chooser { - width = 7, + [2] = UI.Checkbox { formLabel = 'Ignore NBT', formKey = 'ignoreNbtHash', - pruneEmpty = true, - nochoice = 'No', - choices = { - { name = 'Yes', value = true }, - { name = 'No', value = false }, - }, - help = 'Ignore NBT of item' + help = 'Ignore NBT of item', }, [3] = UI.Chooser { width = 13, diff --git a/milo/plugins/trashcanView.lua b/milo/plugins/trashcanView.lua index e81969f..ed377c6 100644 --- a/milo/plugins/trashcanView.lua +++ b/milo/plugins/trashcanView.lua @@ -1,29 +1,52 @@ -local Ansi = require('ansi') -local UI = require('ui') +local Milo = require('milo') +local UI = require('ui') -local colors = _G.colors -local device = _G.device +local colors = _G.colors +local device = _G.device --[[ Configuration Screen ]] -local template = -[[%sUse this inventory as a trashcan%s - -If the number of items exceed the maximum value will be sent to this inventory. - -Any items that cannot fit into a locked chest will automatically be sent to this inventory. -]] - -local trashcanWizardPage = UI.Window { +local wizardPage = UI.Window { title = 'Trashcan', index = 2, backgroundColor = colors.cyan, - [1] = UI.TextArea { - x = 2, ex = -2, y = 2, ey = -2, - value = string.format(template, Ansi.yellow, Ansi.reset), + form = UI.Form { + x = 1, y = 1, ex = -1, ey = -2, + manualControls = true, + [1] = UI.Checkbox { + formLabel = 'Drop', formKey = 'drop', + help = 'Drop the items out of this inventory', + }, + [2] = UI.Chooser { + width = 9, + formLabel = 'Drop direction', formKey = 'dropDirection', + nochoice = 'Down', + choices = { + { name = 'Down', value = 'down' }, + { name = 'Up', value = 'up' }, + { name = 'North', value = 'north' }, + { name = 'South', value = 'south' }, + { name = 'East', value = 'east' }, + { name = 'West', value = 'west' }, + }, + help = 'Drop in a specified direction' + }, }, } -function trashcanWizardPage:isValidType(node) +function wizardPage:enable() + UI.Window.enable(self) + self:focusFirst() +end + +function wizardPage:validate() + return self.form:save() +end + +function wizardPage:setNode(node) + self.form:setValues(node) +end + +function wizardPage:isValidType(node) local m = device[node.name] return m and m.pullItems and { name = 'Trashcan', @@ -32,8 +55,29 @@ function trashcanWizardPage:isValidType(node) } end -function trashcanWizardPage:isValidFor(node) +function wizardPage:isValidFor(node) return node.mtype == 'trashcan' end -UI:getPage('nodeWizard').wizard:add({ trashcan = trashcanWizardPage }) +UI:getPage('nodeWizard').wizard:add({ trashcan = wizardPage }) + +--[[ TASK ]]-- +local task = { + name = 'trashcan', + priority = 90, +} + +local function filter(a) + return a.drop +end + +function task:cycle(context) + for node in context.storage:filterActive('trashcan', filter) do + for k in pairs(node.adapter.list()) do + local direction = node.dropDirection or 'down' + node.adapter.drop(k, 64, direction) + end + end +end + +Milo:registerTask(task) From 38777041d118d9a041282aa31e58b6a55c0deac0 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Tue, 13 Nov 2018 19:06:49 -0500 Subject: [PATCH 087/165] milo: drop from trashcans --- milo/plugins/trashcanView.lua | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/milo/plugins/trashcanView.lua b/milo/plugins/trashcanView.lua index ed377c6..a6e1836 100644 --- a/milo/plugins/trashcanView.lua +++ b/milo/plugins/trashcanView.lua @@ -9,8 +9,15 @@ local wizardPage = UI.Window { title = 'Trashcan', index = 2, backgroundColor = colors.cyan, + info = UI.TextArea { + x = 1, ex = -1, y = 2, ey = 4, + textColor = colors.yellow, + marginLeft = 1, + marginRight = 1, + value = [[ Items can be automatically dropped from this storage.]], + }, form = UI.Form { - x = 1, y = 1, ex = -1, ey = -2, + x = 1, y = 4, ex = -1, ey = -2, manualControls = true, [1] = UI.Checkbox { formLabel = 'Drop', formKey = 'drop', From 8e9ca385d9a02a124286b1915c10b2249d8db70c Mon Sep 17 00:00:00 2001 From: kepler155c Date: Tue, 13 Nov 2018 21:31:27 -0500 Subject: [PATCH 088/165] milo: UI cleanup --- milo/Milo.lua | 21 +++++++++++++++++-- milo/MiloRemote.lua | 36 +++++++++++++++++++++++++------- milo/core/machines.lua | 2 +- milo/plugins/activityView.lua | 6 +++--- milo/plugins/exportView.lua | 8 ++++--- milo/plugins/importView.lua | 8 ++++--- milo/plugins/jobMonitor.lua | 4 ++-- milo/plugins/manipulatorView.lua | 4 ++-- milo/plugins/remote.lua | 6 +++++- milo/plugins/storageView.lua | 8 ++++--- milo/plugins/trashcanView.lua | 4 ++-- 11 files changed, 77 insertions(+), 30 deletions(-) diff --git a/milo/Milo.lua b/milo/Milo.lua index 351c626..b9f0273 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -49,13 +49,30 @@ for _, node in pairs(config.nodes) do end end +local function Syntax(msg) + print([[ +Turtle must be equipped with: + * Introspection module + * Workbench + +Turtle must be connected to: + * Wired modem +]]) + + error(msg) +end + local modem = Peripheral.get('wired_modem') if not modem or not modem.getNameLocal then - error('Wired modem is not connected') + Syntax('Wired modem missing') end local introspection = Peripheral.get('plethora:introspection') or - error('Introspection module not found') + Syntax('Introspection module missing') + +if not device.workbench then + Syntax('Workbench missing') +end local context = { config = config, diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index 2ae9ffa..7c3c68f 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -114,11 +114,24 @@ local page = UI.Page { shadowText = 'Inventory slot #', limit = 5, validate = 'numeric', - required = true, + required = false, }, [4] = UI.Checkbox { formLabel = 'Shield Slot', formKey = 'useShield', - help = 'or use the shield slot for sending' + help = 'Or, use the shield slot for sending' + }, + info = UI.TextArea { + x = 1, ex = -1, y = 7, ey = -3, + textColor = colors.yellow, + marginLeft = 0, + marginRight = 0, + value = [[The Milo turtle must connect to a manipulator with a ]] .. + [[bound introspection module. The neural interface must ]] .. + [[also have an introspection module.]], + }, + [5] = UI.Button { + x = 1, y = -2, + text = 'Force scan', event = 'rescan', help = 'Force a scan of all inventories', }, }, statusBar = UI.StatusBar { @@ -252,12 +265,13 @@ function page:eventHandler(event) UI:exitPullEvents() elseif event.type == 'setup' then + self.setup.form:setValues(config) self.setup:show() elseif event.type == 'form_complete' then Config.update('miloRemote', config) self.setup:hide() - self:refresh() + self:refresh('list') self.grid:draw() elseif event.type == 'form_cancel' then @@ -299,10 +313,16 @@ function page:eventHandler(event) self:setStatus('nope ...') end + elseif event.type == 'rescan' then + self:setFocus(self.statusBar.filter) + self:setStatus('rescanning ...') + self:refresh('scan') + self.grid:draw() + elseif event.type == 'refresh' then self:setFocus(self.statusBar.filter) self:setStatus('updating ...') - self:refresh() + self:refresh('list') self.grid:draw() elseif event.type == 'toggle_display' then @@ -339,14 +359,14 @@ function page:enable() self.setup:show() end Event.onTimeout(.1, function() - self:refresh() + self:refresh('list') self.grid:draw() self:sync() end) end -function page:refresh() - local items = self:sendRequest({ request = 'list' }) +function page:refresh(requestType) + local items = self:sendRequest({ request = requestType }) if items then self.items = items @@ -366,7 +386,7 @@ Event.addRoutine(function() local inv = config.useShield and 'getEquipment' or 'getInventory' if not neural or not neural[inv] then _G._debug('missing Introspection module') - elseif config.server then + elseif config.server and (config.useShield or config.slot) then local method = neural[inv] local item = method and method().getItemMeta(config.useShield and SHIELD_SLOT or config.slot) if item then diff --git a/milo/core/machines.lua b/milo/core/machines.lua index 734e578..34f931c 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -183,7 +183,7 @@ nodeWizard = UI.Page { index = 1, backgroundColor = colors.cyan, form = UI.Form { - x = 1, y = 1, ex = -1, ey = 3, + x = 2, ex = -2, y = 1, ey = 3, manualControls = true, [1] = UI.TextEntry { formLabel = 'Name', formKey = 'displayName', diff --git a/milo/plugins/activityView.lua b/milo/plugins/activityView.lua index 1a15748..222cd04 100644 --- a/milo/plugins/activityView.lua +++ b/milo/plugins/activityView.lua @@ -11,12 +11,11 @@ local monitor = context.storage:getSingleNode('activity') --[[ Configuration Page ]]-- local template = -[[%sDisplays the amount of items entering or leaving storage%s +[[%sDisplays the amount of items entering or leaving storage.%s Right-clicking on the activity monitor will reset the totals. -%sMilo must be restarted to activate diplay. -]] +%sMilo must be restarted to activate diplay.]] local activityWizardPage = UI.Window { title = 'Activity Monitor', @@ -24,6 +23,7 @@ local activityWizardPage = UI.Window { backgroundColor = colors.cyan, [1] = UI.TextArea { x = 2, ex = -2, y = 2, ey = -2, + marginRight = 0, value = string.format(template, Ansi.yellow, Ansi.reset, Ansi.orange), }, } diff --git a/milo/plugins/exportView.lua b/milo/plugins/exportView.lua index 8bfcbf6..80821a0 100644 --- a/milo/plugins/exportView.lua +++ b/milo/plugins/exportView.lua @@ -2,6 +2,7 @@ local itemDB = require('itemDB') local UI = require('ui') local Util = require('util') +local colors = _G.colors local device = _G.device local exportView = UI.Window { @@ -20,17 +21,18 @@ local exportView = UI.Window { }, }, text = UI.Text { - x = 2, y = -2, + x = 3, y = -2, value = 'Slot', + textColor = colors.black, }, slots = UI.Chooser { - x = 7, y = -2, + x = 8, y = -2, width = 7, nochoice = 'All', help = 'Export to this slot', }, add = UI.Button { - x = 15, y = -2, + x = 16, y = -2, text = '+', event = 'add_entry', help = 'Add', }, remove = UI.Button { diff --git a/milo/plugins/importView.lua b/milo/plugins/importView.lua index 96e3db6..b27e8e4 100644 --- a/milo/plugins/importView.lua +++ b/milo/plugins/importView.lua @@ -2,6 +2,7 @@ local itemDB = require('itemDB') local UI = require('ui') local Util = require('util') +local colors = _G.colors local device = _G.device local importView = UI.Window { @@ -20,17 +21,18 @@ local importView = UI.Window { }, }, text = UI.Text { - x = 2, y = -2, + x = 3, y = -2, value = 'Slot', + textColor = colors.black, }, slots = UI.Chooser { - x = 7, y = -2, + x = 8, y = -2, width = 7, nochoice = 'All', help = 'Import from this slot', }, add = UI.Button { - x = 15, y = -2, + x = 16, y = -2, text = '+', event = 'add_entry', help = 'Add', }, remove = UI.Button { diff --git a/milo/plugins/jobMonitor.lua b/milo/plugins/jobMonitor.lua index 33d811a..3f5b217 100644 --- a/milo/plugins/jobMonitor.lua +++ b/milo/plugins/jobMonitor.lua @@ -14,8 +14,7 @@ local monitor = context.storage:getSingleNode('jobs') local template = [[%sDisplays the crafting progress%s -%sMilo must be restarted to activate diplay. -]] +%sMilo must be restarted to activate diplay.]] local jobsWizardPage = UI.Window { title = 'Crafting Monitor', @@ -23,6 +22,7 @@ local jobsWizardPage = UI.Window { backgroundColor = colors.cyan, [1] = UI.TextArea { x = 2, ex = -2, y = 2, ey = -2, + marginRight = 0, value = string.format(template, Ansi.yellow, Ansi.reset, Ansi.orange), }, } diff --git a/milo/plugins/manipulatorView.lua b/milo/plugins/manipulatorView.lua index 5c2feb4..84d9107 100644 --- a/milo/plugins/manipulatorView.lua +++ b/milo/plugins/manipulatorView.lua @@ -13,7 +13,7 @@ local wizardPage = UI.Window { index = 2, backgroundColor = colors.cyan, form = UI.Form { - x = 1, y = 3, ex = -1, ey = -2, + x = 2, ex = -2, y = 3, ey = -2, manualControls = true, [1] = UI.Checkbox { formLabel = 'Import', formKey = 'importEnder', @@ -26,7 +26,7 @@ local wizardPage = UI.Window { }, }, userInfo = UI.TextArea { - x = 2, ex = -2, y = 2, height = 1, + x = 3, ex = -2, y = 2, height = 2, }, } diff --git a/milo/plugins/remote.lua b/milo/plugins/remote.lua index 0f522f8..a9e52ff 100644 --- a/milo/plugins/remote.lua +++ b/milo/plugins/remote.lua @@ -48,7 +48,11 @@ local function client(socket) break end - if data.request == 'list' then + if data.request == 'scan' then -- full scan of all inventories + local items = Milo:mergeResources(Milo:listItems(true)) + socket:write(items) + + elseif data.request == 'list' then local items = Milo:mergeResources(Milo:listItems()) socket:write(items) diff --git a/milo/plugins/storageView.lua b/milo/plugins/storageView.lua index 56bc72c..acacc0f 100644 --- a/milo/plugins/storageView.lua +++ b/milo/plugins/storageView.lua @@ -9,7 +9,7 @@ local storageView = UI.Window { index = 2, backgroundColor = colors.cyan, form = UI.Form { - x = 1, y = 1, ex = -1, ey = -2, + x = 2, ex = -2, y = 1, ey = -2, manualControls = true, [1] = UI.TextEntry { formLabel = 'Priority', formKey = 'priority', @@ -28,6 +28,7 @@ local storageView = UI.Window { [3] = UI.TextArea { x = 12, ex = -2, y = 4, textColor = colors.yellow, + marginRight = 0, value = 'Only specify if you are manually taking items out of this inventory. Value should be > 10', }, }, @@ -67,7 +68,7 @@ local lockView = UI.Window { index = 3, backgroundColor = colors.cyan, form = UI.Form { - x = 1, y = 1, ex = -1, ey = 3, + x = 2, ex = -2, y = 1, ey = 3, manualControls = true, [1] = UI.Checkbox { formLabel = 'Locked', formKey = 'lockWith', @@ -79,11 +80,12 @@ local lockView = UI.Window { }, }, grid = UI.ScrollingGrid { - x = 2, ex = -2, y = 4, ey = -2, + x = 2, ex = -2, y = 5, ey = -2, columns = { { heading = 'Name', key = 'displayName' }, }, sortColumn = 'displayName', + disableHeader = true, }, } diff --git a/milo/plugins/trashcanView.lua b/milo/plugins/trashcanView.lua index a6e1836..e305251 100644 --- a/milo/plugins/trashcanView.lua +++ b/milo/plugins/trashcanView.lua @@ -17,7 +17,7 @@ local wizardPage = UI.Window { value = [[ Items can be automatically dropped from this storage.]], }, form = UI.Form { - x = 1, y = 4, ex = -1, ey = -2, + x = 2, ex = -2, y = 4, ey = -2, manualControls = true, [1] = UI.Checkbox { formLabel = 'Drop', formKey = 'drop', @@ -25,7 +25,7 @@ local wizardPage = UI.Window { }, [2] = UI.Chooser { width = 9, - formLabel = 'Drop direction', formKey = 'dropDirection', + formLabel = 'Direction', formKey = 'dropDirection', nochoice = 'Down', choices = { { name = 'Down', value = 'down' }, From 364ae04ceb719c62da04f29a4b9268c0920746f9 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Wed, 14 Nov 2018 13:22:31 -0500 Subject: [PATCH 089/165] milo performance --- core/apis/chestAdapter18.lua | 4 +- milo/apis/milo.lua | 75 ++++++++++----------------- milo/apis/storage.lua | 35 +++++++++---- milo/plugins/exportTask.lua | 95 ++++++++++++++++++---------------- milo/plugins/importTask.lua | 10 ++-- milo/plugins/limitTask.lua | 23 ++++---- milo/plugins/replenishTask.lua | 25 +++------ 7 files changed, 134 insertions(+), 133 deletions(-) diff --git a/core/apis/chestAdapter18.lua b/core/apis/chestAdapter18.lua index f79aadb..71610b8 100644 --- a/core/apis/chestAdapter18.lua +++ b/core/apis/chestAdapter18.lua @@ -137,8 +137,8 @@ function ChestAdapter:provide(item, qty, slot, direction) local stacks = self.list() for key,stack in Util.rpairs(stacks) do if stack.name == item.name and - (not item.damage or stack.damage == item.damage) and - (not item.nbtHash or stack.nbtHash == item.nbtHash) then + stack.damage == item.damage and + stack.nbtHash == item.nbtHash then local amount = math.min(qty, stack.count) if amount > 0 then amount = self.pushItems(direction or self.direction, key, amount, slot) diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index 7a3fe2a..039eed0 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -19,18 +19,6 @@ function Milo:getContext() return self.context end -function Milo:requestCrafting(item) - local key = Milo:uniqueKey(item) - - if not self.context.craftingQueue[key] then - item.crafted = 0 - item.pending = { } - item.key = key - self.context.craftingQueue[key] = item - os.queueEvent('milo_cycle') - end -end - function Milo:pauseCrafting() self.craftingPaused = true Milo:showError('Crafting Paused') @@ -105,52 +93,33 @@ function Milo:getItem(items, inItem, ignoreDamage, ignoreNbtHash) end end -function Milo:getItemWithQty(res, ignoreDamage, ignoreNbtHash) - local items = self:listItems() - local item = self:getItem(items, res, ignoreDamage, ignoreNbtHash) - local count = 0 - - if item and (ignoreDamage or ignoreNbtHash) then - for _,v in pairs(items) do - if item.name == v.name and - (ignoreDamage or item.damage == v.damage) and - (ignoreNbtHash or item.nbtHash == v.nbtHash) then - count = count + v.count - end - end - elseif item then - count = item.count - end - - return item, count -end - -function Milo:getMatches(items, item, ignoreDamage, ignoreNbtHash) +-- returns a list of items that matches along with a total count +function Milo:getMatches(item, flags) local t = { } + local count = 0 + local items = self:listItems() - if not ignoreDamage and not ignoreNbtHash then + if not flags.ignoreDamage and not flags.ignoreNbtHash then local key = item.key or Milo:uniqueKey(item) - local e = items[key] - if e then - t[key] = Util.shallowCopy(e) + local v = items[key] + if v then + t[key] = Util.shallowCopy(v) + count = v.count end else - for k,v in pairs(items) do + for key,v in pairs(items) do if item.name == v.name and - (ignoreDamage or item.damage == v.damage) and - (ignoreNbtHash or item.nbtHash == v.nbtHash) then - local e = t[k] - if not e then - t[k] = Util.shallowCopy(v) - else - e.count = e.count + item.count - end + (flags.ignoreDamage or item.damage == v.damage) and + (flags.ignoreNbtHash or item.nbtHash == v.nbtHash) then + + t[key] = Util.shallowCopy(v) + count = count + v.count end end end - return t + return t, count end function Milo:clearGrid() @@ -172,6 +141,18 @@ function Milo:getTurtleInventory() return list end +function Milo:requestCrafting(item) + local key = Milo:uniqueKey(item) + + if not self.context.craftingQueue[key] then + item.crafted = 0 + item.pending = { } + item.key = key + self.context.craftingQueue[key] = item + os.queueEvent('milo_cycle') + end +end + -- queue up an action that reliees on the crafting grid function Milo:queueRequest(request, callback) if Util.empty(self.context.queue) then diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 08f7576..718e15c 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -256,9 +256,9 @@ function Storage:export(target, slot, count, item) local amount = adapter:provide(item, count, slot, target) if amount > 0 then - _G._debug('EXT: %s(%d): %s -> %s%s', - item.displayName or item.name, amount, sn(adapter.name), sn(target), - slot and string.format('[%d]', slot) or '') + _G._debug('EXT: %s(%d): %s -> %s%s', + item.displayName or item.name, amount, sn(adapter.name), sn(target), + slot and string.format('[%d]', slot) or '[*]') self:updateCache(adapter, key, -amount) self:updateCache(self, key, -amount) @@ -277,8 +277,17 @@ function Storage:export(target, slot, count, item) end end - _G._debug('STORAGE: MISS: %s - %d', key, count) + _G._debug('MISS: %s(%d): %s%s %s', + item.displayName or item.name, count, sn(target), + slot and string.format('[%d]', slot) or '[*]', key) +-- TODO: If there are misses when a slot is specified than something is wrong... +-- The caller should confirm the quantity beforehand +-- If no slot and full amount is not exported, then no need to check rest of adapters +-- ... so should not reach here + + +--[[ -- not found - scan all others for _, adapter in self:onlineAdapters() do if not adapter.cache or not adapter.cache[key] then @@ -289,11 +298,15 @@ function Storage:export(target, slot, count, item) end end end +--]] return total end function Storage:import(source, slot, count, item) + if not source then error('Storage:import: source is required') end + if not slot then error('Storage:import: slot is required') end + local total = 0 local key = item.key or table.concat({ item.name, item.damage, item.nbtHash }, ':') @@ -305,9 +318,9 @@ function Storage:import(source, slot, count, item) local amount = adapter:insert(slot, count, nil, source) if amount > 0 then -_G._debug('INS: %s(%d): %s[%d] -> %s', - item.displayName or item.name, amount, - sn(source), slot, sn(adapter.name)) + _G._debug('INS: %s(%d): %s[%d] -> %s', + item.displayName or item.name, amount, + sn(source), slot, sn(adapter.name)) self:updateCache(adapter, key, amount) self:updateCache(self, key, amount) @@ -347,11 +360,15 @@ _G._debug('INS: %s(%d): %s[%d] -> %s', end if not itemDB:get(item) then - if not slot then + if item.displayName then + -- this item already has metadata + itemDB:add(item) + elseif not slot then _G._debug("IMPORT: NO SLOT") elseif not device[source] or not device[source].getItemMeta then _G._debug("IMPORT: DEVICE? : " .. source) else + -- get the metadata from the device and add to db itemDB:add(device[source].getItemMeta(slot)) end end @@ -374,7 +391,7 @@ function Storage:trash(source, slot, count) local trashcan = Util.find(self.nodes, 'mtype', 'trashcan') if trashcan and trashcan.adapter and trashcan.adapter.online then -_G._debug('TRA: %s[%d] (%d)', sn(source), slot, count or 64) + _G._debug('TRA: %s[%d] (%d)', sn(source), slot, count or 64) return trashcan.adapter.pullItems(source, slot, count) end diff --git a/milo/plugins/exportTask.lua b/milo/plugins/exportTask.lua index 3fa5958..ef00d3f 100644 --- a/milo/plugins/exportTask.lua +++ b/milo/plugins/exportTask.lua @@ -11,64 +11,71 @@ local function filter(a) end function ExportTask:cycle(context) - for machine in context.storage:filterActive('machine', filter) do - for _, entry in pairs(machine.exports) do + for node in context.storage:filterActive('machine', filter) do + for _, entry in pairs(node.exports) do - local function exportSlot(list, slotNo, item, count) - local slot = list[slotNo] or { count = 0 } + if not entry.filter then + -- exports must have a filter + -- TODO: validate in exportView + break + end - if slot.count == 0 or - (slot.name == item.name and - slot.damage == item.damage and - slot.nbtHash == item.nbtHash) then + local function exportSingleSlot() + local slot = node.adapter.getItemMeta(entry.slot) - local maxCount = itemDB:getMaxCount(item) - count = math.min(maxCount - slot.count, count) + if slot and slot.count == slot.maxCount then + return + end - if count > 0 then --- _debug('attempting to export %s %d into slot %d', item.name, count, slotNo) - count = context.storage:export(machine.name, slotNo, count, item) + if slot then + -- something is in the slot, find what we can export + for key in pairs(entry.filter) do + local filterItem = Milo:splitKey(key) + if (slot.name == filterItem.name and + entry.ignoreDamage or slot.damage == filterItem.damage and + entry.ignoreNbtHash or slot.nbtHash == filterItem.nbtHash) then - if count > 0 then - item.count = item.count - count - list[slotNo] = { - name = item.name, - damage = item.damage, - nbtHash = item.nbtHash, - count = count + slot.count, - } - return true + local items = Milo:getMatches(filterItem, entry) + local _, item = next(items) + if item then + local count = math.min(item.count, slot.maxCount - slot.count) + context.storage:export(node.name, entry.slot, count, item) + end + break end end + return + end + + -- slot is empty - export first matching item we have in storage + for key in pairs(entry.filter) do + local items = Milo:getMatches(Milo:splitKey(key), entry) + local _, item = next(items) + if item then + local count = math.min(item.count, itemDB:getMaxCount(item)) + context.storage:export(node.name, entry.slot, count, item) + break + end end end - local list - local function getLazyList() - if not list then - list = machine.adapter.list() - end - return list - end - - for key in pairs(entry.filter or { }) do - local items = Milo:getMatches(Milo:listItems(), itemDB:splitKey(key), entry.ignoreDamage, entry.ignoreNbtHash) - for _,item in pairs(items) do - if item and item.count > 0 then - if type(entry.slot) == 'number' then - if exportSlot(getLazyList(), entry.slot, item, item.count) then - break - end - else --- _debug('attempting to export %s %d', item.name, item.count) --- TODO: always going to try and export even if the chest is full - if context.storage:export(machine.name, nil, item.count, item) == 0 then - break - end + local function exportItems() + for key in pairs(entry.filter) do + local items = Milo:getMatches(itemDB:splitKey(key), entry) + for _,item in pairs(items) do + if context.storage:export(node.name, nil, item.count, item) == 0 then + -- TODO: really shouldn't break here as there may be room in other slots + -- leaving for now for performance reasons + break end end end end + if type(entry.slot) == 'number' then + exportSingleSlot() + else + exportItems() + end end end end diff --git a/milo/plugins/importTask.lua b/milo/plugins/importTask.lua index 059ba89..12517ca 100644 --- a/milo/plugins/importTask.lua +++ b/milo/plugins/importTask.lua @@ -10,8 +10,8 @@ local function filter(a) end function ImportTask:cycle(context) - for inventory in context.storage:filterActive('machine', filter) do - for _, entry in pairs(inventory.imports) do + for node in context.storage:filterActive('machine', filter) do + for _, entry in pairs(node.imports) do local function itemMatchesFilter(item) if not entry.ignoreDamage and not entry.ignoreNbtHash then @@ -41,16 +41,16 @@ function ImportTask:cycle(context) end local function importSlot(slotNo) - local item = inventory.adapter.getItemMeta(slotNo) + local item = node.adapter.getItemMeta(slotNo) if item and matchesFilter(item) then - context.storage:import(inventory.name, slotNo, item.count, item) + context.storage:import(node.name, slotNo, item.count, item) end end if type(entry.slot) == 'number' then importSlot(entry.slot) else - for i = 1, inventory.adapter.size() do + for i in pairs(node.adapter.list()) do importSlot(i) end end diff --git a/milo/plugins/limitTask.lua b/milo/plugins/limitTask.lua index 30a5270..ec072d4 100644 --- a/milo/plugins/limitTask.lua +++ b/milo/plugins/limitTask.lua @@ -9,16 +9,21 @@ function LimitTask:cycle(context) local trashcan = context.storage:filterActive('trashcan')() if trashcan then - for k,res in pairs(context.resources) do + for key,res in pairs(context.resources) do if res.limit then --- TODO: change to export method of finding items (maybe) - local item, count = Milo:getItemWithQty(Milo:splitKey(k), res.ignoreDamage, res.ignoreNbtHash) - if item and count > res.limit then - context.storage:export( - trashcan.name, - nil, - count - res.limit, - item) + local items, count = Milo:getMatches(Milo:splitKey(key), res) + if count > res.limit then + local amount = count - res.limit + for _, item in pairs(items) do + amount = amount - context.storage:export( + trashcan.name, + nil, + math.min(amount, item.count), + item) + if amount <= 0 then + break + end + end end end end diff --git a/milo/plugins/replenishTask.lua b/milo/plugins/replenishTask.lua index 3a8c36c..ec8b5a2 100644 --- a/milo/plugins/replenishTask.lua +++ b/milo/plugins/replenishTask.lua @@ -1,4 +1,3 @@ -local itemDB = require('itemDB') local Milo = require('milo') local ReplenishTask = { @@ -9,30 +8,22 @@ local ReplenishTask = { function ReplenishTask:cycle(context) for k,res in pairs(context.resources) do if res.low then - local key = Milo:splitKey(k) - local item, count = Milo:getItemWithQty(key, res.ignoreDamage, res.ignoreNbtHash) - if not item then - item = { - damage = key.damage, - nbtHash = key.nbtHash, - name = key.name, - displayName = itemDB:getName(key), - count = 0 - } - end + local item = Milo:splitKey(k) + item.key = k + + local _, count = Milo:getMatches(item, res) if count < res.low then - local nbtHash = item.nbtHash - if res.ignoreNbtHash then - nbtHash = nil + local nbtHash + if not res.ignoreNbtHash then + nbtHash = item.nbtHash end Milo:requestCrafting({ + name = item.name, damage = res.ignoreDamage and 0 or item.damage, nbtHash = nbtHash, requested = res.low - count, count = count, - name = item.name, - displayName = item.displayName, replenish = true, }) else From 1328fc233a227be4c5d0c568e1f74a6e89a81489 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Wed, 14 Nov 2018 13:56:53 -0500 Subject: [PATCH 090/165] milo performance --- milo/apis/storage.lua | 14 +++++++++----- milo/plugins/jobMonitor.lua | 3 +++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 718e15c..731ec90 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -240,7 +240,11 @@ _G._debug('STORAGE: item missing details') end end -local function sn(name) +function Storage:_sn(name) + local node = self.nodes[name] + if node and node.displayName then + return node.displayName + end local t = { name:match(':(.+)_(%d+)$') } if #t ~= 2 then return name @@ -257,7 +261,7 @@ function Storage:export(target, slot, count, item) if amount > 0 then _G._debug('EXT: %s(%d): %s -> %s%s', - item.displayName or item.name, amount, sn(adapter.name), sn(target), + item.displayName or item.name, amount, self:_sn(adapter.name), self:_sn(target), slot and string.format('[%d]', slot) or '[*]') self:updateCache(adapter, key, -amount) @@ -278,7 +282,7 @@ function Storage:export(target, slot, count, item) end _G._debug('MISS: %s(%d): %s%s %s', - item.displayName or item.name, count, sn(target), + item.displayName or item.name, count, self:_sn(target), slot and string.format('[%d]', slot) or '[*]', key) -- TODO: If there are misses when a slot is specified than something is wrong... @@ -320,7 +324,7 @@ function Storage:import(source, slot, count, item) _G._debug('INS: %s(%d): %s[%d] -> %s', item.displayName or item.name, amount, - sn(source), slot, sn(adapter.name)) + self:_sn(source), slot, self:_sn(adapter.name)) self:updateCache(adapter, key, amount) self:updateCache(self, key, amount) @@ -391,7 +395,7 @@ function Storage:trash(source, slot, count) local trashcan = Util.find(self.nodes, 'mtype', 'trashcan') if trashcan and trashcan.adapter and trashcan.adapter.online then - _G._debug('TRA: %s[%d] (%d)', sn(source), slot, count or 64) + _G._debug('TRA: %s[%d] (%d)', self:_sn(source), slot, count or 64) return trashcan.adapter.pullItems(source, slot, count) end diff --git a/milo/plugins/jobMonitor.lua b/milo/plugins/jobMonitor.lua index 3f5b217..6948bea 100644 --- a/milo/plugins/jobMonitor.lua +++ b/milo/plugins/jobMonitor.lua @@ -102,6 +102,9 @@ end function jobMonitor.grid:getDisplayValues(row) row = Util.shallowCopy(row) + if not row.displayName then + row.displayName = itemDB:getName(row) + end if row.requested then row.remaining = math.max(0, row.requested - row.crafted) else From a460bc31f3d608bf7235b264e0a21861f54f3856 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Wed, 14 Nov 2018 14:22:43 -0500 Subject: [PATCH 091/165] itemdb potion hack --- core/apis/itemDB.lua | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/core/apis/itemDB.lua b/core/apis/itemDB.lua index 21d0442..8d2174c 100644 --- a/core/apis/itemDB.lua +++ b/core/apis/itemDB.lua @@ -115,25 +115,31 @@ function itemDB:add(baseItem) nItem.maxCount = baseItem.maxCount nItem.maxDamage = baseItem.maxDamage - for k,item in pairs(self.data) do - if nItem.name == item.name and - nItem.displayName == item.displayName then + -- potions can have the same damage, diff nbts, but same names + -- ie. potion of healing, potion of healing II + -- both show as "Potion of Healing" - if nItem.nbtHash ~= item.nbtHash and nItem.damage ~= item.damage then - nItem.damage = '*' - nItem.nbtHash = nil - nItem.ignoreNBT = true - self.data[k] = nil - break - elseif nItem.damage ~= item.damage then - nItem.damage = '*' - self.data[k] = nil - break - elseif nItem.nbtHash ~= item.nbtHash then - nItem.nbtHash = nil - nItem.ignoreNBT = true - self.data[k] = nil - break + if nItem.name ~= 'minecraft:potion' --[[ HACK ]] then + for k,item in pairs(self.data) do + if nItem.name == item.name and + nItem.displayName == item.displayName then + + if nItem.nbtHash ~= item.nbtHash and nItem.damage ~= item.damage then + nItem.damage = '*' + nItem.nbtHash = nil + nItem.ignoreNBT = true + self.data[k] = nil + break + elseif nItem.damage ~= item.damage then + nItem.damage = '*' + self.data[k] = nil + break + elseif nItem.nbtHash ~= item.nbtHash then + nItem.nbtHash = nil + nItem.ignoreNBT = true + self.data[k] = nil + break + end end end end From 2abbece8c3613550deb6ec376c109da716c2954e Mon Sep 17 00:00:00 2001 From: kepler155c Date: Wed, 14 Nov 2018 19:12:29 -0500 Subject: [PATCH 092/165] neural flying --- milo/apis/storage.lua | 24 ++++---- neural/neuralFly.lua | 133 ++++++++++++++++++++++++------------------ 2 files changed, 88 insertions(+), 69 deletions(-) diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 731ec90..5888dc5 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -351,18 +351,6 @@ function Storage:import(source, slot, count, item) end end - -- is this item in some chest - if self.cache[key] then - for node, adapter in self:onlineAdapters() do - if count <= 0 then - return total - end - if adapter.cache and adapter.cache[key] and not node.lock then - insert(adapter) - end - end - end - if not itemDB:get(item) then if item.displayName then -- this item already has metadata @@ -377,6 +365,18 @@ function Storage:import(source, slot, count, item) end end + -- is this item in some chest + if self.cache[key] then + for node, adapter in self:onlineAdapters() do + if count <= 0 then + return total + end + if adapter.cache and adapter.cache[key] and not node.lock then + insert(adapter) + end + end + end + -- high to low priority for remote in self:onlineAdapters() do if count <= 0 then diff --git a/neural/neuralFly.lua b/neural/neuralFly.lua index 35ae482..6978178 100644 --- a/neural/neuralFly.lua +++ b/neural/neuralFly.lua @@ -1,78 +1,97 @@ _G.requireInjector(_ENV) -local ni = require('neural.interface') +local Config = require('config') local GPS = require('gps') +local ni = _G.device.neuralInterface -local strength = .315 -local delay = .1 +local os = _G.os +local parallel = _G.parallel -while ni.getMetaOwner().health < 26 do - print('health: ' .. ni.getMetaOwner().health) - os.sleep(1) +local id = ni.getID() +local config = Config.load('flight', { }) + +local args = { ... } +if args[1] == 'wp' then + local pt = GPS.locate() + config[args[2]] = pt + Config.update('flight', config) + return end -ni.launch(0, 270, 1.5) -os.sleep(.25) +local wp = config[args[1]] +if not wp then + error('invalid wp') +end -local pt +local pt = GPS.locate() -local function fly() - for i = 1, 100 do - os.sleep(1) - if pt then - print(pt.y) - print(strength) +local function descend() + print('descending to ' .. wp.y) + repeat + local meta = ni.getMetaByID(id) + if meta.motionY < 0 then + ni.launch(0, -90, math.min(4, meta.motionY / -0.5)) end - end + print(math.abs(wp.y - pt.y)) + until math.abs(wp.y - pt.y) < 1 end local function gps() - local lastY = 12 while true do - pt = GPS.locate() - if pt then - local d = math.abs(lastY - pt.y) - - -- force required to get to lvl 12 - - local motionY = ni.getMetaOwner().motionY --- print('y: ' .. pt.y) - if pt.y < 12 then - if pt.y > lastY then - --strength = strength + .001 - else - strength = strength + .02 * d - end - elseif pt.y > 12 then - if pt.y > lastY then - strength = strength - .02 * d - else - --strength = strength - .001 - end - end - lastY = pt.y - - -- force required to offset motion - local om = (motionY - 0.138) / 0.8 - - ni.launch(0, 270, strength-motionY) --- print('strength: ' .. strength) - os.sleep(delay) + local lpt = GPS.locate() + if lpt then + pt = lpt end + os.sleep(.1) end end -parallel.waitForAny(fly, gps) +local function yap(x, y, z) + local pitch = -math.atan2(y, math.sqrt(x * x + z * z)) + local yaw = math.atan2(-(x - .5), z - .5) -repeat - ni.launch(0, 270, .25) - os.sleep(.1) -until not ni.getMetaOwner().isAirborne - -print('descending') -for i = 1, 50 do - ni.launch(0, 270, .2) - os.sleep(.1) + return math.deg(yaw), math.deg(pitch) end -ni.look(180, 0) +local function distance(a, b) + return math.sqrt( + math.pow(a.x - b.x, 2) + + math.pow(a.z - b.z, 2)) +end + +local function hover() + repeat + local meta = ni.getMetaByID(id) + local pitch = 295 + local yaw = yap(wp.x - pt.x, wp.y, wp.z - pt.z) + + if pt.y < wp.y + 16 and meta.motionY < 0 then + ni.launch(yaw, pitch, math.min(4, math.min(4, -meta.motionY * math.abs(pt.y - (wp.y + 16)) / 2))) + end + + until distance(wp, pt) < 2 +end + +local function launch() + ni.launch(0, 270, 3) + + repeat + local meta = ni.getMetaByID(id) + until meta.motionY < 0 + + hover() + + descend() +end + +local s, m = pcall(parallel.waitForAny, launch, gps) + +if not s then + _G.printError(m) +end + +--s, m = pcall(parallel.waitForAny, descend, gps) + +if not s then + error(m) +end From 560b9e42c953ce72c7d42aa7a4517e2ebac4ba43 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Wed, 14 Nov 2018 21:24:14 -0500 Subject: [PATCH 093/165] new icons - thx LDD --- core/{debug.lua => debugMonitor.lua} | 0 core/etc/apps/opus-apps.db | 55 +++++------- core/logMonitor.lua | 126 --------------------------- core/namedb.lua | 50 ----------- milo/etc/apps/apps.db | 6 ++ 5 files changed, 27 insertions(+), 210 deletions(-) rename core/{debug.lua => debugMonitor.lua} (100%) delete mode 100644 core/logMonitor.lua delete mode 100644 core/namedb.lua diff --git a/core/debug.lua b/core/debugMonitor.lua similarity index 100% rename from core/debug.lua rename to core/debugMonitor.lua diff --git a/core/etc/apps/opus-apps.db b/core/etc/apps/opus-apps.db index 443d9a8..0cb8617 100644 --- a/core/etc/apps/opus-apps.db +++ b/core/etc/apps/opus-apps.db @@ -21,6 +21,17 @@ \0300\0317else\140", run = "Script.lua", }, + c5497bca58468ae64aed6c0fd921109217988db3 = { + title = "Events", + category = "System", + icon = "\0304\031f \030 \0311e\ +\030f\031f \0304 \030 \0311ee\031f \ +\030f\031f \0304 \030 \0311e\031f ", + iconExt = "\0300\031f\159\135\030f\0310\156\0301\031f\159\030f\0311\144\0300\031f\147\139\030f\0310\144\ +\0300\128\128\030f\149\0311\157\142\0300\031f\149\0310\128\128\ +\130\139\141\0311\130\131\0310\142\135\129", + run = "Events.lua", + }, [ "7ef35cac539f84722b0a988caee03b2df734c56a" ] = { title = "AppStore", category = "System", @@ -78,13 +89,16 @@ requires = "turtle", }, --]] - [ "a0365977708b7387ee9ce2c13e5820e6e11732cb" ] = { - title = "Pain", - category = "Apps", - icon = "\030 \031f\0307\031f\159\030 \159\030 \ -\030 \031f\0308\031f\135\0307\0318\144\140\030f\0317\159\143\031c\139\0302\135\030f\0312\157\ -\030 \031f\030f\0318\143\133\0312\136\0302\031f\159\159\143\131\030f\0312\132", - run = "http://pastebin.com/raw/wJQ7jav0", + df485c871329671f46570634d63216761441bcd6 = { + title = "Devices", + category = "System", + icon = "\0304 \030 \ +\030f \0304 \0307 \030 \031 \031f_\ +\030f \0304 \0307 \030 \031f/", + iconExt = "\031f\128\128\128\0308\159\143\0300\0317\151\0307\0310\140\148\ +\0314\151\131\0304\031f\148\030f\0318\138\148\0307\0310\138\131\129\ +\0304\031f\138\143\133\030f\0318\131\129\031f\128\128\128", + run = "Devices.lua", }, [ "114edfc04a1ab03541bdc80ce064f66a7cfcedbb" ] = { title = "Recorder", @@ -103,14 +117,6 @@ run = "http://pastebin.com/raw/VXAyXqBv", requires = "turtle", }, - [ "53a5d150062b1e03206b9e15854b81060e3c7552" ] = { - title = "Minesweeper", - category = "Games", - icon = "\030f\031f \03131\0308\031f \030f\031d2\ -\030f\031f \031d2\03131\0308\031f \030f\03131\ -\030f\03131\0308\031f \030f\03131\031e3", - run = "https://pastebin.com/raw/nsKrHTbN", - }, [ "a2accffe95b2c8be30e8a05e0c6ab7e8f5966f43" ] = { title = "Strafe", category = "Games", @@ -122,17 +128,6 @@ \0300\128\030f\159\129\138\0300\031f\143\149\030f\0310\134", run = "https://pastebin.com/raw/jyDH7mLH", }, - [ "48d6857f6b2869d031f463b13aa34df47e18c548" ] = { - title = "Breakout", - category = "Games", - icon = "\0301\031f \0309 \030c \030b \030e \030c \0306 \ -\030 \031f \ -\030 \031f \0300 \0310 ", - iconExt = "\030 \031f\030f\0319\144\030d\031f\159\030b\159\030f\0311\144\031b\144\030c\031f\159\030f\0311\144\ -\030 \031f\030f\0311\130\031b\129\0319\130\031e\130\0310\144\031d\129\0316\129\ -\030 \031f\030f\0310\136\140\140\030 ", - run = "https://gist.github.com/LDDestroier/c7528d95bc0103545c2a/raw", - }, [ "58ec8d6e36e346d9f42eb43935652e3e58e2c829" ] = { title = "Mwm", category = "Apps", @@ -144,12 +139,4 @@ \030 \031f\0304\031f\131\131\131\131\131\131\131\030e\0314\131", run = "mwm.lua usr/config/mwm", }, - [ "01c933b2a36ad8ed2d54089cb2903039046c1216" ] = { - title = "Enchat", - icon = "\030e\031f\151\030f\031e\156\0311\140\0314\140\0315\140\031d\140\031b\140\031a\132\ -\030f\0314\128\030e\031f\132\030f\031e\132\0318nchat\ -\030f\031e\138\141\0311\140\0314\140\0315\132\0317v\03183\031a\132", - category = "Apps", - run = "https://raw.githubusercontent.com/LDDestroier/enchat/master/enchat3.lua", - }, } diff --git a/core/logMonitor.lua b/core/logMonitor.lua deleted file mode 100644 index df98c46..0000000 --- a/core/logMonitor.lua +++ /dev/null @@ -1,126 +0,0 @@ -requireInjector(getfenv(1)) - -local Event = require('event') -local Message = require('message') -local UI = require('ui') -local Util = require('util') - -multishell.setTitle(multishell.getCurrent(), 'Log Monitor') - -if not device.wireless_modem then - error('Wireless modem is required') -end -device.wireless_modem.open(59998) - -local ids = { } -local messages = { } -local terminal = UI.term - -if device.openperipheral_bridge then - - UI.Glasses = require('glasses') - - terminal = UI.Glasses({ - x = 4, - y = 175, - height = 40, - width = 64, - textScale = .5, - backgroundOpacity = .65, - - }) -elseif device.monitor then - terminal = UI.Device({ - deviceType = 'monitor', - textScale = .5 - }) -end - ---[[-- ScrollingText --]]-- -UI.ScrollingText = class(UI.Window) -UI.ScrollingText.defaults = { - UIElement = 'ScrollingText', - backgroundColor = colors.black, - buffer = { }, -} -function UI.ScrollingText:appendLine(text) - if #self.buffer+1 >= self.height then - table.remove(self.buffer, 1) - end - table.insert(self.buffer, text) -end - -function UI.ScrollingText:clear() - self.buffer = { } - UI.Window.clear(self) -end - -function UI.ScrollingText:draw() - for k,text in ipairs(self.buffer) do - self:write(1, k, Util.widthify(text, self.width), self.backgroundColor) - end -end - -terminal:clear() - -function getClient(id) - if not ids[id] then - ids[id] = { - titleBar = UI.TitleBar({ title = 'ID: ' .. id, parent = terminal }), - scrollingText = UI.ScrollingText({ parent = terminal }) - } - local clientCount = Util.size(ids) - local clientHeight = math.floor((terminal.height - clientCount) / clientCount) - terminal:clear() - local y = 1 - for k,v in pairs(ids) do - v.titleBar.y = y - y = y + 1 - v.scrollingText.height = clientHeight - v.scrollingText.y = y - y = y + clientHeight - v.scrollingText:clear() - - v.titleBar:draw() - v.scrollingText:draw() - end - end - return ids[id] -end - -Event.on('logMessage', function() - local t = { } - while #messages > 0 do - local msg = messages[1] - table.remove(messages, 1) - local client = getClient(msg.id) - client.scrollingText:appendLine(string.format('%d %s', math.floor(os.clock()), msg.text)) - t[msg.id] = client - end - for _,client in pairs(t) do - client.scrollingText:draw() - end - terminal:sync() -end) - -Message.addHandler('log', function(h, id, msg) - table.insert(messages, { id = id, text = msg.contents }) - os.queueEvent('logMessage') -end) - -Event.on('monitor_touch', function() - terminal:reset() - ids = { } -end) - -Event.on('mouse_click', function() - terminal:reset() - ids = { } -end) - -Event.on('char', function() - Event.exitPullEvents() -end) - -Event.pullEvents(logWriter) -terminal:reset() diff --git a/core/namedb.lua b/core/namedb.lua deleted file mode 100644 index 7d5fac9..0000000 --- a/core/namedb.lua +++ /dev/null @@ -1,50 +0,0 @@ -_G.requireInjector() - -local itemDB = require('itemDB') -local json = require('json') -local Util = require('util') - -local args = { ... } -local mod = args[1] or error('Syntax: namedb MOD') - ---[[ - "double_plant": { - "name": ["Sunflower", - "Lilac", - "Double Tallgrass", - "Large Fern", - "Rose Bush", - "Peony"], - }, ---]] - -local list = { } - -for _,v in pairs(itemDB.data) do - local t = Util.split(v.name, '(.-):') - - if t[1] == mod then - local name = t[2] - local damage = v.damage or 0 - local entry = list[name] - if not entry then - entry = { } - list[name] = entry - end - if not entry.name and damage == 0 then - entry.name = v.displayName - else - if not entry.name then - entry.name = { } - elseif type(entry.name) == 'string' then - entry.name = { entry.name } - end - while #entry.name < damage do - entry.name[#entry.name + 1] = '' - end - entry.name[damage + 1] = v.displayName - end - end -end - -json.encodeToFile(string.format('usr/etc/names/%s.json', mod), list) diff --git a/milo/etc/apps/apps.db b/milo/etc/apps/apps.db index 76ad61f..a9a8fd5 100644 --- a/milo/etc/apps/apps.db +++ b/milo/etc/apps/apps.db @@ -3,11 +3,17 @@ title = "Remote", category = "Apps", run = "MiloRemote", + iconExt = "\0304\031f\135\129\0314\128\128\031f\130\030f\128\ +\031f\128\031c\159\149\0300\0317\143\0304\031c\149\030f\0314\133\ +\031f\128\030c\0310\142\030f\031c\149\030c\0310\139\030f\031c\149\031f\128", }, [ "eea426f9baef72a8fcefd091e0cec5ab94a76698" ] = { title = "Milo", category = "Apps", run = "Milo", requires = 'advancedTurtle', + iconExt = "\0304\031f\135\129\0314\128\128\031f\130\030f\128\ +\031f\128\031c\159\149\0300\0317\143\0304\031c\149\030f\0314\133\ +\031f\128\030c\0310\142\030f\031c\149\030c\0310\139\030f\031c\149\031f\128", }, } From 3e756abe4290ab6e4e811e5f5d9a22a71df26acc Mon Sep 17 00:00:00 2001 From: kepler155c Date: Thu, 15 Nov 2018 12:17:50 -0500 Subject: [PATCH 094/165] milo wip --- milo/Milo.lua | 29 +++++++++++-------- milo/apis/milo.lua | 31 +++++++++++--------- milo/apis/storage.lua | 18 ++++-------- milo/core/machines.lua | 37 ++++++++++++++++++------ milo/plugins/activityView.lua | 15 ++-------- milo/plugins/jobMonitor.lua | 53 +++++++++++++++++++---------------- milo/plugins/learn.lua | 4 +-- milo/plugins/listing.lua | 22 ++++++++------- milo/plugins/machineLearn.lua | 2 +- milo/plugins/turtleLearn.lua | 2 +- 10 files changed, 118 insertions(+), 95 deletions(-) diff --git a/milo/Milo.lua b/milo/Milo.lua index b9f0273..7b44489 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -78,6 +78,7 @@ local context = { config = config, resources = Util.readTable(Milo.RESOURCE_FILE) or { }, + state = { }, craftingQueue = { }, learnTypes = { }, tasks = { }, @@ -92,10 +93,6 @@ device[context.localName] = introspection.getInventory() _G._p = context --debug -Event.on('storage_offline', function() - Milo:showError('A storage chest has gone offline - Review configuration') -end) - Milo:init(context) context.storage:initStorage() @@ -125,9 +122,11 @@ Milo:clearGrid() UI:setPage(UI:getPage('listing')) +local processing + Event.on('milo_cycle', function() - if not context.turtleBusy then - context.turtleBusy = true + if not processing and not Milo:isCraftingPaused() then + processing = true Milo:resetCraftingStatus() for _, task in ipairs(context.tasks) do @@ -139,7 +138,7 @@ Event.on('milo_cycle', function() -- _G.printError(m) end end - context.turtleBusy = false + processing = false if not Util.empty(context.queue) then os.queueEvent('milo_queue') end @@ -147,8 +146,8 @@ Event.on('milo_cycle', function() end) Event.on('milo_queue', function() - if not context.turtleBusy then - context.turtleBusy = true + if not processing and context.storage:isOnline() then + processing = true for _, key in pairs(Util.keys(context.queue)) do local entry = context.queue[key] @@ -156,16 +155,24 @@ Event.on('milo_queue', function() context.queue[key] = nil end - context.turtleBusy = false + processing = false end end) Event.onInterval(5, function() - if not Milo:isCraftingPaused() and context.storage:isOnline() then + if not Milo:isCraftingPaused() then os.queueEvent('milo_cycle') end end) +Event.on({ 'storage_offline', 'storage_online' }, function() + if context.storage:isOnline() then + Milo:resumeCrafting({ key = 'storageOnline' }) + else + Milo:pauseCrafting({ key = 'storageOnline', msg = 'Storage offline' }) + end +end) + os.queueEvent( context.storage:isOnline() and 'storage_online' or 'storage_offline', context.storage:isOnline()) diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index 039eed0..7ea88fa 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -19,17 +19,29 @@ function Milo:getContext() return self.context end -function Milo:pauseCrafting() - self.craftingPaused = true - Milo:showError('Crafting Paused') +function Milo:pauseCrafting(reason) + local _, key = Util.find(self.context.state, 'key', reason.key) + if not key then + table.insert(self.context.state, reason) + os.queueEvent('milo_pause', reason) + end end -function Milo:resumeCrafting() - self.craftingPaused = false +function Milo:resumeCrafting(reason) + local _, key = Util.find(self.context.state, 'key', reason.key) + if key then + table.remove(self.context.state, key) + local n = self.context.state[#self.context.state] + if n then + os.queueEvent('milo_pause', n) + else + os.queueEvent('milo_resume') + end + end end function Milo:isCraftingPaused() - return self.craftingPaused + return self.context.state[#self.context.state] end function Milo:getState(key) @@ -72,13 +84,6 @@ function Milo:registerTask(task) table.insert(self.context.tasks, task) end -function Milo:showError(msg) - -- TODO: break dependency - if self.context.jobMonitor then - self.context.jobMonitor:showError(msg) - end -end - function Milo:getItem(items, inItem, ignoreDamage, ignoreNbtHash) if not ignoreDamage and not ignoreNbtHash then return items[inItem.key or self:uniqueKey(inItem)] diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 5888dc5..2fdc35a 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -114,7 +114,7 @@ function Storage:filterActive(mtype, filter) end) end -function Storage:onlineAdapters(reversed) +function Storage:onlineAdapters() local iter = { } for _, v in pairs(self.nodes) do if v.adapter and v.adapter.online and v.mtype == 'storage' then @@ -122,20 +122,14 @@ function Storage:onlineAdapters(reversed) end end - local function forwardSort(a, b) + table.sort(iter, function(a, b) if not a.priority then return false elseif not b.priority then return true end return a.priority > b.priority - end - - local function backwardSort(a, b) - return not forwardSort(a, b) - end - - table.sort(iter, reversed and backwardSort or forwardSort) + end) local i = 0 return function() @@ -378,12 +372,12 @@ function Storage:import(source, slot, count, item) end -- high to low priority - for remote in self:onlineAdapters() do + for node in self:onlineAdapters() do if count <= 0 then break end - if not remote.lock then - insert(remote.adapter) + if not node.lock then + insert(node.adapter) end end diff --git a/milo/core/machines.lua b/milo/core/machines.lua index 34f931c..60ccc12 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -59,6 +59,11 @@ local networkPage = UI.Page { help = 'Remove Node', }, statusBar = UI.StatusBar { + ex = -9, + backgroundColor = colors.lightGray, + }, + storageStatus = UI.Text { + x = -8, ex = -1, y = -1, backgroundColor = colors.lightGray, }, notification = UI.Notification { }, @@ -109,16 +114,27 @@ function networkPage:getList() end function networkPage:enable() - self:getList() - self.grid:update() - self:setFocus(self.filter) - UI.Page.enable(self) - self.handler = Event.on({ 'device_attach', 'device_detach'}, function() + local function updateStatus() + local isOnline = context.storage:isOnline() + self.storageStatus.value = isOnline and ' online' or 'offline' + self.storageStatus.textColor = isOnline and colors.lime or colors.red + self.storageStatus:draw() + end + + self.handler = Event.on({ 'device_attach', 'device_detach', 'storage_online', 'storage_offline' }, function() self:getList() - self.grid:update() + self:applyFilter() self.grid:draw() self.grid:sync() + updateStatus() + self:sync() end) + + self:getList() + self:applyFilter() + self:setFocus(self.filter) + UI.Page.enable(self) + updateStatus() end function networkPage:disable() @@ -158,13 +174,16 @@ function networkPage:eventHandler(event) context.config.nodes[node.name] = nil saveConfig() end - self.grid:update() + self:applyFilter() self.grid:draw() elseif event.type == 'text_change' then self:applyFilter() self.grid:draw() + elseif event.type == 'grid_focus_row' then + self.statusBar:setStatus(event.selected.name) + elseif event.type == 'focus_change' then self.statusBar:setStatus(event.focused.help) @@ -298,13 +317,13 @@ function nodeWizard.filter:show(entry, callback, whitelistOnly) UI.SlideOut.show(self) self:setFocus(self.form.scan) - Milo:pauseCrafting() + Milo:pauseCrafting({ key = 'gridInUse', msg = 'Crafting paused' }) sync.lock(turtle) end function nodeWizard.filter:hide() UI.SlideOut.hide(self) - Milo:resumeCrafting() + Milo:resumeCrafting({ key = 'gridInUse' }) sync.release(turtle) end diff --git a/milo/plugins/activityView.lua b/milo/plugins/activityView.lua index 222cd04..67fd7f0 100644 --- a/milo/plugins/activityView.lua +++ b/milo/plugins/activityView.lua @@ -145,20 +145,10 @@ function page:refresh() end function page:update() - if context.storage:isOnline() then - page:refresh() - page:sync() - else - page.grid:clear() - page.grid:centeredWrite(math.ceil(page.height / 2), 'Storage Offline') - page:sync() - end + page:refresh() + page:sync() end -Event.on({ 'storage_offline', 'storage_online' }, function() - page:update() -end) - Event.on('monitor_touch', function(_, side) if side == monitor.adapter.side then page:reset() @@ -166,6 +156,7 @@ Event.on('monitor_touch', function(_, side) end end) +page:enable() page:draw() page:sync() diff --git a/milo/plugins/jobMonitor.lua b/milo/plugins/jobMonitor.lua index 6948bea..f84b5e2 100644 --- a/milo/plugins/jobMonitor.lua +++ b/milo/plugins/jobMonitor.lua @@ -1,14 +1,15 @@ -local Ansi = require('ansi') -local Craft = require('turtle.craft') -local itemDB = require('itemDB') -local Milo = require('milo') -local UI = require('ui') -local Util = require('util') +local Ansi = require('ansi') +local Craft = require('turtle.craft') +local Event = require('event') +local itemDB = require('itemDB') +local Milo = require('milo') +local UI = require('ui') +local Util = require('util') -local colors = _G.colors -local context = Milo:getContext() -local device = _G.device -local monitor = context.storage:getSingleNode('jobs') +local colors = _G.colors +local context = Milo:getContext() +local device = _G.device +local monitor = context.storage:getSingleNode('jobs') --[[ Configuration Screen ]] local template = @@ -16,7 +17,7 @@ local template = %sMilo must be restarted to activate diplay.]] -local jobsWizardPage = UI.Window { +local wizardPage = UI.Window { title = 'Crafting Monitor', index = 2, backgroundColor = colors.cyan, @@ -27,7 +28,7 @@ local jobsWizardPage = UI.Window { }, } -function jobsWizardPage:isValidType(node) +function wizardPage:isValidType(node) local m = device[node.name] return m and m.type == 'monitor' and { name = 'Crafting Monitor', @@ -36,11 +37,11 @@ function jobsWizardPage:isValidType(node) } end -function jobsWizardPage:isValidFor(node) +function wizardPage:isValidFor(node) return node.mtype == 'jobs' end -UI:getPage('nodeWizard').wizard:add({ jobs = jobsWizardPage }) +UI:getPage('nodeWizard').wizard:add({ jobs = wizardPage }) --[[ Display ]] if not monitor then @@ -61,22 +62,16 @@ local jobMonitor = UI.Page { { heading = 'Qty', key = 'remaining', width = 4 }, { heading = 'Crafting', key = 'displayName', }, { heading = 'Status', key = 'status', }, - { heading = 'need', key = 'need', width = 4 }, - { heading = 'total', key = 'total', width = 4 }, - { heading = 'used', key = 'used', width = 4 }, - { heading = 'count', key = 'count', width = 4 }, +-- { heading = 'need', key = 'need', width = 4 }, +-- { heading = 'total', key = 'total', width = 4 }, +-- { heading = 'used', key = 'used', width = 4 }, +-- { heading = 'count', key = 'count', width = 4 }, { heading = 'crafted', key = 'crafted', width = 4 }, -- { heading = 'Progress', key = 'progress', width = 8 }, }, }, } -function jobMonitor:showError(msg) - self.grid:clear() - self.grid:centeredWrite(math.ceil(self.grid.height / 2), msg) - self:sync() -end - function jobMonitor:updateList(craftList) if not Milo:isCraftingPaused() then local t = { } @@ -125,6 +120,16 @@ function jobMonitor.grid:getRowTextColor(row, selected) UI.Grid:getRowTextColor(row, selected) end +Event.on({ 'milo_resume', 'milo_pause' }, function(_, reason) + if reason then + jobMonitor.grid:clear() + jobMonitor.grid:centeredWrite(math.ceil(jobMonitor.grid.height / 2), reason.msg) + else + jobMonitor.grid:draw() + end + jobMonitor:sync() +end) + jobMonitor:enable() jobMonitor:draw() jobMonitor:sync() diff --git a/milo/plugins/learn.lua b/milo/plugins/learn.lua index efb92c0..7172c17 100644 --- a/milo/plugins/learn.lua +++ b/milo/plugins/learn.lua @@ -36,7 +36,7 @@ function learnPage:enable() Milo:getState('learnType') or self.chooser.choices[1].value - Milo:pauseCrafting() + Milo:pauseCrafting({ key = 'gridInUse', msg = 'Crafting paused' }) sync.lock(turtle) self:focusFirst() @@ -50,7 +50,7 @@ end function learnPage:eventHandler(event) if event.type == 'cancel' then sync.release(turtle) - Milo:resumeCrafting() + Milo:resumeCrafting({ key = 'gridInUse' }) UI:setPreviousPage() elseif event.type == 'accept' then diff --git a/milo/plugins/listing.lua b/milo/plugins/listing.lua index aa5a557..b3f5be0 100644 --- a/milo/plugins/listing.lua +++ b/milo/plugins/listing.lua @@ -224,6 +224,7 @@ end function listingPage:enable() self:refresh() self:setFocus(self.statusBar.filter) + self.timer = Event.onInterval(3, function() for _,v in pairs(self.allItems) do local c = context.storage.cache[v.key] @@ -232,11 +233,22 @@ function listingPage:enable() self.grid:draw() self:sync() end) + + self.handler = Event.on({ 'storage_offline', 'storage_online' }, function(_, isOnline) + self.statusBar.storageStatus.value = + isOnline and '' or 'offline' + self.statusBar.storageStatus.textColor = + isOnline and colors.lime or colors.red + self.statusBar.storageStatus:draw() + self:sync() + end) + UI.Page.enable(self) end function listingPage:disable() Event.off(self.timer) + Event.off(self.handler) UI.Page.disable(self) end @@ -250,14 +262,4 @@ function listingPage:applyFilter() self.grid:setValues(t) end -Event.on({ 'storage_offline', 'storage_online' }, function(e, isOnline) - -- TODO: Fix button - listingPage.statusBar.storageStatus.value = - isOnline and '' or 'offline' - listingPage.statusBar.storageStatus.textColor = - isOnline and colors.lime or colors.red - listingPage.statusBar.storageStatus:draw() - listingPage:sync() -end) - UI:addPage('listing', listingPage) diff --git a/milo/plugins/machineLearn.lua b/milo/plugins/machineLearn.lua index 4d33afc..4f1afc6 100644 --- a/milo/plugins/machineLearn.lua +++ b/milo/plugins/machineLearn.lua @@ -135,7 +135,7 @@ function pages.confirmation:validate() end function machineLearnWizard:disable() - Milo:resumeCrafting() + Milo:resumeCrafting({ key = 'gridInUse' }) sync.release(turtle) UI.Page.disable(self) end diff --git a/milo/plugins/turtleLearn.lua b/milo/plugins/turtleLearn.lua index ac5d8ec..3d49455 100644 --- a/milo/plugins/turtleLearn.lua +++ b/milo/plugins/turtleLearn.lua @@ -124,7 +124,7 @@ local turtleLearnWizard = UI.Page { } function turtleLearnWizard:disable() - Milo:resumeCrafting() + Milo:resumeCrafting({ key = 'gridInUse' }) sync.release(turtle) UI.Page.disable(self) end From b73775ad93885b7b13726517e9b915ddaf6b0f29 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Fri, 16 Nov 2018 10:26:41 -0500 Subject: [PATCH 095/165] milo wip --- milo/MiloRemote.lua | 46 ++++++++++++++++++++++--------------- milo/plugins/jobMonitor.lua | 4 ++-- milo/plugins/listing.lua | 21 +++++++++++++++-- 3 files changed, 48 insertions(+), 23 deletions(-) diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index 7c3c68f..4892c3b 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -17,23 +17,33 @@ local SHIELD_SLOT = 2 local config = Config.load('miloRemote', { }) local page = UI.Page { - dummy = UI.Window { - x = 1, ex = -13, y = 1, height = 1, + menuBar = UI.MenuBar { + y = 1, height = 1, + buttons = { + { + text = 'Refresh', + x = -12, + event = 'refresh' + }, + { + text = '\206', + x = -3, + dropdown = { + { text = 'Setup', event = 'setup' }, + UI.MenuBar.spacer, + { + text = 'Rescan storage', + event = 'rescan', + help = 'Rescan all inventories' + }, + }, + }, + }, infoBar = UI.StatusBar { + x = 1, ex = -13, backgroundColor = colors.lightGray, }, }, - refresh = UI.Button { - y = 1, x = -12, - event = 'refresh', - text = 'Refresh', - }, - setupButton = UI.Button { - y = 1, x = -3, - event = 'setup', - text = '\206', - help = 'Configuration', - }, grid = UI.Grid { y = 2, ey = -2, columns = { @@ -129,10 +139,6 @@ local page = UI.Page { [[bound introspection module. The neural interface must ]] .. [[also have an introspection module.]], }, - [5] = UI.Button { - x = 1, y = -2, - text = 'Force scan', event = 'rescan', help = 'Force a scan of all inventories', - }, }, statusBar = UI.StatusBar { backgroundColor = colors.cyan, @@ -164,7 +170,7 @@ local function filterItems(t, filter, displayMode) end function page:setStatus(status) - self.dummy.infoBar:setStatus(status) + self.menuBar.infoBar:setStatus(status) self:sync() end @@ -273,12 +279,14 @@ function page:eventHandler(event) self.setup:hide() self:refresh('list') self.grid:draw() + self:setFocus(self.statusBar.filter) elseif event.type == 'form_cancel' then self.setup:hide() + self:setFocus(self.statusBar.filter) elseif event.type == 'focus_change' then - self.dummy.infoBar:setStatus(event.focused.help) + self.menuBar.infoBar:setStatus(event.focused.help) elseif event.type == 'eject' or event.type == 'grid_select' then local item = self.grid:getSelected() diff --git a/milo/plugins/jobMonitor.lua b/milo/plugins/jobMonitor.lua index f84b5e2..00d40b8 100644 --- a/milo/plugins/jobMonitor.lua +++ b/milo/plugins/jobMonitor.lua @@ -62,11 +62,11 @@ local jobMonitor = UI.Page { { heading = 'Qty', key = 'remaining', width = 4 }, { heading = 'Crafting', key = 'displayName', }, { heading = 'Status', key = 'status', }, --- { heading = 'need', key = 'need', width = 4 }, + { heading = 'need', key = 'need', width = 4 }, -- { heading = 'total', key = 'total', width = 4 }, -- { heading = 'used', key = 'used', width = 4 }, -- { heading = 'count', key = 'count', width = 4 }, - { heading = 'crafted', key = 'crafted', width = 4 }, + { heading = 'crafted', key = 'crafted', width = 5 }, -- { heading = 'Progress', key = 'progress', width = 8 }, }, }, diff --git a/milo/plugins/listing.lua b/milo/plugins/listing.lua index b3f5be0..5bd911d 100644 --- a/milo/plugins/listing.lua +++ b/milo/plugins/listing.lua @@ -37,7 +37,19 @@ local listingPage = UI.Page { { text = 'Craft', event = 'craft' }, { text = 'Edit', event = 'details' }, { text = 'Refresh', event = 'refresh', x = -12 }, - { text = '\206', event = 'network', x = -3 }, + { + text = '\206', + x = -3, + dropdown = { + { text = 'Setup', event = 'network' }, + UI.MenuBar.spacer, + { + text = 'Rescan storage', + event = 'rescan', + help = 'Rescan all inventories' + }, + }, + }, }, }, grid = UI.Grid { @@ -175,9 +187,14 @@ function listingPage:eventHandler(event) end elseif event.type == 'refresh' then + self:refresh() + self.grid:draw() + self:setFocus(self.statusBar.filter) + + elseif event.type == 'rescan' then self:refresh(true) self.grid:draw() - self.statusBar.filter:focus() + self:setFocus(self.statusBar.filter) elseif event.type == 'toggle_display' then local values = { From 72f23d4814f9096a57c41eaaf4760714886b5058 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sat, 17 Nov 2018 17:34:26 -0500 Subject: [PATCH 096/165] deposit toggle --- milo/MiloRemote.lua | 65 ++++++++++++++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index 4892c3b..8f6e5ae 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -16,10 +16,21 @@ local SHIELD_SLOT = 2 local config = Config.load('miloRemote', { }) +local depositMode = { + [ true ] = { text = '\25', textColor = colors.black, help = 'Deposit enabled' }, + [ false ] = { text = '\215', textColor = colors.red, help = 'Deposit disabled' }, +} + local page = UI.Page { menuBar = UI.MenuBar { y = 1, height = 1, buttons = { + { + name = 'depositToggle', + text = '\215', + x = -15, + event = 'toggle_deposit' + }, { text = 'Refresh', x = -12, @@ -40,7 +51,7 @@ local page = UI.Page { }, }, infoBar = UI.StatusBar { - x = 1, ex = -13, + x = 1, ex = -16, backgroundColor = colors.lightGray, }, }, @@ -274,6 +285,13 @@ function page:eventHandler(event) self.setup.form:setValues(config) self.setup:show() + elseif event.type == 'toggle_deposit' then + config.deposit = not config.deposit + Util.merge(self.menuBar.depositToggle, depositMode[config.deposit]) + self.menuBar:draw() + self:setStatus(depositMode[config.deposit].help) + Config.update('miloRemote', config) + elseif event.type == 'form_complete' then Config.update('miloRemote', config) self.setup:hide() @@ -362,6 +380,7 @@ end function page:enable() self:setFocus(self.statusBar.filter) + Util.merge(self.menuBar.depositToggle, depositMode[config.deposit]) UI.Page.enable(self) if not config.server then self.setup:show() @@ -390,28 +409,30 @@ end Event.addRoutine(function() while true do os.sleep(1.5) - local neural = device.neuralInterface - local inv = config.useShield and 'getEquipment' or 'getInventory' - if not neural or not neural[inv] then - _G._debug('missing Introspection module') - elseif config.server and (config.useShield or config.slot) then - local method = neural[inv] - local item = method and method().getItemMeta(config.useShield and SHIELD_SLOT or config.slot) - if item then - local slotNo = config.useShield and 'shield' or config.slot - local response = page:sendRequest({ - request = 'deposit', - slot = slotNo, - count = item.count, - key = table.concat({ item.name, item.damage, item.nbtHash }, ':') - }) - if response then - local ritem = page.items[response.key] - if ritem then - ritem.count = response.current + item.count + if config.deposit then + local neural = device.neuralInterface + local inv = config.useShield and 'getEquipment' or 'getInventory' + if not neural or not neural[inv] then + _G._debug('missing Introspection module') + elseif config.server and (config.useShield or config.slot) then + local method = neural[inv] + local item = method and method().getItemMeta(config.useShield and SHIELD_SLOT or config.slot) + if item then + local slotNo = config.useShield and 'shield' or config.slot + local response = page:sendRequest({ + request = 'deposit', + slot = slotNo, + count = item.count, + key = table.concat({ item.name, item.damage, item.nbtHash }, ':') + }) + if response then + local ritem = page.items[response.key] + if ritem then + ritem.count = response.current + item.count + end + page.grid:draw() + page:sync() end - page.grid:draw() - page:sync() end end end From 39edf76c6b61ba7323143fad0890f8959240339e Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sat, 17 Nov 2018 17:42:09 -0500 Subject: [PATCH 097/165] deposit toggle --- milo/MiloRemote.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index 8f6e5ae..64ea66b 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -14,7 +14,7 @@ local socket local SHIELD_SLOT = 2 -local config = Config.load('miloRemote', { }) +local config = Config.load('miloRemote', { displayMode = 0 }) local depositMode = { [ true ] = { text = '\25', textColor = colors.black, help = 'Deposit enabled' }, @@ -155,7 +155,6 @@ local page = UI.Page { backgroundColor = colors.cyan, }, }, - displayMode = 0, items = { }, } @@ -358,11 +357,12 @@ function page:eventHandler(event) [2] = 'C', } event.button.value = (event.button.value + 1) % 3 - self.displayMode = event.button.value + config.displayMode = event.button.value event.button.text = values[event.button.value] event.button:draw() self:applyFilter() self.grid:draw() + Config.update('miloRemote', config) elseif event.type == 'text_change' and event.element == self.statusBar.filter then self.filter = event.text @@ -402,7 +402,7 @@ function page:refresh(requestType) end function page:applyFilter() - local t = filterItems(self.items, self.filter, self.displayMode) + local t = filterItems(self.items, self.filter, config.displayMode) self.grid:setValues(t) end From 939ec9516d9f42bf96c90665531f13d4541f2617 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sat, 17 Nov 2018 18:03:03 -0500 Subject: [PATCH 098/165] milo: save state --- milo/MiloRemote.lua | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index 64ea66b..8c06513 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -21,6 +21,12 @@ local depositMode = { [ false ] = { text = '\215', textColor = colors.red, help = 'Deposit disabled' }, } +local displayMode = { + [0] = { text = 'A', help = 'Showing all items' }, + [1] = { text = 'I', help = 'Showing inventory items' }, + [2] = { text = 'C', help = 'Showing craftable items' }, +} + local page = UI.Page { menuBar = UI.MenuBar { y = 1, height = 1, @@ -87,14 +93,13 @@ local page = UI.Page { accelerators = { [ 'enter' ] = 'eject_specified', }, - help = 'Specify an amount to send', + help = 'Request amount', }, display = UI.Button { x = -3, event = 'toggle_display', - value = 0, - text = 'A', - help = 'Toggle display mode', + text = displayMode[config.displayMode].text, + help = displayMode[config.displayMode].help, }, }, accelerators = { @@ -351,16 +356,11 @@ function page:eventHandler(event) self.grid:draw() elseif event.type == 'toggle_display' then - local values = { - [0] = 'A', - [1] = 'I', - [2] = 'C', - } - event.button.value = (event.button.value + 1) % 3 - config.displayMode = event.button.value - event.button.text = values[event.button.value] + config.displayMode = (config.displayMode + 1) % 3 + Util.merge(event.button, displayMode[config.displayMode]) event.button:draw() self:applyFilter() + self:setStatus(event.button.help) self.grid:draw() Config.update('miloRemote', config) From 2605410c6d49287e1bace3e54d78c1875b13799a Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sat, 17 Nov 2018 21:27:45 -0500 Subject: [PATCH 099/165] milo: better monitor handling + assigning machines to items --- builder/apis/builder/schematic.lua | 2 +- core/t.lua | 10 +- milo/MiloRemote.lua | 8 +- milo/apis/storage.lua | 1 - milo/apis/turtle/craft.lua | 1 - milo/core/machines.lua | 11 +- milo/plugins/activityView.lua | 216 +++++++++++++++-------------- milo/plugins/item.lua | 27 +++- milo/plugins/jobMonitor.lua | 180 ++++++++++++------------ milo/plugins/listing.lua | 31 +++-- 10 files changed, 262 insertions(+), 225 deletions(-) diff --git a/builder/apis/builder/schematic.lua b/builder/apis/builder/schematic.lua index b530ee2..43228d2 100644 --- a/builder/apis/builder/schematic.lua +++ b/builder/apis/builder/schematic.lua @@ -23,7 +23,7 @@ local gzipMagic = 0x1f8b local Spinner = class() function Spinner:init(args) local defaults = { - timeout = .095, + timeout = .075, c = os.clock(), spinIndex = 0, spinSymbols = { '-', '/', '|', '\\' } diff --git a/core/t.lua b/core/t.lua index 49f2aa1..aef9b51 100644 --- a/core/t.lua +++ b/core/t.lua @@ -1,4 +1,6 @@ -function doCommand(command, moves) +local turtle = _G.turtle + +local function doCommand(command, moves) local function format(value) if type(value) == 'boolean' then @@ -49,6 +51,8 @@ function doCommand(command, moves) [ 'r' ] = turtle.turnRight, [ 'l' ] = turtle.turnLeft, [ 'ta' ] = turtle.turnAround, + [ 'el' ] = turtle.equipLeft, + [ 'er' ] = turtle.equipRight, [ 'DD' ] = turtle.digDown, [ 'DU' ] = turtle.digUp, [ 'D' ] = turtle.dig, @@ -64,7 +68,7 @@ function doCommand(command, moves) if cmds[command] then runCommand(cmds[command], moves) elseif repCmds[command] then - for i = 1, moves do + for _ = 1, moves do if not runCommand(repCmds[command]) then break end @@ -79,7 +83,7 @@ if #args > 0 then else print('Enter command (q to quit):') while true do - local cmd = read() + local cmd = _G.read() if cmd == 'q' then break end args = { } diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index 8c06513..358a6e2 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -21,7 +21,7 @@ local depositMode = { [ false ] = { text = '\215', textColor = colors.red, help = 'Deposit disabled' }, } -local displayMode = { +local displayModes = { [0] = { text = 'A', help = 'Showing all items' }, [1] = { text = 'I', help = 'Showing inventory items' }, [2] = { text = 'C', help = 'Showing craftable items' }, @@ -98,8 +98,8 @@ local page = UI.Page { display = UI.Button { x = -3, event = 'toggle_display', - text = displayMode[config.displayMode].text, - help = displayMode[config.displayMode].help, + text = displayModes[config.displayMode].text, + help = displayModes[config.displayMode].help, }, }, accelerators = { @@ -357,7 +357,7 @@ function page:eventHandler(event) elseif event.type == 'toggle_display' then config.displayMode = (config.displayMode + 1) % 3 - Util.merge(event.button, displayMode[config.displayMode]) + Util.merge(event.button, displayModes[config.displayMode]) event.button:draw() self:applyFilter() self:setStatus(event.button.help) diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 2fdc35a..32fc095 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -164,7 +164,6 @@ function Storage:listItems(throttle) return self.cache end --- TODO: is there any reason now to maintain 2 lists local cache = { } throttle = throttle or Util.throttle() diff --git a/milo/apis/turtle/craft.lua b/milo/apis/turtle/craft.lua index ccb419c..94dfdd3 100644 --- a/milo/apis/turtle/craft.lua +++ b/milo/apis/turtle/craft.lua @@ -126,7 +126,6 @@ local function turtleCraft(recipe, storage, request, count) for k,v in pairs(recipe.ingredients) do local item = splitKey(v) if storage:export(storage.localName, k, count, item) ~= count then - -- TODO: FIX: ingredients cannot be stacked request.status = 'unknown error' request.statusCode = Craft.STATUS_ERROR return diff --git a/milo/core/machines.lua b/milo/core/machines.lua index 60ccc12..c353382 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -438,14 +438,13 @@ function nodeWizard:enable(node) self.node.adapter = adapter node.adapter = adapter -_G._p2 = self.node +_G._p3 = self.node local choices = { { name = 'Ignore', value = 'ignore' }, } for _, page in pairs(self.wizard.pages) do if page.isValidType then - -- TODO: dedupe list local choice = page:isValidType(self.node) if choice and not Util.find(choices, 'value', choice.value) then table.insert(choices, choice) @@ -474,6 +473,10 @@ function nodeWizard:eventHandler(event) UI:setPreviousPage() elseif event.type == 'accept' then + + local adapter = self.node.adapter + self.node.adapter = nil + Util.prune(self.node, function(v) if type(v) == 'boolean' then return v @@ -487,13 +490,15 @@ function nodeWizard:eventHandler(event) Util.clear(context.config.nodes[self.node.name]) Util.merge(context.config.nodes[self.node.name], self.node) + context.config.nodes[self.node.name].adapter = adapter + saveConfig() UI:setPreviousPage() elseif event.type == 'choice_change' then local help - if event.choice and event.choice.help then -- TODO: new param sent by UI api + if event.choice and event.choice.help then help = event.choice.help else help = '' diff --git a/milo/plugins/activityView.lua b/milo/plugins/activityView.lua index 67fd7f0..7af2b03 100644 --- a/milo/plugins/activityView.lua +++ b/milo/plugins/activityView.lua @@ -7,15 +7,12 @@ local Util = require('util') local colors = _G.colors local context = Milo:getContext() local device = _G.device -local monitor = context.storage:getSingleNode('activity') --[[ Configuration Page ]]-- local template = [[%sDisplays the amount of items entering or leaving storage.%s -Right-clicking on the activity monitor will reset the totals. - -%sMilo must be restarted to activate diplay.]] +Right-clicking on the activity monitor will reset the totals.]] local activityWizardPage = UI.Window { title = 'Activity Monitor', @@ -24,7 +21,7 @@ local activityWizardPage = UI.Window { [1] = UI.TextArea { x = 2, ex = -2, y = 2, ey = -2, marginRight = 0, - value = string.format(template, Ansi.yellow, Ansi.reset, Ansi.orange), + value = string.format(template, Ansi.yellow, Ansi.reset), }, } @@ -43,122 +40,128 @@ end UI:getPage('nodeWizard').wizard:add({ activity = activityWizardPage }) -if not monitor then - return -end - -local page = UI.Window { - parent = UI.Device { - device = monitor.adapter, - textScale = .5, - }, - grid = UI.Grid { - columns = { - { heading = 'Qty', key = 'count', width = 6 }, - { heading = 'Change', key = 'change', width = 6 }, - { heading = 'Rate', key = 'rate', width = 6 }, - { heading = 'Name', key = 'displayName' }, +--[[ Display ]]-- +local function createPage(node) + local page = UI.Window { + parent = UI.Device { + device = node.adapter, + textScale = .5, }, - sortColumn = 'displayName', - }, -} + grid = UI.Grid { + columns = { + { heading = 'Qty', key = 'count', width = 6 }, + { heading = 'Change', key = 'change', width = 6 }, + { heading = 'Rate', key = 'rate', width = 6 }, + { heading = 'Name', key = 'displayName' }, + }, + sortColumn = 'displayName', + }, + } -function page.grid:getRowTextColor(row, selected) - if row.lastCount and row.lastCount ~= row.count then - return row.count > row.lastCount and colors.yellow or colors.lightGray - end - return UI.Grid:getRowTextColor(row, selected) -end - -function page.grid:getDisplayValues(row) - row = Util.shallowCopy(row) - - local ind = '+' - if row.change < 0 then - ind = '' - end - - row.change = ind .. Util.toBytes(row.change) - row.count = Util.toBytes(row.count) - row.rate = Util.toBytes(row.rate) - - return row -end - -function page:reset() - self.lastItems = nil - self.grid:setValues({ }) - self.grid:clear() - self.grid:draw() -end - -function page:refresh() - local t = context.storage.cache - - if not self.lastItems then - self.lastItems = { } - for k,v in pairs(t) do - self.lastItems[k] = { - displayName = v.displayName, - initialCount = v.count, - } + function page.grid:getRowTextColor(row, selected) + if row.lastCount and row.lastCount ~= row.count then + return row.count > row.lastCount and colors.yellow or colors.lightGray end - self.timestamp = os.clock() + return UI.Grid:getRowTextColor(row, selected) + end + + function page.grid:getDisplayValues(row) + row = Util.shallowCopy(row) + + local ind = '+' + if row.change < 0 then + ind = '' + end + + row.change = ind .. Util.toBytes(row.change) + row.count = Util.toBytes(row.count) + row.rate = Util.toBytes(row.rate) + + return row + end + + function page:reset() + self.lastItems = nil self.grid:setValues({ }) + self.grid:clear() + self.grid:draw() + end - else - for _,v in pairs(self.lastItems) do - v.lastCount = v.count - v.count = nil - end + function page:refresh() + local t = context.storage.cache - self.elapsed = os.clock() - self.timestamp - - for k,v in pairs(t) do - local v2 = self.lastItems[k] - if v2 then - v2.count = v.count - else + if not self.lastItems then + self.lastItems = { } + for k,v in pairs(t) do self.lastItems[k] = { displayName = v.displayName, - count = v.count, - initialCount = 0, + initialCount = v.count, } end - end + self.timestamp = os.clock() + self.grid:setValues({ }) - local changedItems = { } - for k,v in pairs(self.lastItems) do - if not v.count then - v.count = 0 + else + for _,v in pairs(self.lastItems) do + v.lastCount = v.count + v.count = nil end - if v.count ~= v.initialCount then - v.change = v.count - v.initialCount - v.rate = Util.round(60 / self.elapsed * v.change, 1) - changedItems[k] = v - end - end - self.grid:setValues(changedItems) + self.elapsed = os.clock() - self.timestamp + + for k,v in pairs(t) do + local v2 = self.lastItems[k] + if v2 then + v2.count = v.count + else + self.lastItems[k] = { + displayName = v.displayName, + count = v.count, + initialCount = 0, + } + end + end + + local changedItems = { } + for k,v in pairs(self.lastItems) do + if not v.count then + v.count = 0 + end + if v.count ~= v.initialCount then + v.change = v.count - v.initialCount + v.rate = Util.round(60 / self.elapsed * v.change, 1) + changedItems[k] = v + end + end + + self.grid:setValues(changedItems) + end + self.grid:draw() end - self.grid:draw() -end -function page:update() - page:refresh() - page:sync() -end - -Event.on('monitor_touch', function(_, side) - if side == monitor.adapter.side then - page:reset() + function page:update() + page:refresh() page:sync() end -end) -page:enable() -page:draw() -page:sync() + page:enable() + page:draw() + page:sync() + + return page +end + +local pages = { } + +Event.on('monitor_touch', function(_, side) + local function filter(node) + return node.adapter.name == side and pages[node.name] + end + for node in context.storage:filterActive('activity', filter) do + pages[node.name]:reset() + pages[node.name]:sync() + end +end) --[[ Task ]]-- local ActivityTask = { @@ -167,7 +170,12 @@ local ActivityTask = { } function ActivityTask:cycle() - page:update() + for node in context.storage:filterActive('activity') do + if not pages[node.name] then + pages[node.name] = createPage(node) + end + pages[node.name]:update() + end end Milo:registerTask(ActivityTask) diff --git a/milo/plugins/item.lua b/milo/plugins/item.lua index 052d2d0..24430b5 100644 --- a/milo/plugins/item.lua +++ b/milo/plugins/item.lua @@ -110,10 +110,10 @@ local itemPage = UI.Page { backgroundColor = colors.cyan, titleBar = UI.TitleBar { title = 'Select Machine', - previousPage = true, + event = 'cancel_machine', }, grid = UI.ScrollingGrid { - y = 2, ey = -4, + y = 2, ey = -5, disableHeader = true, values = context.config.nodes, columns = { @@ -122,13 +122,14 @@ local itemPage = UI.Page { sortColumn = 'displayName', }, button1 = UI.Button { - x = -14, y = -2, + x = -14, y = -3, text = 'Ok', event = 'set_machine', }, button2 = UI.Button { - x = -9, y = -2, + x = -9, y = -3, text = 'Cancel', event = 'cancel_machine', }, + statusBar = UI.StatusBar { values = 'Enter or double click to select' }, }, info = UI.SlideOut { titleBar = UI.TitleBar { @@ -155,6 +156,8 @@ function itemPage:enable(item) self.form:setValues(self.res) self.titleBar.title = item.displayName or item.name + self.form[6].inactive = not Craft.machineLookup[self.item.key] + UI.Page.enable(self) self:focusFirst() end @@ -169,6 +172,13 @@ function itemPage.machines.grid:getDisplayValues(row) return row end + function itemPage.machines.grid:getRowTextColor(row, selected) + if row.name == Craft.machineLookup[itemPage.item.key] then + return colors.yellow + end + return UI.Grid:getRowTextColor(row, selected) + end + function itemPage.rsControl:enable() local devices = self.form[1].choices Util.clear(devices) @@ -227,8 +237,15 @@ function itemPage:eventHandler(event) UI:setPreviousPage() + elseif event.type == 'grid_select' then + Craft.machineLookup[self.item.key] = event.selected.name + self.machines.grid:draw() + elseif event.type == 'set_machine' then - --TODO save machine + local machine = self.machines.grid:getSelected() + if machine then + Util.writeTable(Craft.MACHINE_LOOKUP, Craft.machineLookup) + end self.machines:hide() elseif event.type == 'cancel_machine' then diff --git a/milo/plugins/jobMonitor.lua b/milo/plugins/jobMonitor.lua index 00d40b8..eb750d0 100644 --- a/milo/plugins/jobMonitor.lua +++ b/milo/plugins/jobMonitor.lua @@ -1,4 +1,3 @@ -local Ansi = require('ansi') local Craft = require('turtle.craft') local Event = require('event') local itemDB = require('itemDB') @@ -9,14 +8,8 @@ local Util = require('util') local colors = _G.colors local context = Milo:getContext() local device = _G.device -local monitor = context.storage:getSingleNode('jobs') --[[ Configuration Screen ]] -local template = -[[%sDisplays the crafting progress%s - -%sMilo must be restarted to activate diplay.]] - local wizardPage = UI.Window { title = 'Crafting Monitor', index = 2, @@ -24,7 +17,8 @@ local wizardPage = UI.Window { [1] = UI.TextArea { x = 2, ex = -2, y = 2, ey = -2, marginRight = 0, - value = string.format(template, Ansi.yellow, Ansi.reset, Ansi.orange), + textColor = colors.yellow, + value = 'Displays the crafting progress.' }, } @@ -44,105 +38,115 @@ end UI:getPage('nodeWizard').wizard:add({ jobs = wizardPage }) --[[ Display ]] -if not monitor then - return -end - -- TODO: some way to cancel a job - -local jobMonitor = UI.Page { - parent = UI.Device { - device = monitor.adapter, - textScale = .5, - }, - grid = UI.Grid { - sortColumn = 'index', - backgroundFocusColor = colors.black, - columns = { - { heading = 'Qty', key = 'remaining', width = 4 }, - { heading = 'Crafting', key = 'displayName', }, - { heading = 'Status', key = 'status', }, - { heading = 'need', key = 'need', width = 4 }, --- { heading = 'total', key = 'total', width = 4 }, --- { heading = 'used', key = 'used', width = 4 }, --- { heading = 'count', key = 'count', width = 4 }, - { heading = 'crafted', key = 'crafted', width = 5 }, --- { heading = 'Progress', key = 'progress', width = 8 }, +local function createPage(node) + local page = UI.Page { + parent = UI.Device { + device = node.adapter, + textScale = .5, }, - }, -} + grid = UI.Grid { + sortColumn = 'index', + backgroundFocusColor = colors.black, + columns = { + { heading = 'Qty', key = 'remaining', width = 4 }, + { heading = 'Crafting', key = 'displayName', }, + { heading = 'Status', key = 'status', }, + { heading = 'need', key = 'need', width = 4 }, + -- { heading = 'total', key = 'total', width = 4 }, + -- { heading = 'used', key = 'used', width = 4 }, + -- { heading = 'count', key = 'count', width = 4 }, + { heading = 'crafted', key = 'crafted', width = 5 }, + -- { heading = 'Progress', key = 'progress', width = 8 }, + }, + }, + } -function jobMonitor:updateList(craftList) - if not Milo:isCraftingPaused() then - local t = { } - for _,v in pairs(craftList) do - table.insert(t, v) - v.index = #t - for k2,v2 in pairs(v.ingredients or { }) do - if v2.key ~= v.key --[[and v2.statusCode ]] then - table.insert(t, v2) - if not v2.displayName then - v2.displayName = itemDB:getName(k2) + function page:updateList(craftList) + if not Milo:isCraftingPaused() then + local t = { } + for _,v in pairs(craftList) do + table.insert(t, v) + v.index = #t + for k2,v2 in pairs(v.ingredients or { }) do + if v2.key ~= v.key --[[and v2.statusCode ]] then + table.insert(t, v2) + if not v2.displayName then + v2.displayName = itemDB:getName(k2) + end + v2.index = #t end - v2.index = #t end end + self.grid:setValues(t) + self.grid:update() + self:draw() + self:sync() end - self.grid:setValues(t) - self.grid:update() - self:draw() - self:sync() end + + function page.grid:getDisplayValues(row) + row = Util.shallowCopy(row) + if not row.displayName then + row.displayName = itemDB:getName(row) + end + if row.requested then + row.remaining = math.max(0, row.requested - row.crafted) + else + row.displayName = ' ' .. row.displayName + end + --row.progress = string.format('%d/%d', row.crafted, row.count) + return row + end + + function page.grid:getRowTextColor(row, selected) + local statusColor = { + [ Craft.STATUS_ERROR ] = colors.red, + [ Craft.STATUS_WARNING ] = colors.orange, + [ Craft.STATUS_INFO ] = colors.yellow, + [ Craft.STATUS_SUCCESS ] = colors.green, + } + return row.statusCode and statusColor[row.statusCode] or + UI.Grid:getRowTextColor(row, selected) + end + + page:enable() + page:draw() + page:sync() + + return page end -function jobMonitor.grid:getDisplayValues(row) - row = Util.shallowCopy(row) - if not row.displayName then - row.displayName = itemDB:getName(row) - end - if row.requested then - row.remaining = math.max(0, row.requested - row.crafted) - else - row.displayName = ' ' .. row.displayName - end - --row.progress = string.format('%d/%d', row.crafted, row.count) - return row -end - -function jobMonitor.grid:getRowTextColor(row, selected) - local statusColor = { - [ Craft.STATUS_ERROR ] = colors.red, - [ Craft.STATUS_WARNING ] = colors.orange, - [ Craft.STATUS_INFO ] = colors.yellow, - [ Craft.STATUS_SUCCESS ] = colors.green, - } - return row.statusCode and statusColor[row.statusCode] or - UI.Grid:getRowTextColor(row, selected) -end +local pages = { } Event.on({ 'milo_resume', 'milo_pause' }, function(_, reason) - if reason then - jobMonitor.grid:clear() - jobMonitor.grid:centeredWrite(math.ceil(jobMonitor.grid.height / 2), reason.msg) - else - jobMonitor.grid:draw() + for node in context.storage:filterActive('jobs') do + local page = pages[node.name] + if page then + if reason then + page.grid:clear() + page.grid:centeredWrite(math.ceil(page.grid.height / 2), reason.msg) + else + page.grid:draw() + end + page:sync() + end end - jobMonitor:sync() end) -jobMonitor:enable() -jobMonitor:draw() -jobMonitor:sync() - --[[ Task ]] -local jobMonitorTask = { +local task = { name = 'job status', priority = 80, } -function jobMonitorTask:cycle() - jobMonitor:updateList(context.craftingQueue) +function task:cycle() + for node in context.storage:filterActive('jobs') do + if not pages[node.name] then + pages[node.name] = createPage(node) + end + pages[node.name]:updateList(context.craftingQueue) + end end -Milo:registerTask(jobMonitorTask) -context.jobMonitor = jobMonitor +Milo:registerTask(task) diff --git a/milo/plugins/listing.lua b/milo/plugins/listing.lua index 5bd911d..bd554b7 100644 --- a/milo/plugins/listing.lua +++ b/milo/plugins/listing.lua @@ -5,10 +5,17 @@ local Milo = require('milo') local UI = require('ui') local Util = require('util') -local colors = _G.colors -local context = Milo:getContext() +local colors = _G.colors +local context = Milo:getContext() +local displayMode = Milo:getState('displayMode') or 0 -local function filterItems(t, filter, displayMode) +local displayModes = { + [0] = { text = 'A', help = 'Showing all items' }, + [1] = { text = 'I', help = 'Showing inventory items' }, + [2] = { text = 'C', help = 'Showing craftable items' }, +} + +local function filterItems(t, filter) if filter or displayMode > 0 then local r = { } if filter then @@ -96,7 +103,8 @@ local listingPage = UI.Page { x = -3, event = 'toggle_display', value = 0, - text = 'A', + text = displayModes[displayMode].text, + help = displayModes[displayMode].help, }, }, notification = UI.Notification(), @@ -112,7 +120,6 @@ local listingPage = UI.Page { q = 'quit', }, - displayMode = 0, } function listingPage.statusBar:draw() @@ -197,18 +204,12 @@ function listingPage:eventHandler(event) self:setFocus(self.statusBar.filter) elseif event.type == 'toggle_display' then - local values = { - [0] = 'A', - [1] = 'I', - [2] = 'C', - } - - event.button.value = (event.button.value + 1) % 3 - self.displayMode = event.button.value - event.button.text = values[event.button.value] + displayMode = (displayMode + 1) % 3 + Util.merge(event.button, displayModes[displayMode]) event.button:draw() self:applyFilter() self.grid:draw() + Milo:setState('displayMode', displayMode) elseif event.type == 'learn' then UI:setPage('learn') @@ -275,7 +276,7 @@ function listingPage:refresh(force) end function listingPage:applyFilter() - local t = filterItems(self.allItems, self.filter, self.displayMode) + local t = filterItems(self.allItems, self.filter) self.grid:setValues(t) end From 6b11b29bba6d5bc8ae111ec335ff906fd738ac54 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Mon, 19 Nov 2018 13:52:32 -0500 Subject: [PATCH 100/165] recipe reorg + milo fixes --- core/etc/apps/opus-apps.db | 3 ++ milo/Milo.lua | 4 ++ milo/core/machines.lua | 40 ++++++++++--------- milo/plugins/listing.lua | 11 +++-- .../etc/names/appliedenergistics2.json | 0 .../etc/names/computercraft.json | 0 {core => recipeBook}/etc/names/enderio.json | 0 .../etc/names/exnihiloadscensio.json | 0 .../etc/names/extrautils2.json | 0 {core => recipeBook}/etc/names/ironchest.json | 0 {core => recipeBook}/etc/names/rftools.json | 0 .../etc/names/storagedrawers.json | 0 .../etc/names/tconstruct.json | 0 13 files changed, 36 insertions(+), 22 deletions(-) rename {core => recipeBook}/etc/names/appliedenergistics2.json (100%) rename {core => recipeBook}/etc/names/computercraft.json (100%) rename {core => recipeBook}/etc/names/enderio.json (100%) rename {core => recipeBook}/etc/names/exnihiloadscensio.json (100%) rename {core => recipeBook}/etc/names/extrautils2.json (100%) rename {core => recipeBook}/etc/names/ironchest.json (100%) rename {core => recipeBook}/etc/names/rftools.json (100%) rename {core => recipeBook}/etc/names/storagedrawers.json (100%) rename {core => recipeBook}/etc/names/tconstruct.json (100%) diff --git a/core/etc/apps/opus-apps.db b/core/etc/apps/opus-apps.db index 0cb8617..cd82806 100644 --- a/core/etc/apps/opus-apps.db +++ b/core/etc/apps/opus-apps.db @@ -10,6 +10,8 @@ \030 \031f\030f\031f\128\0317\143\031f\128\128\0317\143\031f\128\128\128", run = "Turtles.lua", }, +--[[ +Needs work [ "381e3298b2b8f6caeb2208b57d805ada38402f0b" ] = { title = "Scripts", category = "Apps", @@ -21,6 +23,7 @@ \0300\0317else\140", run = "Script.lua", }, +]] c5497bca58468ae64aed6c0fd921109217988db3 = { title = "Events", category = "System", diff --git a/milo/Milo.lua b/milo/Milo.lua index 7b44489..74a5635 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -67,6 +67,10 @@ if not modem or not modem.getNameLocal then Syntax('Wired modem missing') end +if not modem.getNameLocal() then + Syntax('Wired modem is not active') +end + local introspection = Peripheral.get('plethora:introspection') or Syntax('Introspection module missing') diff --git a/milo/core/machines.lua b/milo/core/machines.lua index c353382..98cbba4 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -143,20 +143,19 @@ function networkPage:disable() end function networkPage:applyFilter() - local t = context.config.nodes - local filter = self.filter.value + local t = Util.filter(context.config.nodes, function(v) + return not v.hidden + end) - if #filter > 0 then - t = { } - filter = filter:lower() - - for _,v in pairs(context.config.nodes) do - if (v.displayName and string.find(string.lower(v.displayName), filter, 1, true)) or - string.find(string.lower(v.name), filter, 1, true) then - table.insert(t, v) - end - end + if #self.filter.value > 0 then + local filter = self.filter.value:lower() + Util.filterInplace(t, function(v) + return v.displayName and + string.find(string.lower(v.displayName), filter, 1, true) or + string.find(string.lower(v.name), filter, 1, true) + end) end + self.grid:setValues(t) end @@ -391,10 +390,12 @@ function nodeWizard.wizard.pages.general:showInventory(node) local inventory if device[node.name] and device[node.name].list then - inventory = device[node.name].list() - for k,v in pairs(inventory) do - v.slot = k - end + pcall(function() + inventory = device[node.name].list() + for k,v in pairs(inventory) do + v.slot = k + end + end) end self.grid:setValues(inventory or { }) @@ -438,16 +439,17 @@ function nodeWizard:enable(node) self.node.adapter = adapter node.adapter = adapter -_G._p3 = self.node +_G._p3 = self.node -- TODO: remove - debugging local choices = { - { name = 'Ignore', value = 'ignore' }, + { name = 'Ignore', value = 'ignore', '' }, + { name = 'Hidden', value = 'hidden', 'Do not show in list' }, } for _, page in pairs(self.wizard.pages) do if page.isValidType then local choice = page:isValidType(self.node) if choice and not Util.find(choices, 'value', choice.value) then - table.insert(choices, choice) + table.insert(choices, 2, choice) end end end diff --git a/milo/plugins/listing.lua b/milo/plugins/listing.lua index bd554b7..f2d5065 100644 --- a/milo/plugins/listing.lua +++ b/milo/plugins/listing.lua @@ -252,15 +252,20 @@ function listingPage:enable() self:sync() end) - self.handler = Event.on({ 'storage_offline', 'storage_online' }, function(_, isOnline) + local function updateStatus() self.statusBar.storageStatus.value = - isOnline and '' or 'offline' + context.storage:isOnline() and '' or 'offline' self.statusBar.storageStatus.textColor = - isOnline and colors.lime or colors.red + context.storage:isOnline() and colors.lime or colors.red + end + + self.handler = Event.on({ 'storage_offline', 'storage_online' }, function() + updateStatus() self.statusBar.storageStatus:draw() self:sync() end) + updateStatus() UI.Page.enable(self) end diff --git a/core/etc/names/appliedenergistics2.json b/recipeBook/etc/names/appliedenergistics2.json similarity index 100% rename from core/etc/names/appliedenergistics2.json rename to recipeBook/etc/names/appliedenergistics2.json diff --git a/core/etc/names/computercraft.json b/recipeBook/etc/names/computercraft.json similarity index 100% rename from core/etc/names/computercraft.json rename to recipeBook/etc/names/computercraft.json diff --git a/core/etc/names/enderio.json b/recipeBook/etc/names/enderio.json similarity index 100% rename from core/etc/names/enderio.json rename to recipeBook/etc/names/enderio.json diff --git a/core/etc/names/exnihiloadscensio.json b/recipeBook/etc/names/exnihiloadscensio.json similarity index 100% rename from core/etc/names/exnihiloadscensio.json rename to recipeBook/etc/names/exnihiloadscensio.json diff --git a/core/etc/names/extrautils2.json b/recipeBook/etc/names/extrautils2.json similarity index 100% rename from core/etc/names/extrautils2.json rename to recipeBook/etc/names/extrautils2.json diff --git a/core/etc/names/ironchest.json b/recipeBook/etc/names/ironchest.json similarity index 100% rename from core/etc/names/ironchest.json rename to recipeBook/etc/names/ironchest.json diff --git a/core/etc/names/rftools.json b/recipeBook/etc/names/rftools.json similarity index 100% rename from core/etc/names/rftools.json rename to recipeBook/etc/names/rftools.json diff --git a/core/etc/names/storagedrawers.json b/recipeBook/etc/names/storagedrawers.json similarity index 100% rename from core/etc/names/storagedrawers.json rename to recipeBook/etc/names/storagedrawers.json diff --git a/core/etc/names/tconstruct.json b/recipeBook/etc/names/tconstruct.json similarity index 100% rename from core/etc/names/tconstruct.json rename to recipeBook/etc/names/tconstruct.json From cc913bc10ca1c7879913b685af166845fab482ef Mon Sep 17 00:00:00 2001 From: kepler155c Date: Mon, 19 Nov 2018 16:10:24 -0500 Subject: [PATCH 101/165] milo fixes --- milo/core/machines.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/milo/core/machines.lua b/milo/core/machines.lua index 98cbba4..3a8effe 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -144,7 +144,7 @@ end function networkPage:applyFilter() local t = Util.filter(context.config.nodes, function(v) - return not v.hidden + return v.mtype ~= 'hidden' end) if #self.filter.value > 0 then @@ -443,7 +443,7 @@ _G._p3 = self.node -- TODO: remove - debugging local choices = { { name = 'Ignore', value = 'ignore', '' }, - { name = 'Hidden', value = 'hidden', 'Do not show in list' }, + { name = 'Hidden', value = 'hidden', help = 'Do not show in list' }, } for _, page in pairs(self.wizard.pages) do if page.isValidType then From 4df1b3009893a78d0cdf9ceeb8967b54832b011f Mon Sep 17 00:00:00 2001 From: kepler155c Date: Mon, 19 Nov 2018 17:18:02 -0500 Subject: [PATCH 102/165] milo fixes --- milo/MiloRemote.lua | 6 +++++- milo/plugins/importTask.lua | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index 358a6e2..a006d4b 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -407,8 +407,9 @@ function page:applyFilter() end Event.addRoutine(function() + local sleepTime = 1.5 while true do - os.sleep(1.5) + os.sleep(sleepTime) if config.deposit then local neural = device.neuralInterface local inv = config.useShield and 'getEquipment' or 'getInventory' @@ -432,7 +433,10 @@ Event.addRoutine(function() end page.grid:draw() page:sync() + sleepTime = math.max(sleepTime - .25, .25) end + else + sleepTime = math.min(sleepTime + .25, 1.5) end end end diff --git a/milo/plugins/importTask.lua b/milo/plugins/importTask.lua index 12517ca..1ec15b5 100644 --- a/milo/plugins/importTask.lua +++ b/milo/plugins/importTask.lua @@ -15,7 +15,8 @@ function ImportTask:cycle(context) local function itemMatchesFilter(item) if not entry.ignoreDamage and not entry.ignoreNbtHash then - return entry.filter[item.key] + local key = Milo:uniqueKey(item) + return entry.filter[key] end for key in pairs(entry.filter) do From d6b0fef5d4075cb577f511c81fe6e61148f74b32 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Mon, 19 Nov 2018 19:36:08 -0500 Subject: [PATCH 103/165] milo fixes --- milo/core/machines.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/milo/core/machines.lua b/milo/core/machines.lua index 3a8effe..014bdf0 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -149,7 +149,7 @@ function networkPage:applyFilter() if #self.filter.value > 0 then local filter = self.filter.value:lower() - Util.filterInplace(t, function(v) + t = Util.filter(t, function(v) return v.displayName and string.find(string.lower(v.displayName), filter, 1, true) or string.find(string.lower(v.name), filter, 1, true) From d0763262734bdf19d7d763c507eb46481e566069 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Tue, 20 Nov 2018 20:54:25 -0500 Subject: [PATCH 104/165] prep for release --- core/Script.lua | 4 ++-- core/Turtles.lua | 2 +- core/etc/apps/opus-apps.db | 25 ++++++++----------------- miners/etc/apps/apps.db | 11 +++++++++++ 4 files changed, 22 insertions(+), 20 deletions(-) create mode 100644 miners/etc/apps/apps.db diff --git a/core/Script.lua b/core/Script.lua index 20c72f5..82e0b30 100644 --- a/core/Script.lua +++ b/core/Script.lua @@ -11,8 +11,8 @@ local fs = _G.fs local os = _G.os local shell = _ENV.shell -local GROUPS_PATH = 'usr/groups' -local SCRIPTS_PATH = 'usr/etc/scripts' +local GROUPS_PATH = 'usr/config/groups' +local SCRIPTS_PATH = 'packages/core/etc/scripts' UI:configure('script', ...) diff --git a/core/Turtles.lua b/core/Turtles.lua index a91fd32..6ce66f6 100644 --- a/core/Turtles.lua +++ b/core/Turtles.lua @@ -31,7 +31,7 @@ local options = { desc = 'Displays the options' }, } -local SCRIPTS_PATH = 'usr/etc/scripts' +local SCRIPTS_PATH = 'packages/core/etc/scripts' local nullTerm = Terminal.getNullTerm(term.current()) local socket diff --git a/core/etc/apps/opus-apps.db b/core/etc/apps/opus-apps.db index cd82806..952d632 100644 --- a/core/etc/apps/opus-apps.db +++ b/core/etc/apps/opus-apps.db @@ -1,4 +1,6 @@ { +--[[ +Needs work [ "90ef98d4b6fd15466f0a1f212ec1db8d9ebe018c" ] = { title = "Turtles", category = "Apps", @@ -10,8 +12,6 @@ \030 \031f\030f\031f\128\0317\143\031f\128\128\0317\143\031f\128\128\128", run = "Turtles.lua", }, ---[[ -Needs work [ "381e3298b2b8f6caeb2208b57d805ada38402f0b" ] = { title = "Scripts", category = "Apps", @@ -57,15 +57,6 @@ Needs work requires = 'turtle', }, --[[ - [ "4486006f811b88cacd5f211fd579717e29b600cd" ] = { - title = "Miner", - category = "Apps", - icon = " \0315\\\030 \031 \ - \0304\031f _ \030 \031c/\0315\\\ - \0304 ", - run = "simpleMiner.lua", - requires = 'turtle', - }, [ "89307d419a2fe4fbb69af92b3d3af27b6ec14d3e" ] = { title = "Telnet", category = "Apps", @@ -91,6 +82,12 @@ Needs work run = "usr/apps/builder.lua", requires = "turtle", }, + [ "d8c298dd41e4a4ec20e8307901797b64688b3b77" ] = { + title = "GPS Deploy", + category = "Apps", + run = "http://pastebin.com/raw/VXAyXqBv", + requires = "turtle", + }, --]] df485c871329671f46570634d63216761441bcd6 = { title = "Devices", @@ -114,12 +111,6 @@ Needs work \030 \031f\030f\031e\139\030e\128\030f\159\129\0314\130\131\131\129", run = "recorder.lua", }, - [ "d8c298dd41e4a4ec20e8307901797b64688b3b77" ] = { - title = "GPS Deploy", - category = "Apps", - run = "http://pastebin.com/raw/VXAyXqBv", - requires = "turtle", - }, [ "a2accffe95b2c8be30e8a05e0c6ab7e8f5966f43" ] = { title = "Strafe", category = "Games", diff --git a/miners/etc/apps/apps.db b/miners/etc/apps/apps.db new file mode 100644 index 0000000..a9ea75e --- /dev/null +++ b/miners/etc/apps/apps.db @@ -0,0 +1,11 @@ +{ + [ "4486006f811b88cacd5f211fd579717e29b600cd" ] = { + title = "Miner", + category = "Apps", + icon = " \0315\\\030 \031 \ + \0304\031f _ \030 \031c/\0315\\\ + \0304 ", + run = "simpleMiner.lua", + requires = 'turtle', + }, +} From a5d62c3271c797c1ed45441dd64e65ede93e8baf Mon Sep 17 00:00:00 2001 From: kepler155c Date: Wed, 21 Nov 2018 12:10:53 -0500 Subject: [PATCH 105/165] new release prep --- builder/.package | 6 +++--- core/.package | 7 ++----- farms/.package | 10 +++++++--- forestry/.package | 7 ++----- glasses/.package | 7 ++----- milo/.package | 4 ++-- miners/.package | 9 ++++----- neural/.package | 7 ++----- pickup/.package | 6 +++--- recipeBook/.package | 7 ++----- storage/.package | 9 +++++++++ {storageManager => storage}/Crafter.lua | 0 {storageManager => storage}/chestManager.lua | 0 {storageManager => storage}/etc/apps/apps.db | 0 {storageManager => storage}/levelEmitter.lua | 0 {storageManager => storage}/storageActivity.lua | 0 storageManager/.package | 9 --------- 17 files changed, 38 insertions(+), 50 deletions(-) create mode 100644 storage/.package rename {storageManager => storage}/Crafter.lua (100%) rename {storageManager => storage}/chestManager.lua (100%) rename {storageManager => storage}/etc/apps/apps.db (100%) rename {storageManager => storage}/levelEmitter.lua (100%) rename {storageManager => storage}/storageActivity.lua (100%) delete mode 100644 storageManager/.package diff --git a/builder/.package b/builder/.package index e50854f..be49c2c 100644 --- a/builder/.package +++ b/builder/.package @@ -1,9 +1,9 @@ { required = { - 'opus-develop-1.8', + 'core', }, title = 'Schematic Builder', - repository = 'kepler155c/opus-apps/develop-1.8/builder', - description = [[ ... ]], + repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/builder', + description = [[ Build structures from schematic files using a turtle or command computer. ]], licence = 'MIT', } diff --git a/core/.package b/core/.package index cc59d0b..7079717 100644 --- a/core/.package +++ b/core/.package @@ -1,9 +1,6 @@ { - required = { - 'opus-develop-1.8', - }, title = 'Core apps and apis', - repository = 'kepler155c/opus-apps/develop-1.8/core', - description = [[ ... ]], + repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/core', + description = [[Provides common files used by Opus applications. Also includes various useful applications.]], licence = 'MIT', } diff --git a/farms/.package b/farms/.package index 929deff..191141a 100644 --- a/farms/.package +++ b/farms/.package @@ -1,9 +1,13 @@ { required = { - 'opus-develop-1.8', + 'core', }, title = 'Programs for farming resources', - repository = 'kepler155c/opus-apps/develop-1.8/farms', - description = [[ ... ]], + repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/farms', + description = [[Includes: + * Tree Farm + * Cow/Sheep Rancher + * Farmer +]], licence = 'MIT', } diff --git a/forestry/.package b/forestry/.package index fa438d9..0b8913e 100644 --- a/forestry/.package +++ b/forestry/.package @@ -1,9 +1,6 @@ { - required = { - 'opus-develop-1.8', - }, title = 'Forestry mod applications', - repository = 'kepler155c/opus-apps/develop-1.8/forestry', - description = [[ ... ]], + repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/forestry', + description = [[WIP]], licence = 'MIT', } diff --git a/glasses/.package b/glasses/.package index 0c7c9c1..beb116e 100644 --- a/glasses/.package +++ b/glasses/.package @@ -1,9 +1,6 @@ { - required = { - 'opus-develop-1.8', - }, title = 'Plethora overlay glasses support', - repository = 'kepler155c/opus-apps/develop-1.8/glasses', - description = [[ ... ]], + repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/glasses', + description = [[WIP]], licence = 'MIT', } diff --git a/milo/.package b/milo/.package index b9690c4..6946b44 100644 --- a/milo/.package +++ b/milo/.package @@ -1,9 +1,9 @@ { required = { - 'opus-develop-1.8', + 'core', }, title = 'Milo: Advanced inventory management', - repository = 'kepler155c/opus-apps/develop-1.8/milo', + repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/milo', description = [[Provides AE style crafting in computercraft. Includes Importing, exporting, autocrafting, replenish and limits, and much more. Includes over 200 standard Minecraft recipes.]], diff --git a/miners/.package b/miners/.package index bc9a002..dca161c 100644 --- a/miners/.package +++ b/miners/.package @@ -1,9 +1,8 @@ { - required = { - 'opus-develop-1.8', - }, title = 'Turtle mining programs', - repository = 'kepler155c/opus-apps/develop-1.8/miners', - description = [[ ... ]], + repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/miners', + description = [[Provides two types of automated mining: + * Scanning Miner: Uses a block scanner to very efficiently mine areas + * Simple Miner: A single or multi-turtle miner]], licence = 'MIT', } diff --git a/neural/.package b/neural/.package index 0d6be54..da1f204 100644 --- a/neural/.package +++ b/neural/.package @@ -1,9 +1,6 @@ { - required = { - 'opus-develop-1.8', - }, title = 'Programs for the neural interface', - repository = 'kepler155c/opus-apps/develop-1.8/neural', - description = [[ ... ]], + repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/neural', + description = [[ WIP ]], licence = 'MIT', } diff --git a/pickup/.package b/pickup/.package index 546c4f9..ea5dd64 100644 --- a/pickup/.package +++ b/pickup/.package @@ -1,9 +1,9 @@ { required = { - 'opus-develop-1.8', + 'core', }, title = 'Move resources around with turtles', - repository = 'kepler155c/opus-apps/develop-1.8/pickup', - description = [[ ... ]], + repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/pickup', + description = [[ WiP ]], licence = 'MIT', } diff --git a/recipeBook/.package b/recipeBook/.package index 1323a51..0662897 100644 --- a/recipeBook/.package +++ b/recipeBook/.package @@ -1,9 +1,6 @@ { - required = { - 'opus-develop-1.8', - }, title = 'Recipe books for crafting programs', - repository = 'kepler155c/opus-apps/develop-1.8/recipeBook', - description = [[ ... ]], + repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/recipeBook', + description = [[ WIP ]], licence = 'MIT', } diff --git a/storage/.package b/storage/.package new file mode 100644 index 0000000..1327c20 --- /dev/null +++ b/storage/.package @@ -0,0 +1,9 @@ +{ + required = { + 'core', + }, + title = 'Manage inventory', + repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/storage', + description = [[Storage manager for Minecraft 1.7 (Use Milo for new versions)]], + licence = 'MIT', +} diff --git a/storageManager/Crafter.lua b/storage/Crafter.lua similarity index 100% rename from storageManager/Crafter.lua rename to storage/Crafter.lua diff --git a/storageManager/chestManager.lua b/storage/chestManager.lua similarity index 100% rename from storageManager/chestManager.lua rename to storage/chestManager.lua diff --git a/storageManager/etc/apps/apps.db b/storage/etc/apps/apps.db similarity index 100% rename from storageManager/etc/apps/apps.db rename to storage/etc/apps/apps.db diff --git a/storageManager/levelEmitter.lua b/storage/levelEmitter.lua similarity index 100% rename from storageManager/levelEmitter.lua rename to storage/levelEmitter.lua diff --git a/storageManager/storageActivity.lua b/storage/storageActivity.lua similarity index 100% rename from storageManager/storageActivity.lua rename to storage/storageActivity.lua diff --git a/storageManager/.package b/storageManager/.package deleted file mode 100644 index 7114d5f..0000000 --- a/storageManager/.package +++ /dev/null @@ -1,9 +0,0 @@ -{ - required = { - 'opus-develop-1.8', - }, - title = 'Manage inventory', - repository = 'kepler155c/opus-apps/develop-1.8/storageManager', - description = [[ ... ]], - licence = 'MIT', -} From 0787cc7913cde5f3050b6be66344ebdf7af2505f Mon Sep 17 00:00:00 2001 From: kepler155c Date: Wed, 21 Nov 2018 17:28:50 -0500 Subject: [PATCH 106/165] milo: peripheral improvements --- milo/Milo.lua | 9 +++++++-- milo/plugins/remote.lua | 40 +++++++++++++++++++++++++++++----------- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/milo/Milo.lua b/milo/Milo.lua index 74a5635..84c3e36 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -24,6 +24,7 @@ local fs = _G.fs local multishell = _ENV.multishell local os = _G.os local shell = _ENV.shell +local turtle = _G.turtle if multishell then multishell.setTitle(multishell.getCurrent(), 'Milo') @@ -71,11 +72,15 @@ if not modem.getNameLocal() then Syntax('Wired modem is not active') end -local introspection = Peripheral.get('plethora:introspection') or +local introspection = device['plethora:introspection'] or + turtle.equip('left', 'plethora:module:0') and device['plethora:introspection'] or Syntax('Introspection module missing') if not device.workbench then - Syntax('Workbench missing') + turtle.equip('right', 'minecraft:crafting_table:0') + if not device.workbench then + Syntax('Workbench missing') + end end local context = { diff --git a/milo/plugins/remote.lua b/milo/plugins/remote.lua index a9e52ff..ca8f6fa 100644 --- a/milo/plugins/remote.lua +++ b/milo/plugins/remote.lua @@ -122,15 +122,33 @@ local function client(socket) _G._debug('REMOTE: disconnected from ' .. socket.dhost) end -if device.wireless_modem then - Event.addRoutine(function() - _G._debug('REMOTE: listening on port 4242') - while true do - local socket = Socket.server(4242) - Event.addRoutine(function() - client(socket) - socket:close() - end) - end - end) +local handler + +local function listen() + if device.wireless_modem then + handler = Event.addRoutine(function() + _G._debug('REMOTE: listening on port 4242') + while true do + local socket = Socket.server(4242) + Event.addRoutine(function() + client(socket) + socket:close() + end) + end + end) + end end + +Event.on({ 'device_attach', 'device_detach' }, function(_, name) + if name == 'wireless_modem' then + if handler then + handler:terminate() + handler = nil + _debug('REMOTE: wireless modem disconnected') + else + listen() + end + end +end) + +listen() From 749827657376719ee79354529ca33e5c2c73acab Mon Sep 17 00:00:00 2001 From: kepler155c Date: Wed, 21 Nov 2018 22:05:04 -0500 Subject: [PATCH 107/165] milo: refresh on storage config change + rabbit + auto detect player name --- milo/MiloRemote.lua | 73 ++++++++++++++++++++++------------------ milo/apis/milo.lua | 5 +-- milo/apis/storage.lua | 4 +++ milo/core/machines.lua | 3 ++ milo/plugins/listing.lua | 56 ++++++++++++++++++------------ 5 files changed, 85 insertions(+), 56 deletions(-) diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index a006d4b..c4637da 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -128,13 +128,6 @@ local page = UI.Page { required = true, }, [2] = UI.TextEntry { - formLabel = 'User Name', formKey = 'user', - help = 'User name for bound manipulator', - shadowText = 'User name', - limit = 50, - required = true, - }, - [3] = UI.TextEntry { formLabel = 'Return Slot', formKey = 'slot', help = 'Use a slot for sending to storage', shadowText = 'Inventory slot #', @@ -142,12 +135,12 @@ local page = UI.Page { validate = 'numeric', required = false, }, - [4] = UI.Checkbox { + [3] = UI.Checkbox { formLabel = 'Shield Slot', formKey = 'useShield', help = 'Or, use the shield slot for sending' }, info = UI.TextArea { - x = 1, ex = -1, y = 7, ey = -3, + x = 1, ex = -1, y = 6, ey = -4, textColor = colors.yellow, marginLeft = 0, marginRight = 0, @@ -163,6 +156,14 @@ local page = UI.Page { items = { }, } +local function getPlayerName() + local neural = device.neuralInterface + + if neural and neural.getName then + return neural.getName() + end +end + local function filterItems(t, filter, displayMode) if filter or displayMode > 0 then local r = { } @@ -194,9 +195,12 @@ function page:sendRequest(data) if not config.server then self:setStatus('Invalid configuration') - Event.onTimeout(2, function() - self:setStatus('') - end) + return + end + + local player = getPlayerName() + if not player then + self:setStatus('Missing neural or introspection') return end @@ -207,7 +211,7 @@ function page:sendRequest(data) self:setStatus('connecting ...') socket, msg = Socket.connect(config.server, 4242) if socket then - socket:write(config.user) + socket:write(player) local r = socket:read(2) if r and not r.msg then self:setStatus('connected ...') @@ -416,27 +420,32 @@ Event.addRoutine(function() if not neural or not neural[inv] then _G._debug('missing Introspection module') elseif config.server and (config.useShield or config.slot) then - local method = neural[inv] - local item = method and method().getItemMeta(config.useShield and SHIELD_SLOT or config.slot) - if item then - local slotNo = config.useShield and 'shield' or config.slot - local response = page:sendRequest({ - request = 'deposit', - slot = slotNo, - count = item.count, - key = table.concat({ item.name, item.damage, item.nbtHash }, ':') - }) - if response then - local ritem = page.items[response.key] - if ritem then - ritem.count = response.current + item.count + local s, m = pcall(function() + local method = neural[inv] + local item = method and method().getItemMeta(config.useShield and SHIELD_SLOT or config.slot) + if item then + local slotNo = config.useShield and 'shield' or config.slot + local response = page:sendRequest({ + request = 'deposit', + slot = slotNo, + count = item.count, + key = table.concat({ item.name, item.damage, item.nbtHash }, ':') + }) + if response then + local ritem = page.items[response.key] + if ritem then + ritem.count = response.current + item.count + end + page.grid:draw() + page:sync() + sleepTime = math.max(sleepTime - .25, .25) end - page.grid:draw() - page:sync() - sleepTime = math.max(sleepTime - .25, .25) + else + sleepTime = math.min(sleepTime + .25, 1.5) end - else - sleepTime = math.min(sleepTime + .25, 1.5) + end) + if not s and m then + _debug(m) end end end diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index 7ea88fa..b372501 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -301,8 +301,9 @@ function Milo:saveResources() end -- Return a list of everything in the system -function Milo:listItems(forceRefresh) - return forceRefresh and self.context.storage:refresh() or self.context.storage:listItems() +function Milo:listItems(forceRefresh, throttle) + return forceRefresh and self.context.storage:refresh(throttle) or + self.context.storage:listItems(throttle) end return Milo diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 32fc095..aa3c1f4 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -141,6 +141,10 @@ function Storage:onlineAdapters() end end +function Storage:setDirty() + self.dirty = true +end + function Storage:refresh(throttle) self.dirty = true self.lastRefresh = os.clock() diff --git a/milo/core/machines.lua b/milo/core/machines.lua index 014bdf0..261317b 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -140,6 +140,9 @@ end function networkPage:disable() UI.Page.disable(self) Event.off(self.handler) + + -- Since some storage may have been added/removed - force a full rescan + context.storage:setDirty() end function networkPage:applyFilter() diff --git a/milo/plugins/listing.lua b/milo/plugins/listing.lua index f2d5065..f28d2b2 100644 --- a/milo/plugins/listing.lua +++ b/milo/plugins/listing.lua @@ -108,6 +108,10 @@ local listingPage = UI.Page { }, }, notification = UI.Notification(), + throttle = UI.Throttle { + textColor = colors.yellow, + borderColor = colors.gray, + }, accelerators = { r = 'refresh', [ 'control-r' ] = 'refresh', @@ -240,32 +244,36 @@ function listingPage:eventHandler(event) end function listingPage:enable() - self:refresh() - self:setFocus(self.statusBar.filter) + Event.onTimeout(0, function() + self:refresh() + self:draw() + self:sync() - self.timer = Event.onInterval(3, function() - for _,v in pairs(self.allItems) do - local c = context.storage.cache[v.key] - v.count = c and c.count or 0 + self.timer = Event.onInterval(3, function() + for _,v in pairs(self.allItems) do + local c = context.storage.cache[v.key] + v.count = c and c.count or 0 + end + self.grid:draw() + self:sync() + end) + + local function updateStatus() + self.statusBar.storageStatus.value = + context.storage:isOnline() and '' or 'offline' + self.statusBar.storageStatus.textColor = + context.storage:isOnline() and colors.lime or colors.red end - self.grid:draw() - self:sync() - end) - - local function updateStatus() - self.statusBar.storageStatus.value = - context.storage:isOnline() and '' or 'offline' - self.statusBar.storageStatus.textColor = - context.storage:isOnline() and colors.lime or colors.red - end - - self.handler = Event.on({ 'storage_offline', 'storage_online' }, function() updateStatus() - self.statusBar.storageStatus:draw() - self:sync() + + self.handler = Event.on({ 'storage_offline', 'storage_online' }, function() + updateStatus() + self.statusBar.storageStatus:draw() + self:sync() + end) end) - updateStatus() + self:setFocus(self.statusBar.filter) UI.Page.enable(self) end @@ -276,8 +284,12 @@ function listingPage:disable() end function listingPage:refresh(force) - self.allItems = Milo:mergeResources(Milo:listItems(force)) + local throttle = function() self.throttle:update() end + + self.throttle:enable() + self.allItems = Milo:mergeResources(Milo:listItems(force, throttle)) self:applyFilter() + self.throttle:disable() end function listingPage:applyFilter() From fa43186d52b140a5145e4b5bcacda3cf6d167194 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Fri, 23 Nov 2018 11:39:39 -0500 Subject: [PATCH 108/165] milo: cleanup --- core/etc/apps/opus-apps.db | 2 +- milo/apis/{turtle/craft.lua => craft2.lua} | 2 +- milo/apis/milo.lua | 2 +- milo/apis/storage.lua | 16 +- milo/core/listing.lua | 301 +++++++++++++++++++++ milo/core/machines.lua | 2 - milo/plugins/craftTask.lua | 8 +- milo/plugins/demandCraft.lua | 2 +- milo/plugins/item.lua | 31 +-- milo/plugins/jobMonitor.lua | 2 +- milo/plugins/listing.lua | 301 +-------------------- milo/plugins/potionImportTask.lua | 2 +- milo/plugins/turtleLearn.lua | 2 +- 13 files changed, 326 insertions(+), 347 deletions(-) rename milo/apis/{turtle/craft.lua => craft2.lua} (99%) create mode 100644 milo/core/listing.lua diff --git a/core/etc/apps/opus-apps.db b/core/etc/apps/opus-apps.db index 952d632..450301f 100644 --- a/core/etc/apps/opus-apps.db +++ b/core/etc/apps/opus-apps.db @@ -46,6 +46,7 @@ Needs work \030 \031 \030f\0317\130\030 \031 \030f\0317\130", run = "Appstore.lua", }, +--[[ [ "131260cbfbb0c821f8eae5e7c3c296c7aa4d50b9" ] = { title = "Music", category = "Apps", @@ -56,7 +57,6 @@ Needs work run = "usr/apps/Music.lua", requires = 'turtle', }, ---[[ [ "89307d419a2fe4fbb69af92b3d3af27b6ec14d3e" ] = { title = "Telnet", category = "Apps", diff --git a/milo/apis/turtle/craft.lua b/milo/apis/craft2.lua similarity index 99% rename from milo/apis/turtle/craft.lua rename to milo/apis/craft2.lua index 94dfdd3..be3cf84 100644 --- a/milo/apis/turtle/craft.lua +++ b/milo/apis/craft2.lua @@ -205,7 +205,7 @@ function Craft.craftRecipeInternal(recipe, count, storage, origItem) _G._debug({'eval', recipe.result, count }) - local maxCount = recipe.maxCount or math.floor(64 / recipe.count) + local maxCount = math.floor((recipe.maxCount or 64) / recipe.count) for key,icount in pairs(Craft.sumIngredients(recipe)) do local itemCount = Craft.getItemCount(origItem.ingredients, key) diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index b372501..20ccba0 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -1,5 +1,5 @@ local Config = require('config') -local Craft = require('turtle.craft') +local Craft = require('craft2') local itemDB = require('itemDB') local Util = require('util') diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index aa3c1f4..f0e8492 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -287,20 +287,6 @@ function Storage:export(target, slot, count, item) -- If no slot and full amount is not exported, then no need to check rest of adapters -- ... so should not reach here - ---[[ - -- not found - scan all others - for _, adapter in self:onlineAdapters() do - if not adapter.cache or not adapter.cache[key] then - provide(adapter) - if count <= 0 then - _G._debug('STORAGE: FOUND: %s - %d', key, count) - break - end - end - end ---]] - return total end @@ -368,7 +354,7 @@ function Storage:import(source, slot, count, item) if count <= 0 then return total end - if adapter.cache and adapter.cache[key] and not node.lock then + if not node.lock and adapter.cache and adapter.cache[key] then insert(adapter) end end diff --git a/milo/core/listing.lua b/milo/core/listing.lua new file mode 100644 index 0000000..4bdd4f4 --- /dev/null +++ b/milo/core/listing.lua @@ -0,0 +1,301 @@ +local Craft = require('craft2') +local itemDB = require('itemDB') +local Event = require('event') +local Milo = require('milo') +local UI = require('ui') +local Util = require('util') + +local colors = _G.colors +local context = Milo:getContext() +local displayMode = Milo:getState('displayMode') or 0 +local string = _G.string + +local displayModes = { + [0] = { text = 'A', help = 'Showing all items' }, + [1] = { text = 'I', help = 'Showing inventory items' }, + [2] = { text = 'C', help = 'Showing craftable items' }, +} + +local function filterItems(t, filter) + if filter or displayMode > 0 then + local r = { } + if filter then + filter = filter:lower() + end + for _,v in pairs(t) do + if not filter or string.find(v.lname, filter, 1, true) then + if not displayMode or + displayMode == 0 or + displayMode == 1 and v.count > 0 or + displayMode == 2 and v.has_recipe then + table.insert(r, v) + end + end + end + return r + end + return t +end + +local listingPage = UI.Page { + menuBar = UI.MenuBar { + buttons = { + { text = 'Learn', event = 'learn' }, + --{ text = 'Forget', event = 'forget' }, + { text = 'Craft', event = 'craft' }, + { text = 'Edit', event = 'details' }, + { text = 'Refresh', event = 'refresh', x = -12 }, + { + text = '\206', + x = -3, + dropdown = { + { text = 'Setup', event = 'network' }, + UI.MenuBar.spacer, + { + text = 'Rescan storage', + event = 'rescan', + help = 'Rescan all inventories' + }, + }, + }, + }, + }, + grid = UI.Grid { + y = 2, ey = -2, + columns = { + { heading = ' Qty', key = 'count' , width = 4, justify = 'right' }, + { heading = 'Name', key = 'displayName' }, + { heading = 'Min', key = 'low' , width = 4 }, + { heading = 'Max', key = 'limit' , width = 4 }, + }, + sortColumn = 'displayName', + }, + statusBar = UI.StatusBar { + filter = UI.TextEntry { + x = 1, ex = -17, + limit = 50, + shadowText = 'filter', + shadowTextColor = colors.gray, + backgroundColor = colors.cyan, + backgroundFocusColor = colors.cyan, + accelerators = { + [ 'enter' ] = 'eject', + }, + }, + storageStatus = UI.Text { + x = -16, ex = -9, + textColor = colors.lime, + backgroundColor = colors.cyan, + value = '', + }, + amount = UI.TextEntry { + x = -8, ex = -4, + limit = 3, + shadowText = '1', + shadowTextColor = colors.gray, + backgroundColor = colors.black, + backgroundFocusColor = colors.black, + accelerators = { + [ 'enter' ] = 'eject_specified', + }, + help = 'Specify an amount to send', + }, + display = UI.Button { + x = -3, + event = 'toggle_display', + value = 0, + text = displayModes[displayMode].text, + help = displayModes[displayMode].help, + }, + }, + notification = UI.Notification(), + throttle = UI.Throttle { + textColor = colors.yellow, + borderColor = colors.gray, + }, + accelerators = { + r = 'refresh', + [ 'control-r' ] = 'refresh', + + [ 'control-e' ] = 'eject', + [ 'control-s' ] = 'eject_stack', + [ 'control-a' ] = 'eject_all', + + [ 'control-m' ] = 'network', + + q = 'quit', + }, +} + +function listingPage.statusBar:draw() + return UI.Window.draw(self) +end + +function listingPage.grid:getRowTextColor(row, selected) + if row.is_craftable then + return colors.yellow + end + if row.has_recipe then + return colors.cyan + end + return UI.Grid:getRowTextColor(row, selected) +end + +function listingPage.grid:getDisplayValues(row) + row = Util.shallowCopy(row) + row.count = row.count > 0 and Util.toBytes(row.count) + if row.low then + row.low = Util.toBytes(row.low) + end + if row.limit then + row.limit = Util.toBytes(row.limit) + end + return row +end + +function listingPage:eventHandler(event) + if event.type == 'quit' then + UI:exitPullEvents() + + elseif event.type == 'eject' or event.type == 'grid_select' then + local item = self.grid:getSelected() + if item then + item.count = Milo:craftAndEject(item, 1) + self.grid:draw() + end + + elseif event.type == 'eject_stack' then + local item = self.grid:getSelected() + if item then + item.count = Milo:craftAndEject(item, itemDB:getMaxCount(item)) + self.grid:draw() + end + + elseif event.type == 'eject_all' then + local item = self.grid:getSelected() + if item then + local updated = Milo:getItem(Milo:listItems(), item) + if updated then + Milo:craftAndEject(item, updated.count) + end + end + + elseif event.type == 'eject_specified' then + local item = self.grid:getSelected() + local count = tonumber(self.statusBar.amount.value) + if item and count then + self.statusBar.amount:reset() + self:setFocus(self.statusBar.filter) + Milo:craftAndEject(item, count) + end + + elseif event.type == 'network' then + UI:setPage('network') + + elseif event.type == 'details' or event.type == 'grid_select_right' then + local item = self.grid:getSelected() + if item then + UI:setPage('item', item) + end + + elseif event.type == 'refresh' then + self:refresh() + self.grid:draw() + self:setFocus(self.statusBar.filter) + + elseif event.type == 'rescan' then + self:refresh(true) + self.grid:draw() + self:setFocus(self.statusBar.filter) + + elseif event.type == 'toggle_display' then + displayMode = (displayMode + 1) % 3 + Util.merge(event.button, displayModes[displayMode]) + event.button:draw() + self:applyFilter() + self.grid:draw() + Milo:setState('displayMode', displayMode) + + elseif event.type == 'learn' then + UI:setPage('learn') + + elseif event.type == 'craft' then + local item = self.grid:getSelected() + if item then + if Craft.findRecipe(item) then -- or item.is_craftable then + UI:setPage('craft', self.grid:getSelected()) + else + self.notification:error('No recipe defined') + end + end + + elseif event.type == 'text_change' and event.element == self.statusBar.filter then + self.filter = event.text + if #self.filter == 0 then + self.filter = nil + end + self:applyFilter() + self.grid:draw() + self.statusBar.filter:focus() + + else + UI.Page.eventHandler(self, event) + end + return true +end + +function listingPage:enable() + Event.onTimeout(0, function() + self:refresh() + self:draw() + self:sync() + + self.timer = Event.onInterval(3, function() + for _,v in pairs(self.allItems) do + local c = context.storage.cache[v.key] + v.count = c and c.count or 0 + end + self.grid:draw() + self:sync() + end) + + local function updateStatus() + self.statusBar.storageStatus.value = + context.storage:isOnline() and '' or 'offline' + self.statusBar.storageStatus.textColor = + context.storage:isOnline() and colors.lime or colors.red + end + updateStatus() + + self.handler = Event.on({ 'storage_offline', 'storage_online' }, function() + updateStatus() + self.statusBar.storageStatus:draw() + self:sync() + end) + end) + + self:setFocus(self.statusBar.filter) + UI.Page.enable(self) +end + +function listingPage:disable() + Event.off(self.timer) + Event.off(self.handler) + UI.Page.disable(self) +end + +function listingPage:refresh(force) + local throttle = function() self.throttle:update() end + + self.throttle:enable() + self.allItems = Milo:mergeResources(Milo:listItems(force, throttle)) + self:applyFilter() + self.throttle:disable() +end + +function listingPage:applyFilter() + local t = filterItems(self.allItems, self.filter) + self.grid:setValues(t) +end + +UI:addPage('listing', listingPage) diff --git a/milo/core/machines.lua b/milo/core/machines.lua index 261317b..68ce4f7 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -442,8 +442,6 @@ function nodeWizard:enable(node) self.node.adapter = adapter node.adapter = adapter -_G._p3 = self.node -- TODO: remove - debugging - local choices = { { name = 'Ignore', value = 'ignore', '' }, { name = 'Hidden', value = 'hidden', help = 'Do not show in list' }, diff --git a/milo/plugins/craftTask.lua b/milo/plugins/craftTask.lua index 2ccb73d..a00b606 100644 --- a/milo/plugins/craftTask.lua +++ b/milo/plugins/craftTask.lua @@ -1,4 +1,4 @@ -local Craft = require('turtle.craft') +local Craft = require('craft2') local Milo = require('milo') local sync = require('sync').sync local Util = require('util') @@ -36,6 +36,7 @@ function craftTask:craft(recipe, item) item.ingredients[recipe.result].total = item.count item.ingredients[recipe.result].crafted = item.crafted +--[[ _G._p2 = item if not item.history then item.history = { } @@ -46,13 +47,14 @@ for k,v in pairs(item.ingredients) do t.history.input[k] = Util.shallowCopy(v) end table.insert(item.history, t) - +]] Craft.craftRecipe(recipe, item.requested - item.crafted, context.storage, item) +--[[ for k,v in pairs(item.ingredients) do t.history.output[k] = Util.shallowCopy(v) end - +]] end function craftTask:cycle() diff --git a/milo/plugins/demandCraft.lua b/milo/plugins/demandCraft.lua index be09056..882944d 100644 --- a/milo/plugins/demandCraft.lua +++ b/milo/plugins/demandCraft.lua @@ -1,4 +1,4 @@ -local Craft = require('turtle.craft') +local Craft = require('craft2') local itemDB = require('itemDB') local Milo = require('milo') local UI = require('ui') diff --git a/milo/plugins/item.lua b/milo/plugins/item.lua index 24430b5..77c11dd 100644 --- a/milo/plugins/item.lua +++ b/milo/plugins/item.lua @@ -1,16 +1,14 @@ -local Ansi = require('ansi') -local Craft = require('turtle.craft') -local Milo = require('milo') -local UI = require('ui') -local Util = require('util') +local Ansi = require('ansi') +local Craft = require('craft2') +local Milo = require('milo') +local UI = require('ui') +local Util = require('util') local colors = _G.colors local device = _G.device local context = Milo:getContext() --- TODO: allow change of machine - local itemPage = UI.Page { titleBar = UI.TitleBar { title = 'Limit Resource', @@ -29,18 +27,6 @@ local itemPage = UI.Page { formLabel = 'Max', formKey = 'limit', help = 'Send to trash if above max', validate = 'numeric', }, ---[[ - [3] = UI.Chooser { - width = 7, - formLabel = 'Autocraft', formKey = 'auto', - nochoice = 'No', - choices = { - { name = 'Yes', value = true }, - { name = 'No', value = false }, - }, - help = 'Craft until out of ingredients' - }, -]] [4] = UI.Checkbox { formLabel = 'Ignore Dmg', formKey = 'ignoreDamage', help = 'Ignore damage of item', @@ -163,7 +149,12 @@ function itemPage:enable(item) end function itemPage.machines.grid:isRowValid(_, value) - return value.mtype == 'machine' + local ignores = Util.transpose({ 'storage', 'ignore', 'hidden' }) + if not ignores[value.mtype] then + local node = context.storage.nodes[value.name] +_debug(node) + return node and node.adapter and node.adapter.online and node.adapter.pushItems + end end function itemPage.machines.grid:getDisplayValues(row) diff --git a/milo/plugins/jobMonitor.lua b/milo/plugins/jobMonitor.lua index eb750d0..e555754 100644 --- a/milo/plugins/jobMonitor.lua +++ b/milo/plugins/jobMonitor.lua @@ -1,4 +1,4 @@ -local Craft = require('turtle.craft') +local Craft = require('craft2') local Event = require('event') local itemDB = require('itemDB') local Milo = require('milo') diff --git a/milo/plugins/listing.lua b/milo/plugins/listing.lua index f28d2b2..8a44d2f 100644 --- a/milo/plugins/listing.lua +++ b/milo/plugins/listing.lua @@ -1,300 +1 @@ -local Craft = require('turtle.craft') -local itemDB = require('itemDB') -local Event = require('event') -local Milo = require('milo') -local UI = require('ui') -local Util = require('util') - -local colors = _G.colors -local context = Milo:getContext() -local displayMode = Milo:getState('displayMode') or 0 - -local displayModes = { - [0] = { text = 'A', help = 'Showing all items' }, - [1] = { text = 'I', help = 'Showing inventory items' }, - [2] = { text = 'C', help = 'Showing craftable items' }, -} - -local function filterItems(t, filter) - if filter or displayMode > 0 then - local r = { } - if filter then - filter = filter:lower() - end - for _,v in pairs(t) do - if not filter or string.find(v.lname, filter, 1, true) then - if not displayMode or - displayMode == 0 or - displayMode == 1 and v.count > 0 or - displayMode == 2 and v.has_recipe then - table.insert(r, v) - end - end - end - return r - end - return t -end - -local listingPage = UI.Page { - menuBar = UI.MenuBar { - buttons = { - { text = 'Learn', event = 'learn' }, - --{ text = 'Forget', event = 'forget' }, - { text = 'Craft', event = 'craft' }, - { text = 'Edit', event = 'details' }, - { text = 'Refresh', event = 'refresh', x = -12 }, - { - text = '\206', - x = -3, - dropdown = { - { text = 'Setup', event = 'network' }, - UI.MenuBar.spacer, - { - text = 'Rescan storage', - event = 'rescan', - help = 'Rescan all inventories' - }, - }, - }, - }, - }, - grid = UI.Grid { - y = 2, ey = -2, - columns = { - { heading = ' Qty', key = 'count' , width = 4, justify = 'right' }, - { heading = 'Name', key = 'displayName' }, - { heading = 'Min', key = 'low' , width = 4 }, - { heading = 'Max', key = 'limit' , width = 4 }, - }, - sortColumn = 'displayName', - }, - statusBar = UI.StatusBar { - filter = UI.TextEntry { - x = 1, ex = -17, - limit = 50, - shadowText = 'filter', - shadowTextColor = colors.gray, - backgroundColor = colors.cyan, - backgroundFocusColor = colors.cyan, - accelerators = { - [ 'enter' ] = 'eject', - }, - }, - storageStatus = UI.Text { - x = -16, ex = -9, - textColor = colors.lime, - backgroundColor = colors.cyan, - value = '', - }, - amount = UI.TextEntry { - x = -8, ex = -4, - limit = 3, - shadowText = '1', - shadowTextColor = colors.gray, - backgroundColor = colors.black, - backgroundFocusColor = colors.black, - accelerators = { - [ 'enter' ] = 'eject_specified', - }, - help = 'Specify an amount to send', - }, - display = UI.Button { - x = -3, - event = 'toggle_display', - value = 0, - text = displayModes[displayMode].text, - help = displayModes[displayMode].help, - }, - }, - notification = UI.Notification(), - throttle = UI.Throttle { - textColor = colors.yellow, - borderColor = colors.gray, - }, - accelerators = { - r = 'refresh', - [ 'control-r' ] = 'refresh', - - [ 'control-e' ] = 'eject', - [ 'control-s' ] = 'eject_stack', - [ 'control-a' ] = 'eject_all', - - [ 'control-m' ] = 'network', - - q = 'quit', - }, -} - -function listingPage.statusBar:draw() - return UI.Window.draw(self) -end - -function listingPage.grid:getRowTextColor(row, selected) - if row.is_craftable then - return colors.yellow - end - if row.has_recipe then - return colors.cyan - end - return UI.Grid:getRowTextColor(row, selected) -end - -function listingPage.grid:getDisplayValues(row) - row = Util.shallowCopy(row) - row.count = row.count > 0 and Util.toBytes(row.count) - if row.low then - row.low = Util.toBytes(row.low) - end - if row.limit then - row.limit = Util.toBytes(row.limit) - end - return row -end - -function listingPage:eventHandler(event) - if event.type == 'quit' then - UI:exitPullEvents() - - elseif event.type == 'eject' or event.type == 'grid_select' then - local item = self.grid:getSelected() - if item then - item.count = Milo:craftAndEject(item, 1) - self.grid:draw() - end - - elseif event.type == 'eject_stack' then - local item = self.grid:getSelected() - if item then - item.count = Milo:craftAndEject(item, itemDB:getMaxCount(item)) - self.grid:draw() - end - - elseif event.type == 'eject_all' then - local item = self.grid:getSelected() - if item then - local updated = Milo:getItem(Milo:listItems(), item) - if updated then - Milo:craftAndEject(item, updated.count) - end - end - - elseif event.type == 'eject_specified' then - local item = self.grid:getSelected() - local count = tonumber(self.statusBar.amount.value) - if item and count then - self.statusBar.amount:reset() - self:setFocus(self.statusBar.filter) - Milo:craftAndEject(item, count) - end - - elseif event.type == 'network' then - UI:setPage('network') - - elseif event.type == 'details' or event.type == 'grid_select_right' then - local item = self.grid:getSelected() - if item then - UI:setPage('item', item) - end - - elseif event.type == 'refresh' then - self:refresh() - self.grid:draw() - self:setFocus(self.statusBar.filter) - - elseif event.type == 'rescan' then - self:refresh(true) - self.grid:draw() - self:setFocus(self.statusBar.filter) - - elseif event.type == 'toggle_display' then - displayMode = (displayMode + 1) % 3 - Util.merge(event.button, displayModes[displayMode]) - event.button:draw() - self:applyFilter() - self.grid:draw() - Milo:setState('displayMode', displayMode) - - elseif event.type == 'learn' then - UI:setPage('learn') - - elseif event.type == 'craft' then - local item = self.grid:getSelected() - if item then - if Craft.findRecipe(item) then -- or item.is_craftable then - UI:setPage('craft', self.grid:getSelected()) - else - self.notification:error('No recipe defined') - end - end - - elseif event.type == 'text_change' and event.element == self.statusBar.filter then - self.filter = event.text - if #self.filter == 0 then - self.filter = nil - end - self:applyFilter() - self.grid:draw() - self.statusBar.filter:focus() - - else - UI.Page.eventHandler(self, event) - end - return true -end - -function listingPage:enable() - Event.onTimeout(0, function() - self:refresh() - self:draw() - self:sync() - - self.timer = Event.onInterval(3, function() - for _,v in pairs(self.allItems) do - local c = context.storage.cache[v.key] - v.count = c and c.count or 0 - end - self.grid:draw() - self:sync() - end) - - local function updateStatus() - self.statusBar.storageStatus.value = - context.storage:isOnline() and '' or 'offline' - self.statusBar.storageStatus.textColor = - context.storage:isOnline() and colors.lime or colors.red - end - updateStatus() - - self.handler = Event.on({ 'storage_offline', 'storage_online' }, function() - updateStatus() - self.statusBar.storageStatus:draw() - self:sync() - end) - end) - - self:setFocus(self.statusBar.filter) - UI.Page.enable(self) -end - -function listingPage:disable() - Event.off(self.timer) - Event.off(self.handler) - UI.Page.disable(self) -end - -function listingPage:refresh(force) - local throttle = function() self.throttle:update() end - - self.throttle:enable() - self.allItems = Milo:mergeResources(Milo:listItems(force, throttle)) - self:applyFilter() - self.throttle:disable() -end - -function listingPage:applyFilter() - local t = filterItems(self.allItems, self.filter) - self.grid:setValues(t) -end - -UI:addPage('listing', listingPage) +--moved file diff --git a/milo/plugins/potionImportTask.lua b/milo/plugins/potionImportTask.lua index 6f5eff4..af34f6b 100644 --- a/milo/plugins/potionImportTask.lua +++ b/milo/plugins/potionImportTask.lua @@ -1,4 +1,4 @@ -local Craft = require('turtle.craft') +local Craft = require('craft2') local itemDB = require('itemDB') local Milo = require('milo') diff --git a/milo/plugins/turtleLearn.lua b/milo/plugins/turtleLearn.lua index 3d49455..23d7743 100644 --- a/milo/plugins/turtleLearn.lua +++ b/milo/plugins/turtleLearn.lua @@ -1,4 +1,4 @@ -local Craft = require('turtle.craft') +local Craft = require('craft2') local itemDB = require('itemDB') local Milo = require('milo') local sync = require('sync') From 252fdfb138ef5c3cd0400649f30734c7db969d2b Mon Sep 17 00:00:00 2001 From: kepler155c Date: Fri, 23 Nov 2018 13:55:21 -0500 Subject: [PATCH 109/165] attack program - new --- farms/attack.lua | 71 +++++++++++++++++++++++++++++++++++++++++++ milo/core/listing.lua | 16 +++++----- 2 files changed, 79 insertions(+), 8 deletions(-) create mode 100644 farms/attack.lua diff --git a/farms/attack.lua b/farms/attack.lua new file mode 100644 index 0000000..c1f1f49 --- /dev/null +++ b/farms/attack.lua @@ -0,0 +1,71 @@ +_G.requireInjector(_ENV) + +local Peripheral = require('peripheral') +local Point = require('point') +local Util = require('util') + +local device = _G.device +local os = _G.os +local turtle = _G.turtle + +local args = { ... } +local mob = args[1] or error('Syntax: attack ') + +local function equip(side, item, rawName) + local equipped = Peripheral.lookup('side/' .. side) + + if equipped and equipped.type == item then + return true + end + + if not turtle.equip(side, rawName or item) then + if not turtle.selectSlotWithQuantity(0) then + error('No slots available') + end + turtle.equip(side) + if not turtle.equip(side, item) then + error('Unable to equip ' .. item) + end + end + + turtle.select(1) +end + +local function setHeading() + equip('right', 'plethora:scanner', 'plethora:module:2') + local scanner = device['plethora:scanner'] + local facing = scanner.getBlockMeta(0, 0, 0).state.facing + turtle.point.heading = Point.facings[facing].heading +end + +equip('left', 'minecraft:diamond_sword') +setHeading() +equip('right', 'plethora:sensor', 'plethora:module:3') + +local sensor = device['plethora:sensor'] + +while true do + local blocks = sensor.sense() + local mobs = Util.filterInplace(blocks, function(b) + if b.name == mob then + b.x = Util.round(b.x) + turtle.point.x + b.y = Util.round(b.y) + turtle.point.y + b.z = Util.round(b.z) + turtle.point.z + return true + end + end) + + if turtle.getFuelLevel() == 0 then + error('Out of fuel') + end + + if #mobs == 0 then + os.sleep(3) + else + Point.eachClosest(turtle.point, mobs, function(b) + if turtle.faceAgainst(b) then + repeat until not turtle.attack() + end + end) + end +end diff --git a/milo/core/listing.lua b/milo/core/listing.lua index 4bdd4f4..03c46b8 100644 --- a/milo/core/listing.lua +++ b/milo/core/listing.lua @@ -245,6 +245,14 @@ function listingPage:eventHandler(event) end function listingPage:enable() + local function updateStatus() + self.statusBar.storageStatus.value = + context.storage:isOnline() and '' or 'offline' + self.statusBar.storageStatus.textColor = + context.storage:isOnline() and colors.lime or colors.red + end + updateStatus() + Event.onTimeout(0, function() self:refresh() self:draw() @@ -259,14 +267,6 @@ function listingPage:enable() self:sync() end) - local function updateStatus() - self.statusBar.storageStatus.value = - context.storage:isOnline() and '' or 'offline' - self.statusBar.storageStatus.textColor = - context.storage:isOnline() and colors.lime or colors.red - end - updateStatus() - self.handler = Event.on({ 'storage_offline', 'storage_online' }, function() updateStatus() self.statusBar.storageStatus:draw() From dc61f8973906f8f39e84aec1bae74d2202eaf68b Mon Sep 17 00:00:00 2001 From: kepler155c Date: Fri, 23 Nov 2018 15:13:56 -0500 Subject: [PATCH 110/165] attack program - new --- farms/attack.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/farms/attack.lua b/farms/attack.lua index c1f1f49..143f4cf 100644 --- a/farms/attack.lua +++ b/farms/attack.lua @@ -44,6 +44,8 @@ equip('right', 'plethora:sensor', 'plethora:module:3') local sensor = device['plethora:sensor'] +turtle.setMovementStrategy('goto') + while true do local blocks = sensor.sense() local mobs = Util.filterInplace(blocks, function(b) From cf39d1d74c41fdebb7ed62938be68c24f9ae22db Mon Sep 17 00:00:00 2001 From: kepler155c Date: Fri, 23 Nov 2018 16:13:53 -0500 Subject: [PATCH 111/165] attack program - new --- farms/attack.lua | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/farms/attack.lua b/farms/attack.lua index 143f4cf..709079e 100644 --- a/farms/attack.lua +++ b/farms/attack.lua @@ -31,21 +31,40 @@ local function equip(side, item, rawName) turtle.select(1) end -local function setHeading() - equip('right', 'plethora:scanner', 'plethora:module:2') - local scanner = device['plethora:scanner'] - local facing = scanner.getBlockMeta(0, 0, 0).state.facing - turtle.point.heading = Point.facings[facing].heading +equip('left', 'minecraft:diamond_sword') + +equip('right', 'plethora:scanner', 'plethora:module:2') +local scanner = device['plethora:scanner'] +local facing = scanner.getBlockMeta(0, 0, 0).state.facing +turtle.point.heading = Point.facings[facing].heading + +local scanned = scanner.scan() +local chest = Util.find(scanned, 'name', 'minecraft:chest') +if chest then + chest.x = Util.round(chest.x) + turtle.point.x + chest.y = Util.round(chest.y) + turtle.point.y + chest.z = Util.round(chest.z) + turtle.point.z end -equip('left', 'minecraft:diamond_sword') -setHeading() equip('right', 'plethora:sensor', 'plethora:module:3') local sensor = device['plethora:sensor'] turtle.setMovementStrategy('goto') +local function dropOff() + if not chest then + return + end + local inv = turtle.getSummedInventory() + for _, slot in pairs(inv) do + if slot.count >= 16 then + turtle.dropDownAt(chest, slot.name) + end + end +end + + while true do local blocks = sensor.sense() local mobs = Util.filterInplace(blocks, function(b) @@ -65,9 +84,9 @@ while true do os.sleep(3) else Point.eachClosest(turtle.point, mobs, function(b) - if turtle.faceAgainst(b) then - repeat until not turtle.attack() - end + repeat until not turtle.attackAt(b) end) end + + dropOff() end From 4774b6b5049bec20c7d55c90001d123e09984db8 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Fri, 23 Nov 2018 16:19:40 -0500 Subject: [PATCH 112/165] attack program - new --- farms/attack.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/farms/attack.lua b/farms/attack.lua index 709079e..4b4159f 100644 --- a/farms/attack.lua +++ b/farms/attack.lua @@ -84,7 +84,9 @@ while true do os.sleep(3) else Point.eachClosest(turtle.point, mobs, function(b) - repeat until not turtle.attackAt(b) + if turtle.faceAgainst(b) then + repeat until not turtle.attack() + end end) end From d426b80475399debe51cf924c6ceee60ca185d6b Mon Sep 17 00:00:00 2001 From: kepler155c Date: Fri, 23 Nov 2018 17:03:33 -0500 Subject: [PATCH 113/165] attack program - new --- farms/attack.lua | 45 +++++++++++++++++++++++++++-------- milo/plugins/activityView.lua | 1 + 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/farms/attack.lua b/farms/attack.lua index 4b4159f..14d140b 100644 --- a/farms/attack.lua +++ b/farms/attack.lua @@ -9,7 +9,13 @@ local os = _G.os local turtle = _G.turtle local args = { ... } -local mob = args[1] or error('Syntax: attack ') +local mobType = args[1] or error('Syntax: attack ') + +local Runners = { + Cow = true, + Chicken = true, + Blaze = false, +} local function equip(side, item, rawName) local equipped = Peripheral.lookup('side/' .. side) @@ -64,14 +70,17 @@ local function dropOff() end end +local function normalize(b) + b.x = Util.round(b.x) + turtle.point.x + b.y = Util.round(b.y) + turtle.point.y + b.z = Util.round(b.z) + turtle.point.z +end while true do local blocks = sensor.sense() local mobs = Util.filterInplace(blocks, function(b) - if b.name == mob then - b.x = Util.round(b.x) + turtle.point.x - b.y = Util.round(b.y) + turtle.point.y - b.z = Util.round(b.z) + turtle.point.z + if b.name == mobType then + normalize(b) return true end end) @@ -83,11 +92,27 @@ while true do if #mobs == 0 then os.sleep(3) else - Point.eachClosest(turtle.point, mobs, function(b) - if turtle.faceAgainst(b) then - repeat until not turtle.attack() - end - end) + if Runners[mobType] then + -- if this mob runs away, just attack next closest + Point.eachClosest(turtle.point, mobs, function(b) + if turtle.faceAgainst(b) then + repeat until not turtle.attack() + end + end) + os.sleep(2) --- give a little time for mobs to calm down + else + -- this mob doesn't run, attack and follow until dead + local mob = Point.closest(turtle.point, mobs) + repeat + if turtle.faceAgainst(mob) then + repeat until not turtle.attack() + end + mob = sensor.getMetaByID(mob.id) + if mob then + normalize(mob) + end + until not mob + end end dropOff() diff --git a/milo/plugins/activityView.lua b/milo/plugins/activityView.lua index 7af2b03..f5937a6 100644 --- a/milo/plugins/activityView.lua +++ b/milo/plugins/activityView.lua @@ -23,6 +23,7 @@ local activityWizardPage = UI.Window { marginRight = 0, value = string.format(template, Ansi.yellow, Ansi.reset), }, + timestamp = os.clock(), } function activityWizardPage:isValidType(node) From 8ff38e52b7235841dd8afa0fc0a0d32bc9056654 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Fri, 23 Nov 2018 17:26:26 -0500 Subject: [PATCH 114/165] attack program - new --- farms/attack.lua | 43 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/farms/attack.lua b/farms/attack.lua index 14d140b..cfb6919 100644 --- a/farms/attack.lua +++ b/farms/attack.lua @@ -58,6 +58,16 @@ local sensor = device['plethora:sensor'] turtle.setMovementStrategy('goto') +function Point.iterateClosest(spt, ipts) + local pts = Util.shallowCopy(ipts) + return function() + local pt = Point.closest(spt, pts) + if pt then + return pt + end + end +end + local function dropOff() if not chest then return @@ -101,17 +111,34 @@ while true do end) os.sleep(2) --- give a little time for mobs to calm down else - -- this mob doesn't run, attack and follow until dead - local mob = Point.closest(turtle.point, mobs) - repeat + local attacked = false + + local function attack() + if turtle.attack() then + attacked = true + return attacked + end + end + + for mob in Point.iterateClosest(turtle.point, mobs) do + -- this mob doesn't run, attack and follow until dead if turtle.faceAgainst(mob) then - repeat until not turtle.attack() + repeat + repeat until not turtle.attack() + mob = sensor.getMetaByID(mob.id) + if not mob then + break + end + normalize(mob) + if not turtle.faceAgainst(mob) then + break + end + until not mob end - mob = sensor.getMetaByID(mob.id) - if mob then - normalize(mob) + if attacked then + break end - until not mob + end end end From c8f3ad3b65c5a53a9fbe6034de1542f78cc24b9b Mon Sep 17 00:00:00 2001 From: kepler155c Date: Fri, 23 Nov 2018 18:26:27 -0500 Subject: [PATCH 115/165] fix potion recipe - revert maxCount in craft --- milo/apis/craft2.lua | 3 ++- milo/plugins/potionImportTask.lua | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/milo/apis/craft2.lua b/milo/apis/craft2.lua index be3cf84..3d6c470 100644 --- a/milo/apis/craft2.lua +++ b/milo/apis/craft2.lua @@ -205,7 +205,8 @@ function Craft.craftRecipeInternal(recipe, count, storage, origItem) _G._debug({'eval', recipe.result, count }) - local maxCount = math.floor((recipe.maxCount or 64) / recipe.count) + --local maxCount = math.floor((recipe.maxCount or 64) / recipe.count) + local maxCount = recipe.maxCount or math.floor(64 / recipe.count) for key,icount in pairs(Craft.sumIngredients(recipe)) do local itemCount = Craft.getItemCount(origItem.ingredients, key) diff --git a/milo/plugins/potionImportTask.lua b/milo/plugins/potionImportTask.lua index af34f6b..e5b6e9c 100644 --- a/milo/plugins/potionImportTask.lua +++ b/milo/plugins/potionImportTask.lua @@ -55,7 +55,7 @@ function PotionImportTask:cycle(context) local recipe = { count = 3, ingredients = { }, - maxCount = 3, + maxCount = 1, } local list = bs.adapter.list() From 8b439baf30f7557a8be66249feb215c26ed5356d Mon Sep 17 00:00:00 2001 From: kepler155c Date: Fri, 23 Nov 2018 18:57:19 -0500 Subject: [PATCH 116/165] attack program - new --- farms/attack.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/farms/attack.lua b/farms/attack.lua index cfb6919..6c62ad6 100644 --- a/farms/attack.lua +++ b/farms/attack.lua @@ -63,6 +63,7 @@ function Point.iterateClosest(spt, ipts) return function() local pt = Point.closest(spt, pts) if pt then + Util.removeByValue(pts, pt) return pt end end @@ -126,7 +127,7 @@ while true do repeat repeat until not turtle.attack() mob = sensor.getMetaByID(mob.id) - if not mob then + if not mob or Util.empty(mob) then break end normalize(mob) From cbee1221657e9ab8c9096115bffd9581086b7d58 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Fri, 23 Nov 2018 20:12:12 -0500 Subject: [PATCH 117/165] attack program - new --- farms/attack.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/farms/attack.lua b/farms/attack.lua index 6c62ad6..caa6f47 100644 --- a/farms/attack.lua +++ b/farms/attack.lua @@ -76,6 +76,9 @@ local function dropOff() local inv = turtle.getSummedInventory() for _, slot in pairs(inv) do if slot.count >= 16 then + if turtle.getFuelLevel() < 1000 then + turtle.refuel(slot.name, 16) + end turtle.dropDownAt(chest, slot.name) end end From dff7f4bdabb26e68e1d6a3efd6e9f5f91bf09e2c Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sat, 24 Nov 2018 13:12:31 -0500 Subject: [PATCH 118/165] milo: quad cooker --- farms/attack.lua | 1 + milo/apps/furni.lua | 136 ++++++++++++++++++++++++++++++++------------ 2 files changed, 102 insertions(+), 35 deletions(-) diff --git a/farms/attack.lua b/farms/attack.lua index caa6f47..aa64575 100644 --- a/farms/attack.lua +++ b/farms/attack.lua @@ -57,6 +57,7 @@ equip('right', 'plethora:sensor', 'plethora:module:3') local sensor = device['plethora:sensor'] turtle.setMovementStrategy('goto') +turtle.setPolicy(turtle.policies.attack) function Point.iterateClosest(spt, ipts) local pts = Util.shallowCopy(ipts) diff --git a/milo/apps/furni.lua b/milo/apps/furni.lua index 8eb5294..21244a4 100644 --- a/milo/apps/furni.lua +++ b/milo/apps/furni.lua @@ -1,54 +1,120 @@ --[[ Use 4 furnaces at once to smelt items. -Set up a turtle with a hopper on top and surrounded by furnaces. +SETUP: + Place an introspection module into the turtles inventory. + Connect with wired modem at bottom of turtle. + Place furnaces on each side EXCEPT for bottom and right. -Set the hopper as a machine used for crafting. -Export coal to slot 2 of each furnace and import from slot 3. +CONFIGURATION: + Set turtle as a "Generic Inventory" + export coal to slot 2 + import from slot 3 + +Use this turtle for machine crafting. --]] _G.requireInjector(_ENV) -local Event = require('event') -local Util = require('util') +local Event = require('event') +local Peripheral = require('peripheral') +local Util = require('util') -local device = _G.device -local os = _G.os -local turtle = _G.turtle +local device = _G.device +local os = _G.os +local peripheral = _G.peripheral +local turtle = _G.turtle -local intro = - device['plethora:introspection'] -local inv = intro.getInventory() +local function equip(side, item, rawName) + local equipped = Peripheral.lookup('side/' .. side) -Event.on('turtle_inventory', function() - while true do - local list = inv.list() - local sleepTime = 10 - if Util.empty(list) then - break + if equipped and equipped.type == item then + return true + end + + if not turtle.equip(side, rawName or item) then + if not turtle.selectSlotWithQuantity(0) then + error('No slots available') end - for k,slot in pairs(list) do - for _ = 1, 4 do - local count = 0 - local s, m = pcall(function() - count = inv.pushItems('front', k, 8, 1) - end) - if not s then - _G.printError(m) - end - if count > 0 then - sleepTime = 0 - end - turtle.turnRight() - slot.count = slot.count - count - if slot.count <= 0 then - break + turtle.equip(side) + if not turtle.equip(side, item) then + error('Unable to equip ' .. item) + end + end + + turtle.select(1) +end + +equip('left', 'plethora:introspection', 'plethora:module:0') +local intro = device['plethora:introspection'] +local inv = intro.getInventory() +local sides = { 'front', 'back', 'right', 'top' } + +-- slot 1: item to cook +-- slot 2: fuel +-- slot 3: return + +local active = false + +local function process(list) + active = false + + for _, side in ipairs(Util.shallowCopy(sides)) do + local f = peripheral.call(side, 'list') + + -- items to cook + local item = list[1] + local cooking = f[1] + + if cooking or item then + active = true + end + + if item and item.count > 0 then + if not cooking or cooking.name == item.name then + local count = cooking and cooking.count or 0 + if count < 64 then + print('cooking : ' .. side) + count = inv.pushItems(side, 1, 8, 1) + item.count = item.count - count + Util.removeByValue(sides, side) + table.insert(sides, side) end end end - os.sleep(sleepTime) + + -- fuel + local fuel = f[2] or { count = 0 } + if fuel.count < 8 then + print('fueling ' ..side) + inv.pushItems(side, 2, 8 - fuel.count, 2) + end + + local result = f[3] + if result then + if not list[3] or result.name == list[3].name then + print('pulling from : ' .. side) + inv.pullItems(side, 3, result.count, 3) + list[3] = result + end + end end + + return active +end + +Event.on('turtle_inventory', function() + print('processing') + while true do + -- furnace block updates can cause errors + local s = pcall(process, inv.list()) + if s and not active then + break + end + os.sleep(3) + end + print('idle') end) os.queueEvent('turtle_inventory') -Event.pullEvents() \ No newline at end of file +Event.pullEvents() From 537db89a828dc6fdd3729154dc7300daed0c415e Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sat, 24 Nov 2018 14:16:44 -0500 Subject: [PATCH 119/165] milo autorun programs --- milo/MiloRemote.lua | 18 +++++++++++++++++- milo/apps/furni.lua | 8 ++++++++ milo/autorun/milo.lua | 2 -- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index c4637da..3434260 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -9,10 +9,12 @@ local Util = require('util') local colors = _G.colors local device = _G.device +local fs = _G.fs local os = _G.os local socket -local SHIELD_SLOT = 2 +local SHIELD_SLOT = 2 +local STARTUP_FILE = 'usr/autorun/miloRemote.lua' local config = Config.load('miloRemote', { displayMode = 0 }) @@ -139,6 +141,10 @@ local page = UI.Page { formLabel = 'Shield Slot', formKey = 'useShield', help = 'Or, use the shield slot for sending' }, + [4] = UI.Checkbox { + formLabel = 'Run on startup', formKey = 'runOnStartup', + help = 'Run this program on startup' + }, info = UI.TextArea { x = 1, ex = -1, y = 6, ey = -4, textColor = colors.yellow, @@ -307,6 +313,16 @@ function page:eventHandler(event) self.grid:draw() self:setFocus(self.statusBar.filter) + if config.runOnStartup then + if not fs.exists(STARTUP_FILE) then + Util.writeFile(STARTUP_FILE, + [[os.sleep(1) +shell.openForegroundTab('packages/milo/MiloRemote')]]) + end + elseif fs.exists(STARTUP_FILE) then + fs.delete(STARTUP_FILE) + end + elseif event.type == 'form_cancel' then self.setup:hide() self:setFocus(self.statusBar.filter) diff --git a/milo/apps/furni.lua b/milo/apps/furni.lua index 21244a4..188519c 100644 --- a/milo/apps/furni.lua +++ b/milo/apps/furni.lua @@ -21,10 +21,13 @@ local Peripheral = require('peripheral') local Util = require('util') local device = _G.device +local fs = _G.fs local os = _G.os local peripheral = _G.peripheral local turtle = _G.turtle +local STARTUP_FILE = 'usr/autorun/miloFurni.lua' + local function equip(side, item, rawName) local equipped = Peripheral.lookup('side/' .. side) @@ -50,6 +53,11 @@ local intro = device['plethora:introspection'] local inv = intro.getInventory() local sides = { 'front', 'back', 'right', 'top' } +if not fs.exists(STARTUP_FILE) then + Util.writeFile(STARTUP_FILE, + [[os.sleep(1) +shell.openForegroundTab('packages/milo/apps/furni')]]) +end -- slot 1: item to cook -- slot 2: fuel -- slot 3: return diff --git a/milo/autorun/milo.lua b/milo/autorun/milo.lua index 3304f36..91e9f22 100644 --- a/milo/autorun/milo.lua +++ b/milo/autorun/milo.lua @@ -3,6 +3,4 @@ local shell = _ENV.shell if device.workbench then shell.openForegroundTab('Milo') -elseif device.neuralInterface then - shell.openForegroundTab('MiloRemote') end From 8c2674e5dd072a5447ca3da99353e22fb78bc9b4 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sat, 24 Nov 2018 17:00:23 -0500 Subject: [PATCH 120/165] milo: slow down retry time --- milo/Milo.lua | 3 +++ milo/MiloRemote.lua | 2 +- milo/apps/furni.lua | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/milo/Milo.lua b/milo/Milo.lua index 84c3e36..ac734fd 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -9,6 +9,9 @@ 2. Equip the turtle with an introspection module. ]]-- + +-- TODO: fix which is primary wired modem + _G.requireInjector(_ENV) local Config = require('config') diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index 3434260..ee00142 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -429,7 +429,7 @@ end Event.addRoutine(function() local sleepTime = 1.5 while true do - os.sleep(sleepTime) + os.sleep(socket and sleepTime or 5) if config.deposit then local neural = device.neuralInterface local inv = config.useShield and 'getEquipment' or 'getInventory' diff --git a/milo/apps/furni.lua b/milo/apps/furni.lua index 188519c..3692f92 100644 --- a/milo/apps/furni.lua +++ b/milo/apps/furni.lua @@ -4,7 +4,7 @@ Use 4 furnaces at once to smelt items. SETUP: Place an introspection module into the turtles inventory. Connect with wired modem at bottom of turtle. - Place furnaces on each side EXCEPT for bottom and right. + Place furnaces on each side EXCEPT for bottom and left. CONFIGURATION: Set turtle as a "Generic Inventory" From 3830a02c34ea1d00d0d29649003a170205e7206c Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sat, 24 Nov 2018 22:14:23 -0500 Subject: [PATCH 121/165] milo: mitigate manipulator bound bug --- milo/plugins/remote.lua | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/milo/plugins/remote.lua b/milo/plugins/remote.lua index ca8f6fa..6aeba40 100644 --- a/milo/plugins/remote.lua +++ b/milo/plugins/remote.lua @@ -11,9 +11,17 @@ local SHIELD_SLOT = 2 local context = Milo:getContext() +local function getNameSafe(v) + local name + pcall(function() + name = v.getName() + end) + return name +end + local function getManipulatorForUser(user) for _,v in pairs(device) do - if v.type == 'manipulator' and v.getName and v.getName() == user then + if v.type == 'manipulator' and v.getName and getNameSafe(v) == user then return v end end @@ -110,6 +118,7 @@ local function client(socket) slot.count) end) end + Milo:clearGrid() -- in case all items do not fit in user's inventory end) end From bf4f45f5e50650997edc259d8f6b73ccd2015033 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sun, 25 Nov 2018 12:33:22 -0500 Subject: [PATCH 122/165] milo: reduce modem traffic --- milo/MiloRemote.lua | 53 +++++++++++++++++++++++++++-------------- milo/plugins/remote.lua | 17 ++++++++++--- 2 files changed, 49 insertions(+), 21 deletions(-) diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index ee00142..aeefc31 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -196,7 +196,7 @@ function page:setStatus(status) self:sync() end -function page:sendRequest(data) +function page:sendRequest(data, statusMsg) local response if not config.server then @@ -230,6 +230,9 @@ function page:sendRequest(data) end end if socket then + if statusMsg then + self:setStatus(statusMsg) + end if socket:write(data) then response = socket:read(2) if response then @@ -244,12 +247,10 @@ function page:sendRequest(data) end end socket:close() + socket = nil end end self:setStatus(msg or 'Failed to connect') - Event.onTimeout(2, function() - self:setStatus('') - end) end) return response @@ -271,8 +272,8 @@ function page.grid:getDisplayValues(row) return row end -function page:transfer(item, count) - local response = self:sendRequest({ request = 'transfer', item = item, count = count }) +function page:transfer(item, count, msg) + local response = self:sendRequest({ request = 'transfer', item = item, count = count }, msg) if response then item.count = response.current - response.count self.grid:draw() @@ -333,22 +334,19 @@ shell.openForegroundTab('packages/milo/MiloRemote')]]) elseif event.type == 'eject' or event.type == 'grid_select' then local item = self.grid:getSelected() if item then - self:setStatus('requesting 1 ...') - self:transfer(item, 1) + self:transfer(item, 1, 'requesting 1 ...') end elseif event.type == 'eject_stack' then local item = self.grid:getSelected() if item then - self:setStatus('requesting stack ...') - self:transfer(item, 'stack') + self:transfer(item, 'stack', 'requesting stack ...') end elseif event.type == 'eject_all' then local item = self.grid:getSelected() if item then - self:setStatus('requesting all ...') - self:transfer(item, 'all') + self:transfer(item, 'all', 'requesting all ...') end elseif event.type == 'eject_specified' then @@ -357,21 +355,18 @@ shell.openForegroundTab('packages/milo/MiloRemote')]]) if item and count then self.statusBar.amount:reset() self:setFocus(self.statusBar.filter) - self:setStatus('requesting ' .. count .. ' ...') - self:transfer(item, count) + self:transfer(item, count, 'requesting ' .. count .. ' ...') else self:setStatus('nope ...') end elseif event.type == 'rescan' then self:setFocus(self.statusBar.filter) - self:setStatus('rescanning ...') self:refresh('scan') self.grid:draw() elseif event.type == 'refresh' then self:setFocus(self.statusBar.filter) - self:setStatus('updating ...') self:refresh('list') self.grid:draw() @@ -412,11 +407,33 @@ function page:enable() end) end +local function splitKey(key) + local t = Util.split(key, '(.-):') + local item = { } + if #t[#t] > 8 then + item.nbtHash = table.remove(t) + end + item.damage = tonumber(table.remove(t)) + item.name = table.concat(t, ':') + return item +end + +function page:expandList(list) + local t = { } + for k,v in pairs(list) do + local item = splitKey(k) + item.count, item.displayName = v:match('(%d+):(.+)') + item.count = tonumber(item.count) or 0 + t[k] = item + end + return t +end + function page:refresh(requestType) - local items = self:sendRequest({ request = requestType }) + local items = self:sendRequest({ request = requestType }, 'refreshing...') if items then - self.items = items + self.items = self:expandList(items) self:applyFilter() end end diff --git a/milo/plugins/remote.lua b/milo/plugins/remote.lua index 6aeba40..3230251 100644 --- a/milo/plugins/remote.lua +++ b/milo/plugins/remote.lua @@ -13,9 +13,12 @@ local context = Milo:getContext() local function getNameSafe(v) local name - pcall(function() + local s, m = pcall(function() name = v.getName() end) + if not s then + _G._debug(m) + end return name end @@ -27,6 +30,14 @@ local function getManipulatorForUser(user) end end +local function compactList(list) + local c = { } + for k,v in pairs(list) do + c[k]= v.count .. ':' .. v.displayName + end + return c +end + local function client(socket) _G._debug('REMOTE: connection from ' .. socket.dhost) @@ -58,11 +69,11 @@ local function client(socket) if data.request == 'scan' then -- full scan of all inventories local items = Milo:mergeResources(Milo:listItems(true)) - socket:write(items) + socket:write(compactList(items)) elseif data.request == 'list' then local items = Milo:mergeResources(Milo:listItems()) - socket:write(items) + socket:write(compactList(items)) elseif data.request == 'deposit' then local function deposit() From c24def5d2e332b2989bee64024e2b91029e618d3 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sun, 25 Nov 2018 12:40:21 -0500 Subject: [PATCH 123/165] milo: reduce modem traffic --- milo/MiloRemote.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index aeefc31..a26d8e8 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -424,6 +424,7 @@ function page:expandList(list) local item = splitKey(k) item.count, item.displayName = v:match('(%d+):(.+)') item.count = tonumber(item.count) or 0 + item.lname = item.displayName:lower() t[k] = item end return t From 427006d2cb38c9a3b114e0e8c9ebe42bb8450134 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Sun, 25 Nov 2018 21:08:27 -0500 Subject: [PATCH 124/165] milo: reduce modem traffic --- milo/MiloRemote.lua | 9 ++++----- milo/plugins/remote.lua | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index a26d8e8..09a7815 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -26,7 +26,6 @@ local depositMode = { local displayModes = { [0] = { text = 'A', help = 'Showing all items' }, [1] = { text = 'I', help = 'Showing inventory items' }, - [2] = { text = 'C', help = 'Showing craftable items' }, } local page = UI.Page { @@ -180,8 +179,7 @@ local function filterItems(t, filter, displayMode) if not filter or string.find(v.lname, filter, 1, true) then if not displayMode or displayMode == 0 or - displayMode == 1 and v.count > 0 or - displayMode == 2 and v.has_recipe then + displayMode == 1 and v.count > 0 then table.insert(r, v) end end @@ -371,7 +369,7 @@ shell.openForegroundTab('packages/milo/MiloRemote')]]) self.grid:draw() elseif event.type == 'toggle_display' then - config.displayMode = (config.displayMode + 1) % 3 + config.displayMode = (config.displayMode + 1) % 2 Util.merge(event.button, displayModes[config.displayMode]) event.button:draw() self:applyFilter() @@ -422,9 +420,10 @@ function page:expandList(list) local t = { } for k,v in pairs(list) do local item = splitKey(k) - item.count, item.displayName = v:match('(%d+):(.+)') + item.has_recipe, item.count, item.displayName = v:match('(%d+):(%d+):(.+)') item.count = tonumber(item.count) or 0 item.lname = item.displayName:lower() + item.has_recipe = item.has_recipe == '1' t[k] = item end return t diff --git a/milo/plugins/remote.lua b/milo/plugins/remote.lua index 3230251..1623168 100644 --- a/milo/plugins/remote.lua +++ b/milo/plugins/remote.lua @@ -33,7 +33,7 @@ end local function compactList(list) local c = { } for k,v in pairs(list) do - c[k]= v.count .. ':' .. v.displayName + c[k]= table.concat({ v.has_recipe and 1 or 0, v.count, v.displayName }, ':') end return c end From 4833f7f12b3b673c8bf332a85a922588092f74ab Mon Sep 17 00:00:00 2001 From: kepler155c Date: Mon, 26 Nov 2018 17:10:39 -0500 Subject: [PATCH 125/165] milo: sounds --- farms/attack.lua | 6 ++++-- milo/MiloRemote.lua | 10 ++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/farms/attack.lua b/farms/attack.lua index aa64575..25c0f4b 100644 --- a/farms/attack.lua +++ b/farms/attack.lua @@ -9,7 +9,9 @@ local os = _G.os local turtle = _G.turtle local args = { ... } -local mobType = args[1] or error('Syntax: attack ') +local mobType = args[1] or error('Syntax: attack ') + +local mobTypes = Util.transpose(args) local Runners = { Cow = true, @@ -94,7 +96,7 @@ end while true do local blocks = sensor.sense() local mobs = Util.filterInplace(blocks, function(b) - if b.name == mobType then + if mobTypes[b.name] then normalize(b) return true end diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index 09a7815..eb388d1 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -13,6 +13,8 @@ local fs = _G.fs local os = _G.os local socket +local speaker = device.speaker + local SHIELD_SLOT = 2 local STARTUP_FILE = 'usr/autorun/miloRemote.lua' @@ -161,6 +163,12 @@ local page = UI.Page { items = { }, } +local function playSound(sound) + if speaker then + speaker.playSound('minecraft:' .. sound) + end +end + local function getPlayerName() local neural = device.neuralInterface @@ -271,6 +279,7 @@ function page.grid:getDisplayValues(row) end function page:transfer(item, count, msg) + playSound('block.metal.fall') local response = self:sendRequest({ request = 'transfer', item = item, count = count }, msg) if response then item.count = response.current - response.count @@ -465,6 +474,7 @@ Event.addRoutine(function() key = table.concat({ item.name, item.damage, item.nbtHash }, ':') }) if response then + playSound('block.lava.pop') local ritem = page.items[response.key] if ritem then ritem.count = response.current + item.count From 6f60d91e529372d176b76cae21bcd7d12df707aa Mon Sep 17 00:00:00 2001 From: kepler155c Date: Mon, 26 Nov 2018 18:32:07 -0500 Subject: [PATCH 126/165] milo: sounds --- milo/MiloRemote.lua | 4 ++-- milo/core/listing.lua | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index eb388d1..d12a3a5 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -279,7 +279,7 @@ function page.grid:getDisplayValues(row) end function page:transfer(item, count, msg) - playSound('block.metal.fall') + playSound('ui.button.click') local response = self:sendRequest({ request = 'transfer', item = item, count = count }, msg) if response then item.count = response.current - response.count @@ -474,7 +474,7 @@ Event.addRoutine(function() key = table.concat({ item.name, item.damage, item.nbtHash }, ':') }) if response then - playSound('block.lava.pop') + playSound('entity.item.pickup') local ritem = page.items[response.key] if ritem then ritem.count = response.current + item.count diff --git a/milo/core/listing.lua b/milo/core/listing.lua index 03c46b8..79b413d 100644 --- a/milo/core/listing.lua +++ b/milo/core/listing.lua @@ -8,8 +8,11 @@ local Util = require('util') local colors = _G.colors local context = Milo:getContext() local displayMode = Milo:getState('displayMode') or 0 +local peripheral = _G.peripheral local string = _G.string +local speaker = peripheral.find('speaker') + local displayModes = { [0] = { text = 'A', help = 'Showing all items' }, [1] = { text = 'I', help = 'Showing inventory items' }, @@ -127,6 +130,12 @@ local listingPage = UI.Page { }, } +local function playSound(sound) + if speaker then + speaker.playSound('minecraft:' .. sound) + end +end + function listingPage.statusBar:draw() return UI.Window.draw(self) end @@ -160,6 +169,7 @@ function listingPage:eventHandler(event) elseif event.type == 'eject' or event.type == 'grid_select' then local item = self.grid:getSelected() if item then + playSound('ui.button.click') item.count = Milo:craftAndEject(item, 1) self.grid:draw() end @@ -167,6 +177,7 @@ function listingPage:eventHandler(event) elseif event.type == 'eject_stack' then local item = self.grid:getSelected() if item then + playSound('ui.button.click') item.count = Milo:craftAndEject(item, itemDB:getMaxCount(item)) self.grid:draw() end @@ -174,6 +185,7 @@ function listingPage:eventHandler(event) elseif event.type == 'eject_all' then local item = self.grid:getSelected() if item then + playSound('ui.button.click') local updated = Milo:getItem(Milo:listItems(), item) if updated then Milo:craftAndEject(item, updated.count) @@ -184,6 +196,7 @@ function listingPage:eventHandler(event) local item = self.grid:getSelected() local count = tonumber(self.statusBar.amount.value) if item and count then + playSound('ui.button.click') self.statusBar.amount:reset() self:setFocus(self.statusBar.filter) Milo:craftAndEject(item, count) From 6138ebcf7c77c7e8d327df968cd2a2e9c9f78877 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Mon, 26 Nov 2018 18:44:51 -0500 Subject: [PATCH 127/165] milo: sounds --- milo/MiloRemote.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index d12a3a5..91101dc 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -163,9 +163,9 @@ local page = UI.Page { items = { }, } -local function playSound(sound) +local function playSound(sound, vol) if speaker then - speaker.playSound('minecraft:' .. sound) + speaker.playSound('minecraft:' .. sound, vol or 1) end end @@ -279,7 +279,7 @@ function page.grid:getDisplayValues(row) end function page:transfer(item, count, msg) - playSound('ui.button.click') + playSound('ui.button.click', .3) local response = self:sendRequest({ request = 'transfer', item = item, count = count }, msg) if response then item.count = response.current - response.count From 94e160f73865bbe0d8fa9b0f2b35eb33c5944f86 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Mon, 26 Nov 2018 20:52:38 -0500 Subject: [PATCH 128/165] milo: sounds --- milo/MiloRemote.lua | 14 ++++---------- milo/core/listing.lua | 19 ++++++------------- 2 files changed, 10 insertions(+), 23 deletions(-) diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index 91101dc..cdebc64 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -2,6 +2,7 @@ _G.requireInjector(_ENV) local Config = require('config') local Event = require('event') +local Sound = require('sound') local Socket = require('socket') local sync = require('sync').sync local UI = require('ui') @@ -13,8 +14,6 @@ local fs = _G.fs local os = _G.os local socket -local speaker = device.speaker - local SHIELD_SLOT = 2 local STARTUP_FILE = 'usr/autorun/miloRemote.lua' @@ -163,12 +162,6 @@ local page = UI.Page { items = { }, } -local function playSound(sound, vol) - if speaker then - speaker.playSound('minecraft:' .. sound, vol or 1) - end -end - local function getPlayerName() local neural = device.neuralInterface @@ -279,7 +272,7 @@ function page.grid:getDisplayValues(row) end function page:transfer(item, count, msg) - playSound('ui.button.click', .3) + Sound.play('ui.button.click', .3) local response = self:sendRequest({ request = 'transfer', item = item, count = count }, msg) if response then item.count = response.current - response.count @@ -364,6 +357,7 @@ shell.openForegroundTab('packages/milo/MiloRemote')]]) self:setFocus(self.statusBar.filter) self:transfer(item, count, 'requesting ' .. count .. ' ...') else + Sound.play('entity.villager.no') self:setStatus('nope ...') end @@ -474,7 +468,7 @@ Event.addRoutine(function() key = table.concat({ item.name, item.damage, item.nbtHash }, ':') }) if response then - playSound('entity.item.pickup') + Sound.play('entity.item.pickup') local ritem = page.items[response.key] if ritem then ritem.count = response.current + item.count diff --git a/milo/core/listing.lua b/milo/core/listing.lua index 79b413d..906b538 100644 --- a/milo/core/listing.lua +++ b/milo/core/listing.lua @@ -2,17 +2,15 @@ local Craft = require('craft2') local itemDB = require('itemDB') local Event = require('event') local Milo = require('milo') +local Sound = require('sound') local UI = require('ui') local Util = require('util') local colors = _G.colors local context = Milo:getContext() local displayMode = Milo:getState('displayMode') or 0 -local peripheral = _G.peripheral local string = _G.string -local speaker = peripheral.find('speaker') - local displayModes = { [0] = { text = 'A', help = 'Showing all items' }, [1] = { text = 'I', help = 'Showing inventory items' }, @@ -130,12 +128,6 @@ local listingPage = UI.Page { }, } -local function playSound(sound) - if speaker then - speaker.playSound('minecraft:' .. sound) - end -end - function listingPage.statusBar:draw() return UI.Window.draw(self) end @@ -169,7 +161,7 @@ function listingPage:eventHandler(event) elseif event.type == 'eject' or event.type == 'grid_select' then local item = self.grid:getSelected() if item then - playSound('ui.button.click') + Sound.play('ui.button.click', .3) item.count = Milo:craftAndEject(item, 1) self.grid:draw() end @@ -177,7 +169,7 @@ function listingPage:eventHandler(event) elseif event.type == 'eject_stack' then local item = self.grid:getSelected() if item then - playSound('ui.button.click') + Sound.play('ui.button.click', .3) item.count = Milo:craftAndEject(item, itemDB:getMaxCount(item)) self.grid:draw() end @@ -185,7 +177,7 @@ function listingPage:eventHandler(event) elseif event.type == 'eject_all' then local item = self.grid:getSelected() if item then - playSound('ui.button.click') + Sound.play('ui.button.click', .3) local updated = Milo:getItem(Milo:listItems(), item) if updated then Milo:craftAndEject(item, updated.count) @@ -196,7 +188,7 @@ function listingPage:eventHandler(event) local item = self.grid:getSelected() local count = tonumber(self.statusBar.amount.value) if item and count then - playSound('ui.button.click') + Sound.play('ui.button.click', .3) self.statusBar.amount:reset() self:setFocus(self.statusBar.filter) Milo:craftAndEject(item, count) @@ -238,6 +230,7 @@ function listingPage:eventHandler(event) if Craft.findRecipe(item) then -- or item.is_craftable then UI:setPage('craft', self.grid:getSelected()) else + Sound.play('entity.villager.no') self.notification:error('No recipe defined') end end From f3411f0ba333ddbaf26114b09063341cfa1e91b7 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Tue, 27 Nov 2018 14:17:20 -0500 Subject: [PATCH 129/165] enchanted items --- core/apis/itemDB.lua | 10 +++++++++- milo/core/listing.lua | 1 + milo/plugins/item.lua | 36 +++++++++++++++++++++++++----------- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/core/apis/itemDB.lua b/core/apis/itemDB.lua index 8d2174c..1689326 100644 --- a/core/apis/itemDB.lua +++ b/core/apis/itemDB.lua @@ -119,7 +119,15 @@ function itemDB:add(baseItem) -- ie. potion of healing, potion of healing II -- both show as "Potion of Healing" - if nItem.name ~= 'minecraft:potion' --[[ HACK ]] then + if baseItem.enchantments then + nItem.displayName = nItem.displayName .. ': ' + for k, v in ipairs(baseItem.enchantments) do + if k > 1 then + nItem.displayName = nItem.displayName .. ', ' + end + nItem.displayName = nItem.displayName .. v.fullName + end + elseif nItem.name ~= 'minecraft:potion' --[[ HACK ]] then for k,item in pairs(self.data) do if nItem.name == item.name and nItem.displayName == item.displayName then diff --git a/milo/core/listing.lua b/milo/core/listing.lua index 906b538..79dff27 100644 --- a/milo/core/listing.lua +++ b/milo/core/listing.lua @@ -126,6 +126,7 @@ local listingPage = UI.Page { q = 'quit', }, + allItems = { } } function listingPage.statusBar:draw() diff --git a/milo/plugins/item.lua b/milo/plugins/item.lua index 77c11dd..bbcec26 100644 --- a/milo/plugins/item.lua +++ b/milo/plugins/item.lua @@ -1,8 +1,9 @@ -local Ansi = require('ansi') -local Craft = require('craft2') -local Milo = require('milo') -local UI = require('ui') -local Util = require('util') +local Ansi = require('ansi') +local Craft = require('craft2') +local itemDB = require('itemDB') +local Milo = require('milo') +local UI = require('ui') +local Util = require('util') local colors = _G.colors local device = _G.device @@ -18,11 +19,17 @@ local itemPage = UI.Page { form = UI.Form { x = 1, y = 2, height = 10, ex = -1, [1] = UI.TextEntry { + formLabel = 'Name', formKey = 'displayName', help = 'Override display name', + shadowText = 'Display name', + required = true, + limit = 120, + }, + [2] = UI.TextEntry { width = 7, formLabel = 'Min', formKey = 'low', help = 'Craft if below min', validate = 'numeric', }, - [2] = UI.TextEntry { + [3] = UI.TextEntry { width = 7, formLabel = 'Max', formKey = 'limit', help = 'Send to trash if above max', validate = 'numeric', @@ -35,7 +42,7 @@ local itemPage = UI.Page { formLabel = 'Ignore NBT', formKey = 'ignoreNbtHash', help = 'Ignore NBT of item', }, - [6] = UI.Button { + machineButton = UI.Button { x = 2, y = -2, width = 10, formLabel = 'Machine', event = 'select_machine', @@ -136,13 +143,14 @@ local itemPage = UI.Page { } function itemPage:enable(item) + self.origItem = item self.item = Util.shallowCopy(item) self.res = item.resource or { } - + self.res.displayName = self.item.displayName self.form:setValues(self.res) - self.titleBar.title = item.displayName or item.name - self.form[6].inactive = not Craft.machineLookup[self.item.key] + self.titleBar.title = item.displayName or item.name + self.form.machineButton.inactive = not Craft.machineLookup[self.item.key] UI.Page.enable(self) self:focusFirst() @@ -171,7 +179,7 @@ end end function itemPage.rsControl:enable() - local devices = self.form[1].choices + local devices = self.form[2].choices Util.clear(devices) for _,dev in pairs(device) do if dev.setOutput then @@ -281,6 +289,12 @@ function itemPage:eventHandler(event) local item = self.item if self.form:save() then + if self.res.displayName ~= self.origItem.displayName then + self.origItem.displayName = self.res.displayName + itemDB:add(self.origItem) + itemDB:flush() + end + self.res.displayName = nil Util.prune(self.res, function(v) if type(v) == 'boolean' then return v From f3240affc9649dce39964b481e64a241a6d1da4b Mon Sep 17 00:00:00 2001 From: kepler155c Date: Wed, 28 Nov 2018 00:20:15 -0500 Subject: [PATCH 130/165] milo: rework potion naming + storage cache updating --- core/apis/itemDB.lua | 36 ++++++++++++++++---- milo/apis/storage.lua | 79 +++++++++++++++++++++++-------------------- 2 files changed, 73 insertions(+), 42 deletions(-) diff --git a/core/apis/itemDB.lua b/core/apis/itemDB.lua index 1689326..5ecea03 100644 --- a/core/apis/itemDB.lua +++ b/core/apis/itemDB.lua @@ -97,6 +97,16 @@ function itemDB:get(key) end end +local function formatTime(t) + local m = math.floor(t/60) + local s = t % 60 + if s < 10 then + s = '0' .. s + end + + return m .. ':' .. s +end + --[[ If the base item contains an NBT hash, then the NBT hash uniquely identifies this item. @@ -115,19 +125,33 @@ function itemDB:add(baseItem) nItem.maxCount = baseItem.maxCount nItem.maxDamage = baseItem.maxDamage - -- potions can have the same damage, diff nbts, but same names - -- ie. potion of healing, potion of healing II - -- both show as "Potion of Healing" - + -- enchanted items if baseItem.enchantments then - nItem.displayName = nItem.displayName .. ': ' + if nItem.name == 'minecraft:enchanted_book' then + nItem.displayName = 'Book: ' + else + nItem.displayName = nItem.displayName .. ': ' + end for k, v in ipairs(baseItem.enchantments) do if k > 1 then nItem.displayName = nItem.displayName .. ', ' end nItem.displayName = nItem.displayName .. v.fullName end - elseif nItem.name ~= 'minecraft:potion' --[[ HACK ]] then + + -- potions + elseif nItem.name == 'minecraft:potion' or nItem.name == 'minecraft:lingering_potion' then + if baseItem.effects then + local effect = baseItem.effects[1] + if effect.amplifier == 1 then + nItem.displayName = nItem.displayName .. ' II' + end + if effect.duration and effect.duration > 0 then + nItem.displayName = string.format('%s (%s)', nItem.displayName, formatTime(effect.duration)) + end + end + + else for k,item in pairs(self.data) do if nItem.name == item.name and nItem.displayName == item.displayName then diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index f0e8492..50003f0 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -200,34 +200,24 @@ _G._debug('STORAGE: refresh in ' .. timer()) return cache end -function Storage:updateCache(adapter, key, count) +function Storage:updateCache(adapter, item, count) if not adapter.cache then adapter.dirty = true self.dirty = true return end + local key = item.key or table.concat({ item.name, item.damage, item.nbtHash }, ':') local entry = adapter.cache[key] if not entry then if count < 0 then - adapter.dirty = true - self.dirty = true + _G._debug('STORAGE: update cache - count < 0', 4) else - -- TODO: all items imported should be updated in itemdb - -- error here if not - local item = itemDB:get(key) - if item then - entry = Util.shallowCopy(item) - entry.count = count - entry.key = key - adapter.cache[key] = entry - else -_G._debug('STORAGE: item missing details') - -- TODO: somehow update itemdb with this maybe new item - adapter.dirty = true - self.dirty = true - end + entry = Util.shallowCopy(item) + entry.count = count + entry.key = key + adapter.cache[key] = entry end else entry.count = entry.count + count @@ -235,6 +225,26 @@ _G._debug('STORAGE: item missing details') adapter.cache[key] = nil end end + + if not entry then + _G._debug('STORAGE: item missing details') + adapter.dirty = true + self.dirty = true + else + local sentry = self.cache[key] + if sentry then + sentry.count = sentry.count + count + if sentry.count <= 0 then + self.cache[key] = nil + end + elseif count > 0 then + sentry = Util.shallowCopy(entry) + sentry.count = count + self.cache[key] = sentry + else + self.dirty = true + end + end end function Storage:_sn(name) @@ -261,8 +271,7 @@ function Storage:export(target, slot, count, item) item.displayName or item.name, amount, self:_sn(adapter.name), self:_sn(target), slot and string.format('[%d]', slot) or '[*]') - self:updateCache(adapter, key, -amount) - self:updateCache(self, key, -amount) + self:updateCache(adapter, item, -amount) end count = count - amount total = total + amount @@ -301,6 +310,19 @@ function Storage:import(source, slot, count, item) self:listItems() end + local entry = itemDB:get(key) + if not entry then + if item.displayName then + -- this item already has metadata + entry = itemDB:add(item) + else + -- get the metadata from the device and add to db + entry = itemDB:add(device[source].getItemMeta(slot)) + end + itemDB:flush() + end + item = entry + local function insert(adapter) local amount = adapter:insert(slot, count, nil, source) if amount > 0 then @@ -309,8 +331,7 @@ function Storage:import(source, slot, count, item) item.displayName or item.name, amount, self:_sn(source), slot, self:_sn(adapter.name)) - self:updateCache(adapter, key, amount) - self:updateCache(self, key, amount) + self:updateCache(adapter, item, amount) -- record that we have imported this item into storage during this cycle self.activity[key] = (self.activity[key] or 0) + amount @@ -322,7 +343,7 @@ function Storage:import(source, slot, count, item) -- find a chest locked with this item for node in self:onlineAdapters() do if node.lock and node.lock[key] then - insert(node.adapter) + insert(node.adapter, item) if count > 0 and node.void then total = total + self:trash(source, slot, count) return total @@ -334,20 +355,6 @@ function Storage:import(source, slot, count, item) end end - if not itemDB:get(item) then - if item.displayName then - -- this item already has metadata - itemDB:add(item) - elseif not slot then - _G._debug("IMPORT: NO SLOT") - elseif not device[source] or not device[source].getItemMeta then - _G._debug("IMPORT: DEVICE? : " .. source) - else - -- get the metadata from the device and add to db - itemDB:add(device[source].getItemMeta(slot)) - end - end - -- is this item in some chest if self.cache[key] then for node, adapter in self:onlineAdapters() do From d4bba2ce4f5a0868bfe4f5ffc81b065860e1b610 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Wed, 28 Nov 2018 01:04:55 -0500 Subject: [PATCH 131/165] milo: quantity update bug in listing --- milo/core/listing.lua | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/milo/core/listing.lua b/milo/core/listing.lua index 79dff27..ed00a7d 100644 --- a/milo/core/listing.lua +++ b/milo/core/listing.lua @@ -163,6 +163,8 @@ function listingPage:eventHandler(event) local item = self.grid:getSelected() if item then Sound.play('ui.button.click', .3) + item = Util.shallowCopy(item) + self.grid.values[self.grid.sorted[self.grid.index]] = item item.count = Milo:craftAndEject(item, 1) self.grid:draw() end @@ -171,6 +173,8 @@ function listingPage:eventHandler(event) local item = self.grid:getSelected() if item then Sound.play('ui.button.click', .3) + item = Util.shallowCopy(item) + self.grid.values[self.grid.sorted[self.grid.index]] = item item.count = Milo:craftAndEject(item, itemDB:getMaxCount(item)) self.grid:draw() end @@ -181,7 +185,9 @@ function listingPage:eventHandler(event) Sound.play('ui.button.click', .3) local updated = Milo:getItem(Milo:listItems(), item) if updated then - Milo:craftAndEject(item, updated.count) + item = Util.shallowCopy(item) + self.grid.values[self.grid.sorted[self.grid.index]] = item + item.count = Milo:craftAndEject(item, updated.count) end end @@ -192,7 +198,9 @@ function listingPage:eventHandler(event) Sound.play('ui.button.click', .3) self.statusBar.amount:reset() self:setFocus(self.statusBar.filter) - Milo:craftAndEject(item, count) + item = Util.shallowCopy(item) + self.grid.values[self.grid.sorted[self.grid.index]] = item + item.count = Milo:craftAndEject(item, count) end elseif event.type == 'network' then @@ -266,7 +274,7 @@ function listingPage:enable() self:sync() self.timer = Event.onInterval(3, function() - for _,v in pairs(self.allItems) do + for _,v in pairs(self.grid.values) do local c = context.storage.cache[v.key] v.count = c and c.count or 0 end From c70a22964a66c4dc7861540a612d691c6258c6b0 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Wed, 28 Nov 2018 14:26:46 -0500 Subject: [PATCH 132/165] milo: refactor eject --- milo/apis/milo.lua | 3 +- milo/core/listing.lua | 65 +++++++++++++++++++++---------------------- 2 files changed, 33 insertions(+), 35 deletions(-) diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index 20ccba0..90d0928 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -175,8 +175,7 @@ function Milo:craftAndEject(item, count) return self:eject(item, request.requested) end) - -- predict that we will eject that amount - return request.current - request.count + return request end function Milo:makeRequest(item, count, callback) diff --git a/milo/core/listing.lua b/milo/core/listing.lua index ed00a7d..fe678fc 100644 --- a/milo/core/listing.lua +++ b/milo/core/listing.lua @@ -155,52 +155,51 @@ function listingPage.grid:getDisplayValues(row) return row end +function listingPage:eject(amount) + local item = self.grid:getSelected() + if item and amount then + -- get most up-to-date item + if item then + if amount == 'stack' then + amount = item.maxCount or 64 + elseif amount == 'all' then + item = Milo:getItem(Milo:listItems(), item) + amount = item.count + end + + if amount > 0 then + item = Util.shallowCopy(item) + self.grid.values[self.grid.sorted[self.grid.index]] = item + local request = Milo:craftAndEject(item, amount) + item.count = request.current - request.count + if request.count + request.craft > 0 then + self.grid:draw() + Sound.play('ui.button.click', .3) + return true + end + end + end + end + Sound.play('entity.villager.no') +end + function listingPage:eventHandler(event) if event.type == 'quit' then UI:exitPullEvents() elseif event.type == 'eject' or event.type == 'grid_select' then - local item = self.grid:getSelected() - if item then - Sound.play('ui.button.click', .3) - item = Util.shallowCopy(item) - self.grid.values[self.grid.sorted[self.grid.index]] = item - item.count = Milo:craftAndEject(item, 1) - self.grid:draw() - end + self:eject(1) elseif event.type == 'eject_stack' then - local item = self.grid:getSelected() - if item then - Sound.play('ui.button.click', .3) - item = Util.shallowCopy(item) - self.grid.values[self.grid.sorted[self.grid.index]] = item - item.count = Milo:craftAndEject(item, itemDB:getMaxCount(item)) - self.grid:draw() - end + self:eject('stack') elseif event.type == 'eject_all' then - local item = self.grid:getSelected() - if item then - Sound.play('ui.button.click', .3) - local updated = Milo:getItem(Milo:listItems(), item) - if updated then - item = Util.shallowCopy(item) - self.grid.values[self.grid.sorted[self.grid.index]] = item - item.count = Milo:craftAndEject(item, updated.count) - end - end + self:eject('all') elseif event.type == 'eject_specified' then - local item = self.grid:getSelected() - local count = tonumber(self.statusBar.amount.value) - if item and count then - Sound.play('ui.button.click', .3) + if self:eject(tonumber(self.statusBar.amount.value)) then self.statusBar.amount:reset() self:setFocus(self.statusBar.filter) - item = Util.shallowCopy(item) - self.grid.values[self.grid.sorted[self.grid.index]] = item - item.count = Milo:craftAndEject(item, count) end elseif event.type == 'network' then From b6bad398ca66ce1c6cf91bd4ebd1a86e8f838b5e Mon Sep 17 00:00:00 2001 From: kepler155c Date: Wed, 28 Nov 2018 16:03:19 -0500 Subject: [PATCH 133/165] attack chests --- farms/attack.lua | 48 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/farms/attack.lua b/farms/attack.lua index 25c0f4b..42c5033 100644 --- a/farms/attack.lua +++ b/farms/attack.lua @@ -11,6 +11,7 @@ local turtle = _G.turtle local args = { ... } local mobType = args[1] or error('Syntax: attack ') +local chest -- a chest/dispenser that is accessible local mobTypes = Util.transpose(args) local Runners = { @@ -46,16 +47,7 @@ local scanner = device['plethora:scanner'] local facing = scanner.getBlockMeta(0, 0, 0).state.facing turtle.point.heading = Point.facings[facing].heading -local scanned = scanner.scan() -local chest = Util.find(scanned, 'name', 'minecraft:chest') -if chest then - chest.x = Util.round(chest.x) + turtle.point.x - chest.y = Util.round(chest.y) + turtle.point.y - chest.z = Util.round(chest.z) + turtle.point.z -end - equip('right', 'plethora:sensor', 'plethora:module:3') - local sensor = device['plethora:sensor'] turtle.setMovementStrategy('goto') @@ -72,17 +64,47 @@ function Point.iterateClosest(spt, ipts) end end -local function dropOff() - if not chest then - return +local function findChests() + if chest then + return { chest } end + equip('right', 'plethora:scanner', 'plethora:module:2') + local chests = scanner.scan() + equip('right', 'plethora:sensor', 'plethora:module:3') + + Util.filterInplace(chests, function(b) + if b.name == 'minecraft:chest' or + b.name == 'minecraft:dispenser' or + b.name == 'minecraft:hopper' then + b.x = Util.round(b.x) + turtle.point.x + b.y = Util.round(b.y) + turtle.point.y + b.z = Util.round(b.z) + turtle.point.z + return true + end + end) + return chests +end + +local function dropOff() local inv = turtle.getSummedInventory() for _, slot in pairs(inv) do if slot.count >= 16 then if turtle.getFuelLevel() < 1000 then turtle.refuel(slot.name, 16) end - turtle.dropDownAt(chest, slot.name) + end + end + + inv = turtle.getSummedInventory() + for _, slot in pairs(inv) do + if slot.count >= 16 then + local chests = findChests() + for c in pairs(Point.iterateClosest(chests)) do + if turtle.dropDownAt(c, slot.name) then + chest = c + break + end + end end end end From f0b058959a9bb66a9af6ce1b5bb48fa3127ba2f2 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Wed, 28 Nov 2018 16:03:59 -0500 Subject: [PATCH 134/165] attack chests --- farms/attack.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/farms/attack.lua b/farms/attack.lua index 42c5033..f5a2ad0 100644 --- a/farms/attack.lua +++ b/farms/attack.lua @@ -99,7 +99,7 @@ local function dropOff() for _, slot in pairs(inv) do if slot.count >= 16 then local chests = findChests() - for c in pairs(Point.iterateClosest(chests)) do + for c in Point.iterateClosest(turtle.point, chests) do if turtle.dropDownAt(c, slot.name) then chest = c break From ccec3ceaf4d3f1240c24b1cce7bf98d0ddb2f88b Mon Sep 17 00:00:00 2001 From: kepler155c Date: Thu, 29 Nov 2018 00:42:17 -0500 Subject: [PATCH 135/165] milo: edit name bug + furni --- milo/apps/furni.lua | 58 +++++++++++++++++++++++++++++++++---------- milo/plugins/item.lua | 6 ++++- 2 files changed, 50 insertions(+), 14 deletions(-) diff --git a/milo/apps/furni.lua b/milo/apps/furni.lua index 3692f92..e80c85c 100644 --- a/milo/apps/furni.lua +++ b/milo/apps/furni.lua @@ -23,7 +23,6 @@ local Util = require('util') local device = _G.device local fs = _G.fs local os = _G.os -local peripheral = _G.peripheral local turtle = _G.turtle local STARTUP_FILE = 'usr/autorun/miloFurni.lua' @@ -51,13 +50,43 @@ end equip('left', 'plethora:introspection', 'plethora:module:0') local intro = device['plethora:introspection'] local inv = intro.getInventory() -local sides = { 'front', 'back', 'right', 'top' } if not fs.exists(STARTUP_FILE) then Util.writeFile(STARTUP_FILE, [[os.sleep(1) shell.openForegroundTab('packages/milo/apps/furni')]]) end + +local furni +local localName + +print('detecting wired modem connected to furnaces...') +for _, dev in pairs(device) do + if dev.type == 'wired_modem' then + local list = dev.getNamesRemote() + furni = { } + localName = dev.getNameLocal() + for _, name in pairs(list) do + if device[name].type ~= 'minecraft:furnace' then + furni = nil + break + end + table.insert(furni, device[name]) + end + end + if furni then + print('Using wired modem: ' .. dev.name) + print('Furnaces: ' .. #furni) + break + end +end + +if not furni then + error('Turtle must be connected to a second wired_modem connected to furnaces only') +end + +_G.printError([[Program must be restarted if new furnaces are added.]]) + -- slot 1: item to cook -- slot 2: fuel -- slot 3: return @@ -67,8 +96,8 @@ local active = false local function process(list) active = false - for _, side in ipairs(Util.shallowCopy(sides)) do - local f = peripheral.call(side, 'list') + for _, furnace in ipairs(Util.shallowCopy(furni)) do + local f = furnace.list() -- items to cook local item = list[1] @@ -82,11 +111,11 @@ local function process(list) if not cooking or cooking.name == item.name then local count = cooking and cooking.count or 0 if count < 64 then - print('cooking : ' .. side) - count = inv.pushItems(side, 1, 8, 1) + print('cooking : ' .. furnace.name) + count = furnace.pullItems(localName, 1, 8, 1) item.count = item.count - count - Util.removeByValue(sides, side) - table.insert(sides, side) + Util.removeByValue(furni, furnace) + table.insert(furni, furnace) end end end @@ -94,15 +123,15 @@ local function process(list) -- fuel local fuel = f[2] or { count = 0 } if fuel.count < 8 then - print('fueling ' ..side) - inv.pushItems(side, 2, 8 - fuel.count, 2) + print('fueling ' ..furnace.name) + furnace.pullItems(localName, 2, 8 - fuel.count, 2) end local result = f[3] if result then if not list[3] or result.name == list[3].name then - print('pulling from : ' .. side) - inv.pullItems(side, 3, result.count, 3) + print('pulling from : ' .. furnace.name) + furnace.pushItems(localName, 3, result.count, 3) list[3] = result end end @@ -115,10 +144,13 @@ Event.on('turtle_inventory', function() print('processing') while true do -- furnace block updates can cause errors - local s = pcall(process, inv.list()) + local s, m = pcall(process, inv.list()) if s and not active then break end + if s and not m then + _G.printError(m) + end os.sleep(3) end print('idle') diff --git a/milo/plugins/item.lua b/milo/plugins/item.lua index bbcec26..09850a5 100644 --- a/milo/plugins/item.lua +++ b/milo/plugins/item.lua @@ -160,7 +160,6 @@ function itemPage.machines.grid:isRowValid(_, value) local ignores = Util.transpose({ 'storage', 'ignore', 'hidden' }) if not ignores[value.mtype] then local node = context.storage.nodes[value.name] -_debug(node) return node and node.adapter and node.adapter.online and node.adapter.pushItems end end @@ -293,6 +292,11 @@ function itemPage:eventHandler(event) self.origItem.displayName = self.res.displayName itemDB:add(self.origItem) itemDB:flush() + + -- TODO: ugh + if context.storage.cache[self.origItem.key] then + context.storage.cache[self.origItem.key].displayName = self.res.displayName + end end self.res.displayName = nil Util.prune(self.res, function(v) From a2a87db49cb83fddd830ac90a05e3ad58aa0a7e7 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Thu, 29 Nov 2018 02:07:11 -0500 Subject: [PATCH 136/165] milo: cobblegen --- milo/apps/cobblegen.lua | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 milo/apps/cobblegen.lua diff --git a/milo/apps/cobblegen.lua b/milo/apps/cobblegen.lua new file mode 100644 index 0000000..a86d2ac --- /dev/null +++ b/milo/apps/cobblegen.lua @@ -0,0 +1,30 @@ +_G.requireInjector(_ENV) + +local Util = require('util') + +local fs = _G.fs +local os = _G.os +local turtle = _G.turtle + +local STARTUP_FILE = 'usr/autorun/cobbleGen.lua' + +if not fs.exists(STARTUP_FILE) then + Util.writeFile(STARTUP_FILE, + [[os.sleep(1) +shell.openForegroundTab('packages/milo/apps/cobblegen')]]) +end + +os.queueEvent('turtle_inventory') +while true do + print('waiting') + os.pullEvent('turtle_inventory') + print('waiting for cobble') + for _ = 1, 20 do + if turtle.inspectDown() then + break + end + os.sleep(.1) + end + print('digging') + turtle.digDown() +end \ No newline at end of file From 7b17e756f9a84c4438f3aa0afe11ebcbd77293cc Mon Sep 17 00:00:00 2001 From: kepler155c Date: Thu, 29 Nov 2018 15:13:36 -0500 Subject: [PATCH 137/165] farm chorus fruit --- farms/farmer.lua | 31 ++++++++++++++++++++++++++--- milo/core/listing.lua | 45 +++++++++++++++++++------------------------ 2 files changed, 48 insertions(+), 28 deletions(-) diff --git a/farms/farmer.lua b/farms/farmer.lua index 877d1a0..fd888e6 100644 --- a/farms/farmer.lua +++ b/farms/farmer.lua @@ -9,7 +9,8 @@ local os = _G.os local peripheral = _G.peripheral local turtle = _G.turtle -local CONFIG_FILE = 'usr/config/farm' +local CONFIG_FILE = 'usr/config/farmer' +local STARTUP_FILE = 'usr/autorun/farmer.lua' local scanner = device['plethora:scanner'] or turtle.equip('right', 'plethora:module:2') and device['plethora:scanner'] or @@ -27,16 +28,25 @@ local crops = Util.readTable(CONFIG_FILE) or { ['minecraft:cocoa'] = { seed = 'minecraft:dye:3', mature = 8, action = 'pick' }, ['minecraft:reeds'] = { action = 'bash' }, + ['minecraft:chorus_flower'] = { action = 'bash' }, + ['minecraft:chorus_plant'] = + { seed = 'minecraft:chorus_flower', mature = 0, action = 'bash-smash', }, ['minecraft:melon_block'] = { action = 'smash' }, ['minecraft:pumpkin'] = { action = 'smash' }, ['minecraft:chest'] = { action = 'drop' }, - ['minecraft:cactus'] = { action = 'bump' }, + ['minecraft:cactus'] = { action = 'smash' }, } if not fs.exists(CONFIG_FILE) then Util.writeTable(CONFIG_FILE, crops) end +if not fs.exists(STARTUP_FILE) then + Util.writeFile(STARTUP_FILE, + [[os.sleep(1) +shell.openForegroundTab('packages/farms/farmer.lua')]]) +end + local retain = Util.transpose { "minecraft:diamond_pickaxe", "plethora:module:2", @@ -70,6 +80,16 @@ local function scan() if b.action == 'drop' then return doDropOff and b.y == -1 end + if b.action == 'bash-smash' then + if b.y == -1 then + b.action = 'smash' + end + if b.y == 0 then + b.action = 'bash' + end + return b.action ~= 'bash-smash' + end + if b.action == 'smash' then return b.y == -1 end @@ -128,7 +148,12 @@ local function harvest(blocks) end elseif b.action == 'smash' then - turtle.digDownAt(b) + if turtle.digDownAt(b) then + if crops[b.name].seed then + turtle.placeDown(crops[b.name].seed) + turtle.select(1) + end + end elseif b.action == 'plant' then if turtle.digDownAt(b) then diff --git a/milo/core/listing.lua b/milo/core/listing.lua index fe678fc..22141ed 100644 --- a/milo/core/listing.lua +++ b/milo/core/listing.lua @@ -1,5 +1,4 @@ local Craft = require('craft2') -local itemDB = require('itemDB') local Event = require('event') local Milo = require('milo') local Sound = require('sound') @@ -14,35 +13,12 @@ local string = _G.string local displayModes = { [0] = { text = 'A', help = 'Showing all items' }, [1] = { text = 'I', help = 'Showing inventory items' }, - [2] = { text = 'C', help = 'Showing craftable items' }, } -local function filterItems(t, filter) - if filter or displayMode > 0 then - local r = { } - if filter then - filter = filter:lower() - end - for _,v in pairs(t) do - if not filter or string.find(v.lname, filter, 1, true) then - if not displayMode or - displayMode == 0 or - displayMode == 1 and v.count > 0 or - displayMode == 2 and v.has_recipe then - table.insert(r, v) - end - end - end - return r - end - return t -end - local listingPage = UI.Page { menuBar = UI.MenuBar { buttons = { { text = 'Learn', event = 'learn' }, - --{ text = 'Forget', event = 'forget' }, { text = 'Craft', event = 'craft' }, { text = 'Edit', event = 'details' }, { text = 'Refresh', event = 'refresh', x = -12 }, @@ -222,7 +198,7 @@ function listingPage:eventHandler(event) self:setFocus(self.statusBar.filter) elseif event.type == 'toggle_display' then - displayMode = (displayMode + 1) % 3 + displayMode = (displayMode + 1) % 2 Util.merge(event.button, displayModes[displayMode]) event.button:draw() self:applyFilter() @@ -308,6 +284,25 @@ function listingPage:refresh(force) end function listingPage:applyFilter() + local function filterItems(t, filter) + if filter or displayMode > 0 then + local r = { } + if filter then + filter = filter:lower() + end + for _,v in pairs(t) do + if not filter or string.find(v.lname, filter, 1, true) then + if displayMode == 0 or + displayMode == 1 and v.count > 0 then + table.insert(r, v) + end + end + end + return r + end + return t + end + local t = filterItems(self.allItems, self.filter) self.grid:setValues(t) end From 995f635807061db701577c7fd5522435c2c0c418 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Thu, 29 Nov 2018 16:18:09 -0500 Subject: [PATCH 138/165] farm chorus fruit --- farms/farmer.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/farms/farmer.lua b/farms/farmer.lua index fd888e6..5024ecd 100644 --- a/farms/farmer.lua +++ b/farms/farmer.lua @@ -119,11 +119,12 @@ end local function harvest(blocks) turtle.equip('right', 'minecraft:diamond_pickaxe') - turtle.select(1) local dropped Point.eachClosest(turtle.point, blocks, function(b) + turtle.select(1) + if b.action == 'bash' then turtle.digForwardAt(b) @@ -192,7 +193,6 @@ local function harvest(blocks) if turtle._goto({ x = b.x + hi.xd, z = b.z + hi.zd, heading = h }) then if turtle.dig() then turtle.place(crops[b.name].seed) - turtle.select(1) end end end From fcbdda953cec009dc8784000fe2dc638d070eca0 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Fri, 30 Nov 2018 00:34:45 -0500 Subject: [PATCH 139/165] farm nether wart --- farms/farmer.lua | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/farms/farmer.lua b/farms/farmer.lua index 5024ecd..3e4583a 100644 --- a/farms/farmer.lua +++ b/farms/farmer.lua @@ -25,6 +25,8 @@ local crops = Util.readTable(CONFIG_FILE) or { { seed = 'minecraft:potato', mature = 7, action = 'plant' }, ['minecraft:beetroots'] = { seed = 'minecraft:beetroot_seeds', mature = 3, action = 'plant' }, + ['minecraft:nether_wart'] = + { seed = 'minecraft:nether_wart', mature = 3, action = 'plant' }, ['minecraft:cocoa'] = { seed = 'minecraft:dye:3', mature = 8, action = 'pick' }, ['minecraft:reeds'] = { action = 'bash' }, @@ -145,21 +147,18 @@ local function harvest(blocks) dropped = true turtle.condense() - turtle.select(1) end elseif b.action == 'smash' then if turtle.digDownAt(b) then if crops[b.name].seed then turtle.placeDown(crops[b.name].seed) - turtle.select(1) end end elseif b.action == 'plant' then if turtle.digDownAt(b) then turtle.placeDown(crops[b.name].seed) - turtle.select(1) end elseif b.action == 'bump' then @@ -182,7 +181,6 @@ local function harvest(blocks) Point.eachClosest(turtle.point, sensed, function(s) turtle.suckDownAt(s) end) - turtle.select(1) end elseif b.action == 'pick' then From 05cbb72c1d154c1893d9c8c1d80e591130a29a75 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Fri, 30 Nov 2018 04:55:19 -0500 Subject: [PATCH 140/165] milo: improved sorting --- farms/farmer.lua | 2 +- milo/MiloRemote.lua | 72 ++++++++++++++++++++++++++++++------------- milo/core/listing.lua | 54 ++++++++++++++++++++++++-------- 3 files changed, 92 insertions(+), 36 deletions(-) diff --git a/farms/farmer.lua b/farms/farmer.lua index 3e4583a..09626d6 100644 --- a/farms/farmer.lua +++ b/farms/farmer.lua @@ -67,7 +67,7 @@ local function scan() local doDropOff for _,v in pairs(summed) do - if v.count > 48 then + if v.count > 32 then doDropOff = true break end diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index cdebc64..4df1c51 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -12,13 +12,14 @@ local colors = _G.colors local device = _G.device local fs = _G.fs local os = _G.os -local socket +local string = _G.string local SHIELD_SLOT = 2 local STARTUP_FILE = 'usr/autorun/miloRemote.lua' local config = Config.load('miloRemote', { displayMode = 0 }) +local socket local depositMode = { [ true ] = { text = '\25', textColor = colors.black, help = 'Deposit enabled' }, [ false ] = { text = '\215', textColor = colors.red, help = 'Deposit disabled' }, @@ -70,7 +71,8 @@ local page = UI.Page { { heading = 'Name', key = 'displayName' }, }, values = { }, - sortColumn = 'displayName', + sortColumn = config.sortColumn or 'count', + inverseSort = config.inverseSort, help = '^(s)tack, ^(a)ll' }, statusBar = UI.Window { @@ -170,26 +172,6 @@ local function getPlayerName() end end -local function filterItems(t, filter, displayMode) - if filter or displayMode > 0 then - local r = { } - if filter then - filter = filter:lower() - end - for _,v in pairs(t) do - if not filter or string.find(v.lname, filter, 1, true) then - if not displayMode or - displayMode == 0 or - displayMode == 1 and v.count > 0 then - table.insert(r, v) - end - end - end - return r - end - return t -end - function page:setStatus(status) self.menuBar.infoBar:setStatus(status) self:sync() @@ -271,6 +253,34 @@ function page.grid:getDisplayValues(row) return row end +function page.grid:sortCompare(a, b) + if self.sortColumn ~= 'displayName' then + if a[self.sortColumn] == b[self.sortColumn] then + if self.inverseSort then + return a.displayName > b.displayName + end + return a.displayName < b.displayName + end + if a[self.sortColumn] == 0 then + return self.inverseSort + end + if b[self.sortColumn] == 0 then + return not self.inverseSort + end + return a[self.sortColumn] < b[self.sortColumn] + end + return UI.Grid.sortCompare(self, a, b) +end + +function page.grid:eventHandler(event) + if event.type == 'grid_sort' then + config.sortColumn = event.sortColumn + config.inverseSort = event.inverseSort + Config.update('miloRemote', config) + end + return UI.Grid.eventHandler(self, event) +end + function page:transfer(item, count, msg) Sound.play('ui.button.click', .3) local response = self:sendRequest({ request = 'transfer', item = item, count = count }, msg) @@ -442,6 +452,24 @@ function page:refresh(requestType) end function page:applyFilter() + local function filterItems(t, filter, displayMode) + if filter or displayMode > 0 then + local r = { } + if filter then + filter = filter:lower() + end + for _,v in pairs(t) do + if not filter or string.find(v.lname, filter, 1, true) then + if filter or --displayMode == 0 or + displayMode == 1 and v.count > 0 then + table.insert(r, v) + end + end + end + return r + end + return t + end local t = filterItems(self.items, self.filter, config.displayMode) self.grid:setValues(t) end diff --git a/milo/core/listing.lua b/milo/core/listing.lua index 22141ed..0123e36 100644 --- a/milo/core/listing.lua +++ b/milo/core/listing.lua @@ -15,7 +15,7 @@ local displayModes = { [1] = { text = 'I', help = 'Showing inventory items' }, } -local listingPage = UI.Page { +local page = UI.Page { menuBar = UI.MenuBar { buttons = { { text = 'Learn', event = 'learn' }, @@ -45,7 +45,8 @@ local listingPage = UI.Page { { heading = 'Min', key = 'low' , width = 4 }, { heading = 'Max', key = 'limit' , width = 4 }, }, - sortColumn = 'displayName', + sortColumn = Milo:getState('sortColumn') or 'count', + inverseSort = Milo:getState('inverseSort'), }, statusBar = UI.StatusBar { filter = UI.TextEntry { @@ -105,11 +106,11 @@ local listingPage = UI.Page { allItems = { } } -function listingPage.statusBar:draw() +function page.statusBar:draw() return UI.Window.draw(self) end -function listingPage.grid:getRowTextColor(row, selected) +function page.grid:getRowTextColor(row, selected) if row.is_craftable then return colors.yellow end @@ -119,7 +120,7 @@ function listingPage.grid:getRowTextColor(row, selected) return UI.Grid:getRowTextColor(row, selected) end -function listingPage.grid:getDisplayValues(row) +function page.grid:getDisplayValues(row) row = Util.shallowCopy(row) row.count = row.count > 0 and Util.toBytes(row.count) if row.low then @@ -131,7 +132,34 @@ function listingPage.grid:getDisplayValues(row) return row end -function listingPage:eject(amount) +function page.grid:sortCompare(a, b) + if self.sortColumn ~= 'displayName' then + if a[self.sortColumn] == b[self.sortColumn] then + if self.inverseSort then + return a.displayName > b.displayName + end + return a.displayName < b.displayName + end + if a[self.sortColumn] == 0 then + return self.inverseSort + end + if b[self.sortColumn] == 0 then + return not self.inverseSort + end + return a[self.sortColumn] < b[self.sortColumn] + end + return UI.Grid.sortCompare(self, a, b) +end + +function page.grid:eventHandler(event) + if event.type == 'grid_sort' then + Milo:setState('sortColumn', event.sortColumn) + Milo:setState('inverseSort', event.inverseSort) + end + return UI.Grid.eventHandler(self, event) +end + +function page:eject(amount) local item = self.grid:getSelected() if item and amount then -- get most up-to-date item @@ -159,7 +187,7 @@ function listingPage:eject(amount) Sound.play('entity.villager.no') end -function listingPage:eventHandler(event) +function page:eventHandler(event) if event.type == 'quit' then UI:exitPullEvents() @@ -234,7 +262,7 @@ function listingPage:eventHandler(event) return true end -function listingPage:enable() +function page:enable() local function updateStatus() self.statusBar.storageStatus.value = context.storage:isOnline() and '' or 'offline' @@ -268,13 +296,13 @@ function listingPage:enable() UI.Page.enable(self) end -function listingPage:disable() +function page:disable() Event.off(self.timer) Event.off(self.handler) UI.Page.disable(self) end -function listingPage:refresh(force) +function page:refresh(force) local throttle = function() self.throttle:update() end self.throttle:enable() @@ -283,7 +311,7 @@ function listingPage:refresh(force) self.throttle:disable() end -function listingPage:applyFilter() +function page:applyFilter() local function filterItems(t, filter) if filter or displayMode > 0 then local r = { } @@ -292,7 +320,7 @@ function listingPage:applyFilter() end for _,v in pairs(t) do if not filter or string.find(v.lname, filter, 1, true) then - if displayMode == 0 or + if filter or --displayMode == 0 or displayMode == 1 and v.count > 0 then table.insert(r, v) end @@ -307,4 +335,4 @@ function listingPage:applyFilter() self.grid:setValues(t) end -UI:addPage('listing', listingPage) +UI:addPage('listing', page) From e9d9025ea7c07a0af7d83e810c0621c006c9329f Mon Sep 17 00:00:00 2001 From: kepler155c Date: Fri, 30 Nov 2018 11:53:18 -0500 Subject: [PATCH 141/165] remove rancher dispenser requirement --- farms/rancher.lua | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/farms/rancher.lua b/farms/rancher.lua index 5a862d8..92fe02e 100644 --- a/farms/rancher.lua +++ b/farms/rancher.lua @@ -25,10 +25,8 @@ local sensor = device['plethora:sensor'] or turtle.equip('right', 'plethora:module:3') and device['plethora:sensor'] or error('Plethora sensor required') -local dispenser = Peripheral.lookup('type/minecraft:dispenser') or - error('Dispenser not found') -local integrator = Peripheral.lookup('type/redstone_integrator') or - error('Integrator not found') +local dispenser = Peripheral.lookup('type/minecraft:dispenser') +local integrator = Peripheral.lookup('type/redstone_integrator') local function pulse() integrator.setOutput('north', true) @@ -37,16 +35,20 @@ local function pulse() end local function turnOffWater() - local list = dispenser.list() - if list[1].name == 'minecraft:bucket' then - pulse() - os.sleep(2) + if dispenser then + local list = dispenser.list() + if list[1].name == 'minecraft:bucket' then + pulse() + os.sleep(2) + end end end local function turnOnWater() - if dispenser.list()[1].name == 'minecraft:water_bucket' then - pulse() + if dispenser then + if dispenser.list()[1].name == 'minecraft:water_bucket' then + pulse() + end end end From 53c11db773e55515755c45b36ff38a66b9409806 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Fri, 30 Nov 2018 13:15:40 -0500 Subject: [PATCH 142/165] farms - improvements --- farms/etc/apps/apps.db | 16 +++++++-- farms/rancher.lua | 77 ++++++++++++++++++++++++++++++++++-------- farms/treefarm.lua | 9 +++++ 3 files changed, 85 insertions(+), 17 deletions(-) diff --git a/farms/etc/apps/apps.db b/farms/etc/apps/apps.db index c9f8d4b..38806c4 100644 --- a/farms/etc/apps/apps.db +++ b/farms/etc/apps/apps.db @@ -4,8 +4,20 @@ icon = "\030 \031f \0305 \030 \030d \030 \ \0305\031f \030d \030 \030d \0305 \030d \ \030 \031f \030c \030 \0304 \030 \030c \030 ", - category = "Apps", + category = "Farms", run = "treefarm.lua", requires = "turtle", }, -} \ No newline at end of file + [ "ff892e4e9538168021ccf2c7add8a46bf97fb180" ] = { + title = "Rancher", + category = "Farms", + run = "rancher.lua", + requires = "turtle", + }, + [ "e5c4e470299e30c9eea1e25de228c14db8c1fd40" ] = { + title = "Farmer", + category = "Farms", + run = "farmer.lua", + requires = "turtle", + }, +} diff --git a/farms/rancher.lua b/farms/rancher.lua index 92fe02e..cae6c27 100644 --- a/farms/rancher.lua +++ b/farms/rancher.lua @@ -1,17 +1,20 @@ _G.requireInjector(_ENV) -local Config = require('config') -local Util = require('util') -local InventoryAdapter = require('chestAdapter18') +local Config = require('config') +local Util = require('util') +local Adapter = require('chestAdapter18') local Peripheral = require('peripheral') local device = _G.device +local fs = _G.fs local os = _G.os local turtle = _G.turtle +local STARTUP_FILE = 'usr/autorun/rancher.lua' + local retain = Util.transpose { 'minecraft:shears', - 'minecraft_wheat', + 'minecraft:wheat', 'minecraft:diamond_sword', 'plethora:module:3', } @@ -21,9 +24,56 @@ local config = { } Config.load('rancher', config) -local sensor = device['plethora:sensor'] or - turtle.equip('right', 'plethora:module:3') and device['plethora:sensor'] or - error('Plethora sensor required') +local ANIMALS = { + Pig = { min = 0, food = 'minecraft:carrot' }, + Sheep = { min = .5, food = 'minecraft:wheat' }, + Cow = { min = .5, food = 'minecraft:wheat' }, +} + +local animal = ANIMALS[config.animal] + +local function equip(side, item, rawName) + local equipped = Peripheral.lookup('side/' .. side) + + if equipped and equipped.type == item then + return true + end + + if not turtle.equip(side, rawName or item) then + if not turtle.selectSlotWithQuantity(0) then + error('No slots available') + end + turtle.equip(side) + if not turtle.equip(side, item) then + error('Unable to equip ' .. item) + end + end + + turtle.select(1) +end + +local function getLocalName() + if not device.wired_modem then + error('wired modem or chest not found') + end + return device.wired_modem.getNameLocal() +end + +equip('left', 'minecraft:diamond_sword') +equip('right', 'plethora:sensor', 'plethora:module:3') + +local sensor = device['plethora:sensor'] +local c = Peripheral.lookup('type/minecraft:chest') or error('Missing chest') + +local directions = { top = 'down', bottom = 'up' } +local direction = directions[c.side] or getLocalName() +local chest = Adapter({ side = c.side, direction = direction }) or error('missing chest') +_G._p = chest +if not fs.exists(STARTUP_FILE) then + Util.writeFile(STARTUP_FILE, + [[os.sleep(1) +shell.openForegroundTab('packages/farms/rancher.lua')]]) +end local dispenser = Peripheral.lookup('type/minecraft:dispenser') local integrator = Peripheral.lookup('type/redstone_integrator') @@ -87,8 +137,7 @@ local function butcher() turtle.eachFilledSlot(function(slot) if not retain[slot.name] then - turtle.select(slot.index) - turtle.dropUp() + chest:insert(slot.index, 64) end end) end @@ -109,9 +158,6 @@ local function breed() end end -local chest = InventoryAdapter({ side = 'top', direction = 'down' }) or - error('missing chest above') - local s, m = turtle.run(function() turnOffWater() @@ -120,9 +166,10 @@ local s, m = turtle.run(function() if animalCount > config.max_animals then turtle.setStatus('Butchering') butcher() - elseif turtle.getItemCount('minecraft:wheat') == 0 then - if chest:provide({ name = 'minecraft:wheat' }, 64) == 0 then - turtle.setStatus('Out of wheat') + elseif turtle.getItemCount(animal.food) == 0 then + if chest:provide({ name = animal.food, damage = 0 }, 64) == 0 then + print('Out of ' .. animal.food) + turtle.setStatus('Out of food') end else turtle.setStatus('Breeding') diff --git a/farms/treefarm.lua b/farms/treefarm.lua index 55e61ec..ecc2912 100644 --- a/farms/treefarm.lua +++ b/farms/treefarm.lua @@ -23,10 +23,13 @@ _G.requireInjector() local Point = require('point') local Util = require('util') +local fs = _G.fs local os = _G.os local read = _G.read local turtle = _G.turtle +local STARTUP_FILE = 'usr/autorun/treefarm.lua' + local FUEL_BASE = 0 local FUEL_DIRE = FUEL_BASE + 10 local FUEL_GOOD = FUEL_BASE + 2000 @@ -75,6 +78,12 @@ local state = Util.readTable('usr/config/treefarm') or { } } +if not fs.exists(STARTUP_FILE) then + Util.writeFile(STARTUP_FILE, + [[os.sleep(1) +shell.openForegroundTab('packages/farms/treefarm.lua')]]) +end + local clock = os.clock() local function inspect(fn) From c5d209161efe7585a5d327cb1aa200e0dc76e42f Mon Sep 17 00:00:00 2001 From: kepler155c Date: Fri, 30 Nov 2018 19:26:56 -0500 Subject: [PATCH 143/165] autoruns --- farms/farmer.lua | 3 ++- farms/rancher.lua | 5 +++-- farms/treefarm.lua | 3 ++- milo/apps/furni.lua | 7 ++++--- miners/scanningMiner.lua | 8 ++++++++ 5 files changed, 19 insertions(+), 7 deletions(-) diff --git a/farms/farmer.lua b/farms/farmer.lua index 09626d6..1a94f74 100644 --- a/farms/farmer.lua +++ b/farms/farmer.lua @@ -46,7 +46,8 @@ end if not fs.exists(STARTUP_FILE) then Util.writeFile(STARTUP_FILE, [[os.sleep(1) -shell.openForegroundTab('packages/farms/farmer.lua')]]) +shell.openForegroundTab('farmer.lua')]]) + print('Autorun program created: ' .. STARTUP_FILE) end local retain = Util.transpose { diff --git a/farms/rancher.lua b/farms/rancher.lua index cae6c27..3ff984b 100644 --- a/farms/rancher.lua +++ b/farms/rancher.lua @@ -68,11 +68,12 @@ local c = Peripheral.lookup('type/minecraft:chest') or error('Missing chest') local directions = { top = 'down', bottom = 'up' } local direction = directions[c.side] or getLocalName() local chest = Adapter({ side = c.side, direction = direction }) or error('missing chest') -_G._p = chest + if not fs.exists(STARTUP_FILE) then Util.writeFile(STARTUP_FILE, [[os.sleep(1) -shell.openForegroundTab('packages/farms/rancher.lua')]]) +shell.openForegroundTab('rancher.lua')]]) + print('Autorun program created: ' .. STARTUP_FILE) end local dispenser = Peripheral.lookup('type/minecraft:dispenser') diff --git a/farms/treefarm.lua b/farms/treefarm.lua index ecc2912..9890bbb 100644 --- a/farms/treefarm.lua +++ b/farms/treefarm.lua @@ -81,7 +81,8 @@ local state = Util.readTable('usr/config/treefarm') or { if not fs.exists(STARTUP_FILE) then Util.writeFile(STARTUP_FILE, [[os.sleep(1) -shell.openForegroundTab('packages/farms/treefarm.lua')]]) +shell.openForegroundTab('treefarm.lua')]]) + print('Autorun program created: ' .. STARTUP_FILE) end local clock = os.clock() diff --git a/milo/apps/furni.lua b/milo/apps/furni.lua index e80c85c..390342e 100644 --- a/milo/apps/furni.lua +++ b/milo/apps/furni.lua @@ -1,10 +1,11 @@ --[[ -Use 4 furnaces at once to smelt items. +Use multiple furnaces at once to smelt items. SETUP: Place an introspection module into the turtles inventory. - Connect with wired modem at bottom of turtle. - Place furnaces on each side EXCEPT for bottom and left. + Connect turtle to milo with a wired modem. + Connect turtle to a second wired modem that is connected to furnaces ONLY. + Add as many furnaces as needed. CONFIGURATION: Set turtle as a "Generic Inventory" diff --git a/miners/scanningMiner.lua b/miners/scanningMiner.lua index 1c3cbe4..fcffcb1 100644 --- a/miners/scanningMiner.lua +++ b/miners/scanningMiner.lua @@ -42,6 +42,7 @@ local MAX_FUEL = turtle.getFuelLimit() local DICTIONARY_FILE = 'usr/config/mining.dictionary' local PROGRESS_FILE = 'usr/config/scanning_mining.progress' +local STARTUP_FILE = 'usr/autorun/scanningMiner.lua' local mining local ignores = { @@ -541,6 +542,13 @@ if not fs.exists(DICTIONARY_FILE) or options.setTrash.value then addTrash() end +if not fs.exists(STARTUP_FILE) then + Util.writeFile(STARTUP_FILE, + [[os.sleep(1) +shell.openForegroundTab('scanningMiner.lua')]]) + print('Autorun program created: ' .. STARTUP_FILE) +end + Event.addRoutine(function() turtle.reset() From e1084422e526796198d72812405f710d9c72e200 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 1 Dec 2018 00:52:39 -0500 Subject: [PATCH 144/165] milo: categories, better listing page separation --- milo/Milo.lua | 47 +++++++++++++++++++--------- milo/apis/storage.lua | 5 ++- milo/core/listing.lua | 11 ++++++- milo/core/machines.lua | 50 +++++++++++++++++++----------- milo/plugins/activityView.lua | 1 + milo/plugins/brewingStandView.lua | 1 + milo/plugins/exportView.lua | 1 + milo/plugins/importView.lua | 1 + milo/plugins/inputChestView.lua | 1 + milo/plugins/item.lua | 51 ++++++++++++++++--------------- milo/plugins/jobMonitor.lua | 1 + milo/plugins/learn.lua | 27 ++++++++-------- milo/plugins/machineLearn.lua | 27 ++++++++-------- milo/plugins/manipulatorView.lua | 1 + milo/plugins/storageView.lua | 2 ++ milo/plugins/trashcanView.lua | 1 + milo/plugins/turtleLearn.lua | 14 ++++----- 17 files changed, 146 insertions(+), 96 deletions(-) diff --git a/milo/Milo.lua b/milo/Milo.lua index ac734fd..5d2a05a 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -33,24 +33,43 @@ if multishell then multishell.setTitle(multishell.getCurrent(), 'Milo') end -local config = { - nodes = { }, -} -Config.load('milo', config) +local nodes = Config.load('milo', { }) -- TODO: remove - temporary -if config.remoteDefaults then - config.nodes = config.remoteDefaults - config.remoteDefaults = nil +if nodes.remoteDefaults then + nodes.nodes = nodes.remoteDefaults + nodes.remoteDefaults = nil end -- TODO: remove - temporary -for _, node in pairs(config.nodes) do - if node.lock and type(node.lock) == 'string' then - node.lock = { - [ node.lock ] = true, - } +if nodes.nodes then + local categories = { + input = 'custom', + trash = 'custom', + machine = 'machine', + brewingStand = 'custom', + activity = 'display', + jobs = 'display', + ignore = 'ignore', + hidden = 'ignore', + manipulator = 'custom', + storage = 'storage', + } + for _, node in pairs(nodes.nodes) do + if node.lock and type(node.lock) == 'string' then + node.lock = { + [ node.lock ] = true, + } + end + if not node.category then + node.category = categories[node.mtype] + if not node.category then + Util.print(node) + error('invalid node') + end + end end + nodes = nodes.nodes end local function Syntax(msg) @@ -87,7 +106,7 @@ if not device.workbench then end local context = { - config = config, + nodes = nodes, resources = Util.readTable(Milo.RESOURCE_FILE) or { }, state = { }, @@ -97,7 +116,7 @@ local context = { queue = { }, localName = modem.getNameLocal(), - storage = Storage(config), + storage = Storage(nodes), turtleInventory = introspection.getInventory(), } diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 50003f0..962e3a7 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -10,16 +10,15 @@ local os = _G.os local Storage = class() -function Storage:init(args) +function Storage:init(nodes) local defaults = { - nodes = { }, + nodes = nodes or { }, dirty = true, activity = { }, storageOnline = true, lastRefresh = os.clock(), } Util.merge(self, defaults) - Util.merge(self, args) local modem = Peripheral.get('wired_modem') or error('Wired modem not attached') self.localName = modem.getNameLocal() diff --git a/milo/core/listing.lua b/milo/core/listing.lua index 0123e36..3e22299 100644 --- a/milo/core/listing.lua +++ b/milo/core/listing.lua @@ -262,7 +262,7 @@ function page:eventHandler(event) return true end -function page:enable() +function page:enable(args) local function updateStatus() self.statusBar.storageStatus.value = context.storage:isOnline() and '' or 'offline' @@ -292,6 +292,15 @@ function page:enable() end) end) + if args and args.filter then + self.filter = args.filter + self.statusBar.filter.value = args.filter + end + + if args and args.message then + self.notification:success(args.message) + end + self:setFocus(self.statusBar.filter) UI.Page.enable(self) end diff --git a/milo/core/machines.lua b/milo/core/machines.lua index 68ce4f7..9856e98 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -16,15 +16,15 @@ local nodeWizard local function saveConfig() local t = { } - for k,v in pairs(context.config.nodes) do + for k,v in pairs(context.nodes) do t[k] = v.adapter v.adapter = nil end - Config.update('milo', context.config) + Config.update('milo', context.nodes) for k,v in pairs(t) do - context.config.nodes[k].adapter = v + context.nodes[k].adapter = v end context.storage:initStorage() end @@ -43,12 +43,13 @@ local networkPage = UI.Page { }, grid = UI.ScrollingGrid { y = 2, ey = -3, - values = context.config.nodes, + values = context.nodes, columns = { { key = 'suffix', width = 4, justify = 'right' }, { heading = 'Name', key = 'displayName' }, { heading = 'Type', key = 'mtype', width = 4 }, - { heading = 'Pri', key = 'priority', width = 3 }, + { heading = 'Cat', key = 'category', width = 3 }, + --{ heading = 'Pri', key = 'priority', width = 3 }, }, sortColumn = 'displayName', help = 'Select Node', @@ -96,16 +97,26 @@ function networkPage.grid:getRowTextColor(row, selected) return UI.Grid:getRowTextColor(row, selected) end +function networkPage.grid:sortCompare(a, b) + if self.sortColumn == 'displayName' then + local an = a.displayName or a.name + local bn = b.displayName or b.name + return an:lower() < bn:lower() + end + return UI.Grid.sortCompare(self, a, b) +end + function networkPage:getList() for _, v in pairs(device) do - if not context.config.nodes[v.name] then + if not context.nodes[v.name] then local node = { name = v.name, mtype = 'ignore', + category = 'ignore', } for _, page in pairs(nodeWizard.wizard.pages) do if page.isValidType and page:isValidType(node) then - context.config.nodes[v.name] = node + context.nodes[v.name] = node break end end @@ -146,7 +157,7 @@ function networkPage:disable() end function networkPage:applyFilter() - local t = Util.filter(context.config.nodes, function(v) + local t = Util.filter(context.nodes, function(v) return v.mtype ~= 'hidden' end) @@ -173,7 +184,7 @@ function networkPage:eventHandler(event) elseif event.type == 'remove_node' then local node = self.grid:getSelected() if node then - context.config.nodes[node.name] = nil + context.nodes[node.name] = nil saveConfig() end self:applyFilter() @@ -412,6 +423,9 @@ end function nodeWizard.wizard.pages.general:validate() if self.form:save() then + _G._p3 = nodeWizard.choices + _G._p4 = nodeWizard.node + nodeWizard.node.category = Util.find(nodeWizard.choices, 'value', nodeWizard.node.mtype).category for _, page in pairs(nodeWizard.wizard.pages) do page.index = nil end @@ -442,20 +456,20 @@ function nodeWizard:enable(node) self.node.adapter = adapter node.adapter = adapter - local choices = { - { name = 'Ignore', value = 'ignore', '' }, - { name = 'Hidden', value = 'hidden', help = 'Do not show in list' }, + self.choices = { + { name = 'Ignore', value = 'ignore', category = 'ignore' }, + { name = 'Hidden', value = 'hidden', category = 'ignore', help = 'Do not show in list' }, } for _, page in pairs(self.wizard.pages) do if page.isValidType then local choice = page:isValidType(self.node) - if choice and not Util.find(choices, 'value', choice.value) then - table.insert(choices, 2, choice) + if choice and not Util.find(self.choices, 'value', choice.value) then + table.insert(self.choices, 2, choice) end end end self.wizard.pages.general.form[1].shadowText = self.node.name - self.wizard.pages.general.form[2].choices = choices + self.wizard.pages.general.form[2].choices = self.choices self.wizard.pages.general.form:setValues(self.node) self.wizard.pages.general:showInventory(self.node) @@ -491,9 +505,9 @@ function nodeWizard:eventHandler(event) return true end) - Util.clear(context.config.nodes[self.node.name]) - Util.merge(context.config.nodes[self.node.name], self.node) - context.config.nodes[self.node.name].adapter = adapter + Util.clear(context.nodes[self.node.name]) + Util.merge(context.nodes[self.node.name], self.node) + context.nodes[self.node.name].adapter = adapter saveConfig() diff --git a/milo/plugins/activityView.lua b/milo/plugins/activityView.lua index f5937a6..50e6341 100644 --- a/milo/plugins/activityView.lua +++ b/milo/plugins/activityView.lua @@ -31,6 +31,7 @@ function activityWizardPage:isValidType(node) return m and m.type == 'monitor' and { name = 'Activity Monitor', value = 'activity', + category = 'display', help = 'Display storage activity' } end diff --git a/milo/plugins/brewingStandView.lua b/milo/plugins/brewingStandView.lua index d67a74e..ec7aba4 100644 --- a/milo/plugins/brewingStandView.lua +++ b/milo/plugins/brewingStandView.lua @@ -30,6 +30,7 @@ function brewingStandView:isValidType(node) return m and m.type == 'minecraft:brewing_stand'and { name = 'Brewing Stand', value = 'brewingStand', + category = 'custom', help = 'Auto-learning brewing stand', } end diff --git a/milo/plugins/exportView.lua b/milo/plugins/exportView.lua index 80821a0..192ab4d 100644 --- a/milo/plugins/exportView.lua +++ b/milo/plugins/exportView.lua @@ -46,6 +46,7 @@ function exportView:isValidType(node) return m and m.pullItems and { name = 'Generic Inventory', value = 'machine', + category = 'machine', help = 'Chest, furnace... (has an inventory)' } end diff --git a/milo/plugins/importView.lua b/milo/plugins/importView.lua index b27e8e4..80131dd 100644 --- a/milo/plugins/importView.lua +++ b/milo/plugins/importView.lua @@ -46,6 +46,7 @@ function importView:isValidType(node) return m and m.pullItems and { name = 'Generic Inventory', value = 'machine', + category = 'machine', help = 'Chest, furnace... (has an inventory)', } end diff --git a/milo/plugins/inputChestView.lua b/milo/plugins/inputChestView.lua index 59d48fe..8a7d752 100644 --- a/milo/plugins/inputChestView.lua +++ b/milo/plugins/inputChestView.lua @@ -26,6 +26,7 @@ function inputChestWizardPage:isValidType(node) return m and m.pullItems and { name = 'Input Chest', value = 'input', + category = 'custom', help = 'Sends all items to storage', } end diff --git a/milo/plugins/item.lua b/milo/plugins/item.lua index 09850a5..e494f3a 100644 --- a/milo/plugins/item.lua +++ b/milo/plugins/item.lua @@ -10,7 +10,7 @@ local device = _G.device local context = Milo:getContext() -local itemPage = UI.Page { +local page = UI.Page { titleBar = UI.TitleBar { title = 'Limit Resource', previousPage = true, @@ -46,7 +46,7 @@ local itemPage = UI.Page { x = 2, y = -2, width = 10, formLabel = 'Machine', event = 'select_machine', - text = 'Configure', + text = 'Assign', }, infoButton = UI.Button { x = 2, y = -2, @@ -108,7 +108,6 @@ local itemPage = UI.Page { grid = UI.ScrollingGrid { y = 2, ey = -5, disableHeader = true, - values = context.config.nodes, columns = { { heading = 'Name', key = 'displayName'}, }, @@ -142,42 +141,48 @@ local itemPage = UI.Page { notification = UI.Notification { }, } -function itemPage:enable(item) +function page:enable(item) self.origItem = item self.item = Util.shallowCopy(item) self.res = item.resource or { } self.res.displayName = self.item.displayName self.form:setValues(self.res) - self.titleBar.title = item.displayName or item.name - self.form.machineButton.inactive = not Craft.machineLookup[self.item.key] + + local machine = Craft.machineLookup[self.item.key] + self.form.machineButton.inactive = not machine + if machine then + self:filterMachines(machine) + end UI.Page.enable(self) self:focusFirst() end -function itemPage.machines.grid:isRowValid(_, value) - local ignores = Util.transpose({ 'storage', 'ignore', 'hidden' }) - if not ignores[value.mtype] then - local node = context.storage.nodes[value.name] - return node and node.adapter and node.adapter.online and node.adapter.pushItems - end +function page:filterMachines(machine) + local t = Util.filter(context.storage.nodes, function(node) + if node.category == 'machine' then + return node.adapter and node.adapter.online and node.adapter.pushItems + end + end) + self.machines.grid:setValues(t) + self.machines.grid:setSelected('name', machine) end -function itemPage.machines.grid:getDisplayValues(row) +function page.machines.grid:getDisplayValues(row) row = Util.shallowCopy(row) row.displayName = row.displayName or row.name return row end - function itemPage.machines.grid:getRowTextColor(row, selected) - if row.name == Craft.machineLookup[itemPage.item.key] then - return colors.yellow - end - return UI.Grid:getRowTextColor(row, selected) +function page.machines.grid:getRowTextColor(row, selected) + if row.name == Craft.machineLookup[page.item.key] then + return colors.yellow end + return UI.Grid:getRowTextColor(row, selected) +end -function itemPage.rsControl:enable() +function page.rsControl:enable() local devices = self.form[2].choices Util.clear(devices) for _,dev in pairs(device) do @@ -193,7 +198,7 @@ function itemPage.rsControl:enable() UI.SlideOut.enable(self) end -function itemPage.rsControl:eventHandler(event) +function page.rsControl:eventHandler(event) if event.type == 'form_cancel' then self:hide() elseif event.type == 'form_complete' then @@ -204,7 +209,7 @@ function itemPage.rsControl:eventHandler(event) return true end -function itemPage:eventHandler(event) +function page:eventHandler(event) if event.type == 'form_cancel' then UI:setPreviousPage() @@ -212,8 +217,6 @@ function itemPage:eventHandler(event) self.rsControl:show() elseif event.type == 'select_machine' then - self.machines.grid:update() - self.machines.grid:setIndex(1) self.machines:show() elseif event.type == 'reset' then @@ -334,4 +337,4 @@ function itemPage:eventHandler(event) return true end -UI:addPage('item', itemPage) +UI:addPage('item', page) diff --git a/milo/plugins/jobMonitor.lua b/milo/plugins/jobMonitor.lua index e555754..378102a 100644 --- a/milo/plugins/jobMonitor.lua +++ b/milo/plugins/jobMonitor.lua @@ -27,6 +27,7 @@ function wizardPage:isValidType(node) return m and m.type == 'monitor' and { name = 'Crafting Monitor', value = 'jobs', + category = 'display', help = 'Display crafting progress / jobs' } end diff --git a/milo/plugins/learn.lua b/milo/plugins/learn.lua index 7172c17..0c9460e 100644 --- a/milo/plugins/learn.lua +++ b/milo/plugins/learn.lua @@ -6,11 +6,15 @@ local context = Milo:getContext() local turtle = _G.turtle local learnPage = UI.Dialog { - height = 6, width = UI.term.width - 6, + height = 9, width = UI.term.width - 6, title = 'Learn Recipe', - chooser = UI.Chooser { - x = 8, y = 3, - width = 20, + grid = UI.ScrollingGrid { + x = 2, ex = -2, y = 3, height = 4, + disableHeader = true, + columns = { + { heading = 'Name', key = 'name'}, + }, + sortColumn = 'name', }, cancel = UI.Button { x = 3, y = -2, @@ -24,17 +28,15 @@ local learnPage = UI.Dialog { } function learnPage:enable() - self.chooser.choices = { } - + local t = { } for k in pairs(context.learnTypes) do - table.insert(self.chooser.choices, { + table.insert(t, { name = k, value = k, }) end - self.chooser.value = - Milo:getState('learnType') or - self.chooser.choices[1].value + self.grid:setValues(t) + self.grid:setSelected('name', Milo:getState('learnType') or '') Milo:pauseCrafting({ key = 'gridInUse', msg = 'Crafting paused' }) sync.lock(turtle) @@ -53,12 +55,11 @@ function learnPage:eventHandler(event) Milo:resumeCrafting({ key = 'gridInUse' }) UI:setPreviousPage() - elseif event.type == 'accept' then - local choice = self.chooser.value + elseif event.type == 'accept' or event.type == 'grid_select' then + local choice = self.grid:getSelected().value Milo:setState('learnType', choice) UI:setPage(context.learnTypes[choice]) - else return UI.Dialog.eventHandler(self, event) end diff --git a/milo/plugins/machineLearn.lua b/milo/plugins/machineLearn.lua index 4f1afc6..b729ce5 100644 --- a/milo/plugins/machineLearn.lua +++ b/milo/plugins/machineLearn.lua @@ -11,7 +11,7 @@ local turtle = _G.turtle local context = Milo:getContext() local machineLearnWizard = UI.Page { - titleBar = UI.TitleBar { title = 'Learn a crafting recipe' }, + titleBar = UI.TitleBar { title = 'Select machine' }, wizard = UI.Wizard { y = 2, ey = -2, pages = { @@ -19,7 +19,6 @@ local machineLearnWizard = UI.Page { index = 1, grid = UI.ScrollingGrid { y = 2, ey = -2, - values = context.config.nodes, columns = { { heading = 'Name', key = 'displayName' }, }, @@ -47,10 +46,6 @@ Example: Slot 1 is the top slot in a furnace.]], local pages = machineLearnWizard.wizard.pages local machine -function pages.machines.grid:isRowValid(_, value) - return value.mtype == 'machine' and value.adapter and value.adapter.online -end - function pages.machines.grid:getDisplayValues(row) row = Util.shallowCopy(row) row.displayName = row.displayName or row.name @@ -58,7 +53,12 @@ function pages.machines.grid:getDisplayValues(row) end function pages.machines:enable() - self.grid:update() + local t = Util.filter(context.storage.nodes, function(node) + if node.category == 'machine' then + return node.adapter and node.adapter.online and node.adapter.pushItems + end + end) + self.grid:setValues(t) UI.Window.enable(self) end @@ -122,15 +122,12 @@ function pages.confirmation:validate() Milo:saveMachineRecipe(recipe, result, machine.name) - local listingPage = UI:getPage('listing') local displayName = itemDB:getName(result) - listingPage.statusBar.filter:setValue(displayName) - listingPage.notification:success('Learned: ' .. displayName) - listingPage.filter = displayName - listingPage:refresh() - listingPage.grid:draw() - + UI:setPage('listing', { + filter = displayName, + message = 'Learned: ' .. displayName, + }) return true end @@ -141,7 +138,7 @@ function machineLearnWizard:disable() end function machineLearnWizard:eventHandler(event) - if event.type == 'cancel' or event.type == 'accept' then + if event.type == 'cancel' then turtle.emptyInventory() UI:setPage('listing') else diff --git a/milo/plugins/manipulatorView.lua b/milo/plugins/manipulatorView.lua index 84d9107..87b2335 100644 --- a/milo/plugins/manipulatorView.lua +++ b/milo/plugins/manipulatorView.lua @@ -38,6 +38,7 @@ function wizardPage:isValidType(node) { name = 'Manipulator', value = 'manipulator', + category = 'custom', help = 'Manipulator w/bound introspection mod' } end diff --git a/milo/plugins/storageView.lua b/milo/plugins/storageView.lua index acacc0f..bbb7c9a 100644 --- a/milo/plugins/storageView.lua +++ b/milo/plugins/storageView.lua @@ -48,6 +48,7 @@ function storageView:isValidType(node) return m and m.pullItems and { name = 'Storage', value = 'storage', + category = 'storage', help = 'Use for item storage', } end @@ -117,6 +118,7 @@ function lockView:isValidType(node) return m and m.pullItems and { name = 'Storage', value = 'storage', + category = 'storage', help = 'Use for item storage', } end diff --git a/milo/plugins/trashcanView.lua b/milo/plugins/trashcanView.lua index e305251..a5df858 100644 --- a/milo/plugins/trashcanView.lua +++ b/milo/plugins/trashcanView.lua @@ -58,6 +58,7 @@ function wizardPage:isValidType(node) return m and m.pullItems and { name = 'Trashcan', value = 'trashcan', + category = 'custom', help = 'An inventory to send unwanted items', } end diff --git a/milo/plugins/turtleLearn.lua b/milo/plugins/turtleLearn.lua index 23d7743..10948c7 100644 --- a/milo/plugins/turtleLearn.lua +++ b/milo/plugins/turtleLearn.lua @@ -133,15 +133,12 @@ function turtleLearnWizard.wizard.pages.confirmation:validate() local recipe, msg = learnRecipe(self) if recipe then - local listingPage = UI:getPage('listing') local displayName = itemDB:getName(recipe) - listingPage.statusBar.filter:setValue(displayName) - listingPage.notification:success('Learned: ' .. displayName) - listingPage.filter = displayName - listingPage:refresh() - listingPage.grid:draw() - + UI:setPage('listing', { + filter = displayName, + message = 'Learned: ' .. displayName, + }) return true else turtleLearnWizard.notification:error(msg) @@ -149,7 +146,8 @@ function turtleLearnWizard.wizard.pages.confirmation:validate() end function turtleLearnWizard:eventHandler(event) - if event.type == 'cancel' or event.type == 'accept' then + if event.type == 'cancel' then + turtle.emptyInventory() UI:setPage('listing') else return UI.Page.eventHandler(self, event) From 0853a16b5960e4483998a6add2ecdc355d939188 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 1 Dec 2018 01:44:47 -0500 Subject: [PATCH 145/165] milo: categories --- milo/Milo.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/milo/Milo.lua b/milo/Milo.lua index 5d2a05a..945a0d7 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -45,7 +45,7 @@ end if nodes.nodes then local categories = { input = 'custom', - trash = 'custom', + trashcan = 'custom', machine = 'machine', brewingStand = 'custom', activity = 'display', From 4a844eebfe84c052e464e5513d16b346f1c80527 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 1 Dec 2018 19:08:57 -0500 Subject: [PATCH 146/165] milo: smart transfer --- milo/Milo.lua | 24 +++++- milo/apis/craft2.lua | 23 +++--- milo/apis/milo.lua | 6 +- milo/apis/storage.lua | 125 +++++++++++++++++++++++------- milo/core/machines.lua | 7 +- milo/plugins/brewingStandView.lua | 2 +- milo/plugins/craftTask.lua | 5 +- milo/plugins/exportTask.lua | 6 +- milo/plugins/exportView.lua | 2 +- milo/plugins/importTask.lua | 2 +- milo/plugins/importView.lua | 2 +- milo/plugins/inputChestTask.lua | 6 +- milo/plugins/learn.lua | 6 +- milo/plugins/limitTask.lua | 2 +- milo/plugins/machineLearn.lua | 2 - milo/plugins/manipulatorView.lua | 14 +--- milo/plugins/potionImportTask.lua | 4 +- milo/plugins/remote.lua | 58 +++++++------- milo/plugins/turtleLearn.lua | 2 - 19 files changed, 177 insertions(+), 121 deletions(-) diff --git a/milo/Milo.lua b/milo/Milo.lua index 945a0d7..a7350f1 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -47,7 +47,7 @@ if nodes.nodes then input = 'custom', trashcan = 'custom', machine = 'machine', - brewingStand = 'custom', + brewingStand = 'machine', activity = 'display', jobs = 'display', ignore = 'ignore', @@ -105,6 +105,8 @@ if not device.workbench then end end +local localName = modem.getNameLocal() + local context = { nodes = nodes, resources = Util.readTable(Milo.RESOURCE_FILE) or { }, @@ -115,20 +117,28 @@ local context = { tasks = { }, queue = { }, - localName = modem.getNameLocal(), storage = Storage(nodes), - turtleInventory = introspection.getInventory(), + turtleInventory = { + name = localName, + mtype = 'hidden', + adapter = introspection.getInventory(), + } } -device[context.localName] = introspection.getInventory() +nodes[localName] = context.turtleInventory +nodes[localName].adapter.name = localName _G._p = context --debug Milo:init(context) context.storage:initStorage() +-- TODO: fix +context.storage.turtleInventory = context.turtleInventory + local function loadDirectory(dir) for _, file in pairs(fs.list(dir)) do +_debug('loading: ' .. file) local s, m = Util.run(_ENV, fs.combine(dir, file)) if not s and m then error(m or 'Unknown error') @@ -190,6 +200,12 @@ Event.on('milo_queue', function() end end) +Event.on('turtle_inventory', function() + if not processing and not Milo:isCraftingPaused() then + Milo:clearGrid() + end +end) + Event.onInterval(5, function() if not Milo:isCraftingPaused() then os.queueEvent('milo_cycle') diff --git a/milo/apis/craft2.lua b/milo/apis/craft2.lua index 3d6c470..ed48863 100644 --- a/milo/apis/craft2.lua +++ b/milo/apis/craft2.lua @@ -35,17 +35,14 @@ local function makeRecipeKey(item) end function Craft.clearGrid(storage) - turtle.eachFilledSlot(function(slot) - storage:import(storage.localName, slot.index, slot.count, slot) - end) + local success = true - for i = 1, 16 do - if turtle.getItemCount(i) ~= 0 then - return false + for index, slot in pairs(storage.turtleInventory.adapter.list()) do + if storage:import(storage.turtleInventory, index, slot.count, slot) ~= slot.count then + success = false end end - - return true + return success end function Craft.getItemCount(items, item) @@ -77,7 +74,7 @@ function Craft.sumIngredients(recipe) end local function machineCraft(recipe, storage, machineName, request, count, item) - local machine = device[machineName] + local machine = storage.nodes[machineName] if not machine then request.status = 'machine not found' request.statusCode = Craft.STATUS_ERROR @@ -85,7 +82,7 @@ local function machineCraft(recipe, storage, machineName, request, count, item) end for k in pairs(recipe.ingredients) do - if machine.getItemMeta(k) then + if machine.adapter.getItemMeta(k) then request.status = 'machine in use' request.statusCode = Craft.STATUS_WARNING return @@ -94,7 +91,7 @@ local function machineCraft(recipe, storage, machineName, request, count, item) local xferred = { } for k,v in pairs(recipe.ingredients) do - local provided = storage:export(machineName, k, count, splitKey(v)) + local provided = storage:export(machine, k, count, splitKey(v)) xferred[k] = { key = v, count = provided, @@ -103,7 +100,7 @@ local function machineCraft(recipe, storage, machineName, request, count, item) -- take back out whatever we put in for k2,v2 in pairs(xferred) do if v2.count > 0 then - storage:import(machineName, k2, v2.count, splitKey(v2.key)) + storage:import(machine, k2, v2.count, splitKey(v2.key)) end end request.status = 'Invalid recipe' @@ -125,7 +122,7 @@ local function turtleCraft(recipe, storage, request, count) for k,v in pairs(recipe.ingredients) do local item = splitKey(v) - if storage:export(storage.localName, k, count, item) ~= count then + if storage:export(storage.turtleInventory, k, count, item) ~= count then request.status = 'unknown error' request.statusCode = Craft.STATUS_ERROR return diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index 90d0928..6e1ba94 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -134,8 +134,8 @@ end function Milo:getTurtleInventory() local list = { } - for i = 1,16 do - local item = self.context.turtleInventory.getItemMeta(i) + for i in pairs(self.context.turtleInventory.adapter.list()) do + local item = self.context.turtleInventory.adapter.getItemMeta(i) if item and not itemDB:get(item) then itemDB:add(item) end @@ -225,7 +225,7 @@ function Milo:makeRequest(item, count, callback) end function Milo:eject(item, count) - count = self.context.storage:export(self.context.storage.localName, nil, count, item) + count = self.context.storage:export(self.context.turtleInventory, nil, count, item) turtle.emptyInventory() return count end diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 962e3a7..3b5e2d1 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -1,9 +1,8 @@ -local class = require('class') -local Event = require('event') -local InventoryAdapter = require('inventoryAdapter') -local itemDB = require('itemDB') -local Peripheral = require('peripheral') -local Util = require('util') +local class = require('class') +local Event = require('event') +local Adapter = require('inventoryAdapter') +local itemDB = require('itemDB') +local Util = require('util') local device = _G.device local os = _G.os @@ -20,9 +19,6 @@ function Storage:init(nodes) } Util.merge(self, defaults) - local modem = Peripheral.get('wired_modem') or error('Wired modem not attached') - self.localName = modem.getNameLocal() - Event.on({ 'device_attach', 'device_detach' }, function(e, dev) _G._debug('%s: %s', e, tostring(dev)) self:initStorage() @@ -34,9 +30,13 @@ end function Storage:showStorage() local t = { } + local ignores = { + ignore = true, + hidden = true, + } for k,v in pairs(self.nodes) do local online = v.adapter and v.adapter.online - if not online and v.mtype ~= 'ignore' then + if not online and not ignores[v.mtype] then table.insert(t, k) end end @@ -58,18 +58,20 @@ function Storage:initStorage() _G._debug('Initializing storage') for k,v in pairs(self.nodes) do - if v.adapter then - v.adapter.online = not not device[k] - elseif device[k] and device[k].list and device[k].size and device[k].pullItems then - v.adapter = InventoryAdapter.wrap({ side = k }) - v.adapter.online = true - v.adapter.dirty = true - elseif device[k] then - v.adapter = device[k] - v.adapter.online = true - end - if v.mtype == 'storage' then - online = online and not not (v.adapter and v.adapter.online) + if v.mtype ~= 'hidden' then + if v.adapter then + v.adapter.online = not not device[k] + elseif device[k] and device[k].list and device[k].size and device[k].pullItems then + v.adapter = Adapter.wrap({ side = k }) + v.adapter.online = true + v.adapter.dirty = true + elseif device[k] then + v.adapter = device[k] + v.adapter.online = true + end + if v.mtype == 'storage' then + online = online and not not (v.adapter and v.adapter.online) + end end end @@ -247,6 +249,10 @@ function Storage:updateCache(adapter, item, count) end function Storage:_sn(name) + if not name then + error('Invalid target', 3) + end + local node = self.nodes[name] if node and node.displayName then return node.displayName @@ -258,16 +264,58 @@ function Storage:_sn(name) return table.concat(t, '_') end +local function isValidTransfer(adapter, target) + for _,v in pairs(adapter.getTransferLocations()) do + if v == target then + return true + end + end +end + +local function rawExport(source, target, item, qty, slot) + local total = 0 + local push = isValidTransfer(source, target.name) + + local s, m = pcall(function() + local stacks = source.list() + for key,stack in Util.rpairs(stacks) do + if stack.name == item.name and + stack.damage == item.damage and + stack.nbtHash == item.nbtHash then + local amount = math.min(qty, stack.count) + if amount > 0 then + if push then + amount = source.pushItems(target.name, key, amount, slot) + else + amount = target.pullItems(source.name, key, amount, slot) + end + end + qty = qty - amount + total = total + amount + if qty <= 0 then + break + end + end + end + end) + + if not s and m then + _debug(m) + end + + return total, m +end + function Storage:export(target, slot, count, item) local total = 0 local key = item.key or table.concat({ item.name, item.damage, item.nbtHash }, ':') local function provide(adapter) - local amount = adapter:provide(item, count, slot, target) + local amount = rawExport(adapter, target.adapter, item, count, slot) if amount > 0 then _G._debug('EXT: %s(%d): %s -> %s%s', - item.displayName or item.name, amount, self:_sn(adapter.name), self:_sn(target), + item.displayName or item.name, amount, self:_sn(adapter.name), self:_sn(target.name), slot and string.format('[%d]', slot) or '[*]') self:updateCache(adapter, item, -amount) @@ -287,7 +335,7 @@ function Storage:export(target, slot, count, item) end _G._debug('MISS: %s(%d): %s%s %s', - item.displayName or item.name, count, self:_sn(target), + item.displayName or item.name, count, self:_sn(target.name), slot and string.format('[%d]', slot) or '[*]', key) -- TODO: If there are misses when a slot is specified than something is wrong... @@ -298,6 +346,23 @@ function Storage:export(target, slot, count, item) return total end +local function rawInsert(source, target, slot, qty) + local count = 0 + + local s, m = pcall(function() + if isValidTransfer(source, target.name) then + count = source.pullItems(target.name, slot, qty) + else + count = target.pushItems(source.name, slot, qty) + end + end) + if not s and m then + _debug(m) + end + + return count +end + function Storage:import(source, slot, count, item) if not source then error('Storage:import: source is required') end if not slot then error('Storage:import: slot is required') end @@ -316,19 +381,19 @@ function Storage:import(source, slot, count, item) entry = itemDB:add(item) else -- get the metadata from the device and add to db - entry = itemDB:add(device[source].getItemMeta(slot)) + entry = itemDB:add(source.adapter.getItemMeta(slot)) end itemDB:flush() end item = entry local function insert(adapter) - local amount = adapter:insert(slot, count, nil, source) + local amount = rawInsert(adapter, source.adapter, slot, count) if amount > 0 then _G._debug('INS: %s(%d): %s[%d] -> %s', item.displayName or item.name, amount, - self:_sn(source), slot, self:_sn(adapter.name)) + self:_sn(source.name), slot, self:_sn(adapter.name)) self:updateCache(adapter, item, amount) @@ -384,9 +449,9 @@ function Storage:trash(source, slot, count) local trashcan = Util.find(self.nodes, 'mtype', 'trashcan') if trashcan and trashcan.adapter and trashcan.adapter.online then - _G._debug('TRA: %s[%d] (%d)', self:_sn(source), slot, count or 64) + _G._debug('TRA: %s[%d] (%d)', self:_sn(source.name), slot, count or 64) - return trashcan.adapter.pullItems(source, slot, count) + return trashcan.adapter.pullItems(source.name, slot, count) end return 0 end diff --git a/milo/core/machines.lua b/milo/core/machines.lua index 9856e98..6a88a29 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -2,7 +2,6 @@ local Config = require('config') local Event = require('event') local itemDB = require('itemDB') local Milo = require('milo') -local sync = require('sync') local UI = require('ui') local Util = require('util') @@ -48,8 +47,7 @@ local networkPage = UI.Page { { key = 'suffix', width = 4, justify = 'right' }, { heading = 'Name', key = 'displayName' }, { heading = 'Type', key = 'mtype', width = 4 }, - { heading = 'Cat', key = 'category', width = 3 }, - --{ heading = 'Pri', key = 'priority', width = 3 }, + { heading = 'Pri', key = 'priority', width = 3 }, }, sortColumn = 'displayName', help = 'Select Node', @@ -136,7 +134,6 @@ function networkPage:enable() self:getList() self:applyFilter() self.grid:draw() - self.grid:sync() updateStatus() self:sync() end) @@ -331,13 +328,11 @@ function nodeWizard.filter:show(entry, callback, whitelistOnly) self:setFocus(self.form.scan) Milo:pauseCrafting({ key = 'gridInUse', msg = 'Crafting paused' }) - sync.lock(turtle) end function nodeWizard.filter:hide() UI.SlideOut.hide(self) Milo:resumeCrafting({ key = 'gridInUse' }) - sync.release(turtle) end function nodeWizard.filter:resetGrid() diff --git a/milo/plugins/brewingStandView.lua b/milo/plugins/brewingStandView.lua index ec7aba4..c63cdc1 100644 --- a/milo/plugins/brewingStandView.lua +++ b/milo/plugins/brewingStandView.lua @@ -30,7 +30,7 @@ function brewingStandView:isValidType(node) return m and m.type == 'minecraft:brewing_stand'and { name = 'Brewing Stand', value = 'brewingStand', - category = 'custom', + category = 'machine', help = 'Auto-learning brewing stand', } end diff --git a/milo/plugins/craftTask.lua b/milo/plugins/craftTask.lua index a00b606..d748e6d 100644 --- a/milo/plugins/craftTask.lua +++ b/milo/plugins/craftTask.lua @@ -1,6 +1,5 @@ local Craft = require('craft2') local Milo = require('milo') -local sync = require('sync').sync local Util = require('util') local context = Milo:getContext() @@ -63,9 +62,7 @@ function craftTask:cycle() if item.requested - item.crafted > 0 then local recipe = Craft.findRecipe(key) if recipe then - sync(turtle, function() - self:craft(recipe, item) - end) + self:craft(recipe, item) if item.callback and item.crafted >= item.requested then item.callback(item) -- invoke callback end diff --git a/milo/plugins/exportTask.lua b/milo/plugins/exportTask.lua index ef00d3f..2e90c5b 100644 --- a/milo/plugins/exportTask.lua +++ b/milo/plugins/exportTask.lua @@ -39,7 +39,7 @@ function ExportTask:cycle(context) local _, item = next(items) if item then local count = math.min(item.count, slot.maxCount - slot.count) - context.storage:export(node.name, entry.slot, count, item) + context.storage:export(node, entry.slot, count, item) end break end @@ -53,7 +53,7 @@ function ExportTask:cycle(context) local _, item = next(items) if item then local count = math.min(item.count, itemDB:getMaxCount(item)) - context.storage:export(node.name, entry.slot, count, item) + context.storage:export(node, entry.slot, count, item) break end end @@ -63,7 +63,7 @@ function ExportTask:cycle(context) for key in pairs(entry.filter) do local items = Milo:getMatches(itemDB:splitKey(key), entry) for _,item in pairs(items) do - if context.storage:export(node.name, nil, item.count, item) == 0 then + if context.storage:export(node, nil, item.count, item) == 0 then -- TODO: really shouldn't break here as there may be room in other slots -- leaving for now for performance reasons break diff --git a/milo/plugins/exportView.lua b/milo/plugins/exportView.lua index 192ab4d..7b1aa53 100644 --- a/milo/plugins/exportView.lua +++ b/milo/plugins/exportView.lua @@ -6,7 +6,7 @@ local colors = _G.colors local device = _G.device local exportView = UI.Window { - title = 'Export item into machine', + title = 'Export item into inventory', index = 3, grid = UI.ScrollingGrid { x = 2, ex = -6, y = 2, ey = -4, diff --git a/milo/plugins/importTask.lua b/milo/plugins/importTask.lua index 1ec15b5..5664c3c 100644 --- a/milo/plugins/importTask.lua +++ b/milo/plugins/importTask.lua @@ -44,7 +44,7 @@ function ImportTask:cycle(context) local function importSlot(slotNo) local item = node.adapter.getItemMeta(slotNo) if item and matchesFilter(item) then - context.storage:import(node.name, slotNo, item.count, item) + context.storage:import(node, slotNo, item.count, item) end end diff --git a/milo/plugins/importView.lua b/milo/plugins/importView.lua index 80131dd..671821b 100644 --- a/milo/plugins/importView.lua +++ b/milo/plugins/importView.lua @@ -6,7 +6,7 @@ local colors = _G.colors local device = _G.device local importView = UI.Window { - title = 'Import item from machine', + title = 'Import item from inventory', index = 4, grid = UI.ScrollingGrid { x = 2, ex = -6, y = 2, ey = -4, diff --git a/milo/plugins/inputChestTask.lua b/milo/plugins/inputChestTask.lua index 198d1e8..85a20d2 100644 --- a/milo/plugins/inputChestTask.lua +++ b/milo/plugins/inputChestTask.lua @@ -6,9 +6,9 @@ local InputChest = { } function InputChest:cycle(context) - for inventory in context.storage:filterActive('input') do - for slot, item in pairs(inventory.adapter.list()) do - context.storage:import(inventory.name, slot, item.count, item) + for node in context.storage:filterActive('input') do + for slot, item in pairs(node.adapter.list()) do + context.storage:import(node, slot, item.count, item) end end end diff --git a/milo/plugins/learn.lua b/milo/plugins/learn.lua index 0c9460e..21fa1ef 100644 --- a/milo/plugins/learn.lua +++ b/milo/plugins/learn.lua @@ -1,9 +1,8 @@ local Milo = require('milo') -local sync = require('sync') local UI = require('ui') local context = Milo:getContext() -local turtle = _G.turtle +local turtle = _G.turtle local learnPage = UI.Dialog { height = 9, width = UI.term.width - 6, @@ -39,7 +38,6 @@ function learnPage:enable() self.grid:setSelected('name', Milo:getState('learnType') or '') Milo:pauseCrafting({ key = 'gridInUse', msg = 'Crafting paused' }) - sync.lock(turtle) self:focusFirst() UI.Dialog.enable(self) @@ -51,8 +49,8 @@ end function learnPage:eventHandler(event) if event.type == 'cancel' then - sync.release(turtle) Milo:resumeCrafting({ key = 'gridInUse' }) + turtle.emptyInventory() UI:setPreviousPage() elseif event.type == 'accept' or event.type == 'grid_select' then diff --git a/milo/plugins/limitTask.lua b/milo/plugins/limitTask.lua index ec072d4..1de414e 100644 --- a/milo/plugins/limitTask.lua +++ b/milo/plugins/limitTask.lua @@ -16,7 +16,7 @@ function LimitTask:cycle(context) local amount = count - res.limit for _, item in pairs(items) do amount = amount - context.storage:export( - trashcan.name, + trashcan, nil, math.min(amount, item.count), item) diff --git a/milo/plugins/machineLearn.lua b/milo/plugins/machineLearn.lua index b729ce5..9a743c4 100644 --- a/milo/plugins/machineLearn.lua +++ b/milo/plugins/machineLearn.lua @@ -1,6 +1,5 @@ local itemDB = require('itemDB') local Milo = require('milo') -local sync = require('sync') local UI = require('ui') local Util = require('util') @@ -133,7 +132,6 @@ end function machineLearnWizard:disable() Milo:resumeCrafting({ key = 'gridInUse' }) - sync.release(turtle) UI.Page.disable(self) end diff --git a/milo/plugins/manipulatorView.lua b/milo/plugins/manipulatorView.lua index 87b2335..87bd2b2 100644 --- a/milo/plugins/manipulatorView.lua +++ b/milo/plugins/manipulatorView.lua @@ -1,11 +1,9 @@ local Ansi = require('ansi') local Milo = require('milo') -local Sync = require('sync') local UI = require('ui') local colors = _G.colors local device = _G.device -local turtle = _G.turtle --[[ Configuration Screen ]]-- local wizardPage = UI.Window { @@ -57,7 +55,7 @@ function wizardPage:validate() return self.form:save() end -UI:getPage('nodeWizard').wizard:add({ manipulator = wizardPage }) +--UI:getPage('nodeWizard').wizard:add({ manipulator = wizardPage }) --[[ Task ]]-- local task = { @@ -72,15 +70,9 @@ function task:cycle(context) for manipulator in context.storage:filterActive('manipulator', filter) do for slot, item in pairs(manipulator.adapter.getEnder().list()) do - Sync.sync(turtle, function() - manipulator.adapter.getEnder().pushItems( - context.localName, - slot, - item.count) - Milo:clearGrid() - end) + context.storage:import('joebodo:enderChest', slot, item.count, item) end end end -Milo:registerTask(task) +--Milo:registerTask(task) diff --git a/milo/plugins/potionImportTask.lua b/milo/plugins/potionImportTask.lua index e5b6e9c..a455793 100644 --- a/milo/plugins/potionImportTask.lua +++ b/milo/plugins/potionImportTask.lua @@ -23,7 +23,7 @@ function PotionImportTask:cycle(context) if not list[5] then local blazePowder = context.storage.cache[BLAZE_POWDER] if blazePowder then - context.storage:export(bs.name, 5, 1, blazePowder) + context.storage:export(bs, 5, 1, blazePowder) else local item = itemDB:get(BLAZE_POWDER) if item then @@ -45,7 +45,7 @@ function PotionImportTask:cycle(context) for slot = 1, 3 do if list[slot] then - context.storage:import(bs.name, slot, 1, list[slot]) + context.storage:import(bs, slot, 1, list[slot]) end end end diff --git a/milo/plugins/remote.lua b/milo/plugins/remote.lua index 1623168..ee74d13 100644 --- a/milo/plugins/remote.lua +++ b/milo/plugins/remote.lua @@ -2,10 +2,8 @@ local Event = require('event') local itemDB = require('itemDB') local Milo = require('milo') local Socket = require('socket') -local Sync = require('sync') local device = _G.device -local turtle = _G.turtle local SHIELD_SLOT = 2 @@ -61,6 +59,17 @@ local function client(socket) data = 'ok', }) + local function makeNode(devType) + local devName = user .. ':' .. devType + local adapter = device[devName] + if adapter then + return { + adapter = adapter, + name = devName, + } + end + end + repeat local data = socket:read() if not data then @@ -77,20 +86,20 @@ local function client(socket) elseif data.request == 'deposit' then local function deposit() - Sync.sync(turtle, function() - if data.slot == 'shield' then - manipulator.getEquipment().pushItems( - context.localName, - SHIELD_SLOT, - data.count) - else - manipulator.getInventory().pushItems( - context.localName, - data.slot, - data.count) + local devType = 'inventory' + local slotNo = data.slot + if data.slot == 'shield' then + slotNo = SHIELD_SLOT + devType = 'equipment' + end + + local node = makeNode(devType) + if node then + local slot = node.adapter.getItemMeta(slotNo) + if slot then + context.storage:import(node, slotNo, slot.count, slot) end - Milo:clearGrid() - end) + end end local list = Milo:listItems() @@ -114,23 +123,14 @@ local function client(socket) end local function transfer(request) - Sync.sync(turtle, function() - local transferred = context.storage:export( - context.localName, + local target = makeNode('inventory') + if target then + context.storage:export( + target, nil, request.requested, data.item) - - if transferred > 0 then - turtle.eachFilledSlot(function(slot) - manipulator.getInventory().pullItems( - context.localName, - slot.index, - slot.count) - end) - end - Milo:clearGrid() -- in case all items do not fit in user's inventory - end) + end end local request = Milo:makeRequest(data.item, count, transfer) diff --git a/milo/plugins/turtleLearn.lua b/milo/plugins/turtleLearn.lua index 10948c7..008a4a8 100644 --- a/milo/plugins/turtleLearn.lua +++ b/milo/plugins/turtleLearn.lua @@ -1,7 +1,6 @@ local Craft = require('craft2') local itemDB = require('itemDB') local Milo = require('milo') -local sync = require('sync') local UI = require('ui') local Util = require('util') @@ -125,7 +124,6 @@ local turtleLearnWizard = UI.Page { function turtleLearnWizard:disable() Milo:resumeCrafting({ key = 'gridInUse' }) - sync.release(turtle) UI.Page.disable(self) end From 27c455ba16ceaa024de76d4e6c0034e3dd4d13c4 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 1 Dec 2018 19:46:12 -0500 Subject: [PATCH 147/165] milo: smart transfer --- milo/apis/storage.lua | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 3b5e2d1..603079f 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -351,8 +351,10 @@ local function rawInsert(source, target, slot, qty) local s, m = pcall(function() if isValidTransfer(source, target.name) then +--_debug('pull %s %s %d %d', source.name, target.name, slot, qty) count = source.pullItems(target.name, slot, qty) else +--_debug('push %s %s', target.name, source.name) count = target.pushItems(source.name, slot, qty) end end) @@ -446,14 +448,23 @@ end -- When importing items into a locked chest, trash any remaining items if full function Storage:trash(source, slot, count) - local trashcan = Util.find(self.nodes, 'mtype', 'trashcan') - if trashcan and trashcan.adapter and trashcan.adapter.online then - - _G._debug('TRA: %s[%d] (%d)', self:_sn(source.name), slot, count or 64) - - return trashcan.adapter.pullItems(source.name, slot, count) + local target = Util.find(self.nodes, 'mtype', 'trashcan') + local amount = 0 + if target and target.adapter and target.adapter.online then + local s, m = pcall(function() + _G._debug('TRA: %s[%d] (%d)', self:_sn(source.name), slot, count or 64) + --return trashcan.adapter.pullItems(source.name, slot, count) + if isValidTransfer(source.adapter, target.name) then + amount = source.adapter.pushItems(target.name, slot, count) + else + amount = target.adapter.pullItems(source.name, slot, count) + end + end) + if not s and m then + _G._debug(m) + end end - return 0 + return amount end return Storage From 4b229b868ed2dd413fe3aff33b251ff2fbc14d78 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sun, 2 Dec 2018 13:16:00 -0500 Subject: [PATCH 148/165] milo: text scaling + manipulator fixes --- milo/apis/storage.lua | 5 +++++ milo/plugins/activityView.lua | 37 +++++++++++++++++++++++++++-------- milo/plugins/jobMonitor.lua | 26 ++++++++++++++++++++++-- 3 files changed, 58 insertions(+), 10 deletions(-) diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 603079f..0d8b8a7 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -21,6 +21,11 @@ function Storage:init(nodes) Event.on({ 'device_attach', 'device_detach' }, function(e, dev) _G._debug('%s: %s', e, tostring(dev)) + if e == 'device_detach' then + if self.nodes[dev] then + self.nodes[dev].adapter = nil + end + end self:initStorage() end) Event.onInterval(15, function() diff --git a/milo/plugins/activityView.lua b/milo/plugins/activityView.lua index 50e6341..31c0f66 100644 --- a/milo/plugins/activityView.lua +++ b/milo/plugins/activityView.lua @@ -11,22 +11,42 @@ local device = _G.device --[[ Configuration Page ]]-- local template = [[%sDisplays the amount of items entering or leaving storage.%s - Right-clicking on the activity monitor will reset the totals.]] -local activityWizardPage = UI.Window { +local wizardPage = UI.Window { title = 'Activity Monitor', index = 2, backgroundColor = colors.cyan, [1] = UI.TextArea { - x = 2, ex = -2, y = 2, ey = -2, + x = 2, ex = -2, y = 2, ey = 6, marginRight = 0, value = string.format(template, Ansi.yellow, Ansi.reset), }, - timestamp = os.clock(), + form = UI.Form { + x = 2, ex = -2, y = 7, ey = -2, + manualControls = true, + [1] = UI.Chooser { + width = 9, + formLabel = 'Font Size', formKey = 'textScale', + nochoice = 'Small', + choices = { + { name = 'Small', value = .5, help = '(requires restart)', }, + { name = 'Large', value = 1, help = '(requires restart)', }, + }, + help = 'Adjust text scaling (requires restart)', + }, + }, } -function activityWizardPage:isValidType(node) +function wizardPage:setNode(node) + self.form:setValues(node) +end + +function wizardPage:validate() + return self.form:save() +end + +function wizardPage:isValidType(node) local m = device[node.name] return m and m.type == 'monitor' and { name = 'Activity Monitor', @@ -36,18 +56,18 @@ function activityWizardPage:isValidType(node) } end -function activityWizardPage:isValidFor(node) +function wizardPage:isValidFor(node) return node.mtype == 'activity' end -UI:getPage('nodeWizard').wizard:add({ activity = activityWizardPage }) +UI:getPage('nodeWizard').wizard:add({ activity = wizardPage }) --[[ Display ]]-- local function createPage(node) local page = UI.Window { parent = UI.Device { device = node.adapter, - textScale = .5, + textScale = node.textScale or .5, }, grid = UI.Grid { columns = { @@ -58,6 +78,7 @@ local function createPage(node) }, sortColumn = 'displayName', }, + timestamp = os.clock(), } function page.grid:getRowTextColor(row, selected) diff --git a/milo/plugins/jobMonitor.lua b/milo/plugins/jobMonitor.lua index 378102a..a73c9c8 100644 --- a/milo/plugins/jobMonitor.lua +++ b/milo/plugins/jobMonitor.lua @@ -15,13 +15,35 @@ local wizardPage = UI.Window { index = 2, backgroundColor = colors.cyan, [1] = UI.TextArea { - x = 2, ex = -2, y = 2, ey = -2, + x = 2, ex = -2, y = 2, ey = 3, marginRight = 0, textColor = colors.yellow, value = 'Displays the crafting progress.' }, + form = UI.Form { + x = 2, ex = -2, y = 4, ey = -2, + manualControls = true, + [1] = UI.Chooser { + width = 9, + formLabel = 'Font Size', formKey = 'textScale', + nochoice = 'Small', + choices = { + { name = 'Small', value = .5, help = '(requires restart)', }, + { name = 'Large', value = 1, help = '(requires restart)', }, + }, + help = 'Adjust text scaling (requires restart)', + }, + }, } +function wizardPage:setNode(node) + self.form:setValues(node) +end + +function wizardPage:validate() + return self.form:save() +end + function wizardPage:isValidType(node) local m = device[node.name] return m and m.type == 'monitor' and { @@ -44,7 +66,7 @@ local function createPage(node) local page = UI.Page { parent = UI.Device { device = node.adapter, - textScale = .5, + textScale = node.textScale or .5, }, grid = UI.Grid { sortColumn = 'index', From ef0cf883877e1e46728e730bd8fcdc360b07d8db Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sun, 2 Dec 2018 15:11:10 -0500 Subject: [PATCH 149/165] manipulator :( --- milo/apis/storage.lua | 5 ----- 1 file changed, 5 deletions(-) diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 0d8b8a7..603079f 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -21,11 +21,6 @@ function Storage:init(nodes) Event.on({ 'device_attach', 'device_detach' }, function(e, dev) _G._debug('%s: %s', e, tostring(dev)) - if e == 'device_detach' then - if self.nodes[dev] then - self.nodes[dev].adapter = nil - end - end self:initStorage() end) Event.onInterval(15, function() From 93d2aa331c3233f0e94c114303520b1fd1461057 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sun, 2 Dec 2018 18:42:49 -0500 Subject: [PATCH 150/165] better wireless --- milo/MiloRemote.lua | 114 +++++++++++++++++++++--------------- milo/apis/milo.lua | 2 + milo/plugins/exportTask.lua | 108 ++++++++++++++++++---------------- milo/plugins/importTask.lua | 80 +++++++++++++------------ milo/plugins/remote.lua | 54 ++++++++++++----- 5 files changed, 209 insertions(+), 149 deletions(-) diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index 4df1c51..42dfa04 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -177,9 +177,62 @@ function page:setStatus(status) self:sync() end -function page:sendRequest(data, statusMsg) - local response +function page:processMessages(s) + Event.addRoutine(function() + repeat + local response = s:read() + if not response then + break + end + if response.type == 'received' then + Sound.play('entity.item.pickup') + local ritem = self.items[response.key] + if ritem then + ritem.count = response.count + self.grid:draw() + self:sync() + end + elseif response.type == 'list' then + self.items = self:expandList(response.list) + self:applyFilter() + self.grid:draw() + self.grid:sync() + + elseif response.type == 'transfer' then + if response.count > 0 then + Sound.play('entity.item.pickup') + local item = self.items[response.key] + if item then + item.count = response.current + self.grid:draw() + self:sync() + end + end + if response.craft then + if response.craft > 0 then + self:setStatus(response.craft .. ' crafting ...') + elseif response.craft + response.count < response.requested then + if response.craft + response.count == 0 then + Sound.play('entity.villager.no') + end + self:setStatus((response.craft + response.count) .. ' available ...') + end + end + end + if response.msg then + self:setStatus(response.msg) + end + until not s.connected + + s:close() + s = nil + self:setStatus('disconnected ...') + Sound.play('entity.villager.no') + end) +end + +function page:sendRequest(data, statusMsg) if not config.server then self:setStatus('Invalid configuration') return @@ -202,30 +255,23 @@ function page:sendRequest(data, statusMsg) local r = socket:read(2) if r and not r.msg then self:setStatus('connected ...') + self:processMessages(socket) else msg = r and r.msg or 'Timed out' socket:close() socket = nil - break end end end if socket then if statusMsg then self:setStatus(statusMsg) + Event.onTimeout(2, function() + self:setStatus('') + end) end if socket:write(data) then - response = socket:read(2) - if response then - if response.msg then - self:setStatus(response.msg) - response = nil - end - Event.onTimeout(2, function() - self:setStatus('') - end) - return - end + return true end socket:close() socket = nil @@ -233,8 +279,6 @@ function page:sendRequest(data, statusMsg) end self:setStatus(msg or 'Failed to connect') end) - - return response end function page.grid:getRowTextColor(row, selected) @@ -282,17 +326,8 @@ function page.grid:eventHandler(event) end function page:transfer(item, count, msg) - Sound.play('ui.button.click', .3) - local response = self:sendRequest({ request = 'transfer', item = item, count = count }, msg) - if response then - item.count = response.current - response.count - self.grid:draw() - if response.craft > 0 then - self:setStatus(response.craft .. ' crafting ...') - elseif response.craft + response.count < response.requested then - self:setStatus((response.craft + response.count) .. ' available ...') - end - end + --Sound.play('ui.button.click', .3) + self:sendRequest({ request = 'transfer', item = item, count = count }, msg) end function page.setup:eventHandler(event) @@ -443,12 +478,7 @@ function page:expandList(list) end function page:refresh(requestType) - local items = self:sendRequest({ request = requestType }, 'refreshing...') - - if items then - self.items = self:expandList(items) - self:applyFilter() - end + self:sendRequest({ request = requestType }, 'refreshing...') end function page:applyFilter() @@ -486,23 +516,13 @@ Event.addRoutine(function() elseif config.server and (config.useShield or config.slot) then local s, m = pcall(function() local method = neural[inv] - local item = method and method().getItemMeta(config.useShield and SHIELD_SLOT or config.slot) + local item = method and method().list()[config.useShield and SHIELD_SLOT or config.slot] if item then - local slotNo = config.useShield and 'shield' or config.slot - local response = page:sendRequest({ + if page:sendRequest({ request = 'deposit', - slot = slotNo, + slot = config.useShield and 'shield' or config.slot, count = item.count, - key = table.concat({ item.name, item.damage, item.nbtHash }, ':') - }) - if response then - Sound.play('entity.item.pickup') - local ritem = page.items[response.key] - if ritem then - ritem.count = response.current + item.count - end - page.grid:draw() - page:sync() + }) then sleepTime = math.max(sleepTime - .25, .25) end else diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index 6e1ba94..ef2432d 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -188,6 +188,7 @@ function Milo:makeRequest(item, count, callback) count = 0, current = current.count, item = item, + key = item.key or Milo:uniqueKey(item), } end @@ -208,6 +209,7 @@ function Milo:makeRequest(item, count, callback) count = math.min(count, current.count), current = current.count, item = item, + key = item.key or Milo:uniqueKey(item), } if request.count > 0 then diff --git a/milo/plugins/exportTask.lua b/milo/plugins/exportTask.lua index 2e90c5b..c02ddf4 100644 --- a/milo/plugins/exportTask.lua +++ b/milo/plugins/exportTask.lua @@ -12,70 +12,76 @@ end function ExportTask:cycle(context) for node in context.storage:filterActive('machine', filter) do - for _, entry in pairs(node.exports) do + local s, m = pcall(function() + for _, entry in pairs(node.exports) do - if not entry.filter then - -- exports must have a filter - -- TODO: validate in exportView - break - end - - local function exportSingleSlot() - local slot = node.adapter.getItemMeta(entry.slot) - - if slot and slot.count == slot.maxCount then - return + if not entry.filter then + -- exports must have a filter + -- TODO: validate in exportView + break end - if slot then - -- something is in the slot, find what we can export - for key in pairs(entry.filter) do - local filterItem = Milo:splitKey(key) - if (slot.name == filterItem.name and - entry.ignoreDamage or slot.damage == filterItem.damage and - entry.ignoreNbtHash or slot.nbtHash == filterItem.nbtHash) then + local function exportSingleSlot() + local slot = node.adapter.getItemMeta(entry.slot) - local items = Milo:getMatches(filterItem, entry) - local _, item = next(items) - if item then - local count = math.min(item.count, slot.maxCount - slot.count) - context.storage:export(node, entry.slot, count, item) + if slot and slot.count == slot.maxCount then + return + end + + if slot then + -- something is in the slot, find what we can export + for key in pairs(entry.filter) do + local filterItem = Milo:splitKey(key) + if (slot.name == filterItem.name and + entry.ignoreDamage or slot.damage == filterItem.damage and + entry.ignoreNbtHash or slot.nbtHash == filterItem.nbtHash) then + + local items = Milo:getMatches(filterItem, entry) + local _, item = next(items) + if item then + local count = math.min(item.count, slot.maxCount - slot.count) + context.storage:export(node, entry.slot, count, item) + end + break end - break end + return end - return - end - -- slot is empty - export first matching item we have in storage - for key in pairs(entry.filter) do - local items = Milo:getMatches(Milo:splitKey(key), entry) - local _, item = next(items) - if item then - local count = math.min(item.count, itemDB:getMaxCount(item)) - context.storage:export(node, entry.slot, count, item) - break - end - end - end - - local function exportItems() - for key in pairs(entry.filter) do - local items = Milo:getMatches(itemDB:splitKey(key), entry) - for _,item in pairs(items) do - if context.storage:export(node, nil, item.count, item) == 0 then - -- TODO: really shouldn't break here as there may be room in other slots - -- leaving for now for performance reasons + -- slot is empty - export first matching item we have in storage + for key in pairs(entry.filter) do + local items = Milo:getMatches(Milo:splitKey(key), entry) + local _, item = next(items) + if item then + local count = math.min(item.count, itemDB:getMaxCount(item)) + context.storage:export(node, entry.slot, count, item) break end end end + + local function exportItems() + for key in pairs(entry.filter) do + local items = Milo:getMatches(itemDB:splitKey(key), entry) + for _,item in pairs(items) do + if context.storage:export(node, nil, item.count, item) == 0 then + -- TODO: really shouldn't break here as there may be room in other slots + -- leaving for now for performance reasons + break + end + end + end + end + if type(entry.slot) == 'number' then + exportSingleSlot() + else + exportItems() + end end - if type(entry.slot) == 'number' then - exportSingleSlot() - else - exportItems() - end + end) + if not s and m then + _G._debug('Importer error') + _G._debug(m) end end end diff --git a/milo/plugins/importTask.lua b/milo/plugins/importTask.lua index 5664c3c..4cf05ee 100644 --- a/milo/plugins/importTask.lua +++ b/milo/plugins/importTask.lua @@ -11,50 +11,56 @@ end function ImportTask:cycle(context) for node in context.storage:filterActive('machine', filter) do - for _, entry in pairs(node.imports) do + local s, m = pcall(function() + for _, entry in pairs(node.imports) do - local function itemMatchesFilter(item) - if not entry.ignoreDamage and not entry.ignoreNbtHash then - local key = Milo:uniqueKey(item) - return entry.filter[key] + local function itemMatchesFilter(item) + if not entry.ignoreDamage and not entry.ignoreNbtHash then + local key = Milo:uniqueKey(item) + return entry.filter[key] + end + + for key in pairs(entry.filter) do + local v = Milo:splitKey(key) + if item.name == v.name and + (entry.ignoreDamage or item.damage == v.damage) and + (entry.ignoreNbtHash or item.nbtHash == v.nbtHash) then + return true + end + end end - for key in pairs(entry.filter) do - local v = Milo:splitKey(key) - if item.name == v.name and - (entry.ignoreDamage or item.damage == v.damage) and - (entry.ignoreNbtHash or item.nbtHash == v.nbtHash) then + local function matchesFilter(item) + if not entry.filter then return true end + + if entry.blacklist then + return not itemMatchesFilter(item) + end + + return itemMatchesFilter(item) + end + + local function importSlot(slotNo) + local item = node.adapter.getItemMeta(slotNo) + if item and matchesFilter(item) then + context.storage:import(node, slotNo, item.count, item) + end + end + + if type(entry.slot) == 'number' then + importSlot(entry.slot) + else + for i in pairs(node.adapter.list()) do + importSlot(i) + end end end - - local function matchesFilter(item) - if not entry.filter then - return true - end - - if entry.blacklist then - return not itemMatchesFilter(item) - end - - return itemMatchesFilter(item) - end - - local function importSlot(slotNo) - local item = node.adapter.getItemMeta(slotNo) - if item and matchesFilter(item) then - context.storage:import(node, slotNo, item.count, item) - end - end - - if type(entry.slot) == 'number' then - importSlot(entry.slot) - else - for i in pairs(node.adapter.list()) do - importSlot(i) - end - end + end) + if not s and m then + _G._debug('Importer error') + _G._debug(m) end end end diff --git a/milo/plugins/remote.lua b/milo/plugins/remote.lua index ee74d13..a93437b 100644 --- a/milo/plugins/remote.lua +++ b/milo/plugins/remote.lua @@ -75,14 +75,22 @@ local function client(socket) if not data then break end +--_G._debug(data) + socket.co = coroutine.running() if data.request == 'scan' then -- full scan of all inventories local items = Milo:mergeResources(Milo:listItems(true)) - socket:write(compactList(items)) + socket:write({ + type = 'list', + list = compactList(items), + }) elseif data.request == 'list' then local items = Milo:mergeResources(Milo:listItems()) - socket:write(compactList(items)) + socket:write({ + type = 'list', + list = compactList(items), + }) elseif data.request == 'deposit' then local function deposit() @@ -97,19 +105,20 @@ local function client(socket) if node then local slot = node.adapter.getItemMeta(slotNo) if slot then - context.storage:import(node, slotNo, slot.count, slot) + if context.storage:import(node, slotNo, slot.count, slot) then + local item = Milo:getItem(Milo:listItems(), slot) + if item then + socket:write({ + type = 'received', + key = item.key, + count = item.count, + }) + end + end end end end - local list = Milo:listItems() - local current = list[data.key] and list[data.key].count or 0 - - socket:write({ - key = data.key, - current = current, - }) - Milo:queueRequest({ }, deposit) elseif data.request == 'transfer' then @@ -125,17 +134,34 @@ local function client(socket) local function transfer(request) local target = makeNode('inventory') if target then - context.storage:export( + local amount = context.storage:export( target, nil, request.requested, data.item) + local item = Milo:listItems()[request.key] + socket:write({ + type = 'transfer', + key = request.key, + requested = request.requested, + current = item and item.count or 0, + count = amount, + craft = request.craft, + }) end end local request = Milo:makeRequest(data.item, count, transfer) - - socket:write(request) + if (request.craft + request.count == 0) or + (request.craft > 0 and request.count == 0) then + socket:write({ + type = 'transfer', + key = request.key, + requested = request.requested, + count = request.current, + craft = request.craft, + }) + end end until not socket.connected From a49d2d820dcfe384d743c8fdbee1ad53902458c7 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Mon, 3 Dec 2018 15:40:51 -0500 Subject: [PATCH 151/165] app tweaks --- core/Music.lua | 70 ++++++++++++++---------------------- core/Turtles.lua | 22 ++++++------ milo/apis/storage.lua | 2 -- milo/plugins/refreshTask.lua | 6 ---- 4 files changed, 38 insertions(+), 62 deletions(-) diff --git a/core/Music.lua b/core/Music.lua index c33f3ef..118ca4f 100644 --- a/core/Music.lua +++ b/core/Music.lua @@ -3,23 +3,32 @@ _G.requireInjector() local Event = require('event') local UI = require('ui') -local colors = _G.colors -local device = _G.device -local turtle = _G.turtle +local colors = _G.colors +local peripheral = _G.peripheral -if not turtle then - error('This program can only be run on a turtle') -end - -local radio = device.drive or error('No drive attached') -if radio.side ~= 'top' and radio.side ~= 'bottom' then - error('Disk drive must be above or below turtle') -end +local speaker = peripheral.find('speaker') or + error('Speaker must be attached') UI:configure('Music', ...) UI.Button.defaults.backgroundFocusColor = colors.gray +local songs = { + { record = 'record.11', title = '11' }, + { record = 'record.13', title = '13' }, + { record = 'record.block', title = 'Block' }, + { record = 'record.cat', title = 'Cat' }, + { record = 'record.chirp', title = 'Chirp' }, + { record = 'record.far', title = 'Faf' }, + { record = 'record.mall', title = 'Mall' }, + { record = 'record.mellohi', title = 'Mellohi' }, + { record = 'record.stal', title = 'Stal' }, + { record = 'record.strad', title = 'Strad' }, + { record = 'record.wait', title = 'Wait' }, + { record = 'record.ward', title = 'Ward' }, +} +local songNo = 1 + local page = UI.Page({ volume = 15, stationName = UI.Text({ @@ -168,41 +177,18 @@ function page:setVolume(volume) end function page:seek() - - local actions = { - top = { - suck = turtle.suckUp, - drop = turtle.dropUp, - }, - bottom = { - suck = turtle.suckDown, - drop = turtle.dropDown, - }, - } - - local slot = turtle.selectOpenSlot() - actions[radio.side].suck() - repeat - slot = slot + 1 - if (slot > 16) then - slot = 1 - end - until turtle.getItemCount(slot) >= 1 - turtle.select(slot) - actions[radio.side].drop() + songNo = songNo + 1 + if songNo > #songs then + songNo = 1 + end self:updateStationName() end function page:play(onOff) self.playing = onOff if self.playing then - - if not radio.hasAudio() then - self:seek() - end - self:updateStationName() - radio.playAudio() + speaker.playSound(songs[songNo]) Event.addNamedTimer('songTimer', 180, false, function() if self.playing then @@ -213,7 +199,7 @@ function page:play(onOff) end) else - radio.stopAudio() + --radio.stopAudio() end end @@ -223,7 +209,7 @@ function page.stationName:draw() end function page:updateStationName() - local title = radio.getAudioTitle() + local title = songs[songNo].title if title then self.stationName.value = title @@ -248,9 +234,7 @@ page:setVolume(page.volume, true) UI:setPage(page) -turtle.setStatus('Jamming') UI:pullEvents() -turtle.setStatus('idle') page:play(false) UI.term:reset() diff --git a/core/Turtles.lua b/core/Turtles.lua index 6ce66f6..2af92e3 100644 --- a/core/Turtles.lua +++ b/core/Turtles.lua @@ -43,7 +43,7 @@ local page = UI.Page { }, tabs = UI.Tabs { x = 1, y = 5, ey = -2, - scripts = UI.Grid { + scripts = UI.ScrollingGrid { tabTitle = 'Run', backgroundColor = UI.TabBar.defaults.selectedBackgroundColor, columns = { @@ -53,7 +53,7 @@ local page = UI.Page { sortColumn = 'label', autospace = true, }, - turtles = UI.Grid { + turtles = UI.ScrollingGrid { tabTitle = 'Select', backgroundColor = UI.TabBar.defaults.selectedBackgroundColor, columns = { @@ -66,7 +66,7 @@ local page = UI.Page { sortColumn = 'label', autospace = true, }, - inventory = UI.Grid { + inventory = UI.ScrollingGrid { backgroundColor = UI.TabBar.defaults.selectedBackgroundColor, tabTitle = 'Inv', columns = { @@ -78,7 +78,7 @@ local page = UI.Page { sortColumn = 'index', }, --[[ - policy = UI.Grid { + policy = UI.ScrollingGrid { tabTitle = 'Mod', backgroundColor = UI.TabBar.defaults.selectedBackgroundColor, columns = { @@ -196,7 +196,7 @@ function page.tabs.inventory:getRowTextColor(row, selected) if page.turtle and row.selected then return colors.yellow end - return UI.Grid.getRowTextColor(self, row, selected) + return UI.ScrollingGrid.getRowTextColor(self, row, selected) end function page.tabs.inventory:draw() @@ -217,7 +217,7 @@ function page.tabs.inventory:draw() end self:adjustWidth() self:update() - UI.Grid.draw(self) + UI.ScrollingGrid.draw(self) end function page.tabs.inventory:eventHandler(event) @@ -225,7 +225,7 @@ function page.tabs.inventory:eventHandler(event) local fn = string.format('turtle.select(%d)', event.selected.index) page:runFunction(fn) else - return UI.Grid.eventHandler(self, event) + return UI.ScrollingGrid.eventHandler(self, event) end return true end @@ -238,14 +238,14 @@ function page.tabs.scripts:draw() table.insert(self.values, { label = path, path = fs.combine(SCRIPTS_PATH, path) }) end self:update() - UI.Grid.draw(self) + UI.ScrollingGrid.draw(self) end function page.tabs.scripts:eventHandler(event) if event.type == 'grid_select' then page:runScript(event.selected.label) else - return UI.Grid.eventHandler(self, event) + return UI.ScrollingGrid.eventHandler(self, event) end return true end @@ -269,7 +269,7 @@ function page.tabs.turtles:draw() end end self:update() - UI.Grid.draw(self) + UI.ScrollingGrid.draw(self) end function page.tabs.turtles:eventHandler(event) @@ -283,7 +283,7 @@ function page.tabs.turtles:eventHandler(event) socket = nil end else - return UI.Grid.eventHandler(self, event) + return UI.ScrollingGrid.eventHandler(self, event) end return true end diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 603079f..a5acbd6 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -15,7 +15,6 @@ function Storage:init(nodes) dirty = true, activity = { }, storageOnline = true, - lastRefresh = os.clock(), } Util.merge(self, defaults) @@ -148,7 +147,6 @@ end function Storage:refresh(throttle) self.dirty = true - self.lastRefresh = os.clock() _G._debug('STORAGE: Forcing full refresh') for _, adapter in self:onlineAdapters() do adapter.dirty = true diff --git a/milo/plugins/refreshTask.lua b/milo/plugins/refreshTask.lua index 9b89f81..3d87078 100644 --- a/milo/plugins/refreshTask.lua +++ b/milo/plugins/refreshTask.lua @@ -1,7 +1,5 @@ local Milo = require('milo') --- Do a full scan of inventories every minute - local RefreshTask = { name = 'refresher', priority = 0, @@ -20,10 +18,6 @@ function RefreshTask:cycle(context) end end end - --- if os.clock() - context.storage.lastRefresh > 60 then --- context.storage:refresh() --- end end Milo:registerTask(RefreshTask) From 9c81386a8ebe928e698e3e5d053508e417c83261 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Mon, 3 Dec 2018 18:55:20 -0500 Subject: [PATCH 152/165] milo: improve list perf --- core/Music.lua | 70 ++++++++++++++++++++++++--------------- milo/MiloRemote.lua | 17 +++++++--- milo/apis/miniAdapter.lua | 57 +++++++++++++++++++++++++++++++ milo/apis/storage.lua | 4 +-- milo/plugins/remote.lua | 2 +- 5 files changed, 115 insertions(+), 35 deletions(-) create mode 100644 milo/apis/miniAdapter.lua diff --git a/core/Music.lua b/core/Music.lua index 118ca4f..c33f3ef 100644 --- a/core/Music.lua +++ b/core/Music.lua @@ -3,32 +3,23 @@ _G.requireInjector() local Event = require('event') local UI = require('ui') -local colors = _G.colors -local peripheral = _G.peripheral +local colors = _G.colors +local device = _G.device +local turtle = _G.turtle -local speaker = peripheral.find('speaker') or - error('Speaker must be attached') +if not turtle then + error('This program can only be run on a turtle') +end + +local radio = device.drive or error('No drive attached') +if radio.side ~= 'top' and radio.side ~= 'bottom' then + error('Disk drive must be above or below turtle') +end UI:configure('Music', ...) UI.Button.defaults.backgroundFocusColor = colors.gray -local songs = { - { record = 'record.11', title = '11' }, - { record = 'record.13', title = '13' }, - { record = 'record.block', title = 'Block' }, - { record = 'record.cat', title = 'Cat' }, - { record = 'record.chirp', title = 'Chirp' }, - { record = 'record.far', title = 'Faf' }, - { record = 'record.mall', title = 'Mall' }, - { record = 'record.mellohi', title = 'Mellohi' }, - { record = 'record.stal', title = 'Stal' }, - { record = 'record.strad', title = 'Strad' }, - { record = 'record.wait', title = 'Wait' }, - { record = 'record.ward', title = 'Ward' }, -} -local songNo = 1 - local page = UI.Page({ volume = 15, stationName = UI.Text({ @@ -177,18 +168,41 @@ function page:setVolume(volume) end function page:seek() - songNo = songNo + 1 - if songNo > #songs then - songNo = 1 - end + + local actions = { + top = { + suck = turtle.suckUp, + drop = turtle.dropUp, + }, + bottom = { + suck = turtle.suckDown, + drop = turtle.dropDown, + }, + } + + local slot = turtle.selectOpenSlot() + actions[radio.side].suck() + repeat + slot = slot + 1 + if (slot > 16) then + slot = 1 + end + until turtle.getItemCount(slot) >= 1 + turtle.select(slot) + actions[radio.side].drop() self:updateStationName() end function page:play(onOff) self.playing = onOff if self.playing then + + if not radio.hasAudio() then + self:seek() + end + self:updateStationName() - speaker.playSound(songs[songNo]) + radio.playAudio() Event.addNamedTimer('songTimer', 180, false, function() if self.playing then @@ -199,7 +213,7 @@ function page:play(onOff) end) else - --radio.stopAudio() + radio.stopAudio() end end @@ -209,7 +223,7 @@ function page.stationName:draw() end function page:updateStationName() - local title = songs[songNo].title + local title = radio.getAudioTitle() if title then self.stationName.value = title @@ -234,7 +248,9 @@ page:setVolume(page.volume, true) UI:setPage(page) +turtle.setStatus('Jamming') UI:pullEvents() +turtle.setStatus('idle') page:play(false) UI.term:reset() diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index 42dfa04..6a3e3b1 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -244,6 +244,7 @@ function page:sendRequest(data, statusMsg) return end + local success sync(self, function() local msg for _ = 1, 2 do @@ -271,7 +272,8 @@ function page:sendRequest(data, statusMsg) end) end if socket:write(data) then - return true + success = true + return end socket:close() socket = nil @@ -279,6 +281,8 @@ function page:sendRequest(data, statusMsg) end self:setStatus(msg or 'Failed to connect') end) + + return success end function page.grid:getRowTextColor(row, selected) @@ -505,8 +509,13 @@ function page:applyFilter() end Event.addRoutine(function() - local sleepTime = 1.5 + local lastTransfer while true do + local sleepTime = 1.5 + if lastTransfer and os.clock() - lastTransfer < 3 then + sleepTime = .25 + end + os.sleep(socket and sleepTime or 5) if config.deposit then local neural = device.neuralInterface @@ -523,10 +532,8 @@ Event.addRoutine(function() slot = config.useShield and 'shield' or config.slot, count = item.count, }) then - sleepTime = math.max(sleepTime - .25, .25) + lastTransfer = os.clock() end - else - sleepTime = math.min(sleepTime + .25, 1.5) end end) if not s and m then diff --git a/milo/apis/miniAdapter.lua b/milo/apis/miniAdapter.lua new file mode 100644 index 0000000..93e5318 --- /dev/null +++ b/milo/apis/miniAdapter.lua @@ -0,0 +1,57 @@ +local class = require('class') +local itemDB = require('itemDB') +local Util = require('util') + +local device = _G.device + +local Adapter = class() + +function Adapter:init(args) + if args.side then + local inventory = device[args.side] + if inventory then + Util.merge(self, inventory) + end + end +end + +function Adapter:listItems(throttle) + local cache = { } + throttle = throttle or Util.throttle() + + for k,v in pairs(self.list()) do + if v.count > 0 then + local key = table.concat({ v.name, v.damage, v.nbtHash }, ':') + + local entry = cache[key] + if not entry then + local cached = itemDB:get(v) + if cached then + cached = Util.shallowCopy(cached) + else + cached = self.getItemMeta(k) + if cached then + cached = Util.shallowCopy(itemDB:add(cached)) + end + end + if cached then + entry = cached + entry.count = 0 + cache[key] = entry + else + _G._debug('Adapter: failed to get item details') + end + end + + if entry then + entry.count = entry.count + v.count + end + throttle() + end + end + itemDB:flush() + + self.cache = cache +end + +return Adapter diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index a5acbd6..2bd308f 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -1,6 +1,6 @@ +local Adapter = require('miniAdapter') local class = require('class') local Event = require('event') -local Adapter = require('inventoryAdapter') local itemDB = require('itemDB') local Util = require('util') @@ -61,7 +61,7 @@ function Storage:initStorage() if v.adapter then v.adapter.online = not not device[k] elseif device[k] and device[k].list and device[k].size and device[k].pullItems then - v.adapter = Adapter.wrap({ side = k }) + v.adapter = Adapter({ side = k }) v.adapter.online = true v.adapter.dirty = true elseif device[k] then diff --git a/milo/plugins/remote.lua b/milo/plugins/remote.lua index a93437b..6981c8f 100644 --- a/milo/plugins/remote.lua +++ b/milo/plugins/remote.lua @@ -75,7 +75,7 @@ local function client(socket) if not data then break end ---_G._debug(data) +_G._debug(data) socket.co = coroutine.running() if data.request == 'scan' then -- full scan of all inventories From 1421d3ed1b3d0555891136375a4c4b3aefb16f85 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Mon, 3 Dec 2018 19:22:06 -0500 Subject: [PATCH 153/165] milo: improve list perf --- milo/apis/miniAdapter.lua | 1 - milo/apis/storage.lua | 70 +++++++++++++++++++++++++-------------- 2 files changed, 45 insertions(+), 26 deletions(-) diff --git a/milo/apis/miniAdapter.lua b/milo/apis/miniAdapter.lua index 93e5318..90d472b 100644 --- a/milo/apis/miniAdapter.lua +++ b/milo/apis/miniAdapter.lua @@ -49,7 +49,6 @@ function Adapter:listItems(throttle) throttle() end end - itemDB:flush() self.cache = cache end diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 2bd308f..fa747fb 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -2,6 +2,7 @@ local Adapter = require('miniAdapter') local class = require('class') local Event = require('event') local itemDB = require('itemDB') +local sync = require('sync').sync local Util = require('util') local device = _G.device @@ -168,35 +169,54 @@ function Storage:listItems(throttle) end local cache = { } - throttle = throttle or Util.throttle() + sync(self, function() - local timer = Timer() - for _, adapter in self:onlineAdapters() do - if adapter.dirty then - _G._debug('STORAGE: refreshing ' .. adapter.name) - adapter:listItems(throttle) - adapter.dirty = false - end - local rcache = adapter.cache or { } - for key,v in pairs(rcache) do - local entry = cache[key] - if not entry then - entry = Util.shallowCopy(v) - entry.count = v.count - entry.key = key - cache[key] = entry - else - entry.count = entry.count + v.count + throttle = throttle or Util.throttle() + + local t = { } + for _, adapter in self:onlineAdapters() do + if adapter.dirty then + table.insert(t, function() + adapter:listItems(throttle) + adapter.dirty = false + end) end - - throttle() end - end -_G._debug('STORAGE: refresh in ' .. timer()) - self.dirty = false - self.cache = cache - return cache + _G._debug('STORAGE: refreshing ' .. #t .. ' inventories') + local timer = Timer() + parallel.waitForAll(table.unpack(t)) + _G._debug('STORAGE: refresh in ' .. timer()) + + local timer = Timer() + for _, adapter in self:onlineAdapters() do + if adapter.dirty then + _G._debug('STORAGE: refreshing ' .. adapter.name) + --adapter:listItems(throttle) + --adapter.dirty = false + end + local rcache = adapter.cache or { } + for key,v in pairs(rcache) do + local entry = cache[key] + if not entry then + entry = Util.shallowCopy(v) + entry.count = v.count + entry.key = key + cache[key] = entry + else + entry.count = entry.count + v.count + end + + throttle() + end + end + itemDB:flush() + _G._debug('STORAGE: cached in ' .. timer()) + + self.dirty = false + self.cache = cache + end) + return self.cache end function Storage:updateCache(adapter, item, count) From 21d182fd55beb8fc70d8f638ad266b26a26e99ed Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Mon, 3 Dec 2018 20:04:43 -0500 Subject: [PATCH 154/165] milo: improve list perf --- milo/Milo.lua | 1 - milo/apis/storage.lua | 32 ++++++++++++++++---------------- milo/core/machines.lua | 22 +++++++++++----------- 3 files changed, 27 insertions(+), 28 deletions(-) diff --git a/milo/Milo.lua b/milo/Milo.lua index a7350f1..9d531cd 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -108,7 +108,6 @@ end local localName = modem.getNameLocal() local context = { - nodes = nodes, resources = Util.readTable(Milo.RESOURCE_FILE) or { }, state = { }, diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index fa747fb..71ff3ea 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -5,10 +5,11 @@ local itemDB = require('itemDB') local sync = require('sync').sync local Util = require('util') -local device = _G.device -local os = _G.os +local device = _G.device +local os = _G.os +local parallel = _G.parallel -local Storage = class() +local Storage = class() function Storage:init(nodes) local defaults = { @@ -164,13 +165,13 @@ end -- provide a consolidated list of items function Storage:listItems(throttle) - if not self.dirty then - return self.cache - end - - local cache = { } - sync(self, function() + --sync(self, function() + if not self.dirty then + return self.cache + end + local timer = Timer() + local cache = { } throttle = throttle or Util.throttle() local t = { } @@ -183,12 +184,11 @@ function Storage:listItems(throttle) end end - _G._debug('STORAGE: refreshing ' .. #t .. ' inventories') - local timer = Timer() - parallel.waitForAll(table.unpack(t)) - _G._debug('STORAGE: refresh in ' .. timer()) + if #t > 0 then + _G._debug('STORAGE: refreshing ' .. #t .. ' inventories') + parallel.waitForAll(table.unpack(t)) + end - local timer = Timer() for _, adapter in self:onlineAdapters() do if adapter.dirty then _G._debug('STORAGE: refreshing ' .. adapter.name) @@ -211,11 +211,11 @@ function Storage:listItems(throttle) end end itemDB:flush() - _G._debug('STORAGE: cached in ' .. timer()) + _G._debug('STORAGE: refresh in ' .. timer()) self.dirty = false self.cache = cache - end) + --end) return self.cache end diff --git a/milo/core/machines.lua b/milo/core/machines.lua index 6a88a29..2742439 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -15,15 +15,15 @@ local nodeWizard local function saveConfig() local t = { } - for k,v in pairs(context.nodes) do + for k,v in pairs(context.storage.nodes) do t[k] = v.adapter v.adapter = nil end - Config.update('milo', context.nodes) + Config.update('milo', context.storage.nodes) for k,v in pairs(t) do - context.nodes[k].adapter = v + context.storage.nodes[k].adapter = v end context.storage:initStorage() end @@ -42,7 +42,7 @@ local networkPage = UI.Page { }, grid = UI.ScrollingGrid { y = 2, ey = -3, - values = context.nodes, + values = context.storage.nodes, columns = { { key = 'suffix', width = 4, justify = 'right' }, { heading = 'Name', key = 'displayName' }, @@ -106,7 +106,7 @@ end function networkPage:getList() for _, v in pairs(device) do - if not context.nodes[v.name] then + if not context.storage.nodes[v.name] then local node = { name = v.name, mtype = 'ignore', @@ -114,7 +114,7 @@ function networkPage:getList() } for _, page in pairs(nodeWizard.wizard.pages) do if page.isValidType and page:isValidType(node) then - context.nodes[v.name] = node + context.storage.nodes[v.name] = node break end end @@ -154,7 +154,7 @@ function networkPage:disable() end function networkPage:applyFilter() - local t = Util.filter(context.nodes, function(v) + local t = Util.filter(context.storage.nodes, function(v) return v.mtype ~= 'hidden' end) @@ -181,7 +181,7 @@ function networkPage:eventHandler(event) elseif event.type == 'remove_node' then local node = self.grid:getSelected() if node then - context.nodes[node.name] = nil + context.storage.nodes[node.name] = nil saveConfig() end self:applyFilter() @@ -500,9 +500,9 @@ function nodeWizard:eventHandler(event) return true end) - Util.clear(context.nodes[self.node.name]) - Util.merge(context.nodes[self.node.name], self.node) - context.nodes[self.node.name].adapter = adapter + Util.clear(context.storage.nodes[self.node.name]) + Util.merge(context.storage.nodes[self.node.name], self.node) + context.storage.nodes[self.node.name].adapter = adapter saveConfig() From 0d13af3827797df37ba30a6cf7372a2ec40de65d Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Tue, 4 Dec 2018 01:33:17 -0500 Subject: [PATCH 155/165] milo: speaker setup --- milo/core/listing.lua | 2 +- milo/plugins/speakerView.lua | 73 ++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 milo/plugins/speakerView.lua diff --git a/milo/core/listing.lua b/milo/core/listing.lua index 3e22299..5e01adb 100644 --- a/milo/core/listing.lua +++ b/milo/core/listing.lua @@ -178,7 +178,7 @@ function page:eject(amount) item.count = request.current - request.count if request.count + request.craft > 0 then self.grid:draw() - Sound.play('ui.button.click', .3) + Sound.play('ui.button.click') return true end end diff --git a/milo/plugins/speakerView.lua b/milo/plugins/speakerView.lua new file mode 100644 index 0000000..e51978c --- /dev/null +++ b/milo/plugins/speakerView.lua @@ -0,0 +1,73 @@ +local Milo = require('milo') +local Sound = require('sound') +local UI = require('ui') + +local colors = _G.colors +local context = Milo:getContext() +local device = _G.device + +local speakerNode = context.storage:getSingleNode('speaker') +if speakerNode then + Sound.setVolume(speakerNode.volume) +end + +local wizardPage = UI.Window { + title = 'Speaker', + index = 2, + backgroundColor = colors.cyan, + [1] = UI.Text { + x = 2, y = 2, + textColor = colors.yellow, + value = 'Set the volume for sound effects', + }, + form = UI.Form { + x = 2, ex = -2, y = 3, ey = -2, + manualControls = true, + volume = UI.TextEntry { + formLabel = 'Volume', formKey = 'volume', + width = 5, limit = 3, + validate = 'numeric', + help = 'A value from 0 (mute) to 1 (loud)', + }, + testSound = UI.Button { + x = 15, y = 2, + text = 'Test', event = 'test_sound', + help = 'Test sound volume', + }, + }, +} + +function wizardPage:setNode(node) + self.node = node + self.form:setValues(node) +end + +function wizardPage:validate() + if self.form:save() then + Sound.setVolume(self.node.volume) + return true + end +end + +function wizardPage:isValidType(node) + local m = device[node.name] + return m and m.type == 'speaker' and { + name = 'Speaker', + value = 'speaker', + category = 'custom', + help = 'Sound effects', + } +end + +function wizardPage:isValidFor(node) + return node.mtype == 'speaker' +end + +function wizardPage:eventHandler(event) + if event.type == 'test_sound' then + local vol = tonumber(self.form.volume.value) + Sound.play('entity.item.pickup', vol) + end +end + +UI:getPage('nodeWizard').wizard:add({ speaker = wizardPage }) From e90e6cebdd2eb8acffea91a2c98c9bacb1b92924 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Tue, 4 Dec 2018 02:14:31 -0500 Subject: [PATCH 156/165] floppy disk name fix --- core/apis/itemDB.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/apis/itemDB.lua b/core/apis/itemDB.lua index 5ecea03..eec7ce8 100644 --- a/core/apis/itemDB.lua +++ b/core/apis/itemDB.lua @@ -139,6 +139,10 @@ function itemDB:add(baseItem) nItem.displayName = nItem.displayName .. v.fullName end + -- disks + elseif baseItem.media then + -- don't ignore nbt... as disks can be labeled + -- potions elseif nItem.name == 'minecraft:potion' or nItem.name == 'minecraft:lingering_potion' then if baseItem.effects then From fff04ec01a3033f4f048d5bbe31a54bf121d60aa Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Tue, 4 Dec 2018 18:49:01 -0500 Subject: [PATCH 157/165] milo: simplify learn types + monitor resize --- milo/Milo.lua | 1 - milo/apis/craft2.lua | 10 +++- milo/apps/cobblegen.lua | 2 +- milo/core/learnWizard.lua | 90 +++++++++++++++++++++++++++++++ milo/core/listing.lua | 3 +- milo/core/machines.lua | 53 ++++++++++-------- milo/plugins/activityView.lua | 30 ++++++++--- milo/plugins/brewingStandView.lua | 4 +- milo/plugins/craftTask.lua | 1 - milo/plugins/demandCraft.lua | 5 +- milo/plugins/jobMonitor.lua | 26 +++++++-- milo/plugins/learn.lua | 68 +---------------------- milo/plugins/machineLearn.lua | 79 ++++++++++----------------- milo/plugins/redstoneTask.lua | 32 ----------- milo/plugins/redstoneView.lua | 73 ------------------------- milo/plugins/remote.lua | 2 +- milo/plugins/speakerView.lua | 10 ++-- milo/plugins/turtleLearn.lua | 41 ++++---------- 18 files changed, 225 insertions(+), 305 deletions(-) create mode 100644 milo/core/learnWizard.lua delete mode 100644 milo/plugins/redstoneTask.lua delete mode 100644 milo/plugins/redstoneView.lua diff --git a/milo/Milo.lua b/milo/Milo.lua index 9d531cd..eb412dd 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -112,7 +112,6 @@ local context = { state = { }, craftingQueue = { }, - learnTypes = { }, tasks = { }, queue = { }, diff --git a/milo/apis/craft2.lua b/milo/apis/craft2.lua index ed48863..daa76bd 100644 --- a/milo/apis/craft2.lua +++ b/milo/apis/craft2.lua @@ -1,7 +1,6 @@ local itemDB = require('itemDB') local Util = require('util') -local device = _G.device local fs = _G.fs local turtle = _G.turtle @@ -81,8 +80,15 @@ local function machineCraft(recipe, storage, machineName, request, count, item) return end + if not machine.adapter or not machine.adapter.online then + request.status = 'machine offline' + request.statusCode = Craft.STATUS_ERROR + return + end + + local list = machine.adapter.list() for k in pairs(recipe.ingredients) do - if machine.adapter.getItemMeta(k) then + if list[k] then request.status = 'machine in use' request.statusCode = Craft.STATUS_WARNING return diff --git a/milo/apps/cobblegen.lua b/milo/apps/cobblegen.lua index a86d2ac..be7ae54 100644 --- a/milo/apps/cobblegen.lua +++ b/milo/apps/cobblegen.lua @@ -27,4 +27,4 @@ while true do end print('digging') turtle.digDown() -end \ No newline at end of file +end diff --git a/milo/core/learnWizard.lua b/milo/core/learnWizard.lua new file mode 100644 index 0000000..f99e8c8 --- /dev/null +++ b/milo/core/learnWizard.lua @@ -0,0 +1,90 @@ +local Milo = require('milo') +local UI = require('ui') + +local turtle = _G.turtle + +local learnPage = UI.Page { + titleBar = UI.TitleBar { title = 'Learn Recipe' }, + wizard = UI.Wizard { + y = 2, ey = -2, + pages = { + general = UI.Window { + index = 1, + grid = UI.ScrollingGrid { + x = 2, ex = -2, y = 2, ey = -2, + disableHeader = true, + columns = { + { heading = 'Name', key = 'name'}, + }, + sortColumn = 'name', + }, + }, + }, + }, + notification = UI.Notification { }, +} + +local general = learnPage.wizard.pages.general + +function general:validate() + Milo:setState('learnType', self.grid:getSelected().value) + return true +end + +function learnPage:enable() + local t = { } + + for _, page in pairs(self.wizard.pages) do + if page.validFor then + t[page.validFor] = { + name = page.validFor, + value = page.validFor, + } + end + end + general.grid:setValues(t) + general.grid:setSelected('name', Milo:getState('learnType') or '') + + Milo:pauseCrafting({ key = 'gridInUse', msg = 'Crafting paused' }) + + self:focusFirst() + UI.Page.enable(self) +end + +function learnPage:disable() + Milo:resumeCrafting({ key = 'gridInUse' }) + return UI.Page.disable(self) +end + +function learnPage.wizard:getPage(index) + local pages = { } + table.insert(pages, general) + local selected = general.grid:getSelected() + for _, page in pairs(self.pages) do + if page.validFor and (not selected or selected.value == page.validFor) then + table.insert(pages, page) + end + end + table.sort(pages, function(a, b) + return a.index < b.index + end) + + return pages[index] +end + +function learnPage:eventHandler(event) + if event.type == 'cancel' then + turtle.emptyInventory() + UI:setPreviousPage() + + elseif event.type == 'form_invalid' or event.type == 'general_error' then + self.notification:error(event.message) + self:setFocus(event.field) + + else + return UI.Page.eventHandler(self, event) + end + return true +end + +UI:addPage('learnWizard', learnPage) diff --git a/milo/core/listing.lua b/milo/core/listing.lua index 5e01adb..ba085a9 100644 --- a/milo/core/listing.lua +++ b/milo/core/listing.lua @@ -234,7 +234,7 @@ function page:eventHandler(event) Milo:setState('displayMode', displayMode) elseif event.type == 'learn' then - UI:setPage('learn') + UI:setPage('learnWizard') elseif event.type == 'craft' then local item = self.grid:getSelected() @@ -242,7 +242,6 @@ function page:eventHandler(event) if Craft.findRecipe(item) then -- or item.is_craftable then UI:setPage('craft', self.grid:getSelected()) else - Sound.play('entity.villager.no') self.notification:error('No recipe defined') end end diff --git a/milo/core/machines.lua b/milo/core/machines.lua index 2742439..fb79700 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -395,6 +395,10 @@ function nodeWizard.wizard.pages.general:enable() self:focusFirst() end +function nodeWizard.wizard.pages.general:isValidFor() + return false +end + function nodeWizard.wizard.pages.general:showInventory(node) local inventory @@ -418,31 +422,28 @@ end function nodeWizard.wizard.pages.general:validate() if self.form:save() then - _G._p3 = nodeWizard.choices - _G._p4 = nodeWizard.node nodeWizard.node.category = Util.find(nodeWizard.choices, 'value', nodeWizard.node.mtype).category + + nodeWizard.nodePages = { } + table.insert(nodeWizard.nodePages, nodeWizard.wizard.pages.general) for _, page in pairs(nodeWizard.wizard.pages) do - page.index = nil - end - local index = 2 - nodeWizard.wizard.pages.general.index = 1 - nodeWizard.wizard.pages.confirmation.index = 2 - for _, page in pairs(nodeWizard.wizard.pages) do - if not page.index then - if not page.isValidFor or page:isValidFor(nodeWizard.node) then - page.index = index - index = index + 1 - if page.setNode then - page:setNode(nodeWizard.node) - end + if not page.isValidFor or page:isValidFor(nodeWizard.node) then + table.insert(nodeWizard.nodePages, page) + if page.setNode then + page:setNode(nodeWizard.node) end end end - nodeWizard.wizard.pages.confirmation.index = index + table.insert(nodeWizard.nodePages, nodeWizard.wizard.pages.confirmation) return true end end +--[[ Confirmation ]]-- +function nodeWizard.wizard.pages.confirmation:isValidFor() + return false +end + --[[ Wizard ]] -- function nodeWizard:enable(node) local adapter = node.adapter @@ -469,17 +470,17 @@ function nodeWizard:enable(node) self.wizard.pages.general:showInventory(self.node) - -- restore indices - for _, page in pairs(self.wizard.pages) do - if not page.oindex then - page.oindex = page.index - end - page.index = page.oindex - end + self.nodePages = { } + table.insert(self.nodePages, self.wizard.pages.general) + table.insert(self.nodePages, self.wizard.pages.confirmation) UI.Page.enable(self) end +function nodeWizard.wizard:getPage(index) + return nodeWizard.nodePages[index] +end + function nodeWizard:eventHandler(event) if event.type == 'cancel' then UI:setPreviousPage() @@ -500,6 +501,12 @@ function nodeWizard:eventHandler(event) return true end) + for _, page in pairs(self.nodePages) do + if page.saveNode then + page:saveNode(self.node) + end + end + Util.clear(context.storage.nodes[self.node.name]) Util.merge(context.storage.nodes[self.node.name], self.node) context.storage.nodes[self.node.name].adapter = adapter diff --git a/milo/plugins/activityView.lua b/milo/plugins/activityView.lua index 31c0f66..c2d5347 100644 --- a/milo/plugins/activityView.lua +++ b/milo/plugins/activityView.lua @@ -7,6 +7,7 @@ local Util = require('util') local colors = _G.colors local context = Milo:getContext() local device = _G.device +local os = _G.os --[[ Configuration Page ]]-- local template = @@ -30,10 +31,10 @@ local wizardPage = UI.Window { formLabel = 'Font Size', formKey = 'textScale', nochoice = 'Small', choices = { - { name = 'Small', value = .5, help = '(requires restart)', }, - { name = 'Large', value = 1, help = '(requires restart)', }, + { name = 'Small', value = .5 }, + { name = 'Large', value = 1 }, }, - help = 'Adjust text scaling (requires restart)', + help = 'Adjust text scaling', }, }, } @@ -46,6 +47,10 @@ function wizardPage:validate() return self.form:save() end +function wizardPage:saveNode(node) + os.queueEvent('monitor_resize', node.name) +end + function wizardPage:isValidType(node) local m = device[node.name] return m and m.type == 'monitor' and { @@ -71,8 +76,8 @@ local function createPage(node) }, grid = UI.Grid { columns = { - { heading = 'Qty', key = 'count', width = 6 }, - { heading = 'Change', key = 'change', width = 6 }, + { heading = 'Qty', key = 'count', width = 5 }, + { heading = 'Change', key = 'change', width = 5 }, { heading = 'Rate', key = 'rate', width = 6 }, { heading = 'Name', key = 'displayName' }, }, @@ -106,7 +111,6 @@ local function createPage(node) function page:reset() self.lastItems = nil self.grid:setValues({ }) - self.grid:clear() self.grid:draw() end @@ -176,6 +180,20 @@ end local pages = { } +Event.on('monitor_resize', function(_, side) + for node in context.storage:filterActive('activity') do + if node.name == side and pages[node.name] then + local p = pages[node.name] + p.parent:setTextScale(node.textScale or .5) + p.parent:resize() + p:resize() + p:draw() + p:sync() + break + end + end +end) + Event.on('monitor_touch', function(_, side) local function filter(node) return node.adapter.name == side and pages[node.name] diff --git a/milo/plugins/brewingStandView.lua b/milo/plugins/brewingStandView.lua index c63cdc1..1d11816 100644 --- a/milo/plugins/brewingStandView.lua +++ b/milo/plugins/brewingStandView.lua @@ -8,8 +8,8 @@ local template = [[%sBrewing stands have the ability to automatically learn recipes.%s Simply craft potions in the brewing stand as normal except for these conditions. -1. Place item in top slot first. -2. At least 1 bottle must be placed in the first slot. +1. Place item in top slot FIRST. +2. Place all 3 bottles. When finished brewing, the recipe will be available upon refreshing. diff --git a/milo/plugins/craftTask.lua b/milo/plugins/craftTask.lua index d748e6d..546027e 100644 --- a/milo/plugins/craftTask.lua +++ b/milo/plugins/craftTask.lua @@ -3,7 +3,6 @@ local Milo = require('milo') local Util = require('util') local context = Milo:getContext() -local turtle = _G.turtle local craftTask = { name = 'crafting', diff --git a/milo/plugins/demandCraft.lua b/milo/plugins/demandCraft.lua index 882944d..c546f2d 100644 --- a/milo/plugins/demandCraft.lua +++ b/milo/plugins/demandCraft.lua @@ -63,10 +63,7 @@ end function craftPage.wizard.pages.resources.grid:getDisplayValues(row) local function dv(v) - if v == 0 then - return '' - end - return Util.toBytes(v) + return v == 0 and '' or Util.toBytes(v) end row = Util.shallowCopy(row) row.total = Util.toBytes(row.total) diff --git a/milo/plugins/jobMonitor.lua b/milo/plugins/jobMonitor.lua index a73c9c8..0d34a47 100644 --- a/milo/plugins/jobMonitor.lua +++ b/milo/plugins/jobMonitor.lua @@ -8,6 +8,7 @@ local Util = require('util') local colors = _G.colors local context = Milo:getContext() local device = _G.device +local os = _G.os --[[ Configuration Screen ]] local wizardPage = UI.Window { @@ -28,10 +29,10 @@ local wizardPage = UI.Window { formLabel = 'Font Size', formKey = 'textScale', nochoice = 'Small', choices = { - { name = 'Small', value = .5, help = '(requires restart)', }, - { name = 'Large', value = 1, help = '(requires restart)', }, + { name = 'Small', value = .5 }, + { name = 'Large', value = 1 }, }, - help = 'Adjust text scaling (requires restart)', + help = 'Adjust text scaling', }, }, } @@ -40,6 +41,10 @@ function wizardPage:setNode(node) self.form:setValues(node) end +function wizardPage:saveNode(node) + os.queueEvent('monitor_resize', node.name) +end + function wizardPage:validate() return self.form:save() end @@ -70,7 +75,6 @@ local function createPage(node) }, grid = UI.Grid { sortColumn = 'index', - backgroundFocusColor = colors.black, columns = { { heading = 'Qty', key = 'remaining', width = 4 }, { heading = 'Crafting', key = 'displayName', }, @@ -142,6 +146,20 @@ end local pages = { } +Event.on('monitor_resize', function(_, side) + for node in context.storage:filterActive('jobs') do + if node.name == side and pages[node.name] then + local p = pages[node.name] + p.parent:setTextScale(node.textScale or .5) + p.parent:resize() + p:resize() + p:draw() + p:sync() + break + end + end +end) + Event.on({ 'milo_resume', 'milo_pause' }, function(_, reason) for node in context.storage:filterActive('jobs') do local page = pages[node.name] diff --git a/milo/plugins/learn.lua b/milo/plugins/learn.lua index 21fa1ef..bc9235c 100644 --- a/milo/plugins/learn.lua +++ b/milo/plugins/learn.lua @@ -1,67 +1 @@ -local Milo = require('milo') -local UI = require('ui') - -local context = Milo:getContext() -local turtle = _G.turtle - -local learnPage = UI.Dialog { - height = 9, width = UI.term.width - 6, - title = 'Learn Recipe', - grid = UI.ScrollingGrid { - x = 2, ex = -2, y = 3, height = 4, - disableHeader = true, - columns = { - { heading = 'Name', key = 'name'}, - }, - sortColumn = 'name', - }, - cancel = UI.Button { - x = 3, y = -2, - text = 'Cancel', event = 'cancel' - }, - accept = UI.Button { - ex = -3, y = -2, - width = 8, - text = 'Ok', event = 'accept', - }, -} - -function learnPage:enable() - local t = { } - for k in pairs(context.learnTypes) do - table.insert(t, { - name = k, - value = k, - }) - end - self.grid:setValues(t) - self.grid:setSelected('name', Milo:getState('learnType') or '') - - Milo:pauseCrafting({ key = 'gridInUse', msg = 'Crafting paused' }) - - self:focusFirst() - UI.Dialog.enable(self) -end - -function learnPage:disable() - UI.Dialog.disable(self) -end - -function learnPage:eventHandler(event) - if event.type == 'cancel' then - Milo:resumeCrafting({ key = 'gridInUse' }) - turtle.emptyInventory() - UI:setPreviousPage() - - elseif event.type == 'accept' or event.type == 'grid_select' then - local choice = self.grid:getSelected().value - - Milo:setState('learnType', choice) - UI:setPage(context.learnTypes[choice]) - else - return UI.Dialog.eventHandler(self, event) - end - return true -end - -UI:addPage('learn', learnPage) +-- moved \ No newline at end of file diff --git a/milo/plugins/machineLearn.lua b/milo/plugins/machineLearn.lua index 9a743c4..38fccb0 100644 --- a/milo/plugins/machineLearn.lua +++ b/milo/plugins/machineLearn.lua @@ -5,46 +5,38 @@ local Util = require('util') local colors = _G.colors local device = _G.device -local turtle = _G.turtle local context = Milo:getContext() +local machine -local machineLearnWizard = UI.Page { - titleBar = UI.TitleBar { title = 'Select machine' }, - wizard = UI.Wizard { - y = 2, ey = -2, - pages = { - machines = UI.Window { - index = 1, - grid = UI.ScrollingGrid { - y = 2, ey = -2, - columns = { - { heading = 'Name', key = 'displayName' }, - }, - sortColumn = 'displayName', - }, +local pages = { + machines = UI.Window { + index = 2, + validFor = 'Machine Processing', + grid = UI.ScrollingGrid { + y = 2, ey = -2, + columns = { + { heading = 'Name', key = 'displayName' }, }, - confirmation = UI.Window { - index = 2, - notice = UI.TextArea { - x = 2, ex = -2, y = 2, ey = -2, - backgroundColor = colors.black, - value = + sortColumn = 'displayName', + }, + }, + confirmation = UI.Window { + index = 3, + validFor = 'Machine Processing', + notice = UI.TextArea { + x = 2, ex = -2, y = 2, ey = -2, + backgroundColor = colors.black, + value = [[Place items in slots according to the machine's inventory. Place the result in the last slot of the turtle. Example: Slot 1 is the top slot in a furnace.]], - }, - }, }, }, - notification = UI.Notification { }, } -local pages = machineLearnWizard.wizard.pages -local machine - function pages.machines.grid:getDisplayValues(row) row = Util.shallowCopy(row) row.displayName = row.displayName or row.name @@ -64,18 +56,18 @@ end function pages.machines:validate() local selected = self.grid:getSelected() if not selected then - machineLearnWizard.notification:error('No machines configured') + self:emit({ type = 'general_error', message = 'No machines configured' }) return end machine = device[selected.name] if not machine then - machineLearnWizard.notification:error('Machine not found') + self:emit({type = 'general_error', message = 'Machine not found' }) return end if not machine.size then - machineLearnWizard.notification:error('Invalid machine') + self:emit({ type = 'general_error', message = 'Invalid machine' }) return end @@ -90,19 +82,21 @@ function pages.confirmation:validate() inventory[16] = nil if not result then - machineLearnWizard.notification:error('Result must be placed in last slot') + self:emit({ type = 'general_error', message = 'Result must be placed in last slot' }) return end if Util.empty(inventory) then - machineLearnWizard.notification:error('Ingredients not present') + self:emit({ type = 'general_error', message = 'Ingredients not present' }) return end for k in pairs(inventory) do if k > slotCount then - machineLearnWizard.notification:error( - 'Slot ' .. k .. ' is not valid\nThe valid slots are 1 - ' .. machine.size()) + self:emit({ + type = 'general_error', + message = 'Slot ' .. k .. ' is not valid\nThe valid slots are 1 - ' .. machine.size() + }) return end end @@ -130,19 +124,4 @@ function pages.confirmation:validate() return true end -function machineLearnWizard:disable() - Milo:resumeCrafting({ key = 'gridInUse' }) - UI.Page.disable(self) -end - -function machineLearnWizard:eventHandler(event) - if event.type == 'cancel' then - turtle.emptyInventory() - UI:setPage('listing') - else - return UI.Page.eventHandler(self, event) - end - return true -end - -context.learnTypes['Machine processing'] = machineLearnWizard +UI:getPage('learnWizard').wizard:add(pages) diff --git a/milo/plugins/redstoneTask.lua b/milo/plugins/redstoneTask.lua deleted file mode 100644 index 6d4e841..0000000 --- a/milo/plugins/redstoneTask.lua +++ /dev/null @@ -1,32 +0,0 @@ -local Event = require('event') -local Milo = require('milo') - -local device = _G.device - -local RedstoneTask = { - name = 'redstone', - priority = 40, -} - -function RedstoneTask:cycle(context) - for v in context.storage:filterActive({ 'mtype', 'machine' }) do - if v.redstone then - local ri = device[v.redstone.integrator] - if not ri or not v.adapter then - _debug(v.redstone) - else - local function conditionsSatisfied() - return not not next(v.adapter.list()) - end - if conditionsSatisfied() then - ri.setOutput(v.redstone.side, true) - Event.onTimeout(.25, function() - ri.setOutput(v.redstone.side, false) - end) - end - end - end - end -end - -Milo:registerTask(RedstoneTask) diff --git a/milo/plugins/redstoneView.lua b/milo/plugins/redstoneView.lua deleted file mode 100644 index 07678a3..0000000 --- a/milo/plugins/redstoneView.lua +++ /dev/null @@ -1,73 +0,0 @@ -local UI = require('ui') - -local colors = _G.colors -local device = _G.device - -local dispenserView = UI.Window { - index = 10, - title = 'Redstone Control', - backgroundColor = colors.cyan, - form = UI.Form { - x = 1, y = 2, ex = -1, ey = -2, - manualControls = true, - [1] = UI.TextEntry { - formLabel = 'Interval', formKey = 'interval', - help = 'Pulse redstone if items are present', - limit = 6, - validate = 'numeric', - }, - [2] = UI.Chooser { - formLabel = 'Integrator', formKey = 'integrator', - nochoice = 'disable', - help = 'Control via redstone', - }, - [3] = UI.Chooser { - width = 10, - formLabel = 'Side', formKey = 'side', - choices = { - { name = 'up', value = 'up' }, - { name = 'down', value = 'down' }, - { name = 'east', value = 'east' }, - { name = 'north', value = 'north' }, - { name = 'west', value = 'west' }, - { name = 'south', value = 'south' }, - }, - help = 'Output side', - }, - }, -} - -function dispenserView:isValidFor(node) - if node.mtype == 'machine' then - local m = device[node.name] - --return m and m.type == 'minecraft:dispenser' - end -end - -function dispenserView:enable() - UI.Window.enable(self) - self:focusFirst() - - self.form[2].choices = { } - for _,m in pairs(device) do - if m.type == 'redstone_integrator' then - table.insert(self.form[2].choices, { - name = m.name, - value = m.name, - }) - end - end -end - -function dispenserView:validate() - return self.form:save() -end - -function dispenserView:setNode(node) - if not node.redstone then - node.redstone = { } - end - self.form:setValues(node.redstone) -end - ---UI:getPage('nodeWizard').wizard:add({ dispenser = dispenserView }) diff --git a/milo/plugins/remote.lua b/milo/plugins/remote.lua index 6981c8f..a93437b 100644 --- a/milo/plugins/remote.lua +++ b/milo/plugins/remote.lua @@ -75,7 +75,7 @@ local function client(socket) if not data then break end -_G._debug(data) +--_G._debug(data) socket.co = coroutine.running() if data.request == 'scan' then -- full scan of all inventories diff --git a/milo/plugins/speakerView.lua b/milo/plugins/speakerView.lua index e51978c..da063bd 100644 --- a/milo/plugins/speakerView.lua +++ b/milo/plugins/speakerView.lua @@ -38,15 +38,15 @@ local wizardPage = UI.Window { } function wizardPage:setNode(node) - self.node = node self.form:setValues(node) end +function wizardPage:saveNode(node) + Sound.setVolume(node.volume) +end + function wizardPage:validate() - if self.form:save() then - Sound.setVolume(self.node.volume) - return true - end + return self.form:save() end function wizardPage:isValidType(node) diff --git a/milo/plugins/turtleLearn.lua b/milo/plugins/turtleLearn.lua index 008a4a8..e5096ae 100644 --- a/milo/plugins/turtleLearn.lua +++ b/milo/plugins/turtleLearn.lua @@ -104,30 +104,19 @@ local function learnRecipe() return recipe end -local turtleLearnWizard = UI.Page { - titleBar = UI.TitleBar { title = 'Learn a crafting recipe' }, - wizard = UI.Wizard { - y = 2, ey = -3, - pages = { - confirmation = UI.Window { - index = 1, - notice = UI.TextArea { - x = 2, ex = -2, y = 2, ey = -2, - value = +local pages = { + turtleCraft = UI.Window { + index = 2, + validFor = 'Turtle Crafting', + notice = UI.TextArea { + x = 2, ex = -2, y = 2, ey = -2, + value = [[Place recipe in turtle!]], - }, - }, }, }, - notification = UI.Notification { }, } -function turtleLearnWizard:disable() - Milo:resumeCrafting({ key = 'gridInUse' }) - UI.Page.disable(self) -end - -function turtleLearnWizard.wizard.pages.confirmation:validate() +function pages.turtleCraft:validate() local recipe, msg = learnRecipe(self) if recipe then @@ -139,18 +128,8 @@ function turtleLearnWizard.wizard.pages.confirmation:validate() }) return true else - turtleLearnWizard.notification:error(msg) + self:emit({ type = 'general_error', message = msg }) end end -function turtleLearnWizard:eventHandler(event) - if event.type == 'cancel' then - turtle.emptyInventory() - UI:setPage('listing') - else - return UI.Page.eventHandler(self, event) - end - return true -end - -context.learnTypes['Turtle crafting'] = turtleLearnWizard +UI:getPage('learnWizard').wizard:add(pages) From aa3e3e615ca8c3717018268fe2569cec11aa21cc Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Tue, 4 Dec 2018 21:21:32 -0500 Subject: [PATCH 158/165] learn --- milo/core/learnWizard.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/milo/core/learnWizard.lua b/milo/core/learnWizard.lua index f99e8c8..34e6633 100644 --- a/milo/core/learnWizard.lua +++ b/milo/core/learnWizard.lua @@ -18,6 +18,9 @@ local learnPage = UI.Page { }, sortColumn = 'name', }, + accelerators = { + grid_select = 'nextView', + }, }, }, }, From c79c93e4d713cb1c6e57c0e655853f7c384b15d9 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Wed, 5 Dec 2018 00:55:49 -0500 Subject: [PATCH 159/165] milo tweaks --- milo/MiloRemote.lua | 25 ++++++++++++------------- milo/apps/water.lua | 14 +++++++++++++- milo/plugins/machineLearn.lua | 4 +++- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index 6a3e3b1..4a36429 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -21,8 +21,8 @@ local config = Config.load('miloRemote', { displayMode = 0 }) local socket local depositMode = { - [ true ] = { text = '\25', textColor = colors.black, help = 'Deposit enabled' }, - [ false ] = { text = '\215', textColor = colors.red, help = 'Deposit disabled' }, + [ true ] = { text = '\25', textColor = colors.black, help = 'Deposit enabled' }, + [ false ] = { text = '\215', textColor = colors.red, help = 'Deposit disabled' }, } local displayModes = { @@ -34,12 +34,6 @@ local page = UI.Page { menuBar = UI.MenuBar { y = 1, height = 1, buttons = { - { - name = 'depositToggle', - text = '\215', - x = -15, - event = 'toggle_deposit' - }, { text = 'Refresh', x = -12, @@ -78,7 +72,7 @@ local page = UI.Page { statusBar = UI.Window { y = -1, filter = UI.TextEntry { - x = 1, ex = -9, + x = 1, ex = -12, limit = 50, shadowText = 'filter', backgroundColor = colors.cyan, @@ -88,7 +82,7 @@ local page = UI.Page { }, }, amount = UI.TextEntry { - x = -8, ex = -4, + x = -11, ex = -7, limit = 3, shadowText = '1', shadowTextColor = colors.gray, @@ -99,6 +93,11 @@ local page = UI.Page { }, help = 'Request amount', }, + depositToggle = UI.Button { + x = -6, + event = 'toggle_deposit', + text = '\215', + }, display = UI.Button { x = -3, event = 'toggle_display', @@ -351,8 +350,8 @@ function page:eventHandler(event) elseif event.type == 'toggle_deposit' then config.deposit = not config.deposit - Util.merge(self.menuBar.depositToggle, depositMode[config.deposit]) - self.menuBar:draw() + Util.merge(self.statusBar.depositToggle, depositMode[config.deposit]) + self.statusBar:draw() self:setStatus(depositMode[config.deposit].help) Config.update('miloRemote', config) @@ -445,7 +444,7 @@ end function page:enable() self:setFocus(self.statusBar.filter) - Util.merge(self.menuBar.depositToggle, depositMode[config.deposit]) + Util.merge(self.statusBar.depositToggle, depositMode[config.deposit]) UI.Page.enable(self) if not config.server then self.setup:show() diff --git a/milo/apps/water.lua b/milo/apps/water.lua index 15fd48a..79f985d 100644 --- a/milo/apps/water.lua +++ b/milo/apps/water.lua @@ -1,6 +1,18 @@ -local os = _G.os +_G.requireInjector(_ENV) + +local Util = require('util') + +local fs = _G.fs +local os = _G.os local turtle = _G.turtle +local STARTUP_FILE = 'usr/autorun/miloWater.lua' +if not fs.exists(STARTUP_FILE) then + Util.writeFile(STARTUP_FILE, + [[os.sleep(2) +shell.openForegroundTab('packages/milo/apps/water')]]) +end + while true do turtle.placeDown('minecraft:bucket:0') turtle.placeDown('minecraft:glass_bottle:0') diff --git a/milo/plugins/machineLearn.lua b/milo/plugins/machineLearn.lua index 38fccb0..b94914f 100644 --- a/milo/plugins/machineLearn.lua +++ b/milo/plugins/machineLearn.lua @@ -5,6 +5,7 @@ local Util = require('util') local colors = _G.colors local device = _G.device +local turtle = _G.turtle local context = Milo:getContext() local machine @@ -114,13 +115,14 @@ function pages.confirmation:validate() end Milo:saveMachineRecipe(recipe, result, machine.name) + turtle.emptyInventory() local displayName = itemDB:getName(result) - UI:setPage('listing', { filter = displayName, message = 'Learned: ' .. displayName, }) + return true end From 74aa9829c373096a93b7b89372d1e39a69e507e6 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Wed, 5 Dec 2018 01:16:42 -0500 Subject: [PATCH 160/165] milo tweaks --- milo/apis/milo.lua | 2 ++ milo/apps/furni.lua | 5 +++++ milo/core/listing.lua | 1 - 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index ef2432d..b69e72c 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -1,6 +1,7 @@ local Config = require('config') local Craft = require('craft2') local itemDB = require('itemDB') +local Sound = require('sound') local Util = require('util') local os = _G.os @@ -228,6 +229,7 @@ end function Milo:eject(item, count) count = self.context.storage:export(self.context.turtleInventory, nil, count, item) + Sound.play('ui.button.click') turtle.emptyInventory() return count end diff --git a/milo/apps/furni.lua b/milo/apps/furni.lua index 390342e..6969d3b 100644 --- a/milo/apps/furni.lua +++ b/milo/apps/furni.lua @@ -157,5 +157,10 @@ Event.on('turtle_inventory', function() print('idle') end) +Event.onInterval(5, function() + -- for some reason, it keeps stalling ... + os.queueEvent('turtle_inventory') +end) + os.queueEvent('turtle_inventory') Event.pullEvents() diff --git a/milo/core/listing.lua b/milo/core/listing.lua index ba085a9..dc00592 100644 --- a/milo/core/listing.lua +++ b/milo/core/listing.lua @@ -178,7 +178,6 @@ function page:eject(amount) item.count = request.current - request.count if request.count + request.craft > 0 then self.grid:draw() - Sound.play('ui.button.click') return true end end From 0041ffaa5e06c0b0f05c03f63cf9c4aa932b56c3 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Wed, 5 Dec 2018 01:28:42 -0500 Subject: [PATCH 161/165] milo tweaks --- milo/plugins/potionImportTask.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/milo/plugins/potionImportTask.lua b/milo/plugins/potionImportTask.lua index a455793..7f3a34a 100644 --- a/milo/plugins/potionImportTask.lua +++ b/milo/plugins/potionImportTask.lua @@ -1,6 +1,7 @@ local Craft = require('craft2') local itemDB = require('itemDB') local Milo = require('milo') +local Util = require('util') local BLAZE_POWDER = "minecraft:blaze_powder:0" @@ -27,9 +28,12 @@ function PotionImportTask:cycle(context) else local item = itemDB:get(BLAZE_POWDER) if item then - item.requested = 1 - Milo:requestCrafting(item) + item = Util.shallowCopy(item) + else + item = Milo:splitKey(BLAZE_POWDER) end + item.requested = 1 + Milo:requestCrafting(item) end end From f209719c9dfc016e631dc63afb109b151efdcad7 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Wed, 5 Dec 2018 12:04:04 -0500 Subject: [PATCH 162/165] reassign machines --- milo/apis/storage.lua | 17 ++++++ milo/core/machines.lua | 22 +------ milo/plugins/machineMover.lua | 107 ++++++++++++++++++++++++++++++++++ 3 files changed, 127 insertions(+), 19 deletions(-) create mode 100644 milo/plugins/machineMover.lua diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 71ff3ea..ba0409f 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -1,5 +1,6 @@ local Adapter = require('miniAdapter') local class = require('class') +local Config = require('config') local Event = require('event') local itemDB = require('itemDB') local sync = require('sync').sync @@ -84,6 +85,22 @@ function Storage:initStorage() end end +function Storage:saveConfiguration() + local t = { } + for k,v in pairs(self.nodes) do + t[k] = v.adapter + v.adapter = nil + end + + -- TODO: Should be named 'storage' + Config.update('milo', self.nodes) + + for k,v in pairs(t) do + self.nodes[k].adapter = v + end + self:initStorage() +end + function Storage:getSingleNode(mtype) local node = Util.find(self.nodes, 'mtype', mtype) if node and node.adapter and node.adapter.online then diff --git a/milo/core/machines.lua b/milo/core/machines.lua index fb79700..0cf90ec 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -1,4 +1,3 @@ -local Config = require('config') local Event = require('event') local itemDB = require('itemDB') local Milo = require('milo') @@ -13,21 +12,6 @@ local context = Milo:getContext() local nodeWizard -local function saveConfig() - local t = { } - for k,v in pairs(context.storage.nodes) do - t[k] = v.adapter - v.adapter = nil - end - - Config.update('milo', context.storage.nodes) - - for k,v in pairs(t) do - context.storage.nodes[k].adapter = v - end - context.storage:initStorage() -end - local networkPage = UI.Page { titleBar = UI.TitleBar { previousPage = true, @@ -173,7 +157,7 @@ end function networkPage:eventHandler(event) if event.type == 'grid_select' then if not device[event.selected.name] then - self.notification:error('Unable to edit while disconnected') + UI:setPage('machineMover', event.selected) else UI:setPage('nodeWizard', event.selected) end @@ -182,7 +166,7 @@ function networkPage:eventHandler(event) local node = self.grid:getSelected() if node then context.storage.nodes[node.name] = nil - saveConfig() + context.storage:saveConfiguration() end self:applyFilter() self.grid:draw() @@ -511,7 +495,7 @@ function nodeWizard:eventHandler(event) Util.merge(context.storage.nodes[self.node.name], self.node) context.storage.nodes[self.node.name].adapter = adapter - saveConfig() + context.storage:saveConfiguration() UI:setPreviousPage() diff --git a/milo/plugins/machineMover.lua b/milo/plugins/machineMover.lua new file mode 100644 index 0000000..a2d91be --- /dev/null +++ b/milo/plugins/machineMover.lua @@ -0,0 +1,107 @@ +local Craft = require('craft2') +local Milo = require('milo') +local UI = require('ui') +local Util = require('util') + +local colors = _G.colors +local context = Milo:getContext() +local device = _G.device + +local page = UI.Page { + titleBar = UI.TitleBar { title = 'Reassign Machine' }, + grid = UI.ScrollingGrid { + y = 2, ey = -4, + values = context.storage.nodes, + columns = { + { key = 'suffix', width = 4, justify = 'right' }, + { heading = 'Name', key = 'displayName' }, + { heading = 'Type', key = 'mtype', width = 4 }, + { heading = 'Pri', key = 'priority', width = 3 }, + }, + sortColumn = 'displayName', + help = 'Select Node', + }, + accept = UI.Button { + x = -9, y = -2, + event = 'grid_select', + text = 'Accept', + }, + cancel = UI.Button { + x = -18, y = -2, + event = 'cancel', + text = 'Cancel', + }, + accelerators = { + grid_select = 'nextView', + }, + notification = UI.Notification { }, +} + +function page.grid:getDisplayValues(row) + row = Util.shallowCopy(row) + local t = { row.name:match(':(.+)_(%d+)$') } + if #t ~= 2 then + t = { row.name:match('(.+)_(%d+)$') } + end + if t and #t == 2 then + row.name, row.suffix = table.unpack(t) + row.name = row.name .. '_' .. row.suffix + end + row.displayName = row.displayName or row.name + return row +end + +function page.grid:getRowTextColor(row, selected) + if row.mtype == 'ignore' then + return colors.lightGray + end + return UI.Grid:getRowTextColor(row, selected) +end + +function page:applyFilter() + local t = Util.filter(context.storage.nodes, function(v) + return v.mtype ~= 'hidden' and device[v.name] + end) + + self.grid:setValues(t) +end + +function page:enable(machine) + self.machine = machine + self:applyFilter() + + UI.Page.enable(self) +end + +function page:eventHandler(event) + if event.type == 'grid_select' then + local target = self.grid:getSelected() + if target then + local adapter = target.adapter + local name = target.name + Util.merge(target, self.machine) + target.adapter = adapter + target.name = name + + context.storage.nodes[self.machine.name] = nil + context.storage:saveConfiguration() + + for k,v in pairs(Craft.machineLookup) do + if v == self.machine.name then + Craft.machineLookup[k] = name + end + Util.writeTable(Craft.MACHINE_LOOKUP, Craft.machineLookup) + end + + UI:setPreviousPage() + end + + elseif event.type == 'cancel' then + UI:setPreviousPage() + + else + return UI.Page.eventHandler(self, event) + end +end + +UI:addPage('machineMover', page) From 11c46702133916c2714afe807664e19e178cc5da Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Wed, 5 Dec 2018 21:20:50 -0500 Subject: [PATCH 163/165] milo: single wired modem only --- milo/Milo.lua | 11 ++++++++++- milo/plugins/remote.lua | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/milo/Milo.lua b/milo/Milo.lua index eb412dd..4d06bc0 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -85,7 +85,16 @@ Turtle must be connected to: error(msg) end -local modem = Peripheral.get('wired_modem') +local modem +for _,v in pairs(device) do + if v.type == 'wired_modem' then + if modem then + Syntax('Only 1 wired modem can be connected') + end + modem = v + end +end + if not modem or not modem.getNameLocal then Syntax('Wired modem missing') end diff --git a/milo/plugins/remote.lua b/milo/plugins/remote.lua index a93437b..2aaab82 100644 --- a/milo/plugins/remote.lua +++ b/milo/plugins/remote.lua @@ -105,7 +105,7 @@ local function client(socket) if node then local slot = node.adapter.getItemMeta(slotNo) if slot then - if context.storage:import(node, slotNo, slot.count, slot) then + if context.storage:import(node, slotNo, slot.count, slot) > 0 then local item = Milo:getItem(Milo:listItems(), slot) if item then socket:write({ From 5c28bc736ff7e5e5bc59e7522e034407b8c305ff Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Fri, 7 Dec 2018 13:43:33 -0500 Subject: [PATCH 164/165] milo: tweaks --- core/apis/itemDB.lua | 3 +++ milo/Milo.lua | 13 ++++--------- milo/MiloRemote.lua | 1 - milo/plugins/remote.lua | 1 + 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/core/apis/itemDB.lua b/core/apis/itemDB.lua index eec7ce8..a5adfa4 100644 --- a/core/apis/itemDB.lua +++ b/core/apis/itemDB.lua @@ -142,6 +142,9 @@ function itemDB:add(baseItem) -- disks elseif baseItem.media then -- don't ignore nbt... as disks can be labeled + if baseItem.media.recordTitle then + nItem.displayName = nItem.displayName .. ': ' .. baseItem.media.recordTitle + end -- potions elseif nItem.name == 'minecraft:potion' or nItem.name == 'minecraft:lingering_potion' then diff --git a/milo/Milo.lua b/milo/Milo.lua index 4d06bc0..ab7b60e 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -1,23 +1,17 @@ --[[ - Provides: autocrafting, resource limits, on-demand crafting, storage stocker. - - Using a turtle allows for crafting of items eliminating the need for AE/RS - molecular assemblers / crafters. + Provides: autocrafting, resource limits, on-demand crafting. Turtle crafting: 1. The turtle must have a crafting table equipped. 2. Equip the turtle with an introspection module. ]]-- - --- TODO: fix which is primary wired modem - _G.requireInjector(_ENV) local Config = require('config') local Event = require('event') local Milo = require('milo') -local Peripheral = require('peripheral') +local Sound = require('sound') local Storage = require('storage') local UI = require('ui') local Util = require('util') @@ -145,9 +139,9 @@ context.storage.turtleInventory = context.turtleInventory local function loadDirectory(dir) for _, file in pairs(fs.list(dir)) do -_debug('loading: ' .. file) local s, m = Util.run(_ENV, fs.combine(dir, file)) if not s and m then + _G.printError('Error loading: ' .. file) error(m or 'Unknown error') end end @@ -169,6 +163,7 @@ end Milo:clearGrid() UI:setPage(UI:getPage('listing')) +Sound.play('ui.toast.challenge_complete') local processing diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index 4a36429..ee9853f 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -329,7 +329,6 @@ function page.grid:eventHandler(event) end function page:transfer(item, count, msg) - --Sound.play('ui.button.click', .3) self:sendRequest({ request = 'transfer', item = item, count = count }, msg) end diff --git a/milo/plugins/remote.lua b/milo/plugins/remote.lua index 2aaab82..e0398e2 100644 --- a/milo/plugins/remote.lua +++ b/milo/plugins/remote.lua @@ -139,6 +139,7 @@ local function client(socket) nil, request.requested, data.item) + local item = Milo:listItems()[request.key] socket:write({ type = 'transfer', From d0d825dd9a846ff49dde991af297c0363861af3d Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 8 Dec 2018 13:27:26 -0500 Subject: [PATCH 165/165] treefarm doc --- farms/treefarm.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/farms/treefarm.lua b/farms/treefarm.lua index 9890bbb..8bf9040 100644 --- a/farms/treefarm.lua +++ b/farms/treefarm.lua @@ -6,7 +6,7 @@ _G.requireInjector() Area around turtle must be flat and can only be dirt or grass (10 blocks in each direction from turtle) Turtle must have: crafting table, chest - Turtle must have a pick equipped on the RIGHT side + Turtle must have a pick equipped on the LEFT side Optional: Add additional sapling types that can grow with a single sapling