From 045b32884fc7e44f1fa68af85dcd7f3082e5156f Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Tue, 18 Jun 2019 15:23:20 -0400 Subject: [PATCH] spaces->tab, equipper improvements, supertreefarm rewrite, follow improvements, sensor cleanup, milo multiple items allowed in recipes, remote canvas access --- builder/apis/base64.lua | 198 +- builder/apis/blocks.lua | 968 +++---- builder/apis/builder.lua | 104 +- builder/apis/commands.lua | 114 +- builder/apis/deflatelua.lua | 1092 ++++---- builder/apis/schematic.lua | 1958 ++++++------- builder/apis/turtle.lua | 1896 ++++++------- builder/builder.lua | 1136 ++++---- builder/supplier.lua | 594 ++-- builder/viewer.lua | 168 +- ccemux/autorun/startup.lua | 14 +- common/Appstore.lua | 544 ++-- common/Devices.lua | 282 +- common/DiskCopy.lua | 344 +-- common/Events.lua | 202 +- common/Follow.lua | 299 +- common/Turtles.lua | 546 ++-- common/autorun/common.lua | 6 +- common/canvasClient.lua | 45 + common/edit.lua | 2044 +++++++------- common/multiMiner.lua | 862 +++--- core/apis/chestAdapter.lua | 214 +- core/apis/chestAdapter18.lua | 218 +- core/apis/itemDB.lua | 416 +-- core/apis/meAdapter.lua | 352 +-- core/apis/meAdapter18.lua | 114 +- core/apis/message.lua | 114 +- core/apis/nameDB.lua | 68 +- core/apis/proxy.lua | 32 + core/apis/refinedAdapter.lua | 172 +- core/apis/swarm.lua | 80 +- core/apis/tableDB.lua | 52 +- core/etc/names/minecraft.json | 4196 ++++++++++++++-------------- farms/apis/crafting.lua | 84 +- farms/apis/level.lua | 238 +- farms/attack.lua | 8 +- farms/farmer.lua | 373 +-- farms/help/superTreefarm | 9 +- farms/rancher.lua | 142 +- farms/spawner.lua | 14 +- farms/superTreefarm.lua | 1017 +++---- farms/treefarm.lua | 1010 +++---- games/ccTunes.lua | 36 +- gps/gpsServer.lua | 1 + milo/MiloLocal.lua | 222 +- milo/MiloRemote.lua | 794 +++--- milo/apis/craft2.lua | 67 +- milo/apis/fuzzyMatch.lua | 10 +- milo/apis/init.lua | 5 +- milo/apis/massAdapter.lua | 68 +- milo/apis/miniAdapter.lua | 48 +- milo/apis/storage.lua | 912 +++--- milo/apis/taskRunner.lua | 94 +- milo/apps/brewArray.lua | 176 +- milo/apps/cobblegen.lua | 26 +- milo/apps/enderchest.lua | 54 +- milo/apps/furni.lua | 222 +- milo/apps/storageGen.lua | 14 +- milo/core/learnWizard.lua | 122 +- milo/core/listing.lua | 610 ++-- milo/etc/apps.db | 28 +- milo/plugins/activityView.lua | 362 +-- milo/plugins/backupView.lua | 132 +- milo/plugins/craftTask.lua | 94 +- milo/plugins/demandCraft.lua | 200 +- milo/plugins/inputChestView.lua | 30 +- milo/plugins/item.lua | 74 +- milo/plugins/item/machinesTab.lua | 84 +- milo/plugins/item/manageTab.lua | 146 +- milo/plugins/item/recipeTab.lua | 130 +- milo/plugins/item/resetTab.lua | 64 +- milo/plugins/jobMonitor.lua | 342 +-- milo/plugins/machineLearn.lua | 9 +- milo/plugins/machineMover.lua | 144 +- milo/plugins/massStorageView.lua | 28 +- milo/plugins/remote/craft.lua | 18 +- milo/plugins/remote/deposit.lua | 62 +- milo/plugins/remote/depositAll.lua | 178 +- milo/plugins/remote/feeder.lua | 76 +- milo/plugins/remote/setup.lua | 128 +- milo/plugins/remoteCraft.lua | 68 +- milo/plugins/replenishTask.lua | 56 +- milo/plugins/speakerView.lua | 78 +- milo/plugins/statsView.lua | 778 +++--- milo/plugins/trashcanView.lua | 114 +- miners/findSwarm.lua | 242 +- miners/scanningMiner.lua | 910 +++--- miners/simpleMiner.lua | 894 +++--- monitor/etc/apps.db | 14 +- monitor/mirrorClient.lua | 104 +- monitor/mirrorHost.lua | 46 +- monitor/mwm.lua | 676 ++--- neural/.package | 3 + neural/Scanner.lua | 140 +- neural/Sensor.lua | 38 +- neural/apis/angle.lua | 4 +- neural/apis/mobs.lua | 36 +- neural/apis/statemachine.lua | 196 +- neural/autorun/interface.lua | 46 +- neural/autorun/splash.lua | 112 +- neural/canvasServer.lua | 189 ++ neural/disableAI.lua | 4 +- neural/dropNeural.lua | 6 +- neural/elytraFly.lua | 226 +- neural/etc/apps.db | 60 +- neural/fisher.lua | 138 +- neural/mobFollow.lua | 56 +- neural/mobPickup.lua | 98 +- neural/mobRancher.lua | 158 +- neural/neuralLook.lua | 94 +- neural/neuralRecorder.lua | 118 +- neural/neuralReplay.lua | 58 +- neural/ores.lua | 191 +- neural/shootingGallery.lua | 64 +- oc/3dprint.lua | 46 +- recipeBook/etc/apps.db | 12 +- recipeBook/etc/recipeBook.db | 12 +- shellex/apis/computer.lua | 2 +- shellex/apis/filesystem.lua | 106 +- shellex/apis/globtopattern.lua | 212 +- shellex/apis/keyboard.lua | 2 +- shellex/apis/sh.lua | 4 +- shellex/apis/shell.lua | 8 +- shellex/apis/text.lua | 622 ++--- shellex/apis/transfer.lua | 456 +-- shellex/apis/transforms.lua | 306 +- shellex/apis/tty.lua | 46 +- shellex/apis/unicode.lua | 2 +- shellex/autorun/startup.lua | 4 +- shellex/cat.lua | 38 +- shellex/cp.lua | 26 +- shellex/df.lua | 104 +- shellex/dmesg.lua | 38 +- shellex/du.lua | 152 +- shellex/find.lua | 158 +- shellex/grep.lua | 416 +-- shellex/head.lua | 176 +- shellex/hostname.lua | 16 +- shellex/less.lua | 216 +- shellex/ln.lua | 18 +- shellex/ls.lua | 624 ++--- shellex/mv.lua | 36 +- shellex/rm.lua | 176 +- shellex/rmdir.lua | 132 +- shellex/time.lua | 4 +- shellex/touch.lua | 58 +- shellex/which.lua | 30 +- swshop/installPlugin.lua | 2 +- swshop/jua.lua | 148 +- swshop/k.lua | 532 ++-- swshop/r.lua | 76 +- swshop/shopConfig.lua | 66 +- swshop/shopTab.lua | 110 +- swshop/shopView-example.lua | 78 +- swshop/shopView.lua | 306 +- swshop/swshop.lua | 184 +- swshop/w.lua | 186 +- turtle/apis/equipper.lua | 194 +- turtle/autorun/startup.lua | 122 +- turtle/lavaRefuel.lua | 2 +- turtle/obsidian.lua | 152 +- turtle/t.lua | 154 +- 162 files changed, 20448 insertions(+), 20286 deletions(-) create mode 100644 common/canvasClient.lua create mode 100644 core/apis/proxy.lua create mode 100644 neural/canvasServer.lua diff --git a/builder/apis/base64.lua b/builder/apis/base64.lua index f35623b..39ae0b0 100644 --- a/builder/apis/base64.lua +++ b/builder/apis/base64.lua @@ -15,37 +15,37 @@ local os = _G.os local alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" local function sixBitToBase64(input) - return _sub(alphabet, input+1, input+1) + return _sub(alphabet, input+1, input+1) end local function base64ToSixBit(input) - for i=1, 64 do - if input == _sub(alphabet, i, i) then - return i-1 - end - end + for i=1, 64 do + if input == _sub(alphabet, i, i) then + return i-1 + end + end end local function octetToBase64(o1, o2, o3) - local i1 = sixBitToBase64(_brshift(_band(o1, 0xFC), 2)) - local i2 - local i3 = "=" - local i4 = "=" - if o2 then - i2 = sixBitToBase64(_bor( _blshift(_band(o1, 3), 4), _brshift(_band(o2, 0xF0), 4) )) - if not o3 then - i3 = sixBitToBase64(_blshift(_band(o2, 0x0F), 2)) - else - i3 = sixBitToBase64(_bor( _blshift(_band(o2, 0x0F), 2), _brshift(_band(o3, 0xC0), 6) )) - end - else - i2 = sixBitToBase64(_blshift(_band(o1, 3), 4)) - end - if o3 then - i4 = sixBitToBase64(_band(o3, 0x3F)) - end + local i1 = sixBitToBase64(_brshift(_band(o1, 0xFC), 2)) + local i2 + local i3 = "=" + local i4 = "=" + if o2 then + i2 = sixBitToBase64(_bor( _blshift(_band(o1, 3), 4), _brshift(_band(o2, 0xF0), 4) )) + if not o3 then + i3 = sixBitToBase64(_blshift(_band(o2, 0x0F), 2)) + else + i3 = sixBitToBase64(_bor( _blshift(_band(o2, 0x0F), 2), _brshift(_band(o3, 0xC0), 6) )) + end + else + i2 = sixBitToBase64(_blshift(_band(o1, 3), 4)) + end + if o3 then + i4 = sixBitToBase64(_band(o3, 0x3F)) + end - return i1..i2..i3..i4 + return i1..i2..i3..i4 end -- octet 1 needs characters 1/2 @@ -53,61 +53,61 @@ end -- octet 3 needs characters 3/4 local function base64ToThreeOctet(s1) - local c1 = base64ToSixBit(_sub(s1, 1, 1)) - local c2 = base64ToSixBit(_sub(s1, 2, 2)) - local c3 - local c4 - local o1 - local o2 - local o3 - if _sub(s1, 3, 3) == "=" then - c3 = nil - c4 = nil - elseif _sub(s1, 4, 4) == "=" then - c3 = base64ToSixBit(_sub(s1, 3, 3)) - c4 = nil - else - c3 = base64ToSixBit(_sub(s1, 3, 3)) - c4 = base64ToSixBit(_sub(s1, 4, 4)) - end - o1 = _bor( _blshift(c1, 2), _brshift(_band( c2, 0x30 ), 4) ) - if c3 then - o2 = _bor( _blshift(_band(c2, 0x0F), 4), _brshift(_band( c3, 0x3C ), 2) ) - else - o2 = nil - end - if c4 then - o3 = _bor( _blshift(_band(c3, 3), 6), c4 ) - else - o3 = nil - end - return o1, o2, o3 + local c1 = base64ToSixBit(_sub(s1, 1, 1)) + local c2 = base64ToSixBit(_sub(s1, 2, 2)) + local c3 + local c4 + local o1 + local o2 + local o3 + if _sub(s1, 3, 3) == "=" then + c3 = nil + c4 = nil + elseif _sub(s1, 4, 4) == "=" then + c3 = base64ToSixBit(_sub(s1, 3, 3)) + c4 = nil + else + c3 = base64ToSixBit(_sub(s1, 3, 3)) + c4 = base64ToSixBit(_sub(s1, 4, 4)) + end + o1 = _bor( _blshift(c1, 2), _brshift(_band( c2, 0x30 ), 4) ) + if c3 then + o2 = _bor( _blshift(_band(c2, 0x0F), 4), _brshift(_band( c3, 0x3C ), 2) ) + else + o2 = nil + end + if c4 then + o3 = _bor( _blshift(_band(c3, 3), 6), c4 ) + else + o3 = nil + end + return o1, o2, o3 end local function splitIntoBlocks(bytes) - local blockNum = 1 - local blocks = {} - for i=1, #bytes, 3 do - blocks[blockNum] = {bytes[i], bytes[i+1], bytes[i+2]} - --[[ - if #blocks[blockNum] < 3 then - for j=#blocks[blockNum]+1, 3 do - table.insert(blocks[blockNum], 0) - end - end - ]] - blockNum = blockNum+1 - end - return blocks + local blockNum = 1 + local blocks = {} + for i=1, #bytes, 3 do + blocks[blockNum] = {bytes[i], bytes[i+1], bytes[i+2]} + --[[ + if #blocks[blockNum] < 3 then + for j=#blocks[blockNum]+1, 3 do + table.insert(blocks[blockNum], 0) + end + end + ]] + blockNum = blockNum+1 + end + return blocks end function Base64.encode(bytes) - local blocks = splitIntoBlocks(bytes) - local output = "" - for i=1, #blocks do - output = output..octetToBase64( unpack(blocks[i]) ) - end - return output + local blocks = splitIntoBlocks(bytes) + local output = "" + for i=1, #blocks do + output = output..octetToBase64( unpack(blocks[i]) ) + end + return output end local function Throttle() @@ -123,32 +123,32 @@ local function Throttle() end function Base64.decode(str) - local bytes = {} - local blocks = {} - local blockNum = 1 - local throttle = Throttle() - for i=1, #str, 4 do - blocks[blockNum] = _sub(str, i, i+3) - blockNum = blockNum+1 - end - for i=1, #blocks do - local o1, o2, o3 = base64ToThreeOctet(blocks[i]) - table.insert(bytes, o1) - table.insert(bytes, o2) - table.insert(bytes, o3) - throttle() - end - -- Remove padding: - --[[ - for i=#bytes, 1, -1 do - if bytes[i] ~= 0 then - break - else - bytes[i] = nil - end - end - ]] - return bytes + local bytes = {} + local blocks = {} + local blockNum = 1 + local throttle = Throttle() + for i=1, #str, 4 do + blocks[blockNum] = _sub(str, i, i+3) + blockNum = blockNum+1 + end + for i=1, #blocks do + local o1, o2, o3 = base64ToThreeOctet(blocks[i]) + table.insert(bytes, o1) + table.insert(bytes, o2) + table.insert(bytes, o3) + throttle() + end + -- Remove padding: + --[[ + for i=#bytes, 1, -1 do + if bytes[i] ~= 0 then + break + else + bytes[i] = nil + end + end + ]] + return bytes end return Base64 diff --git a/builder/apis/blocks.lua b/builder/apis/blocks.lua index b1c2f61..1b509dc 100644 --- a/builder/apis/blocks.lua +++ b/builder/apis/blocks.lua @@ -10,42 +10,42 @@ local JSON = require('json') local blockDB = TableDB() function blockDB:load() - local blocks = JSON.decodeFromFile('packages/core/etc/names/minecraft.json') + local blocks = JSON.decodeFromFile('packages/core/etc/names/minecraft.json') - if not blocks then - error('Unable to read blocks.json') - end + if not blocks then + error('Unable to read blocks.json') + end - for strId, block in pairs(blocks) do - strId = 'minecraft:' .. strId - if type(block.name) == 'string' then - self:add(block.id, 0, block.name, strId, block.place) - else - for nid,name in pairs(block.name) do - self:add(block.id, nid - 1, name, strId, block.place) - end - end - end + for strId, block in pairs(blocks) do + strId = 'minecraft:' .. strId + if type(block.name) == 'string' then + self:add(block.id, 0, block.name, strId, block.place) + else + for nid,name in pairs(block.name) do + self:add(block.id, nid - 1, name, strId, block.place) + end + end + end end function blockDB:lookup(id, dmg) - if not id then - return - end + if not id then + return + end - return self.data[id .. ':' .. dmg] + return self.data[id .. ':' .. dmg] end function blockDB:add(id, dmg, name, strId, place) - local key = id .. ':' .. dmg + local key = id .. ':' .. dmg - TableDB.add(self, key, { - id = id, - dmg = dmg, - name = name, - strId = strId, - place = place, - }) + TableDB.add(self, key, { + id = id, + dmg = dmg, + name = name, + strId = strId, + place = place, + }) end --[[-- placementDB --]]-- @@ -53,497 +53,497 @@ end local placementDB = TableDB() function placementDB:load(sbDB, btDB) - for k,v in pairs(sbDB.data) do - if v.place then - local bt = btDB.data[v.place] - if not bt then - error('missing block type: ' .. v.place) - end - local id, dmg = string.match(k, '(%d+):*(%d+)') - self:addSubsForBlockType(tonumber(id), tonumber(dmg), bt) - end - end + for k,v in pairs(sbDB.data) do + if v.place then + local bt = btDB.data[v.place] + if not bt then + error('missing block type: ' .. v.place) + end + local id, dmg = string.match(k, '(%d+):*(%d+)') + self:addSubsForBlockType(tonumber(id), tonumber(dmg), bt) + end + end - -- special case for quartz pillars - self:addSubsForBlockType(155, 2, btDB.data['quartz-pillar']) + -- special case for quartz pillars + self:addSubsForBlockType(155, 2, btDB.data['quartz-pillar']) end function placementDB:addSubsForBlockType(id, dmg, bt) - for _,sub in pairs(bt) do - local odmg = sub.odmg - if type(sub.odmg) == 'string' then - odmg = dmg + tonumber(string.match(odmg, '+(%d+)')) - end + for _,sub in pairs(bt) do + local odmg = sub.odmg + if type(sub.odmg) == 'string' then + odmg = dmg + tonumber(string.match(odmg, '+(%d+)')) + end - local b = blockDB:lookup(id, dmg) - local strId = tostring(id) - if b then - strId = b.strId - end + local b = blockDB:lookup(id, dmg) + local strId = tostring(id) + if b then + strId = b.strId + end - self:add( - id, - odmg, - sub.sid or strId, - sub.sdmg or dmg, - sub.dir, - sub.extra) - end + self:add( + id, + odmg, + sub.sid or strId, + sub.sdmg or dmg, + sub.dir, + sub.extra) + end end function placementDB:add(id, dmg, sid, sdmg, direction, extra) - if direction and #direction == 0 then - direction = nil - end + if direction and #direction == 0 then + direction = nil + end - local entry = { - oid = id, -- numeric ID - odmg = dmg, -- dmg with placement info - id = sid, -- string ID - dmg = sdmg, -- dmg without placement info - direction = direction, - } - if extra then - Util.merge(entry, extra) - end + local entry = { + oid = id, -- numeric ID + odmg = dmg, -- dmg with placement info + id = sid, -- string ID + dmg = sdmg, -- dmg without placement info + direction = direction, + } + if extra then + Util.merge(entry, extra) + end - self.data[id .. ':' .. dmg] = entry + self.data[id .. ':' .. dmg] = entry end --[[-- BlockTypeDB --]]-- local blockTypeDB = TableDB() function blockTypeDB:addTemp(blockType, subs) - local bt = self.data[blockType] - if not bt then - bt = { } - self.data[blockType] = bt - end - for _,sub in pairs(subs) do - table.insert(bt, { - odmg = sub[1], - sid = sub[2], - sdmg = sub[3], - dir = sub[4], - extra = sub[5] - }) - end - self.dirty = true + local bt = self.data[blockType] + if not bt then + bt = { } + self.data[blockType] = bt + end + for _,sub in pairs(subs) do + table.insert(bt, { + odmg = sub[1], + sid = sub[2], + sdmg = sub[3], + dir = sub[4], + extra = sub[5] + }) + end + self.dirty = true end function blockTypeDB:load() - blockTypeDB:addTemp('stairs', { - { 0, nil, 0, 'east-up' }, - { 1, nil, 0, 'west-up' }, - { 2, nil, 0, 'south-up' }, - { 3, nil, 0, 'north-up' }, - { 4, nil, 0, 'east-down' }, - { 5, nil, 0, 'west-down' }, - { 6, nil, 0, 'south-down' }, - { 7, nil, 0, 'north-down' }, - }) - blockTypeDB:addTemp('gate', { - { 0, nil, 0, 'north' }, - { 1, nil, 0, 'east' }, - { 2, nil, 0, 'south' }, - { 3, nil, 0, 'west' }, - { 4, nil, 0, 'north' }, - { 5, nil, 0, 'east' }, - { 6, nil, 0, 'south' }, - { 7, nil, 0, 'west' }, - }) - blockTypeDB:addTemp('pumpkin', { - { 0, nil, 0, 'north-block' }, - { 1, nil, 0, 'east-block' }, - { 2, nil, 0, 'south-block' }, - { 3, nil, 0, 'west-block' }, - { 4, nil, 0, 'north-block' }, - { 5, nil, 0, 'east-block' }, - { 6, nil, 0, 'south-block' }, - { 7, nil, 0, 'west-block' }, - }) - blockTypeDB:addTemp('anvil', { - { 0, nil, 0, 'south' }, - { 1, nil, 0, 'east' }, - { 2, nil, 0, 'south'}, - { 3, nil, 0, 'east' }, - { 4, nil, 0, 'south' }, - { 5, nil, 0, 'east' }, - { 6, nil, 0, 'east' }, - { 7, nil, 0, 'south' }, - { 8, nil, 0, 'south' }, - { 9, nil, 0, 'east' }, - { 10, nil, 0, 'east' }, - { 11, nil, 0, 'south' }, - { 12, nil, 0 }, - { 13, nil, 0 }, - { 14, nil, 0 }, - { 15, nil, 0 }, - }) - blockTypeDB:addTemp('bed', { - { 0, nil, 0, 'south' }, - { 1, nil, 0, 'west' }, - { 2, nil, 0, 'north' }, - { 3, nil, 0, 'east' }, - { 4, nil, 0, 'south' }, - { 5, nil, 0, 'west' }, - { 6, nil, 0, 'north' }, - { 7, nil, 0, 'east' }, - { 8, 'minecraft:air', 0 }, - { 9, 'minecraft:air', 0 }, - { 10, 'minecraft:air', 0 }, - { 11, 'minecraft:air', 0 }, - { 12, 'minecraft:air', 0 }, - { 13, 'minecraft:air', 0 }, - { 14, 'minecraft:air', 0 }, - { 15, 'minecraft:air', 0 }, - }) - blockTypeDB:addTemp('comparator', { - { 0, nil, 0, 'south' }, - { 1, nil, 0, 'west' }, - { 2, nil, 0, 'north' }, - { 3, nil, 0, 'east' }, - { 4, nil, 0, 'south' }, - { 5, nil, 0, 'west' }, - { 6, nil, 0, 'north' }, - { 7, nil, 0, 'east' }, - { 8, nil, 0, 'south' }, - { 9, nil, 0, 'west' }, - { 10, nil, 0, 'north' }, - { 11, nil, 0, 'east' }, - { 12, nil, 0, 'south' }, - { 13, nil, 0, 'west' }, - { 14, nil, 0, 'north' }, - { 15, nil, 0, 'east' }, - }) - blockTypeDB:addTemp('quartz-pillar', { - { 2, nil, 2 }, - { 3, nil, 2, 'north-south-block' }, - { 4, nil, 2, 'east-west-block' }, -- should be east-west-block - }) - blockTypeDB:addTemp('hay-bale', { - { 0, nil, 0 }, - { 4, nil, 0, 'east-west-block' }, -- should be east-west-block - { 8, nil, 0, 'north-south-block' }, - }) - blockTypeDB:addTemp('button', { - { 1, nil, 0, 'west-block' }, - { 2, nil, 0, 'east-block' }, - { 3, nil, 0, 'north-block' }, - { 4, nil, 0, 'south-block' }, - { 5, nil, 0 }, -- block top - }) - blockTypeDB:addTemp('cauldron', { - { 0, nil, 0 }, - { 1, nil, 0 }, - { 2, nil, 0 }, - { 3, nil, 0 }, - }) - blockTypeDB:addTemp('dispenser', { - { 0, nil, 0, 'wrench-down' }, - { 1, nil, 0, 'wrench-up' }, - { 2, nil, 0, 'south' }, - { 3, nil, 0, 'north' }, - { 4, nil, 0, 'east' }, - { 5, nil, 0, 'west' }, - { 9, nil, 0 }, - }) - blockTypeDB:addTemp('end_rod', { - { 0, nil, 0, 'wrench-down' }, - { 1, nil, 0, 'wrench-up' }, - { 2, nil, 0, 'south-block-flip' }, - { 3, nil, 0, 'north-block-flip' }, - { 4, nil, 0, 'east-block-flip' }, - { 5, nil, 0, 'west-block-flip' }, - { 9, nil, 0 }, - }) - blockTypeDB:addTemp('hopper', { - { 0, nil, 0 }, - { 1, nil, 0 }, - { 2, nil, 0, 'south-block' }, - { 3, nil, 0, 'north-block' }, - { 4, nil, 0, 'east-block' }, - { 5, nil, 0, 'west-block' }, - { 8, nil, 0 }, - { 9, nil, 0 }, - { 10, nil, 0 }, - { 11, nil, 0, 'south-block' }, - { 12, nil, 0, 'north-block' }, - { 13, nil, 0, 'east-block' }, - { 14, nil, 0, 'west-block' }, - }) - blockTypeDB:addTemp('mobhead', { - { 0, nil, 0 }, - { 1, nil, 0 }, - { 2, nil, 0, 'south-block' }, - { 3, nil, 0, 'north-block' }, - { 4, nil, 0, 'west-block' }, - { 5, nil, 0, 'east-block' }, - }) - blockTypeDB:addTemp('rail', { - { 0, nil, 0, 'south' }, - { 1, nil, 0, 'east' }, - { 2, nil, 0, 'east' }, - { 3, nil, 0, 'east' }, - { 4, nil, 0, 'south' }, - { 5, nil, 0, 'south' }, - { 6, nil, 0, 'east' }, - { 7, nil, 0, 'south' }, - { 8, nil, 0, 'east' }, - { 9, nil, 0, 'south' }, - }) - blockTypeDB:addTemp('adp-rail', { - { 0, nil, 0, 'south' }, - { 1, nil, 0, 'east' }, - { 2, nil, 0, 'east' }, - { 3, nil, 0, 'east' }, - { 4, nil, 0, 'south' }, - { 5, nil, 0, 'south' }, - { 8, nil, 0, 'south' }, - { 9, nil, 0, 'east' }, - { 10, nil, 0, 'east' }, - { 11, nil, 0, 'east' }, - { 12, nil, 0, 'south' }, - { 13, nil, 0, 'south' }, - }) - blockTypeDB:addTemp('signpost', { - { 0, nil, 0, 'north' }, - { 1, nil, 0, 'north', { facing = 1 } }, - { 2, nil, 0, 'north', { facing = 2 } }, - { 3, nil, 0, 'north', { facing = 3 } }, - { 4, nil, 0, 'east' }, - { 5, nil, 0, 'east', { facing = 1 } }, - { 6, nil, 0, 'east', { facing = 2 } }, - { 7, nil, 0, 'east', { facing = 3 } }, - { 8, nil, 0, 'south' }, - { 9, nil, 0, 'south', { facing = 1 } }, - { 10, nil, 0, 'south', { facing = 2 } }, - { 11, nil, 0, 'south', { facing = 3 } }, - { 12, nil, 0, 'west' }, - { 13, nil, 0, 'west', { facing = 1 } }, - { 14, nil, 0, 'west', { facing = 2 } }, - { 15, nil, 0, 'west', { facing = 3 } }, - }) - blockTypeDB:addTemp('vine', { - { 0, nil, 0 }, - { 1, nil, 0, 'south-block-vine' }, - { 2, nil, 0, 'west-block-vine' }, - { 3, nil, 0, 'south-block-vine' }, - { 4, nil, 0, 'north-block-vine' }, - { 5, nil, 0, 'south-block-vine' }, - { 6, nil, 0, 'north-block-vine' }, - { 7, nil, 0, 'south-block-vine' }, - { 8, nil, 0, 'east-block-vine' }, - { 9, nil, 0, 'south-block-vine' }, - { 10, nil, 0, 'east-block-vine' }, - { 11, nil, 0, 'east-block-vine' }, - { 12, nil, 0, 'east-block-vine' }, - { 13, nil, 0, 'east-block-vine' }, - { 14, nil, 0, 'east-block-vine' }, - { 15, nil, 0, 'east-block-vine' }, - }) - blockTypeDB:addTemp('torch', { - { 0, nil, 0 }, - { 1, nil, 0, 'west-block' }, - { 2, nil, 0, 'east-block' }, - { 3, nil, 0, 'north-block' }, - { 4, nil, 0, 'south-block' }, - { 5, nil, 0 }, - }) - blockTypeDB:addTemp('tripwire', { - { 0, nil, 0, 'north-block' }, - { 1, nil, 0, 'east-block' }, - { 2, nil, 0, 'south-block' }, - { 3, nil, 0, 'west-block' }, - }) - blockTypeDB:addTemp('trapdoor', { - { 0, nil, 0, 'south-block' }, - { 1, nil, 0, 'north-block' }, - { 2, nil, 0, 'east-block' }, - { 3, nil, 0, 'west-block' }, - { 4, nil, 0, 'south-block' }, - { 5, nil, 0, 'north-block' }, - { 6, nil, 0, 'east-block' }, - { 7, nil, 0, 'west-block' }, - { 8, nil, 0, 'south' }, - { 9, nil, 0, 'north' }, - { 10, nil, 0, 'east' }, - { 11, nil, 0, 'west' }, - { 12, nil, 0, 'south' }, - { 13, nil, 0, 'north' }, - { 14, nil, 0, 'east' }, - { 15, nil, 0, 'west' }, - }) - blockTypeDB:addTemp('piston', { - { 0, nil, 0, 'piston-down' }, - { 1, nil, 0, 'piston-up' }, - { 2, nil, 0, 'piston-north' }, - { 3, nil, 0, 'piston-south' }, - { 4, nil, 0, 'piston-west' }, - { 5, nil, 0, 'piston-east' }, - { 8, nil, 0, 'piston-down' }, - { 9, nil, 0, 'piston-up' }, - { 10, nil, 0, 'piston-north' }, - { 11, nil, 0, 'piston-south' }, - { 12, nil, 0, 'piston-west' }, - { 13, nil, 0, 'piston-east' }, - }) - blockTypeDB:addTemp('lever', { - { 0, nil, 0, 'up' }, - { 1, nil, 0, 'west-block' }, - { 2, nil, 0, 'east-block' }, - { 3, nil, 0, 'north-block' }, - { 4, nil, 0, 'south-block' }, - { 5, nil, 0, 'north' }, - { 6, nil, 0, 'west' }, - { 7, nil, 0, 'up' }, - { 8, nil, 0, 'up' }, - { 9, nil, 0, 'west-block' }, - { 10, nil, 0, 'east-block' }, - { 11, nil, 0, 'north-block' }, - { 12, nil, 0, 'south-block' }, - { 13, nil, 0, 'north' }, - { 14, nil, 0, 'west' }, - { 15, nil, 0, 'up' }, - }) - blockTypeDB:addTemp('wallsign-ladder', { - { 0, nil, 0 }, - { 1, nil, 0 }, - { 2, nil, 0, 'south-block' }, - { 3, nil, 0, 'north-block' }, - { 4, nil, 0, 'east-block' }, - { 5, nil, 0, 'west-block' }, - }) - blockTypeDB:addTemp('chest-furnace', { - { 0, nil, 0 }, - { 2, nil, 0, 'south' }, - { 3, nil, 0, 'north' }, - { 4, nil, 0, 'east' }, - { 5, nil, 0, 'west' }, - }) - blockTypeDB:addTemp('repeater', { - { 0, nil, 0, 'north' }, - { 1, nil, 0, 'east' }, - { 2, nil, 0, 'south' }, - { 3, nil, 0, 'west' }, - { 4, nil, 0, 'north' }, - { 5, nil, 0, 'east' }, - { 6, nil, 0, 'south' }, - { 7, nil, 0, 'west' }, - { 8, nil, 0, 'north' }, - { 9, nil, 0, 'east' }, - { 10, nil, 0, 'south' }, - { 11, nil, 0, 'west' }, - { 12, nil, 0, 'north' }, - { 13, nil, 0, 'east' }, - { 14, nil, 0, 'south' }, - { 15, nil, 0, 'west' }, - }) - blockTypeDB:addTemp('flatten', { - { 0, nil, 0 }, - { 1, nil, 0 }, - { 2, nil, 0 }, - { 3, nil, 0 }, - { 4, nil, 0 }, - { 5, nil, 0 }, - { 6, nil, 0 }, - { 7, nil, 0 }, - { 8, nil, 0 }, - { 9, nil, 0 }, - { 10, nil, 0 }, - { 11, nil, 0 }, - { 12, nil, 0 }, - { 13, nil, 0 }, - { 14, nil, 0 }, - { 15, nil, 0 }, - }) - blockTypeDB:addTemp('sapling', { - { '+0', nil, nil }, - { '+8', nil, nil }, - }) - blockTypeDB:addTemp('leaves', { - { '+0', nil, nil }, - { '+4', nil, nil }, - { '+8', nil, nil }, - { '+12', nil, nil }, - }) - blockTypeDB:addTemp('slab', { - { '+0', nil, nil, 'bottom' }, - { '+8', nil, nil, 'top' }, - }) - blockTypeDB:addTemp('largeplant', { - { '+0', nil, nil, 'east-door', { twoHigh = true } }, -- should use a generic double tall keyword - { '+8', 'minecraft:air', 0 }, - }) - blockTypeDB:addTemp('wood', { - { '+0', nil, nil }, - { '+4', nil, nil, 'east-west-block' }, - { '+8', nil, nil, 'north-south-block' }, - { '+12', nil, nil }, - }) - blockTypeDB:addTemp('door', { - { 0, nil, 0, 'east-door', { twoHigh = true } }, - { 1, nil, 0, 'south-door', { twoHigh = true } }, - { 2, nil, 0, 'west-door', { twoHigh = true } }, - { 3, nil, 0, 'north-door', { twoHigh = true } }, - { 4, nil, 0, 'east-door', { twoHigh = true } }, - { 5, nil, 0, 'south-door', { twoHigh = true } }, - { 6, nil, 0, 'west-door', { twoHigh = true } }, - { 7, nil, 0, 'north-door', { twoHigh = true } }, - { 8,'minecraft:air', 0 }, - { 9,'minecraft:air', 0 }, - { 10,'minecraft:air', 0 }, - { 11,'minecraft:air', 0 }, - { 12,'minecraft:air', 0 }, - { 13,'minecraft:air', 0 }, - { 14,'minecraft:air', 0 }, - { 15,'minecraft:air', 0 }, - }) - blockTypeDB:addTemp('cocoa', { - { 0, nil, 0, 'south-block' }, - { 1, nil, 0, 'west-block' }, - { 2, nil, 0, 'north-block' }, - { 3, nil, 0, 'east-block' }, - { 4, nil, 0, 'south-block' }, - { 5, nil, 0, 'west-block' }, - { 6, nil, 0, 'north-block' }, - { 7, nil, 0, 'east-block' }, - { 8, nil, 0, 'south-block' }, - { 9, nil, 0, 'west-block' }, - { 10, nil, 0, 'north-block' }, - { 11, nil, 0, 'east-block' }, - }) + blockTypeDB:addTemp('stairs', { + { 0, nil, 0, 'east-up' }, + { 1, nil, 0, 'west-up' }, + { 2, nil, 0, 'south-up' }, + { 3, nil, 0, 'north-up' }, + { 4, nil, 0, 'east-down' }, + { 5, nil, 0, 'west-down' }, + { 6, nil, 0, 'south-down' }, + { 7, nil, 0, 'north-down' }, + }) + blockTypeDB:addTemp('gate', { + { 0, nil, 0, 'north' }, + { 1, nil, 0, 'east' }, + { 2, nil, 0, 'south' }, + { 3, nil, 0, 'west' }, + { 4, nil, 0, 'north' }, + { 5, nil, 0, 'east' }, + { 6, nil, 0, 'south' }, + { 7, nil, 0, 'west' }, + }) + blockTypeDB:addTemp('pumpkin', { + { 0, nil, 0, 'north-block' }, + { 1, nil, 0, 'east-block' }, + { 2, nil, 0, 'south-block' }, + { 3, nil, 0, 'west-block' }, + { 4, nil, 0, 'north-block' }, + { 5, nil, 0, 'east-block' }, + { 6, nil, 0, 'south-block' }, + { 7, nil, 0, 'west-block' }, + }) + blockTypeDB:addTemp('anvil', { + { 0, nil, 0, 'south' }, + { 1, nil, 0, 'east' }, + { 2, nil, 0, 'south'}, + { 3, nil, 0, 'east' }, + { 4, nil, 0, 'south' }, + { 5, nil, 0, 'east' }, + { 6, nil, 0, 'east' }, + { 7, nil, 0, 'south' }, + { 8, nil, 0, 'south' }, + { 9, nil, 0, 'east' }, + { 10, nil, 0, 'east' }, + { 11, nil, 0, 'south' }, + { 12, nil, 0 }, + { 13, nil, 0 }, + { 14, nil, 0 }, + { 15, nil, 0 }, + }) + blockTypeDB:addTemp('bed', { + { 0, nil, 0, 'south' }, + { 1, nil, 0, 'west' }, + { 2, nil, 0, 'north' }, + { 3, nil, 0, 'east' }, + { 4, nil, 0, 'south' }, + { 5, nil, 0, 'west' }, + { 6, nil, 0, 'north' }, + { 7, nil, 0, 'east' }, + { 8, 'minecraft:air', 0 }, + { 9, 'minecraft:air', 0 }, + { 10, 'minecraft:air', 0 }, + { 11, 'minecraft:air', 0 }, + { 12, 'minecraft:air', 0 }, + { 13, 'minecraft:air', 0 }, + { 14, 'minecraft:air', 0 }, + { 15, 'minecraft:air', 0 }, + }) + blockTypeDB:addTemp('comparator', { + { 0, nil, 0, 'south' }, + { 1, nil, 0, 'west' }, + { 2, nil, 0, 'north' }, + { 3, nil, 0, 'east' }, + { 4, nil, 0, 'south' }, + { 5, nil, 0, 'west' }, + { 6, nil, 0, 'north' }, + { 7, nil, 0, 'east' }, + { 8, nil, 0, 'south' }, + { 9, nil, 0, 'west' }, + { 10, nil, 0, 'north' }, + { 11, nil, 0, 'east' }, + { 12, nil, 0, 'south' }, + { 13, nil, 0, 'west' }, + { 14, nil, 0, 'north' }, + { 15, nil, 0, 'east' }, + }) + blockTypeDB:addTemp('quartz-pillar', { + { 2, nil, 2 }, + { 3, nil, 2, 'north-south-block' }, + { 4, nil, 2, 'east-west-block' }, -- should be east-west-block + }) + blockTypeDB:addTemp('hay-bale', { + { 0, nil, 0 }, + { 4, nil, 0, 'east-west-block' }, -- should be east-west-block + { 8, nil, 0, 'north-south-block' }, + }) + blockTypeDB:addTemp('button', { + { 1, nil, 0, 'west-block' }, + { 2, nil, 0, 'east-block' }, + { 3, nil, 0, 'north-block' }, + { 4, nil, 0, 'south-block' }, + { 5, nil, 0 }, -- block top + }) + blockTypeDB:addTemp('cauldron', { + { 0, nil, 0 }, + { 1, nil, 0 }, + { 2, nil, 0 }, + { 3, nil, 0 }, + }) + blockTypeDB:addTemp('dispenser', { + { 0, nil, 0, 'wrench-down' }, + { 1, nil, 0, 'wrench-up' }, + { 2, nil, 0, 'south' }, + { 3, nil, 0, 'north' }, + { 4, nil, 0, 'east' }, + { 5, nil, 0, 'west' }, + { 9, nil, 0 }, + }) + blockTypeDB:addTemp('end_rod', { + { 0, nil, 0, 'wrench-down' }, + { 1, nil, 0, 'wrench-up' }, + { 2, nil, 0, 'south-block-flip' }, + { 3, nil, 0, 'north-block-flip' }, + { 4, nil, 0, 'east-block-flip' }, + { 5, nil, 0, 'west-block-flip' }, + { 9, nil, 0 }, + }) + blockTypeDB:addTemp('hopper', { + { 0, nil, 0 }, + { 1, nil, 0 }, + { 2, nil, 0, 'south-block' }, + { 3, nil, 0, 'north-block' }, + { 4, nil, 0, 'east-block' }, + { 5, nil, 0, 'west-block' }, + { 8, nil, 0 }, + { 9, nil, 0 }, + { 10, nil, 0 }, + { 11, nil, 0, 'south-block' }, + { 12, nil, 0, 'north-block' }, + { 13, nil, 0, 'east-block' }, + { 14, nil, 0, 'west-block' }, + }) + blockTypeDB:addTemp('mobhead', { + { 0, nil, 0 }, + { 1, nil, 0 }, + { 2, nil, 0, 'south-block' }, + { 3, nil, 0, 'north-block' }, + { 4, nil, 0, 'west-block' }, + { 5, nil, 0, 'east-block' }, + }) + blockTypeDB:addTemp('rail', { + { 0, nil, 0, 'south' }, + { 1, nil, 0, 'east' }, + { 2, nil, 0, 'east' }, + { 3, nil, 0, 'east' }, + { 4, nil, 0, 'south' }, + { 5, nil, 0, 'south' }, + { 6, nil, 0, 'east' }, + { 7, nil, 0, 'south' }, + { 8, nil, 0, 'east' }, + { 9, nil, 0, 'south' }, + }) + blockTypeDB:addTemp('adp-rail', { + { 0, nil, 0, 'south' }, + { 1, nil, 0, 'east' }, + { 2, nil, 0, 'east' }, + { 3, nil, 0, 'east' }, + { 4, nil, 0, 'south' }, + { 5, nil, 0, 'south' }, + { 8, nil, 0, 'south' }, + { 9, nil, 0, 'east' }, + { 10, nil, 0, 'east' }, + { 11, nil, 0, 'east' }, + { 12, nil, 0, 'south' }, + { 13, nil, 0, 'south' }, + }) + blockTypeDB:addTemp('signpost', { + { 0, nil, 0, 'north' }, + { 1, nil, 0, 'north', { facing = 1 } }, + { 2, nil, 0, 'north', { facing = 2 } }, + { 3, nil, 0, 'north', { facing = 3 } }, + { 4, nil, 0, 'east' }, + { 5, nil, 0, 'east', { facing = 1 } }, + { 6, nil, 0, 'east', { facing = 2 } }, + { 7, nil, 0, 'east', { facing = 3 } }, + { 8, nil, 0, 'south' }, + { 9, nil, 0, 'south', { facing = 1 } }, + { 10, nil, 0, 'south', { facing = 2 } }, + { 11, nil, 0, 'south', { facing = 3 } }, + { 12, nil, 0, 'west' }, + { 13, nil, 0, 'west', { facing = 1 } }, + { 14, nil, 0, 'west', { facing = 2 } }, + { 15, nil, 0, 'west', { facing = 3 } }, + }) + blockTypeDB:addTemp('vine', { + { 0, nil, 0 }, + { 1, nil, 0, 'south-block-vine' }, + { 2, nil, 0, 'west-block-vine' }, + { 3, nil, 0, 'south-block-vine' }, + { 4, nil, 0, 'north-block-vine' }, + { 5, nil, 0, 'south-block-vine' }, + { 6, nil, 0, 'north-block-vine' }, + { 7, nil, 0, 'south-block-vine' }, + { 8, nil, 0, 'east-block-vine' }, + { 9, nil, 0, 'south-block-vine' }, + { 10, nil, 0, 'east-block-vine' }, + { 11, nil, 0, 'east-block-vine' }, + { 12, nil, 0, 'east-block-vine' }, + { 13, nil, 0, 'east-block-vine' }, + { 14, nil, 0, 'east-block-vine' }, + { 15, nil, 0, 'east-block-vine' }, + }) + blockTypeDB:addTemp('torch', { + { 0, nil, 0 }, + { 1, nil, 0, 'west-block' }, + { 2, nil, 0, 'east-block' }, + { 3, nil, 0, 'north-block' }, + { 4, nil, 0, 'south-block' }, + { 5, nil, 0 }, + }) + blockTypeDB:addTemp('tripwire', { + { 0, nil, 0, 'north-block' }, + { 1, nil, 0, 'east-block' }, + { 2, nil, 0, 'south-block' }, + { 3, nil, 0, 'west-block' }, + }) + blockTypeDB:addTemp('trapdoor', { + { 0, nil, 0, 'south-block' }, + { 1, nil, 0, 'north-block' }, + { 2, nil, 0, 'east-block' }, + { 3, nil, 0, 'west-block' }, + { 4, nil, 0, 'south-block' }, + { 5, nil, 0, 'north-block' }, + { 6, nil, 0, 'east-block' }, + { 7, nil, 0, 'west-block' }, + { 8, nil, 0, 'south' }, + { 9, nil, 0, 'north' }, + { 10, nil, 0, 'east' }, + { 11, nil, 0, 'west' }, + { 12, nil, 0, 'south' }, + { 13, nil, 0, 'north' }, + { 14, nil, 0, 'east' }, + { 15, nil, 0, 'west' }, + }) + blockTypeDB:addTemp('piston', { + { 0, nil, 0, 'piston-down' }, + { 1, nil, 0, 'piston-up' }, + { 2, nil, 0, 'piston-north' }, + { 3, nil, 0, 'piston-south' }, + { 4, nil, 0, 'piston-west' }, + { 5, nil, 0, 'piston-east' }, + { 8, nil, 0, 'piston-down' }, + { 9, nil, 0, 'piston-up' }, + { 10, nil, 0, 'piston-north' }, + { 11, nil, 0, 'piston-south' }, + { 12, nil, 0, 'piston-west' }, + { 13, nil, 0, 'piston-east' }, + }) + blockTypeDB:addTemp('lever', { + { 0, nil, 0, 'up' }, + { 1, nil, 0, 'west-block' }, + { 2, nil, 0, 'east-block' }, + { 3, nil, 0, 'north-block' }, + { 4, nil, 0, 'south-block' }, + { 5, nil, 0, 'north' }, + { 6, nil, 0, 'west' }, + { 7, nil, 0, 'up' }, + { 8, nil, 0, 'up' }, + { 9, nil, 0, 'west-block' }, + { 10, nil, 0, 'east-block' }, + { 11, nil, 0, 'north-block' }, + { 12, nil, 0, 'south-block' }, + { 13, nil, 0, 'north' }, + { 14, nil, 0, 'west' }, + { 15, nil, 0, 'up' }, + }) + blockTypeDB:addTemp('wallsign-ladder', { + { 0, nil, 0 }, + { 1, nil, 0 }, + { 2, nil, 0, 'south-block' }, + { 3, nil, 0, 'north-block' }, + { 4, nil, 0, 'east-block' }, + { 5, nil, 0, 'west-block' }, + }) + blockTypeDB:addTemp('chest-furnace', { + { 0, nil, 0 }, + { 2, nil, 0, 'south' }, + { 3, nil, 0, 'north' }, + { 4, nil, 0, 'east' }, + { 5, nil, 0, 'west' }, + }) + blockTypeDB:addTemp('repeater', { + { 0, nil, 0, 'north' }, + { 1, nil, 0, 'east' }, + { 2, nil, 0, 'south' }, + { 3, nil, 0, 'west' }, + { 4, nil, 0, 'north' }, + { 5, nil, 0, 'east' }, + { 6, nil, 0, 'south' }, + { 7, nil, 0, 'west' }, + { 8, nil, 0, 'north' }, + { 9, nil, 0, 'east' }, + { 10, nil, 0, 'south' }, + { 11, nil, 0, 'west' }, + { 12, nil, 0, 'north' }, + { 13, nil, 0, 'east' }, + { 14, nil, 0, 'south' }, + { 15, nil, 0, 'west' }, + }) + blockTypeDB:addTemp('flatten', { + { 0, nil, 0 }, + { 1, nil, 0 }, + { 2, nil, 0 }, + { 3, nil, 0 }, + { 4, nil, 0 }, + { 5, nil, 0 }, + { 6, nil, 0 }, + { 7, nil, 0 }, + { 8, nil, 0 }, + { 9, nil, 0 }, + { 10, nil, 0 }, + { 11, nil, 0 }, + { 12, nil, 0 }, + { 13, nil, 0 }, + { 14, nil, 0 }, + { 15, nil, 0 }, + }) + blockTypeDB:addTemp('sapling', { + { '+0', nil, nil }, + { '+8', nil, nil }, + }) + blockTypeDB:addTemp('leaves', { + { '+0', nil, nil }, + { '+4', nil, nil }, + { '+8', nil, nil }, + { '+12', nil, nil }, + }) + blockTypeDB:addTemp('slab', { + { '+0', nil, nil, 'bottom' }, + { '+8', nil, nil, 'top' }, + }) + blockTypeDB:addTemp('largeplant', { + { '+0', nil, nil, 'east-door', { twoHigh = true } }, -- should use a generic double tall keyword + { '+8', 'minecraft:air', 0 }, + }) + blockTypeDB:addTemp('wood', { + { '+0', nil, nil }, + { '+4', nil, nil, 'east-west-block' }, + { '+8', nil, nil, 'north-south-block' }, + { '+12', nil, nil }, + }) + blockTypeDB:addTemp('door', { + { 0, nil, 0, 'east-door', { twoHigh = true } }, + { 1, nil, 0, 'south-door', { twoHigh = true } }, + { 2, nil, 0, 'west-door', { twoHigh = true } }, + { 3, nil, 0, 'north-door', { twoHigh = true } }, + { 4, nil, 0, 'east-door', { twoHigh = true } }, + { 5, nil, 0, 'south-door', { twoHigh = true } }, + { 6, nil, 0, 'west-door', { twoHigh = true } }, + { 7, nil, 0, 'north-door', { twoHigh = true } }, + { 8,'minecraft:air', 0 }, + { 9,'minecraft:air', 0 }, + { 10,'minecraft:air', 0 }, + { 11,'minecraft:air', 0 }, + { 12,'minecraft:air', 0 }, + { 13,'minecraft:air', 0 }, + { 14,'minecraft:air', 0 }, + { 15,'minecraft:air', 0 }, + }) + blockTypeDB:addTemp('cocoa', { + { 0, nil, 0, 'south-block' }, + { 1, nil, 0, 'west-block' }, + { 2, nil, 0, 'north-block' }, + { 3, nil, 0, 'east-block' }, + { 4, nil, 0, 'south-block' }, + { 5, nil, 0, 'west-block' }, + { 6, nil, 0, 'north-block' }, + { 7, nil, 0, 'east-block' }, + { 8, nil, 0, 'south-block' }, + { 9, nil, 0, 'west-block' }, + { 10, nil, 0, 'north-block' }, + { 11, nil, 0, 'east-block' }, + }) end local Blocks = class() function Blocks:init(args) - Util.merge(self, args) - self.blockDB = blockDB + Util.merge(self, args) + self.blockDB = blockDB - blockDB:load() - blockTypeDB:load() - placementDB:load(blockDB, blockTypeDB) + blockDB:load() + blockTypeDB:load() + placementDB:load(blockDB, blockTypeDB) end -- for an ID / dmg (with placement info) -- return the correct block (without the placment info embedded in the dmg) function Blocks:getPlaceableBlock(id, dmg) - local p = placementDB:get({id, dmg}) - if p then - return Util.shallowCopy(p) - end + local p = placementDB:get({id, dmg}) + if p then + return Util.shallowCopy(p) + end - local b = blockDB:get({id, dmg}) - if b then - return { id = b.strId, dmg = b.dmg } - end + local b = blockDB:get({id, dmg}) + if b then + return { id = b.strId, dmg = b.dmg } + end - b = blockDB:get({id, 0}) - if b then - return { id = b.strId, dmg = b.dmg } - end + b = blockDB:get({id, 0}) + if b then + return { id = b.strId, dmg = b.dmg } + end - return { id = id, dmg = dmg } + return { id = id, dmg = dmg } end return Blocks diff --git a/builder/apis/builder.lua b/builder/apis/builder.lua index 5bbd4b6..2f51ccd 100644 --- a/builder/apis/builder.lua +++ b/builder/apis/builder.lua @@ -9,10 +9,10 @@ local turtle = _G.turtle local Builder = class() Util.merge(Builder, { - isCommandComputer = not turtle, - loc = { }, - index = 1, - mode = 'build', + isCommandComputer = not turtle, + loc = { }, + index = 1, + mode = 'build', }) local BUILDER_DIR = 'usr/builder' @@ -20,82 +20,82 @@ local BUILDER_DIR = 'usr/builder' local blockInfo = Blocks() function Builder:getBlockCounts() - local blocks = { } + local blocks = { } - for k = self.index, #self.schematic.blocks do - local b = self.schematic.blocks[k] - local key = tostring(b.id) .. ':' .. b.dmg - local block = blocks[key] - if not block then - block = Util.shallowCopy(b) - block.qty = 0 - block.need = 0 - blocks[key] = block - end - block.need = block.need + 1 - end + for k = self.index, #self.schematic.blocks do + local b = self.schematic.blocks[k] + local key = tostring(b.id) .. ':' .. b.dmg + local block = blocks[key] + if not block then + block = Util.shallowCopy(b) + block.qty = 0 + block.need = 0 + blocks[key] = block + end + block.need = block.need + 1 + end - return blocks + return blocks end function Builder:substituteBlocks(throttle) - for _,b in pairs(self.schematic.blocks) do + for _,b in pairs(self.schematic.blocks) do - -- replace schematic block type with substitution - local pb = blockInfo:getPlaceableBlock(b.id, b.dmg) + -- replace schematic block type with substitution + local pb = blockInfo:getPlaceableBlock(b.id, b.dmg) - Util.merge(b, pb) + Util.merge(b, pb) - b.odmg = pb.odmg or pb.dmg + b.odmg = pb.odmg or pb.dmg - local sub = self.subDB:get({ b.id, b.dmg }) - if sub then - b.id, b.dmg = self.subDB:extract(sub) - end - throttle() - end + local sub = self.subDB:get({ b.id, b.dmg }) + if sub then + b.id, b.dmg = self.subDB:extract(sub) + end + throttle() + end end function Builder:reloadSchematic(throttle) - self.schematic:reload(throttle) - self:substituteBlocks(throttle) + self.schematic:reload(throttle) + self:substituteBlocks(throttle) end function Builder:log(...) - Util.print(...) + Util.print(...) end function Builder:dumpInventory() end function Builder:logBlock(index, b) - local bdir = b.direction or '' - local logText = string.format('%d %s:%d (x:%d,z:%d:y:%d) %s', - index, b.id, b.dmg, b.x, b.z, b.y, bdir) - self:log(logText) - -- self:log(b.index) -- unique identifier of block + local bdir = b.direction or '' + local logText = string.format('%d %s:%d (x:%d,z:%d:y:%d) %s', + index, b.id, b.dmg, b.x, b.z, b.y, bdir) + self:log(logText) + -- self:log(b.index) -- unique identifier of block - if device.wireless_modem then - Message.broadcast('builder', { x = b.x, y = b.y, z = b.z, heading = b.heading }) - end + if device.wireless_modem then + Message.broadcast('builder', { x = b.x, y = b.y, z = b.z, heading = b.heading }) + end end function Builder:saveProgress(index) - Util.writeTable( - fs.combine(BUILDER_DIR, self.schematic.filename .. '.progress'), - { index = index, loc = self.loc } - ) + Util.writeTable( + fs.combine(BUILDER_DIR, self.schematic.filename .. '.progress'), + { index = index, loc = self.loc } + ) end function Builder:loadProgress(filename) - local progress = Util.readTable(fs.combine(BUILDER_DIR, filename)) - if progress then - self.index = progress.index - if self.index > #self.schematic.blocks then - self.index = 1 - end - self.loc = progress.loc or { } - end + local progress = Util.readTable(fs.combine(BUILDER_DIR, filename)) + if progress then + self.index = progress.index + if self.index > #self.schematic.blocks then + self.index = 1 + end + self.loc = progress.loc or { } + end end return Builder diff --git a/builder/apis/commands.lua b/builder/apis/commands.lua index f300bae..32bfbf7 100644 --- a/builder/apis/commands.lua +++ b/builder/apis/commands.lua @@ -8,77 +8,77 @@ local os = _G.os local read = _G.read function Builder:begin() - local direction = 1 - local last = #self.schematic.blocks - local throttle = Util.throttle() + local direction = 1 + local last = #self.schematic.blocks + local throttle = Util.throttle() - local cx, cy, cz = commands.getBlockPosition() - if self.loc.x then - cx, cy, cz = self.loc.rx, self.loc.ry, self.loc.rz - end + local cx, cy, cz = commands.getBlockPosition() + if self.loc.x then + cx, cy, cz = self.loc.rx, self.loc.ry, self.loc.rz + end - if self.mode == 'destroy' then - direction = -1 - last = 1 - end + if self.mode == 'destroy' then + direction = -1 + last = 1 + end - for i = self.index, last, direction do - self.index = i + for i = self.index, last, direction do + self.index = i - local b = self.schematic:getComputedBlock(i) + local b = self.schematic:getComputedBlock(i) - if b.id ~= 'minecraft:air' then + if b.id ~= 'minecraft:air' then - self:logBlock(self.index, b) + self:logBlock(self.index, b) - local id = b.id - if self.mode == 'destroy' then - id = 'minecraft:air' - end + local id = b.id + if self.mode == 'destroy' then + id = 'minecraft:air' + end - local function placeBlock(bid, dmg, x, y, z) - local command = table.concat({ - "setblock", - cx + x + 1, - cy + y, - cz + z + 1, - bid, - dmg, - }, ' ') + local function placeBlock(bid, dmg, x, y, z) + local command = table.concat({ + "setblock", + cx + x + 1, + cy + y, + cz + z + 1, + bid, + dmg, + }, ' ') - commands.execAsync(command) + commands.execAsync(command) - local result = { os.pullEvent("task_complete") } - if not result[4] then - Util.print(result[5]) - if self.mode ~= 'destroy' then - read() - end - end - end + local result = { os.pullEvent("task_complete") } + if not result[4] then + Util.print(result[5]) + if self.mode ~= 'destroy' then + read() + end + end + end - placeBlock(id, b.odmg, b.x, b.y, b.z) + placeBlock(id, b.odmg, b.x, b.y, b.z) - if b.twoHigh then - local _, topBlock = self.schematic:findIndexAt(b.x, b.z, b.y + 1, true) - if topBlock then - placeBlock(id, topBlock.odmg, b.x, b.y + 1, b.z) - end - end + if b.twoHigh then + local _, topBlock = self.schematic:findIndexAt(b.x, b.z, b.y + 1, true) + if topBlock then + placeBlock(id, topBlock.odmg, b.x, b.y + 1, b.z) + end + end - if self.mode == 'destroy' then - self:saveProgress(math.max(self.index, 1)) - else - self:saveProgress(self.index + 1) - end - else - throttle() -- sleep in case there are a large # of skipped blocks - end - end + if self.mode == 'destroy' then + self:saveProgress(math.max(self.index, 1)) + else + self:saveProgress(self.index + 1) + end + else + throttle() -- sleep in case there are a large # of skipped blocks + end + end - fs.delete(self.schematic.filename .. '.progress') - print('Finished') - Event.exitPullEvents() + fs.delete(self.schematic.filename .. '.progress') + print('Finished') + Event.exitPullEvents() end return Builder diff --git a/builder/apis/deflatelua.lua b/builder/apis/deflatelua.lua index f096e0c..45a2d70 100644 --- a/builder/apis/deflatelua.lua +++ b/builder/apis/deflatelua.lua @@ -2,121 +2,121 @@ LUA MODULE - compress.deflatelua - deflate (and gunzip/zlib) implemented in Lua. + compress.deflatelua - deflate (and gunzip/zlib) implemented in Lua. SYNOPSIS - local DEFLATE = require 'compress.deflatelua' - -- uncompress gzip file - local fh = assert(io.open'foo.txt.gz', 'rb') - local ofh = assert(io.open'foo.txt', 'wb') - DEFLATE.gunzip {input=fh, output=ofh} - fh:close(); ofh:close() - -- can also uncompress from string including zlib and raw DEFLATE formats. + local DEFLATE = require 'compress.deflatelua' + -- uncompress gzip file + local fh = assert(io.open'foo.txt.gz', 'rb') + local ofh = assert(io.open'foo.txt', 'wb') + DEFLATE.gunzip {input=fh, output=ofh} + fh:close(); ofh:close() + -- can also uncompress from string including zlib and raw DEFLATE formats. DESCRIPTION - This is a pure Lua implementation of decompressing the DEFLATE format, - including the related zlib and gzip formats. + This is a pure Lua implementation of decompressing the DEFLATE format, + including the related zlib and gzip formats. - Note: This library only supports decompression. - Compression is not currently implemented. + Note: This library only supports decompression. + Compression is not currently implemented. API - Note: in the following functions, input stream `fh` may be - a file handle, string, or an iterator function that returns strings. - Output stream `ofh` may be a file handle or a function that - consumes one byte (number 0..255) per call. + Note: in the following functions, input stream `fh` may be + a file handle, string, or an iterator function that returns strings. + Output stream `ofh` may be a file handle or a function that + consumes one byte (number 0..255) per call. - DEFLATE.inflate {input=fh, output=ofh} + DEFLATE.inflate {input=fh, output=ofh} - Decompresses input stream `fh` in the DEFLATE format - while writing to output stream `ofh`. - DEFLATE is detailed in http://tools.ietf.org/html/rfc1951 . + Decompresses input stream `fh` in the DEFLATE format + while writing to output stream `ofh`. + DEFLATE is detailed in http://tools.ietf.org/html/rfc1951 . - DEFLATE.gunzip {input=fh, output=ofh, disable_crc=disable_crc} + DEFLATE.gunzip {input=fh, output=ofh, disable_crc=disable_crc} - Decompresses input stream `fh` with the gzip format - while writing to output stream `ofh`. - `disable_crc` (defaults to `false`) will disable CRC-32 checking - to increase speed. - gzip is detailed in http://tools.ietf.org/html/rfc1952 . + Decompresses input stream `fh` with the gzip format + while writing to output stream `ofh`. + `disable_crc` (defaults to `false`) will disable CRC-32 checking + to increase speed. + gzip is detailed in http://tools.ietf.org/html/rfc1952 . - DEFLATE.inflate_zlib {input=fh, output=ofh, disable_crc=disable_crc} + DEFLATE.inflate_zlib {input=fh, output=ofh, disable_crc=disable_crc} - Decompresses input stream `fh` with the zlib format - while writing to output stream `ofh`. - `disable_crc` (defaults to `false`) will disable CRC-32 checking - to increase speed. - zlib is detailed in http://tools.ietf.org/html/rfc1950 . + Decompresses input stream `fh` with the zlib format + while writing to output stream `ofh`. + `disable_crc` (defaults to `false`) will disable CRC-32 checking + to increase speed. + zlib is detailed in http://tools.ietf.org/html/rfc1950 . - DEFLATE.adler32(byte, crc) --> rcrc + DEFLATE.adler32(byte, crc) --> rcrc - Returns adler32 checksum of byte `byte` (number 0..255) appended - to string with adler32 checksum `crc`. This is internally used by - `inflate_zlib`. - ADLER32 in detailed in http://tools.ietf.org/html/rfc1950 . + Returns adler32 checksum of byte `byte` (number 0..255) appended + to string with adler32 checksum `crc`. This is internally used by + `inflate_zlib`. + ADLER32 in detailed in http://tools.ietf.org/html/rfc1950 . COMMAND LINE UTILITY - A `gunziplua` command line utility (in folder `bin`) is also provided. - This mimicks the *nix `gunzip` utility but is a pure Lua implementation - that invokes this library. For help do + A `gunziplua` command line utility (in folder `bin`) is also provided. + This mimicks the *nix `gunzip` utility but is a pure Lua implementation + that invokes this library. For help do - gunziplua -h + gunziplua -h DEPENDENCIES - Requires 'digest.crc32lua' (used for optional CRC-32 checksum checks). - https://github.com/davidm/lua-digest-crc32lua + Requires 'digest.crc32lua' (used for optional CRC-32 checksum checks). + https://github.com/davidm/lua-digest-crc32lua - Will use a bit library ('bit', 'bit32', 'bit.numberlua') if available. This - is not that critical for this library but is required by digest.crc32lua. + Will use a bit library ('bit', 'bit32', 'bit.numberlua') if available. This + is not that critical for this library but is required by digest.crc32lua. - 'pythonic.optparse' is only required by the optional `gunziplua` - command-line utilty for command line parsing. - https://github.com/davidm/lua-pythonic-optparse + 'pythonic.optparse' is only required by the optional `gunziplua` + command-line utilty for command line parsing. + https://github.com/davidm/lua-pythonic-optparse INSTALLATION - Copy the `compress` directory into your LUA_PATH. + Copy the `compress` directory into your LUA_PATH. REFERENCES - [1] DEFLATE Compressed Data Format Specification version 1.3 - http://tools.ietf.org/html/rfc1951 - [2] GZIP file format specification version 4.3 - http://tools.ietf.org/html/rfc1952 - [3] http://en.wikipedia.org/wiki/DEFLATE - [4] pyflate, by Paul Sladen - http://www.paul.sladen.org/projects/pyflate/ - [5] Compress::Zlib::Perl - partial pure Perl implementation of - Compress::Zlib - http://search.cpan.org/~nwclark/Compress-Zlib-Perl/Perl.pm + [1] DEFLATE Compressed Data Format Specification version 1.3 + http://tools.ietf.org/html/rfc1951 + [2] GZIP file format specification version 4.3 + http://tools.ietf.org/html/rfc1952 + [3] http://en.wikipedia.org/wiki/DEFLATE + [4] pyflate, by Paul Sladen + http://www.paul.sladen.org/projects/pyflate/ + [5] Compress::Zlib::Perl - partial pure Perl implementation of + Compress::Zlib + http://search.cpan.org/~nwclark/Compress-Zlib-Perl/Perl.pm LICENSE - (c) 2008-2011 David Manura. Licensed under the same terms as Lua (MIT). + (c) 2008-2011 David Manura. Licensed under the same terms as Lua (MIT). - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - (end license) + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + (end license) --]] local M = {_TYPE='module', _NAME='compress.deflatelua', _VERSION='0.3.20111128'} @@ -146,58 +146,58 @@ local NATIVE_BITOPS = (bit ~= nil) local band, lshift, rshift if NATIVE_BITOPS then - band = bit.band - lshift = bit.lshift - rshift = bit.rshift + band = bit.band + lshift = bit.lshift + rshift = bit.rshift end local function warn(s) - io.stderr:write(s, '\n') + io.stderr:write(s, '\n') end local function runtime_error(s, level) - level = level or 1 - error({s}, level+1) + level = level or 1 + error({s}, level+1) end local function make_outstate(outbs) - local outstate = {} - outstate.outbs = outbs - outstate.window = {} - outstate.window_pos = 1 - return outstate + local outstate = {} + outstate.outbs = outbs + outstate.window = {} + outstate.window_pos = 1 + return outstate end local function output(outstate, byte) - local window_pos = outstate.window_pos - outstate.outbs(byte) - outstate.window[window_pos] = byte - outstate.window_pos = window_pos % 32768 + 1 -- 32K + local window_pos = outstate.window_pos + outstate.outbs(byte) + outstate.window[window_pos] = byte + outstate.window_pos = window_pos % 32768 + 1 -- 32K end local function noeof(val) - return assert(val, 'unexpected end of file') + return assert(val, 'unexpected end of file') end local function hasbit(bits, bit) - return bits % (bit + bit) >= bit + return bits % (bit + bit) >= bit end local function memoize(f) - local mt = {} - local t = setmetatable({}, mt) - function mt:__index(k) - local v = f(k) - t[k] = v - return v - end - return t + local mt = {} + local t = setmetatable({}, mt) + function mt:__index(k) + local v = f(k) + t[k] = v + return v + end + return t end @@ -214,343 +214,343 @@ local pow2 = memoize(function(n) return 2^n end) local is_bitstream = setmetatable({}, {__mode='k'}) local function bytestream_from_file(fh) - local o = {} - function o:read() - local sb = fh:read(1) - if sb then return sb:byte() end - end - return o + local o = {} + function o:read() + local sb = fh:read(1) + if sb then return sb:byte() end + end + return o end local function bytestream_from_string(s) - local i = 1 - local o = {} - function o:read() - local by - if i <= #s then - by = s:byte(i) - i = i + 1 - end - return by - end - return o + local i = 1 + local o = {} + function o:read() + local by + if i <= #s then + by = s:byte(i) + i = i + 1 + end + return by + end + return o end local function bytestream_from_function(f) - local o = {} - function o:read() - return f() - end - return o + local o = {} + function o:read() + return f() + end + return o end local function bitstream_from_bytestream(bys) - local buf_byte = 0 - local buf_nbit = 0 - local o = {} + local buf_byte = 0 + local buf_nbit = 0 + local o = {} - function o:nbits_left_in_byte() - return buf_nbit - end + function o:nbits_left_in_byte() + return buf_nbit + end - if NATIVE_BITOPS then - function o:read(nbits) - nbits = nbits or 1 - while buf_nbit < nbits do - local byte = bys:read() - if not byte then return end -- note: more calls also return nil - buf_byte = buf_byte + lshift(byte, buf_nbit) - buf_nbit = buf_nbit + 8 - end - local bits - if nbits == 0 then - bits = 0 - elseif nbits == 32 then - bits = buf_byte - buf_byte = 0 - else - bits = band(buf_byte, rshift(0xffffffff, 32 - nbits)) - buf_byte = rshift(buf_byte, nbits) - end - buf_nbit = buf_nbit - nbits - return bits - end - else - function o:read(nbits) - nbits = nbits or 1 - while buf_nbit < nbits do - local byte = bys:read() - if not byte then return end -- note: more calls also return nil - buf_byte = buf_byte + pow2[buf_nbit] * byte - buf_nbit = buf_nbit + 8 - end - local m = pow2[nbits] - local bits = buf_byte % m - buf_byte = (buf_byte - bits) / m - buf_nbit = buf_nbit - nbits - return bits - end - end - - is_bitstream[o] = true + if NATIVE_BITOPS then + function o:read(nbits) + nbits = nbits or 1 + while buf_nbit < nbits do + local byte = bys:read() + if not byte then return end -- note: more calls also return nil + buf_byte = buf_byte + lshift(byte, buf_nbit) + buf_nbit = buf_nbit + 8 + end + local bits + if nbits == 0 then + bits = 0 + elseif nbits == 32 then + bits = buf_byte + buf_byte = 0 + else + bits = band(buf_byte, rshift(0xffffffff, 32 - nbits)) + buf_byte = rshift(buf_byte, nbits) + end + buf_nbit = buf_nbit - nbits + return bits + end + else + function o:read(nbits) + nbits = nbits or 1 + while buf_nbit < nbits do + local byte = bys:read() + if not byte then return end -- note: more calls also return nil + buf_byte = buf_byte + pow2[buf_nbit] * byte + buf_nbit = buf_nbit + 8 + end + local m = pow2[nbits] + local bits = buf_byte % m + buf_byte = (buf_byte - bits) / m + buf_nbit = buf_nbit - nbits + return bits + end + end + + is_bitstream[o] = true - return o + return o end local function get_bitstream(o) - local bs - if is_bitstream[o] then - return o - elseif io.type(o) == 'file' then - bs = bitstream_from_bytestream(bytestream_from_file(o)) - elseif type(o) == 'string' then - bs = bitstream_from_bytestream(bytestream_from_string(o)) - elseif type(o) == 'function' then - bs = bitstream_from_bytestream(bytestream_from_function(o)) - else - runtime_error 'unrecognized type' - end - return bs + local bs + if is_bitstream[o] then + return o + elseif io.type(o) == 'file' then + bs = bitstream_from_bytestream(bytestream_from_file(o)) + elseif type(o) == 'string' then + bs = bitstream_from_bytestream(bytestream_from_string(o)) + elseif type(o) == 'function' then + bs = bitstream_from_bytestream(bytestream_from_function(o)) + else + runtime_error 'unrecognized type' + end + return bs end local function get_obytestream(o) - local bs - if io.type(o) == 'file' then - bs = function(sbyte) o:write(string_char(sbyte)) end - elseif type(o) == 'function' then - bs = o - else - runtime_error('unrecognized type: ' .. tostring(o)) - end - return bs + local bs + if io.type(o) == 'file' then + bs = function(sbyte) o:write(string_char(sbyte)) end + elseif type(o) == 'function' then + bs = o + else + runtime_error('unrecognized type: ' .. tostring(o)) + end + return bs end local function HuffmanTable(init, is_full) - local t = {} - if is_full then - for val,nbits in pairs(init) do - if nbits ~= 0 then - t[#t+1] = {val=val, nbits=nbits} - end - end - else - for i=1,#init-2,2 do - local firstval, nbits, nextval = init[i], init[i+1], init[i+2] - if nbits ~= 0 then - for val=firstval,nextval-1 do - t[#t+1] = {val=val, nbits=nbits} - end - end - end - end - table_sort(t, function(a,b) - return a.nbits == b.nbits and a.val < b.val or a.nbits < b.nbits - end) + local t = {} + if is_full then + for val,nbits in pairs(init) do + if nbits ~= 0 then + t[#t+1] = {val=val, nbits=nbits} + end + end + else + for i=1,#init-2,2 do + local firstval, nbits, nextval = init[i], init[i+1], init[i+2] + if nbits ~= 0 then + for val=firstval,nextval-1 do + t[#t+1] = {val=val, nbits=nbits} + end + end + end + end + table_sort(t, function(a,b) + return a.nbits == b.nbits and a.val < b.val or a.nbits < b.nbits + end) - -- assign codes - local code = 1 -- leading 1 marker - local nbits = 0 - for i,s in ipairs(t) do - if s.nbits ~= nbits then - code = code * pow2[s.nbits - nbits] - nbits = s.nbits - end - s.code = code - code = code + 1 - end + -- assign codes + local code = 1 -- leading 1 marker + local nbits = 0 + for i,s in ipairs(t) do + if s.nbits ~= nbits then + code = code * pow2[s.nbits - nbits] + nbits = s.nbits + end + s.code = code + code = code + 1 + end - local minbits = math.huge - local look = {} - for i,s in ipairs(t) do - minbits = math.min(minbits, s.nbits) - look[s.code] = s.val - end + local minbits = math.huge + local look = {} + for i,s in ipairs(t) do + minbits = math.min(minbits, s.nbits) + look[s.code] = s.val + end - local msb = NATIVE_BITOPS and function(bits, nbits) - local res = 0 - for i=1,nbits do - res = lshift(res, 1) + band(bits, 1) - bits = rshift(bits, 1) - end - return res - end or function(bits, nbits) - local res = 0 - for _=1,nbits do - local b = bits % 2 - bits = (bits - b) / 2 - res = res * 2 + b - end - return res - end + local msb = NATIVE_BITOPS and function(bits, nbits) + local res = 0 + for i=1,nbits do + res = lshift(res, 1) + band(bits, 1) + bits = rshift(bits, 1) + end + return res + end or function(bits, nbits) + local res = 0 + for _=1,nbits do + local b = bits % 2 + bits = (bits - b) / 2 + res = res * 2 + b + end + return res + end - local tfirstcode = memoize( - function(bits) return pow2[minbits] + msb(bits, minbits) end) + local tfirstcode = memoize( + function(bits) return pow2[minbits] + msb(bits, minbits) end) - function t:read(bs) - local code = 1 -- leading 1 marker - local nbits = 0 - while 1 do - if nbits == 0 then -- small optimization (optional) - code = tfirstcode[noeof(bs:read(minbits))] - nbits = nbits + minbits - else - local b = noeof(bs:read()) - nbits = nbits + 1 - code = code * 2 + b -- MSB first - end - local val = look[code] - if val then - return val - end - end - end + function t:read(bs) + local code = 1 -- leading 1 marker + local nbits = 0 + while 1 do + if nbits == 0 then -- small optimization (optional) + code = tfirstcode[noeof(bs:read(minbits))] + nbits = nbits + minbits + else + local b = noeof(bs:read()) + nbits = nbits + 1 + code = code * 2 + b -- MSB first + end + local val = look[code] + if val then + return val + end + end + end - return t + return t end local function parse_gzip_header(bs) - -- local FLG_FTEXT = 2^0 - local FLG_FHCRC = 2^1 - local FLG_FEXTRA = 2^2 - local FLG_FNAME = 2^3 - local FLG_FCOMMENT = 2^4 + -- local FLG_FTEXT = 2^0 + local FLG_FHCRC = 2^1 + local FLG_FEXTRA = 2^2 + local FLG_FNAME = 2^3 + local FLG_FCOMMENT = 2^4 - local id1 = bs:read(8) - local id2 = bs:read(8) - if id1 ~= 31 or id2 ~= 139 then - runtime_error 'not in gzip format' - end - local cm = bs:read(8) -- compression method - local flg = bs:read(8) -- FLaGs - local mtime = bs:read(32) -- Modification TIME - local xfl = bs:read(8) -- eXtra FLags - local os = bs:read(8) -- Operating System + local id1 = bs:read(8) + local id2 = bs:read(8) + if id1 ~= 31 or id2 ~= 139 then + runtime_error 'not in gzip format' + end + local cm = bs:read(8) -- compression method + local flg = bs:read(8) -- FLaGs + local mtime = bs:read(32) -- Modification TIME + local xfl = bs:read(8) -- eXtra FLags + local os = bs:read(8) -- Operating System - if not os then runtime_error 'invalid header' end + if not os then runtime_error 'invalid header' end - if hasbit(flg, FLG_FEXTRA) then - local xlen = bs:read(16) - local extra = 0 - for i=1,xlen do - extra = bs:read(8) - end - if not extra then runtime_error 'invalid header' end - end + if hasbit(flg, FLG_FEXTRA) then + local xlen = bs:read(16) + local extra = 0 + for i=1,xlen do + extra = bs:read(8) + end + if not extra then runtime_error 'invalid header' end + end - local function parse_zstring(bs) - repeat - local by = bs:read(8) - if not by then runtime_error 'invalid header' end - until by == 0 - end + local function parse_zstring(bs) + repeat + local by = bs:read(8) + if not by then runtime_error 'invalid header' end + until by == 0 + end - if hasbit(flg, FLG_FNAME) then - parse_zstring(bs) - end + if hasbit(flg, FLG_FNAME) then + parse_zstring(bs) + end - if hasbit(flg, FLG_FCOMMENT) then - parse_zstring(bs) - end + if hasbit(flg, FLG_FCOMMENT) then + parse_zstring(bs) + end - if hasbit(flg, FLG_FHCRC) then - local crc16 = bs:read(16) - if not crc16 then runtime_error 'invalid header' end - -- IMPROVE: check CRC. where is an example .gz file that - -- has this set? - end + if hasbit(flg, FLG_FHCRC) then + local crc16 = bs:read(16) + if not crc16 then runtime_error 'invalid header' end + -- IMPROVE: check CRC. where is an example .gz file that + -- has this set? + end end local function parse_zlib_header(bs) - local cm = bs:read(4) -- Compression Method - local cinfo = bs:read(4) -- Compression info - local fcheck = bs:read(5) -- FLaGs: FCHECK (check bits for CMF and FLG) - local fdict = bs:read(1) -- FLaGs: FDICT (present dictionary) - local flevel = bs:read(2) -- FLaGs: FLEVEL (compression level) - local cmf = cinfo * 16 + cm -- CMF (Compresion Method and flags) - local flg = fcheck + fdict * 32 + flevel * 64 -- FLaGs - - if cm ~= 8 then -- not "deflate" - runtime_error("unrecognized zlib compression method: " + cm) - end - if cinfo > 7 then - runtime_error("invalid zlib window size: cinfo=" + cinfo) - end - local window_size = 2^(cinfo + 8) - - if (cmf*256 + flg) % 31 ~= 0 then - runtime_error("invalid zlib header (bad fcheck sum)") - end - - if fdict == 1 then - runtime_error("FIX:TODO - FDICT not currently implemented") - local dictid_ = bs:read(32) - end - - return window_size + local cm = bs:read(4) -- Compression Method + local cinfo = bs:read(4) -- Compression info + local fcheck = bs:read(5) -- FLaGs: FCHECK (check bits for CMF and FLG) + local fdict = bs:read(1) -- FLaGs: FDICT (present dictionary) + local flevel = bs:read(2) -- FLaGs: FLEVEL (compression level) + local cmf = cinfo * 16 + cm -- CMF (Compresion Method and flags) + local flg = fcheck + fdict * 32 + flevel * 64 -- FLaGs + + if cm ~= 8 then -- not "deflate" + runtime_error("unrecognized zlib compression method: " + cm) + end + if cinfo > 7 then + runtime_error("invalid zlib window size: cinfo=" + cinfo) + end + local window_size = 2^(cinfo + 8) + + if (cmf*256 + flg) % 31 ~= 0 then + runtime_error("invalid zlib header (bad fcheck sum)") + end + + if fdict == 1 then + runtime_error("FIX:TODO - FDICT not currently implemented") + local dictid_ = bs:read(32) + end + + return window_size end local function parse_huffmantables(bs) - local hlit = bs:read(5) -- # of literal/length codes - 257 - local hdist = bs:read(5) -- # of distance codes - 1 - local hclen = noeof(bs:read(4)) -- # of code length codes - 4 + local hlit = bs:read(5) -- # of literal/length codes - 257 + local hdist = bs:read(5) -- # of distance codes - 1 + local hclen = noeof(bs:read(4)) -- # of code length codes - 4 - local ncodelen_codes = hclen + 4 - local codelen_init = {} - local codelen_vals = { - 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15} - for i=1,ncodelen_codes do - local nbits = bs:read(3) - local val = codelen_vals[i] - codelen_init[val] = nbits - end - local codelentable = HuffmanTable(codelen_init, true) + local ncodelen_codes = hclen + 4 + local codelen_init = {} + local codelen_vals = { + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15} + for i=1,ncodelen_codes do + local nbits = bs:read(3) + local val = codelen_vals[i] + codelen_init[val] = nbits + end + local codelentable = HuffmanTable(codelen_init, true) - local function decode(ncodes) - local init = {} - local nbits - local val = 0 - while val < ncodes do - local codelen = codelentable:read(bs) - --FIX:check nil? - local nrepeat - if codelen <= 15 then - nrepeat = 1 - nbits = codelen - elseif codelen == 16 then - nrepeat = 3 + noeof(bs:read(2)) - -- nbits unchanged - elseif codelen == 17 then - nrepeat = 3 + noeof(bs:read(3)) - nbits = 0 - elseif codelen == 18 then - nrepeat = 11 + noeof(bs:read(7)) - nbits = 0 - else - error 'ASSERT' - end - for i=1,nrepeat do - init[val] = nbits - val = val + 1 - end - end - local huffmantable = HuffmanTable(init, true) - return huffmantable - end + local function decode(ncodes) + local init = {} + local nbits + local val = 0 + while val < ncodes do + local codelen = codelentable:read(bs) + --FIX:check nil? + local nrepeat + if codelen <= 15 then + nrepeat = 1 + nbits = codelen + elseif codelen == 16 then + nrepeat = 3 + noeof(bs:read(2)) + -- nbits unchanged + elseif codelen == 17 then + nrepeat = 3 + noeof(bs:read(3)) + nbits = 0 + elseif codelen == 18 then + nrepeat = 11 + noeof(bs:read(7)) + nbits = 0 + else + error 'ASSERT' + end + for i=1,nrepeat do + init[val] = nbits + val = val + 1 + end + end + local huffmantable = HuffmanTable(init, true) + return huffmantable + end - local nlit_codes = hlit + 257 - local ndist_codes = hdist + 1 + local nlit_codes = hlit + 257 + local ndist_codes = hdist + 1 - local littable = decode(nlit_codes) - local disttable = decode(ndist_codes) + local littable = decode(nlit_codes) + local disttable = decode(ndist_codes) - return littable, disttable + return littable, disttable end @@ -559,210 +559,210 @@ local tdecode_len_nextrabits local tdecode_dist_base local tdecode_dist_nextrabits local function parse_compressed_item(bs, outstate, littable, disttable) - local val = littable:read(bs) - if val < 256 then -- literal - output(outstate, val) - elseif val == 256 then -- end of block - return true - else - if not tdecode_len_base then - local t = {[257]=3} - local skip = 1 - for i=258,285,4 do - for j=i,i+3 do t[j] = t[j-1] + skip end - if i ~= 258 then skip = skip * 2 end - end - t[285] = 258 - tdecode_len_base = t - end - if not tdecode_len_nextrabits then - local t = {} - if NATIVE_BITOPS then - for i=257,285 do - local j = math_max(i - 261, 0) - t[i] = rshift(j, 2) - end - else - for i=257,285 do - local j = math_max(i - 261, 0) - t[i] = (j - (j % 4)) / 4 - end - end - t[285] = 0 - tdecode_len_nextrabits = t - end - local len_base = tdecode_len_base[val] - local nextrabits = tdecode_len_nextrabits[val] - local extrabits = bs:read(nextrabits) - local len = len_base + extrabits + local val = littable:read(bs) + if val < 256 then -- literal + output(outstate, val) + elseif val == 256 then -- end of block + return true + else + if not tdecode_len_base then + local t = {[257]=3} + local skip = 1 + for i=258,285,4 do + for j=i,i+3 do t[j] = t[j-1] + skip end + if i ~= 258 then skip = skip * 2 end + end + t[285] = 258 + tdecode_len_base = t + end + if not tdecode_len_nextrabits then + local t = {} + if NATIVE_BITOPS then + for i=257,285 do + local j = math_max(i - 261, 0) + t[i] = rshift(j, 2) + end + else + for i=257,285 do + local j = math_max(i - 261, 0) + t[i] = (j - (j % 4)) / 4 + end + end + t[285] = 0 + tdecode_len_nextrabits = t + end + local len_base = tdecode_len_base[val] + local nextrabits = tdecode_len_nextrabits[val] + local extrabits = bs:read(nextrabits) + local len = len_base + extrabits - if not tdecode_dist_base then - local t = {[0]=1} - local skip = 1 - for i=1,29,2 do - for j=i,i+1 do t[j] = t[j-1] + skip end - if i ~= 1 then skip = skip * 2 end - end - tdecode_dist_base = t - end - if not tdecode_dist_nextrabits then - local t = {} - if NATIVE_BITOPS then - for i=0,29 do - local j = math_max(i - 2, 0) - t[i] = rshift(j, 1) - end - else - for i=0,29 do - local j = math_max(i - 2, 0) - t[i] = (j - (j % 2)) / 2 - end - end - tdecode_dist_nextrabits = t - end - local dist_val = disttable:read(bs) - local dist_base = tdecode_dist_base[dist_val] - local dist_nextrabits = tdecode_dist_nextrabits[dist_val] - local dist_extrabits = bs:read(dist_nextrabits) - local dist = dist_base + dist_extrabits + if not tdecode_dist_base then + local t = {[0]=1} + local skip = 1 + for i=1,29,2 do + for j=i,i+1 do t[j] = t[j-1] + skip end + if i ~= 1 then skip = skip * 2 end + end + tdecode_dist_base = t + end + if not tdecode_dist_nextrabits then + local t = {} + if NATIVE_BITOPS then + for i=0,29 do + local j = math_max(i - 2, 0) + t[i] = rshift(j, 1) + end + else + for i=0,29 do + local j = math_max(i - 2, 0) + t[i] = (j - (j % 2)) / 2 + end + end + tdecode_dist_nextrabits = t + end + local dist_val = disttable:read(bs) + local dist_base = tdecode_dist_base[dist_val] + local dist_nextrabits = tdecode_dist_nextrabits[dist_val] + local dist_extrabits = bs:read(dist_nextrabits) + local dist = dist_base + dist_extrabits - for i=1,len do - local pos = (outstate.window_pos - 1 - dist) % 32768 + 1 -- 32K - output(outstate, assert(outstate.window[pos], 'invalid distance')) - end - end - return false + for i=1,len do + local pos = (outstate.window_pos - 1 - dist) % 32768 + 1 -- 32K + output(outstate, assert(outstate.window[pos], 'invalid distance')) + end + end + return false end local function parse_block(bs, outstate) - local bfinal = bs:read(1) - local btype = bs:read(2) + local bfinal = bs:read(1) + local btype = bs:read(2) - local BTYPE_NO_COMPRESSION = 0 - local BTYPE_FIXED_HUFFMAN = 1 - local BTYPE_DYNAMIC_HUFFMAN = 2 - local BTYPE_RESERVED_ = 3 + local BTYPE_NO_COMPRESSION = 0 + local BTYPE_FIXED_HUFFMAN = 1 + local BTYPE_DYNAMIC_HUFFMAN = 2 + local BTYPE_RESERVED_ = 3 - if btype == BTYPE_NO_COMPRESSION then - bs:read(bs:nbits_left_in_byte()) - local len = bs:read(16) - local nlen_ = noeof(bs:read(16)) + if btype == BTYPE_NO_COMPRESSION then + bs:read(bs:nbits_left_in_byte()) + local len = bs:read(16) + local nlen_ = noeof(bs:read(16)) - for _=1,len do - local by = noeof(bs:read(8)) - output(outstate, by) - end - elseif btype == BTYPE_FIXED_HUFFMAN or btype == BTYPE_DYNAMIC_HUFFMAN then - local littable, disttable - if btype == BTYPE_DYNAMIC_HUFFMAN then - littable, disttable = parse_huffmantables(bs) - else - littable = HuffmanTable {0,8, 144,9, 256,7, 280,8, 288,nil} - disttable = HuffmanTable {0,5, 32,nil} - end + for _=1,len do + local by = noeof(bs:read(8)) + output(outstate, by) + end + elseif btype == BTYPE_FIXED_HUFFMAN or btype == BTYPE_DYNAMIC_HUFFMAN then + local littable, disttable + if btype == BTYPE_DYNAMIC_HUFFMAN then + littable, disttable = parse_huffmantables(bs) + else + littable = HuffmanTable {0,8, 144,9, 256,7, 280,8, 288,nil} + disttable = HuffmanTable {0,5, 32,nil} + end - repeat - local is_done = parse_compressed_item( - bs, outstate, littable, disttable) - until is_done - else - runtime_error 'unrecognized compression type' - end + repeat + local is_done = parse_compressed_item( + bs, outstate, littable, disttable) + until is_done + else + runtime_error 'unrecognized compression type' + end - return bfinal ~= 0 + return bfinal ~= 0 end function M.inflate(t) - local bs = get_bitstream(t.input) - local outbs = get_obytestream(t.output) - local outstate = make_outstate(outbs) + local bs = get_bitstream(t.input) + local outbs = get_obytestream(t.output) + local outstate = make_outstate(outbs) - repeat - local is_final = parse_block(bs, outstate) - until is_final + repeat + local is_final = parse_block(bs, outstate) + until is_final end local inflate = M.inflate function M.gunzip(t) - local bs = get_bitstream(t.input) - local outbs = get_obytestream(t.output) - local disable_crc = t.disable_crc - if disable_crc == nil then disable_crc = false end + local bs = get_bitstream(t.input) + local outbs = get_obytestream(t.output) + local disable_crc = t.disable_crc + if disable_crc == nil then disable_crc = false end - parse_gzip_header(bs) + parse_gzip_header(bs) - local data_crc32 = 0 + local data_crc32 = 0 - inflate{input=bs, output= - disable_crc and outbs or - function(byte) - data_crc32 = crc32(byte, data_crc32) - outbs(byte) - end - } + inflate{input=bs, output= + disable_crc and outbs or + function(byte) + data_crc32 = crc32(byte, data_crc32) + outbs(byte) + end + } - bs:read(bs:nbits_left_in_byte()) + bs:read(bs:nbits_left_in_byte()) - local expected_crc32 = bs:read(32) - local isize = bs:read(32) -- ignored + local expected_crc32 = bs:read(32) + local isize = bs:read(32) -- ignored - if not disable_crc and data_crc32 then - if data_crc32 ~= expected_crc32 then - runtime_error('invalid compressed data--crc error') - end - end - if bs:read() then - warn 'trailing garbage ignored' - end + if not disable_crc and data_crc32 then + if data_crc32 ~= expected_crc32 then + runtime_error('invalid compressed data--crc error') + end + end + if bs:read() then + warn 'trailing garbage ignored' + end end function M.adler32(byte, crc) - local s1 = crc % 65536 - local s2 = (crc - s1) / 65536 - s1 = (s1 + byte) % 65521 - s2 = (s2 + s1) % 65521 - return s2*65536 + s1 + local s1 = crc % 65536 + local s2 = (crc - s1) / 65536 + s1 = (s1 + byte) % 65521 + s2 = (s2 + s1) % 65521 + return s2*65536 + s1 end -- 65521 is the largest prime smaller than 2^16 function M.inflate_zlib(t) - local bs = get_bitstream(t.input) - local outbs = get_obytestream(t.output) - local disable_crc = t.disable_crc - if disable_crc == nil then disable_crc = false end + local bs = get_bitstream(t.input) + local outbs = get_obytestream(t.output) + local disable_crc = t.disable_crc + if disable_crc == nil then disable_crc = false end - local window_size_ = parse_zlib_header(bs) + local window_size_ = parse_zlib_header(bs) - local data_adler32 = 1 + local data_adler32 = 1 - inflate{input=bs, output= - disable_crc and outbs or - function(byte) - data_adler32 = M.adler32(byte, data_adler32) - outbs(byte) - end - } + inflate{input=bs, output= + disable_crc and outbs or + function(byte) + data_adler32 = M.adler32(byte, data_adler32) + outbs(byte) + end + } - bs:read(bs:nbits_left_in_byte()) + bs:read(bs:nbits_left_in_byte()) - local b3 = bs:read(8) - local b2 = bs:read(8) - local b1 = bs:read(8) - local b0 = bs:read(8) - local expected_adler32 = ((b3*256 + b2)*256 + b1)*256 + b0 + local b3 = bs:read(8) + local b2 = bs:read(8) + local b1 = bs:read(8) + local b0 = bs:read(8) + local expected_adler32 = ((b3*256 + b2)*256 + b1)*256 + b0 - if not disable_crc then - if data_adler32 ~= expected_adler32 then - runtime_error('invalid compressed data--crc error') - end - end - if bs:read() then - warn 'trailing garbage ignored' - end + if not disable_crc then + if data_adler32 ~= expected_adler32 then + runtime_error('invalid compressed data--crc error') + end + end + if bs:read() then + warn 'trailing garbage ignored' + end end diff --git a/builder/apis/schematic.lua b/builder/apis/schematic.lua index fedffe7..68a888f 100644 --- a/builder/apis/schematic.lua +++ b/builder/apis/schematic.lua @@ -9,11 +9,11 @@ local os = _G.os local term = _G.term local function getHeadingInfo(heading) - return Point.headings[heading] + return Point.headings[heading] end --[[ - Loading and manipulating a schematic + Loading and manipulating a schematic --]] local schematicMagic = 0x0a00 @@ -22,1223 +22,1223 @@ local gzipMagic = 0x1f8b --[[-- Spinner --]]-- local Spinner = class() function Spinner:init(args) - local defaults = { - timeout = .075, - c = os.clock(), - spinIndex = 0, - spinSymbols = { '-', '/', '|', '\\' } - } - defaults.x, defaults.y = term.getCursorPos() - defaults.startX = defaults.x - defaults.startY = defaults.y + local defaults = { + timeout = .075, + c = os.clock(), + spinIndex = 0, + spinSymbols = { '-', '/', '|', '\\' } + } + defaults.x, defaults.y = term.getCursorPos() + defaults.startX = defaults.x + defaults.startY = defaults.y - Util.merge(self, defaults) - Util.merge(self, args) + Util.merge(self, defaults) + Util.merge(self, args) end function Spinner:spin(text) - local cc = os.clock() - if text then - self.text = text - end - if cc > self.c + self.timeout then - term.setCursorPos(self.x, self.y) - local str = self.spinSymbols[self.spinIndex % #self.spinSymbols + 1] - if self.text then - str = str .. ' ' .. self.text - self.text = nil - end - term.write(str) - self.spinIndex = self.spinIndex + 1 - os.sleep(0) - self.c = os.clock() - end + local cc = os.clock() + if text then + self.text = text + end + if cc > self.c + self.timeout then + term.setCursorPos(self.x, self.y) + local str = self.spinSymbols[self.spinIndex % #self.spinSymbols + 1] + if self.text then + str = str .. ' ' .. self.text + self.text = nil + end + term.write(str) + self.spinIndex = self.spinIndex + 1 + os.sleep(0) + self.c = os.clock() + end end function Spinner:stop(text) - term.setCursorPos(self.x, self.y) - local str = string.rep(' ', #self.spinSymbols) - if text then - str = str .. ' ' .. text - end - term.write(str) - term.setCursorPos(self.startX, self.startY) + term.setCursorPos(self.x, self.y) + local str = string.rep(' ', #self.spinSymbols) + if text then + str = str .. ' ' .. text + end + term.write(str) + term.setCursorPos(self.startX, self.startY) end local Schematic = class() function Schematic:init() - self.blocks = { } - self.damages = { } - self.originalBlocks = { } - self.x, self.y, self.z = 0, 0, 0 - self.height = 0 - self.index = 1 + self.blocks = { } + self.damages = { } + self.originalBlocks = { } + self.x, self.y, self.z = 0, 0, 0 + self.height = 0 + self.index = 1 end --[[ - Credit to Orwell for the schematic file reader code - http://www.computercraft.info/forums2/index.php?/topic/1949-turtle-schematic-file-builder/ + Credit to Orwell for the schematic file reader code + http://www.computercraft.info/forums2/index.php?/topic/1949-turtle-schematic-file-builder/ - Some parts of the file reader code was modified from the original + Some parts of the file reader code was modified from the original --]] function Schematic:discardBytes(h, n, spinner) - for i = 1,n do - h:readbyte() - if (i % 1000) == 0 then - spinner:spin() - end - end + for i = 1,n do + h:readbyte() + if (i % 1000) == 0 then + spinner:spin() + end + end end function Schematic:readname(h) - local n1 = h:readbyte(h) - local n2 = h:readbyte(h) + local n1 = h:readbyte(h) + local n2 = h:readbyte(h) - if(n1 == nil or n2 == nil) then - return "" - end + if(n1 == nil or n2 == nil) then + return "" + end - local n = n1*256 + n2 + local n = n1*256 + n2 - local str = "" - for _=1,n do - local c = h:readbyte(h) - if c == nil then - return - end - str = str .. string.char(c) - end - return str + local str = "" + for _=1,n do + local c = h:readbyte(h) + if c == nil then + return + end + str = str .. string.char(c) + end + return str end function Schematic:parse(a, h, containsName, spinner) - if a==0 then - return - end + if a==0 then + return + end - local name - if containsName then - name = self:readname(h) - end + local name + if containsName then + name = self:readname(h) + end - if a==1 then - self:discardBytes(h, 1, spinner) - elseif a==2 then - local i1 = h:readbyte(h) - local i2 = h:readbyte(h) - local i = i1*256 + i2 - if(name=="Height") then - --self.height = i - elseif (name=="Length") then - self.length = i - elseif (name=="Width") then - self.width = i - end - return 2 - elseif a==3 then - self:discardBytes(h, 4, spinner) - return 4 - elseif a==4 then - self:discardBytes(h,8, spinner) - return 8 - elseif a==5 then - self:discardBytes(h,4, spinner) - return 4 - elseif a==6 then - self:discardBytes(h,8, spinner) - elseif a==7 then - local i1 = h:readbyte(h) - local i2 = h:readbyte(h) - local i3 = h:readbyte(h) - local i4 = h:readbyte(h) - local i = bit.blshift(i1, 24) + bit.blshift(i2, 16) + bit.blshift(i3, 8) + i4 + if a==1 then + self:discardBytes(h, 1, spinner) + elseif a==2 then + local i1 = h:readbyte(h) + local i2 = h:readbyte(h) + local i = i1*256 + i2 + if(name=="Height") then + --self.height = i + elseif (name=="Length") then + self.length = i + elseif (name=="Width") then + self.width = i + end + return 2 + elseif a==3 then + self:discardBytes(h, 4, spinner) + return 4 + elseif a==4 then + self:discardBytes(h,8, spinner) + return 8 + elseif a==5 then + self:discardBytes(h,4, spinner) + return 4 + elseif a==6 then + self:discardBytes(h,8, spinner) + elseif a==7 then + local i1 = h:readbyte(h) + local i2 = h:readbyte(h) + local i3 = h:readbyte(h) + local i4 = h:readbyte(h) + local i = bit.blshift(i1, 24) + bit.blshift(i2, 16) + bit.blshift(i3, 8) + i4 - if name == "Blocks" then - for i = 1, i do - local id = h:readbyte(h) - if id > 0 then - table.insert(self.blocks, { - id = id, - index = i, - }) - end - if (i % 1000) == 0 then - spinner:spin() - end - end - elseif name == "Data" then - for i = 1, i do - local dmg = h:readbyte(h) - if dmg > 0 then - self.damages[i] = dmg - end - if (i % 1000) == 0 then - spinner:spin() - end - end - else - self:discardBytes(h,i, spinner) - end - elseif a==8 then - local i1 = h:readbyte(h) - local i2 = h:readbyte(h) - local i = i1*256 + i2 - self:discardBytes(h,i, spinner) - elseif a==9 then - local type = h:readbyte(h) - local i1 = h:readbyte(h) - local i2 = h:readbyte(h) - local i3 = h:readbyte(h) - local i4 = h:readbyte(h) - local i = bit.blshift(i1, 24) + bit.blshift(i2, 16) + bit.blshift(i3, 8) + i4 + if name == "Blocks" then + for i = 1, i do + local id = h:readbyte(h) + if id > 0 then + table.insert(self.blocks, { + id = id, + index = i, + }) + end + if (i % 1000) == 0 then + spinner:spin() + end + end + elseif name == "Data" then + for i = 1, i do + local dmg = h:readbyte(h) + if dmg > 0 then + self.damages[i] = dmg + end + if (i % 1000) == 0 then + spinner:spin() + end + end + else + self:discardBytes(h,i, spinner) + end + elseif a==8 then + local i1 = h:readbyte(h) + local i2 = h:readbyte(h) + local i = i1*256 + i2 + self:discardBytes(h,i, spinner) + elseif a==9 then + local type = h:readbyte(h) + local i1 = h:readbyte(h) + local i2 = h:readbyte(h) + local i3 = h:readbyte(h) + local i4 = h:readbyte(h) + local i = bit.blshift(i1, 24) + bit.blshift(i2, 16) + bit.blshift(i3, 8) + i4 - for j=1,i do - self:parse(type, h, false, spinner) - end - elseif a > 11 then - error('invalid tag') - end + for j=1,i do + self:parse(type, h, false, spinner) + end + elseif a > 11 then + error('invalid tag') + end end -- end http://www.computercraft.info/forums2/index.php?/topic/1949-turtle-schematic-file-builder/ function Schematic:copyBlocks(iblocks, oblocks, throttle) - for k,b in ipairs(iblocks) do - oblocks[k] = Util.shallowCopy(b) - --if (k % 1000) == 0 then - throttle() - --end - end + for k,b in ipairs(iblocks) do + oblocks[k] = Util.shallowCopy(b) + --if (k % 1000) == 0 then + throttle() + --end + end end function Schematic:reload(throttle) - self.blocks = { } - self:copyBlocks(self.originalBlocks, self.blocks, throttle) + self.blocks = { } + self:copyBlocks(self.originalBlocks, self.blocks, throttle) - for _,ri in pairs(self.rowIndex) do - ri.loaded = false - end + for _,ri in pairs(self.rowIndex) do + ri.loaded = false + end end function Schematic:getMagic(fh) - fh:open() + fh:open() - local magic = fh:readbyte() * 256 + fh:readbyte() + local magic = fh:readbyte() * 256 + fh:readbyte() - fh:close() + fh:close() - return magic + return magic end function Schematic:isCompressed(filename) - local h = fs.open(filename, "rb") + local h = fs.open(filename, "rb") - if not h then - error('unable to open: ' .. filename) - end + if not h then + error('unable to open: ' .. filename) + end - local magic = h.read() * 256 + h.read() + local magic = h.read() * 256 + h.read() - h.close() + h.close() - return magic == gzipMagic + return magic == gzipMagic end function Schematic:checkFileType(fh) - local magic = self:getMagic(fh) - if magic ~= schematicMagic then - error('Unknown file type') - end + local magic = self:getMagic(fh) + if magic ~= schematicMagic then + error('Unknown file type') + end end local DiskFile = class() function DiskFile:init(args) - Util.merge(self, args) + Util.merge(self, args) end function DiskFile:open() - self.h = fs.open(self.filename, "rb") - if not self.h then - error('unable to open: ' .. self.filename) - end + self.h = fs.open(self.filename, "rb") + if not self.h then + error('unable to open: ' .. self.filename) + end end function DiskFile:readbyte() - return self.h.read() + return self.h.read() end function DiskFile:close() - self.h.close() + self.h.close() end local MemoryFile = class() function MemoryFile:init() - self.s = { } - self.i = 1 + self.s = { } + self.i = 1 end function MemoryFile:open() - self.i = 1 + self.i = 1 end function MemoryFile:close() end function MemoryFile:readbyte() - local b = self.s[self.i] - self.i = self.i + 1 - return b + local b = self.s[self.i] + self.i = self.i + 1 + return b end function MemoryFile:write(b) - self.s[#self.s + 1] = b + self.s[#self.s + 1] = b end function Schematic:decompress(ifname, spinner) - local ifh = fs.open(ifname, "rb") - if not ifh then - error('Unable to open ' .. ifname) - end + local ifh = fs.open(ifname, "rb") + if not ifh then + error('Unable to open ' .. ifname) + end - local mh = MemoryFile() + local mh = MemoryFile() - DEFLATE.gunzip({ - input=function() spinner:spin() return ifh.read() end, - output=function(b) mh:write(b) end, - disable_crc=true - }) + DEFLATE.gunzip({ + input=function() spinner:spin() return ifh.read() end, + output=function(b) mh:write(b) end, + disable_crc=true + }) - ifh.close() + ifh.close() - spinner:stop() + spinner:stop() - return mh + return mh end function Schematic:loadpass(fh, spinner) - fh:open() + fh:open() - while true do - local a = fh:readbyte() + while true do + local a = fh:readbyte() - if not a then - break - end - self:parse(a, fh, true, spinner) + if not a then + break + end + self:parse(a, fh, true, spinner) - spinner:spin() - end + spinner:spin() + end - fh:close() + fh:close() - spinner.text = 'Assigning coords ' - local index = 1 - for _, b in ipairs(self.blocks) do - while index < b.index do - self.x = self.x + 1 - if self.x >= self.width then - self.x = 0 - self.z = self.z + 1 - end - if self.z >= self.length then - self.z = 0 - self.y = self.y + 1 - end - if self.y >= self.height then - self.height = self.y + 1 - end - index = index + 1 - end - b.x = self.x - b.y = self.y - b.z = self.z - spinner:spin() - end + spinner.text = 'Assigning coords ' + local index = 1 + for _, b in ipairs(self.blocks) do + while index < b.index do + self.x = self.x + 1 + if self.x >= self.width then + self.x = 0 + self.z = self.z + 1 + end + if self.z >= self.length then + self.z = 0 + self.y = self.y + 1 + end + if self.y >= self.height then + self.height = self.y + 1 + end + index = index + 1 + end + b.x = self.x + b.y = self.y + b.z = self.z + spinner:spin() + end - self:assignDamages(spinner) - self.damages = nil + self:assignDamages(spinner) + self.damages = nil - self:copyBlocks(self.blocks, self.originalBlocks, function() spinner:spin() end) + self:copyBlocks(self.blocks, self.originalBlocks, function() spinner:spin() end) - spinner:stop() + spinner:stop() end function Schematic:load(filename) - local _, cy = term.getCursorPos() - local spinner = Spinner({ - x = 1, - y = cy, - }) - local f + local _, cy = term.getCursorPos() + local spinner = Spinner({ + x = 1, + y = cy, + }) + local f - if filename:match("^(https?:)") then - local c, msg = Util.httpGet(filename, nil, true) - if not c then - error(msg) - end - f = MemoryFile() + if filename:match("^(https?:)") then + local c, msg = Util.httpGet(filename, nil, true) + if not c then + error(msg) + end + f = MemoryFile() - local i = 0 - local sz = #c - DEFLATE.gunzip({ - input=function() - spinner:spin() - if i < sz then - i = i + 1 - return c:byte(i) - end - end, - output=function(b) f:write(b) end, - disable_crc=true - }) + local i = 0 + local sz = #c + DEFLATE.gunzip({ + input=function() + spinner:spin() + if i < sz then + i = i + 1 + return c:byte(i) + end + end, + output=function(b) f:write(b) end, + disable_crc=true + }) - spinner:stop() + spinner:stop() - elseif self:isCompressed(filename) then - local originalFile = filename - filename = originalFile .. '.uncompressed' + elseif self:isCompressed(filename) then + local originalFile = filename + filename = originalFile .. '.uncompressed' - if not fs.exists(filename) then - spinner.text = 'Decompressing' - f = self:decompress(originalFile, spinner) - end - end + if not fs.exists(filename) then + spinner.text = 'Decompressing' + f = self:decompress(originalFile, spinner) + end + end - self.filename = string.match(filename, '([^/]+)$') + self.filename = string.match(filename, '([^/]+)$') - if not f then - f = DiskFile({ filename = filename }) - end + if not f then + f = DiskFile({ filename = filename }) + end - self:checkFileType(f) + self:checkFileType(f) - spinner.text = 'Loading blocks ' - self:loadpass(f, spinner) + spinner.text = 'Loading blocks ' + self:loadpass(f, spinner) - self.rowIndex = { } - for k,b in ipairs(self.blocks) do - local ri = self.rowIndex[b.y] - if not ri then - self.rowIndex[b.y] = { s = k, e = k } - else - ri.e = k - end - end + self.rowIndex = { } + for k,b in ipairs(self.blocks) do + local ri = self.rowIndex[b.y] + if not ri then + self.rowIndex[b.y] = { s = k, e = k } + else + ri.e = k + end + end - self.cache = Util.readTable('usr/builder/' .. self.filename .. '.cache') or { } + self.cache = Util.readTable('usr/builder/' .. self.filename .. '.cache') or { } end function Schematic:assignDamages(spinner) - spinner.text = 'Assigning damages' + spinner.text = 'Assigning damages' - for _,b in pairs(self.blocks) do - b.dmg = self.damages[b.index] or 0 - spinner:spin() - end + for _,b in pairs(self.blocks) do + b.dmg = self.damages[b.index] or 0 + spinner:spin() + end end function Schematic:findIndexAt(x, z, y, allBlocks) - if y < 0 then - return - end + if y < 0 then + return + end - local ri = self.rowIndex[y] - if ri then - for i = ri.s, ri.e do - local b = self.blocks[i] - if b.x == x and b.z == z and b.y == y then - if b.id == 'minecraft:air' and not allBlocks then - -- this will possibly screw up placement order if a substition is made with air after starting - -- as blocks will be placed differently and could have a different heading - break - end - return i, b - end - end - end + local ri = self.rowIndex[y] + if ri then + for i = ri.s, ri.e do + local b = self.blocks[i] + if b.x == x and b.z == z and b.y == y then + if b.id == 'minecraft:air' and not allBlocks then + -- this will possibly screw up placement order if a substition is made with air after starting + -- as blocks will be placed differently and could have a different heading + break + end + return i, b + end + end + end end function Schematic:findBlockAtSide(b, side) - local hi = getHeadingInfo(side) - local index = self:findIndexAt(b.x + hi.xd, b.z + hi.zd, b.y + hi.yd) - if index then - return self.blocks[index] -- could be better - end + local hi = getHeadingInfo(side) + local index = self:findIndexAt(b.x + hi.xd, b.z + hi.zd, b.y + hi.yd) + if index then + return self.blocks[index] -- could be better + end end function Schematic:addPlacementChain(chains, chain) - local t = { } - for _,v in ipairs(chain) do - local k = self:findIndexAt(v.x, v.z, v.y) - if k then - local b = self.blocks[k] -- could be better - b.index = v.y * self.width * self.length + v.z * self.width + v.x + 1 - table.insert(t, b) - end - end - if #t > 1 then - local keys = { } - for _,b in pairs(t) do - keys[b.index] = true - end - table.insert(chains, { - blocks = t, - keys = keys - }) - end + local t = { } + for _,v in ipairs(chain) do + local k = self:findIndexAt(v.x, v.z, v.y) + if k then + local b = self.blocks[k] -- could be better + b.index = v.y * self.width * self.length + v.z * self.width + v.x + 1 + table.insert(t, b) + end + end + if #t > 1 then + local keys = { } + for _,b in pairs(t) do + keys[b.index] = true + end + table.insert(chains, { + blocks = t, + keys = keys + }) + end end function Schematic:bestSide(b, chains, ...) - local directions = { ... } - local blocks = { } + local directions = { ... } + local blocks = { } - for k,d in pairs(directions) do - local hi = getHeadingInfo(d) - local sb = self:findIndexAt(b.x - hi.xd, b.z - hi.zd, b.y) - if not sb then - b.heading = getHeadingInfo(d).heading - b.direction = d .. '-block' - return - end - blocks[k] = { - b = self.blocks[sb], - hi = hi, - d = d - } - end + for k,d in pairs(directions) do + local hi = getHeadingInfo(d) + local sb = self:findIndexAt(b.x - hi.xd, b.z - hi.zd, b.y) + if not sb then + b.heading = getHeadingInfo(d).heading + b.direction = d .. '-block' + return + end + blocks[k] = { + b = self.blocks[sb], + hi = hi, + d = d + } + end - local bestBlock - for _,sb in ipairs(blocks) do - if not sb.b.direction then -- could be better - bestBlock = sb - break - end - end + local bestBlock + for _,sb in ipairs(blocks) do + if not sb.b.direction then -- could be better + bestBlock = sb + break + end + end - if not bestBlock then - local sideDirections = { - [ 'east-block' ] = 'east', - [ 'south-block' ] = 'south', - [ 'west-block' ] = 'west', - [ 'north-block' ] = 'north' - } - for _,sb in ipairs(blocks) do - if not bestBlock then - bestBlock = sb - end - if not sideDirections[sb.b.direction] then - bestBlock = sb - break - end - end - end + if not bestBlock then + local sideDirections = { + [ 'east-block' ] = 'east', + [ 'south-block' ] = 'south', + [ 'west-block' ] = 'west', + [ 'north-block' ] = 'north' + } + for _,sb in ipairs(blocks) do + if not bestBlock then + bestBlock = sb + end + if not sideDirections[sb.b.direction] then + bestBlock = sb + break + end + end + end - local hi = bestBlock.hi - b.heading = hi.heading -- ????????????????????????????????? - b.direction = bestBlock.d .. '-block' - self:addPlacementChain(chains, { - { x = b.x, z = b.z, y = b.y }, - { x = b.x - hi.xd, z = b.z - hi.zd, y = b.y } - }) + local hi = bestBlock.hi + b.heading = hi.heading -- ????????????????????????????????? + b.direction = bestBlock.d .. '-block' + self:addPlacementChain(chains, { + { x = b.x, z = b.z, y = b.y }, + { x = b.x - hi.xd, z = b.z - hi.zd, y = b.y } + }) end function Schematic:bestFlipSide(b, chains) - -- If there is a block to place this one against + -- If there is a block to place this one against - local directions = { - [ 'east-block-flip' ] = 'east', - [ 'west-block-flip' ] = 'west', - [ 'north-block-flip' ] = 'north', - [ 'south-block-flip' ] = 'south', - } + local directions = { + [ 'east-block-flip' ] = 'east', + [ 'west-block-flip' ] = 'west', + [ 'north-block-flip' ] = 'north', + [ 'south-block-flip' ] = 'south', + } - local d = directions[b.direction] - local hi = getHeadingInfo(d) - local _, fb = self:findIndexAt(b.x + hi.xd, b.z + hi.zd, b.y) + local d = directions[b.direction] + local hi = getHeadingInfo(d) + local _, fb = self:findIndexAt(b.x + hi.xd, b.z + hi.zd, b.y) - if fb then - self:addPlacementChain(chains, { - { x = b.x + hi.xd, z = b.z + hi.zd, y = b.y }, -- block we are placing against - { x = b.x, z = b.z, y = b.y }, -- the block (or torch, etc) - { x = b.x - hi.xd, z = b.z - hi.zd, y = b.y }, -- room for the turtle - }) - b.direction = d .. '-block' - else - self:addPlacementChain(chains, { - { x = b.x, z = b.z, y = b.y }, -- the block (or torch, etc) - { x = b.x - hi.xd, z = b.z - hi.zd, y = b.y }, -- room for the turtle - { x = b.x + hi.xd, z = b.z + hi.zd, y = b.y }, -- block we are placing against - }) - b.direction = getHeadingInfo((hi.heading + 2) % 4).direction .. '-block' - end + if fb then + self:addPlacementChain(chains, { + { x = b.x + hi.xd, z = b.z + hi.zd, y = b.y }, -- block we are placing against + { x = b.x, z = b.z, y = b.y }, -- the block (or torch, etc) + { x = b.x - hi.xd, z = b.z - hi.zd, y = b.y }, -- room for the turtle + }) + b.direction = d .. '-block' + else + self:addPlacementChain(chains, { + { x = b.x, z = b.z, y = b.y }, -- the block (or torch, etc) + { x = b.x - hi.xd, z = b.z - hi.zd, y = b.y }, -- room for the turtle + { x = b.x + hi.xd, z = b.z + hi.zd, y = b.y }, -- block we are placing against + }) + b.direction = getHeadingInfo((hi.heading + 2) % 4).direction .. '-block' + end end function Schematic:bestOfTwoSides(b, chains, side1, side2) -- could be better - local sb - local fb = b -- first block - local lb = b -- last block - local od = b.direction -- original direction + local sb + local fb = b -- first block + local lb = b -- last block + local od = b.direction -- original direction - -- find the last block in the row with the same two-sided direction - while true do - sb = self:findBlockAtSide(lb, side2) - if not sb or sb.direction ~= b.direction then - break - end - lb = sb - end + -- find the last block in the row with the same two-sided direction + while true do + sb = self:findBlockAtSide(lb, side2) + if not sb or sb.direction ~= b.direction then + break + end + lb = sb + end - -- find the first block - while true do - sb = self:findBlockAtSide(fb, side1) - if not sb or sb.direction ~= b.direction then - break - end - fb = sb - end + -- find the first block + while true do + sb = self:findBlockAtSide(fb, side1) + if not sb or sb.direction ~= b.direction then + break + end + fb = sb + end - -- set the placement order to side1 -> side2 - if fb ~= lb then -- only 1 block + -- set the placement order to side1 -> side2 + if fb ~= lb then -- only 1 block - local pc = { } -- placementChain - b = fb + local pc = { } -- placementChain + b = fb - while true do + while true do - table.insert(pc, { x = b.x, z = b.z, y = b.y }) + table.insert(pc, { x = b.x, z = b.z, y = b.y }) - b.direction = side1 .. '-block' - b.heading = getHeadingInfo(side1).heading + b.direction = side1 .. '-block' + b.heading = getHeadingInfo(side1).heading - if b == lb then - break - end + if b == lb then + break + end - b = self:findBlockAtSide(b, side2) - end + b = self:findBlockAtSide(b, side2) + end - self:addPlacementChain(chains, pc) - end + self:addPlacementChain(chains, pc) + end - -- can we place the first block from the side (instead of using piston) ? - sb = self:findBlockAtSide(fb, side1) - if not sb then - local ub = self:findBlockAtSide(fb, 'down') - if not ub then - fb.direction = side1 .. '-block' - fb.heading = getHeadingInfo(side1).heading - else - fb.direction = od - end - else -- really should use placement chain - fb.direction = od - end + -- can we place the first block from the side (instead of using piston) ? + sb = self:findBlockAtSide(fb, side1) + if not sb then + local ub = self:findBlockAtSide(fb, 'down') + if not ub then + fb.direction = side1 .. '-block' + fb.heading = getHeadingInfo(side1).heading + else + fb.direction = od + end + else -- really should use placement chain + fb.direction = od + end - -- can we place the last block from the side (instead of using piston) ? - sb = self:findBlockAtSide(lb, side2) - if not sb then - local ub = self:findBlockAtSide(lb, 'down') - if not ub then - lb.direction = side1 .. '-block' - lb.heading = getHeadingInfo(side1).heading - else - fb.direction = od - end - else - lb.direction = od - end + -- can we place the last block from the side (instead of using piston) ? + sb = self:findBlockAtSide(lb, side2) + if not sb then + local ub = self:findBlockAtSide(lb, 'down') + if not ub then + lb.direction = side1 .. '-block' + lb.heading = getHeadingInfo(side1).heading + else + fb.direction = od + end + else + lb.direction = od + end end -- Determine the best way to place each block function Schematic:determineBlockPlacement(y) - -- NOTE: blocks are evaluated top to bottom + -- NOTE: blocks are evaluated top to bottom - print('Processing level ' .. y) + print('Processing level ' .. y) - local spinner = Spinner({ - x = 1, - spinSymbols = { 'o.....', '.o....', '..o...', '...o..', '....o.', '.....o' } - }) - local stairDownDirections = { - [ 'north-down' ] = 'north', - [ 'south-down' ] = 'south', - [ 'east-down' ] = 'east', - [ 'west-down' ] = 'west' - } - local stairUpDirections = { - [ 'east-up' ] = { 'east', 'east-block', 1, 0, 'west-block' }, - [ 'west-up' ] = { 'west', 'west-block', -1, 0, 'east-block' }, - [ 'north-up' ] = { 'north', 'north-block', 0, -1, 'south-block' }, - [ 'south-up' ] = { 'south', 'south-block', 0, 1, 'north-block' } - } - local twoSideDirections = { - [ 'east-west-block' ] = true, - [ 'north-south-block' ] = true, - } - local directions = { - [ 'north' ] = 'north', - [ 'south' ] = 'south', - [ 'east' ] = 'east', - [ 'west' ] = 'west', - } - local blockDirections = { - [ 'east-block' ] = 'east', - [ 'south-block' ] = 'south', - [ 'west-block' ] = 'west', - [ 'north-block' ] = 'north', - } - local doorDirections = { - [ 'east-door' ] = 'east', - [ 'south-door' ] = 'south', - [ 'west-door' ] = 'west', - [ 'north-door' ] = 'north', - } - local vineDirections = { - [ 'east-block-vine' ] = 'east-block', - [ 'south-block-vine' ] = 'south-block', - [ 'west-block-vine' ] = 'west-block', - [ 'north-block-vine' ] = 'north-block' - } - local flipDirections = { - [ 'east-block-flip' ] = 'east-block', - [ 'south-block-flip' ] = 'south-block', - [ 'west-block-flip' ] = 'west-block', - [ 'north-block-flip' ] = 'north-block' - } + local spinner = Spinner({ + x = 1, + spinSymbols = { 'o.....', '.o....', '..o...', '...o..', '....o.', '.....o' } + }) + local stairDownDirections = { + [ 'north-down' ] = 'north', + [ 'south-down' ] = 'south', + [ 'east-down' ] = 'east', + [ 'west-down' ] = 'west' + } + local stairUpDirections = { + [ 'east-up' ] = { 'east', 'east-block', 1, 0, 'west-block' }, + [ 'west-up' ] = { 'west', 'west-block', -1, 0, 'east-block' }, + [ 'north-up' ] = { 'north', 'north-block', 0, -1, 'south-block' }, + [ 'south-up' ] = { 'south', 'south-block', 0, 1, 'north-block' } + } + local twoSideDirections = { + [ 'east-west-block' ] = true, + [ 'north-south-block' ] = true, + } + local directions = { + [ 'north' ] = 'north', + [ 'south' ] = 'south', + [ 'east' ] = 'east', + [ 'west' ] = 'west', + } + local blockDirections = { + [ 'east-block' ] = 'east', + [ 'south-block' ] = 'south', + [ 'west-block' ] = 'west', + [ 'north-block' ] = 'north', + } + local doorDirections = { + [ 'east-door' ] = 'east', + [ 'south-door' ] = 'south', + [ 'west-door' ] = 'west', + [ 'north-door' ] = 'north', + } + local vineDirections = { + [ 'east-block-vine' ] = 'east-block', + [ 'south-block-vine' ] = 'south-block', + [ 'west-block-vine' ] = 'west-block', + [ 'north-block-vine' ] = 'north-block' + } + local flipDirections = { + [ 'east-block-flip' ] = 'east-block', + [ 'south-block-flip' ] = 'south-block', + [ 'west-block-flip' ] = 'west-block', + [ 'north-block-flip' ] = 'north-block' + } - local dirtyBlocks = {} - local dirtyBlocks2 = {} - local chains = {} + local dirtyBlocks = {} + local dirtyBlocks2 = {} + local chains = {} - local ri = self.rowIndex[y] - if not ri then - ri = { s = -1, e = -2 } - self.rowIndex[y] = ri - end + local ri = self.rowIndex[y] + if not ri then + ri = { s = -1, e = -2 } + self.rowIndex[y] = ri + end - for k = ri.s, ri.e do - local b = self.blocks[k] - local d = b.direction + for k = ri.s, ri.e do + local b = self.blocks[k] + local d = b.direction - if d then - if vineDirections[d] then - local _, aboveBlock = self:findIndexAt(b.x, b.z, b.y+1) + if d then + if vineDirections[d] then + local _, aboveBlock = self:findIndexAt(b.x, b.z, b.y+1) - if aboveBlock and aboveBlock.id == b.id and aboveBlock.dmg == b.dmg and aboveBlock.direction == d then - -- only need to place top vine - b.id = 'minecraft:air' - b.dmg = 0 - b.direction = nil - else - b.direction = vineDirections[d] - table.insert(dirtyBlocks, b) - end - elseif twoSideDirections[d] then - table.insert(dirtyBlocks2, b) - else - table.insert(dirtyBlocks, b) - end - spinner:spin(#dirtyBlocks + #dirtyBlocks2 .. ' blocks remaining ') - end - end + if aboveBlock and aboveBlock.id == b.id and aboveBlock.dmg == b.dmg and aboveBlock.direction == d then + -- only need to place top vine + b.id = 'minecraft:air' + b.dmg = 0 + b.direction = nil + else + b.direction = vineDirections[d] + table.insert(dirtyBlocks, b) + end + elseif twoSideDirections[d] then + table.insert(dirtyBlocks2, b) + else + table.insert(dirtyBlocks, b) + end + spinner:spin(#dirtyBlocks + #dirtyBlocks2 .. ' blocks remaining ') + end + end -- Util.filterInplace(dirtyBlocks, function(b) return b.id ~= 'minecraft:air' end) - -- remove directional info from slabs where possible - -- iterate backwards to process top planes first - for k = #dirtyBlocks, 1, -1 do - local b = dirtyBlocks[k] - local d = b.direction + -- remove directional info from slabs where possible + -- iterate backwards to process top planes first + for k = #dirtyBlocks, 1, -1 do + local b = dirtyBlocks[k] + local d = b.direction - if d == 'top' then - -- slab occupying top of voxel - -- can be placed from the top if there is no block below - local belowBlock = self:findIndexAt(b.x, b.z, b.y-1) - if not belowBlock then - b.direction = nil - table.remove(dirtyBlocks, k) - end - elseif d == 'bottom' then - b.bottom = true -- flag this as a bottom block - local _,db = self:findIndexAt(b.x, b.z, b.y-1) - if db then - if not db.direction or db.direction ~= 'bottom' then - -- not a slab below, ok to place from above - if not db.bottom then - b.direction = nil - end - end - -- it is a slab below - must be pistoned - table.remove(dirtyBlocks, k) - end - end - spinner:spin(#dirtyBlocks + #dirtyBlocks2 .. ' blocks remaining ') - end + if d == 'top' then + -- slab occupying top of voxel + -- can be placed from the top if there is no block below + local belowBlock = self:findIndexAt(b.x, b.z, b.y-1) + if not belowBlock then + b.direction = nil + table.remove(dirtyBlocks, k) + end + elseif d == 'bottom' then + b.bottom = true -- flag this as a bottom block + local _,db = self:findIndexAt(b.x, b.z, b.y-1) + if db then + if not db.direction or db.direction ~= 'bottom' then + -- not a slab below, ok to place from above + if not db.bottom then + b.direction = nil + end + end + -- it is a slab below - must be pistoned + table.remove(dirtyBlocks, k) + end + end + spinner:spin(#dirtyBlocks + #dirtyBlocks2 .. ' blocks remaining ') + end - -- iterate through the directional blocks setting the placement strategy - while #dirtyBlocks > 0 do - local b = table.remove(dirtyBlocks) - local d = b.direction or '' + -- iterate through the directional blocks setting the placement strategy + while #dirtyBlocks > 0 do + local b = table.remove(dirtyBlocks) + local d = b.direction or '' - spinner:spin(#dirtyBlocks + #dirtyBlocks2 .. ' blocks remaining ') + spinner:spin(#dirtyBlocks + #dirtyBlocks2 .. ' blocks remaining ') - if directions[d] then - b.heading = getHeadingInfo(directions[d]).heading - end + if directions[d] then + b.heading = getHeadingInfo(directions[d]).heading + end - if doorDirections[d] then + if doorDirections[d] then - local hi = getHeadingInfo(doorDirections[d]) - b.heading = hi.heading + local hi = getHeadingInfo(doorDirections[d]) + b.heading = hi.heading - self:addPlacementChain(chains, { - { x = b.x, z = b.z, y = b.y }, - { x = b.x - hi.xd, z = b.z - hi.zd, y = b.y }, - }) - end + self:addPlacementChain(chains, { + { x = b.x, z = b.z, y = b.y }, + { x = b.x - hi.xd, z = b.z - hi.zd, y = b.y }, + }) + end - if stairDownDirections[d] then - if not self:findIndexAt(b.x, b.z, b.y-1) then - b.direction = stairDownDirections[b.direction] - b.heading = getHeadingInfo(b.direction).heading - else - b.heading = getHeadingInfo(stairDownDirections[b.direction]).heading - end - end + if stairDownDirections[d] then + if not self:findIndexAt(b.x, b.z, b.y-1) then + b.direction = stairDownDirections[b.direction] + b.heading = getHeadingInfo(b.direction).heading + else + b.heading = getHeadingInfo(stairDownDirections[b.direction]).heading + end + end - if d == 'bottom' then - -- slab occupying bottom of voxel - -- can be placed from top if a block is below - -- otherwise, needs to be placed from side + if d == 'bottom' then + -- slab occupying bottom of voxel + -- can be placed from top if a block is below + -- otherwise, needs to be placed from side - -- except... if the block below is a slab :( - --local _,db = self:findIndexAt(b.x, b.z, b.y-1) - --if not db then - -- no block below, place from side + -- except... if the block below is a slab :( + --local _,db = self:findIndexAt(b.x, b.z, b.y-1) + --if not db then + -- no block below, place from side - -- took care of all other cases above - self:bestSide(b, chains, 'east', 'south', 'west', 'north') + -- took care of all other cases above + self:bestSide(b, chains, 'east', 'south', 'west', 'north') - -- elseif not db.direction or db.direction ~= 'bottom' then - -- not a slab below, ok to place from above - -- b.direction = nil - --end - -- otherwise, builder will piston it in from above + -- elseif not db.direction or db.direction ~= 'bottom' then + -- not a slab below, ok to place from above + -- b.direction = nil + --end + -- otherwise, builder will piston it in from above - elseif stairUpDirections[d] then - -- a directional stair - -- turtle can place correctly from above if there is a block below - -- otherwise, the turtle must place the block from the same plane - -- against another block - -- if no block to place against (from side) then the turtle must place from - -- the other side - -- - -- Stair bug in 1.7 - placing a stair southward doesn't respect the turtle's direction - -- all other directions are fine - -- any stair southwards that can't be placed against another block must be pistoned - local sd = stairUpDirections[d] + elseif stairUpDirections[d] then + -- a directional stair + -- turtle can place correctly from above if there is a block below + -- otherwise, the turtle must place the block from the same plane + -- against another block + -- if no block to place against (from side) then the turtle must place from + -- the other side + -- + -- Stair bug in 1.7 - placing a stair southward doesn't respect the turtle's direction + -- all other directions are fine + -- any stair southwards that can't be placed against another block must be pistoned + local sd = stairUpDirections[d] - if self:findIndexAt(b.x, b.z, b.y-1) then - -- there's a block below - b.direction = sd[1] - b.heading = getHeadingInfo(b.direction).heading - else - local _,pb = self:findIndexAt(b.x + sd[3], b.z + sd[4], b.y) - if pb and pb.direction ~= sd[5] then - -- place stair against another block (that's not relying on this block to be down first) - d = sd[2] -- fall through to the blockDirections code below - b.direction = sd[2] - else - b.heading = (getHeadingInfo(sd[1]).heading + 2) % 4 - end - end - elseif flipDirections[d] then - self:bestFlipSide(b, chains) - end + if self:findIndexAt(b.x, b.z, b.y-1) then + -- there's a block below + b.direction = sd[1] + b.heading = getHeadingInfo(b.direction).heading + else + local _,pb = self:findIndexAt(b.x + sd[3], b.z + sd[4], b.y) + if pb and pb.direction ~= sd[5] then + -- place stair against another block (that's not relying on this block to be down first) + d = sd[2] -- fall through to the blockDirections code below + b.direction = sd[2] + else + b.heading = (getHeadingInfo(sd[1]).heading + 2) % 4 + end + end + elseif flipDirections[d] then + self:bestFlipSide(b, chains) + end - if blockDirections[d] then - -- placing a block from the side - local hi = getHeadingInfo(blockDirections[d]) - b.heading = hi.heading - self:addPlacementChain(chains, { - { x = b.x + hi.xd, z = b.z + hi.zd, y = b.y }, -- block we are placing against - { x = b.x, z = b.z, y = b.y }, -- the block (or torch, etc) - { x = b.x - hi.xd, z = b.z - hi.zd, y = b.y } -- room for the turtle - }) - end - end + if blockDirections[d] then + -- placing a block from the side + local hi = getHeadingInfo(blockDirections[d]) + b.heading = hi.heading + self:addPlacementChain(chains, { + { x = b.x + hi.xd, z = b.z + hi.zd, y = b.y }, -- block we are placing against + { x = b.x, z = b.z, y = b.y }, -- the block (or torch, etc) + { x = b.x - hi.xd, z = b.z - hi.zd, y = b.y } -- room for the turtle + }) + end + end - -- pass 3 - while #dirtyBlocks2 > 0 do - local b = table.remove(dirtyBlocks2) - local d = b.direction + -- pass 3 + while #dirtyBlocks2 > 0 do + local b = table.remove(dirtyBlocks2) + local d = b.direction - spinner:spin(#dirtyBlocks2 .. ' blocks remaining ') + spinner:spin(#dirtyBlocks2 .. ' blocks remaining ') - if d == 'east-west-block' then - self:bestOfTwoSides(b, chains, 'east', 'west') - elseif d == 'north-south-block' then - self:bestOfTwoSides(b, chains, 'north', 'south') - end - end + if d == 'east-west-block' then + self:bestOfTwoSides(b, chains, 'east', 'west') + elseif d == 'north-south-block' then + self:bestOfTwoSides(b, chains, 'north', 'south') + end + end - term.clearLine() + term.clearLine() - self:setPlacementOrder(spinner, chains) - local plane = self:optimizeRoute(spinner, y) + self:setPlacementOrder(spinner, chains) + local plane = self:optimizeRoute(spinner, y) - term.clearLine() - spinner:stop() + term.clearLine() + spinner:stop() - for k,b in ipairs(plane) do - self.blocks[ri.s + k - 1] = b - end + for k,b in ipairs(plane) do + self.blocks[ri.s + k - 1] = b + end end function Schematic:getComputedBlock(i) - local b = self.blocks[i] + local b = self.blocks[i] - -- has this level been computed ? - if not self.rowIndex[b.y].loaded then - -- compute each level up til this one (unless saved in cache) + -- has this level been computed ? + if not self.rowIndex[b.y].loaded then + -- compute each level up til this one (unless saved in cache) - for y = 0, b.y - 1 do - if not self.cache[y] then - self:determineBlockPlacement(y) - end - end - self:determineBlockPlacement(b.y) - -- get the block now at the computed location - b = self.blocks[i] - end - return b + for y = 0, b.y - 1 do + if not self.cache[y] then + self:determineBlockPlacement(y) + end + end + self:determineBlockPlacement(b.y) + -- get the block now at the computed location + b = self.blocks[i] + end + return b end -- set the order for block dependencies function Schematic:setPlacementOrder(spinner, placementChains) - -- optimize for overlapping check - for _,chain in pairs(placementChains) do - for index,_ in pairs(chain.keys) do - if not chain.startRow or (index < chain.startRow) then - chain.startRow = index - end - if not chain.endRow or (index > chain.endRow) then - chain.endRow = index - end - end - end + -- optimize for overlapping check + for _,chain in pairs(placementChains) do + for index,_ in pairs(chain.keys) do + if not chain.startRow or (index < chain.startRow) then + chain.startRow = index + end + if not chain.endRow or (index > chain.endRow) then + chain.endRow = index + end + end + end - local function groupOverlappingChains(t, groupedChain, chain, spinner) - local found = true + local function groupOverlappingChains(t, groupedChain, chain, spinner) + local found = true - local function overlaps(chain1, chain2) - if chain1.startRow > chain2.endRow or - chain2.startRow > chain1.endRow then - return false - end - for k,_ in pairs(chain1.keys) do - if chain2.keys[k] then - return true - end - end - end + local function overlaps(chain1, chain2) + if chain1.startRow > chain2.endRow or + chain2.startRow > chain1.endRow then + return false + end + for k,_ in pairs(chain1.keys) do + if chain2.keys[k] then + return true + end + end + end - while found do - found = false - for k, v in pairs(t) do - local o = overlaps(chain, v) - if o then - table.remove(t, k) - table.insert(groupedChain, v) - groupOverlappingChains(t, groupedChain, v, spinner) - spinner:spin() - found = true - break - end - end - end - end + while found do + found = false + for k, v in pairs(t) do + local o = overlaps(chain, v) + if o then + table.remove(t, k) + table.insert(groupedChain, v) + groupOverlappingChains(t, groupedChain, v, spinner) + spinner:spin() + found = true + break + end + end + end + end - -- group together any placement chains with overlapping blocks - local groupedChains = {} - while #placementChains > 0 do - local groupedChain = {} - local chain = table.remove(placementChains) - table.insert(groupedChain, chain) - table.insert(groupedChains, groupedChain) - groupOverlappingChains(placementChains, groupedChain, chain, spinner) - spinner:spin('chains: ' .. #groupedChains .. ' ' .. #placementChains .. ' ') - end + -- group together any placement chains with overlapping blocks + local groupedChains = {} + while #placementChains > 0 do + local groupedChain = {} + local chain = table.remove(placementChains) + table.insert(groupedChain, chain) + table.insert(groupedChains, groupedChain) + groupOverlappingChains(placementChains, groupedChain, chain, spinner) + spinner:spin('chains: ' .. #groupedChains .. ' ' .. #placementChains .. ' ') + end - --Logger.log('schematic', 'groups: ' .. #groupedChains) - --Logger.setFileLogging('chains') + --Logger.log('schematic', 'groups: ' .. #groupedChains) + --Logger.setFileLogging('chains') - local function mergeChains(chains) + local function mergeChains(chains) - --[[ - Logger.debug('---------------') - Logger.log('schematic', 'mergeChains: ' .. #chains) - for _,chain in ipairs(chains) do - Logger.log('schematic', chain) - for _,e in ipairs(chain) do - Logger.log('schematic', string.format('%d:%d:%d %s %d:%d', - e.block.x, e.block.z, e.block.y, tostring(e.block.direction), e.block.id, e.block.dmg)) - end - end - ]]-- + --[[ + Logger.debug('---------------') + Logger.log('schematic', 'mergeChains: ' .. #chains) + for _,chain in ipairs(chains) do + Logger.log('schematic', chain) + for _,e in ipairs(chain) do + Logger.log('schematic', string.format('%d:%d:%d %s %d:%d', + e.block.x, e.block.z, e.block.y, tostring(e.block.direction), e.block.id, e.block.dmg)) + end + end + ]]-- - local masterChain = table.remove(chains) + local masterChain = table.remove(chains) - --[[ it's something like this: + --[[ it's something like this: - A chain B chain result - 1 1 - 2 -------- 2 2 - 3 3 - 4 4 - 5 5 - 6 -------- 6 6 - 7 7 - --]] - local function splice(chain1, chain2) - for k,v in ipairs(chain1) do - for k2,v2 in ipairs(chain2) do - if v == v2 then - local index = k - local dupe - for i = k2-1, 1, -1 do - dupe = false - -- traverse back through the first chain aligning on matches - for j = index-1, 1, -1 do - if chain1[j] == chain2[i] then - index = j - dupe = true - break - end - end - if not dupe then - table.insert(chain1, index, chain2[i]) - end - end - index = k+1 - for i = k2+1, #chain2, 1 do - dupe = false - for j = index, #chain1, 1 do - if chain1[j] == chain2[i] then - index = j - dupe = true - break - end - end - if not dupe then - table.insert(chain1, index, chain2[i]) - end - index = index + 1 - end - return true - end - end - end - end + A chain B chain result + 1 1 + 2 -------- 2 2 + 3 3 + 4 4 + 5 5 + 6 -------- 6 6 + 7 7 + --]] + local function splice(chain1, chain2) + for k,v in ipairs(chain1) do + for k2,v2 in ipairs(chain2) do + if v == v2 then + local index = k + local dupe + for i = k2-1, 1, -1 do + dupe = false + -- traverse back through the first chain aligning on matches + for j = index-1, 1, -1 do + if chain1[j] == chain2[i] then + index = j + dupe = true + break + end + end + if not dupe then + table.insert(chain1, index, chain2[i]) + end + end + index = k+1 + for i = k2+1, #chain2, 1 do + dupe = false + for j = index, #chain1, 1 do + if chain1[j] == chain2[i] then + index = j + dupe = true + break + end + end + if not dupe then + table.insert(chain1, index, chain2[i]) + end + index = index + 1 + end + return true + end + end + end + end - while #chains > 0 do - for k,chain in pairs(chains) do - if splice(masterChain.blocks, chain.blocks) then - table.remove(chains, k) - break - end - end - end + while #chains > 0 do + for k,chain in pairs(chains) do + if splice(masterChain.blocks, chain.blocks) then + table.remove(chains, k) + break + end + end + end - --[[ - Logger.log('schematic', 'master chain: ') - Logger.log('schematic', masterChain) - Logger.log('schematic', '---------------') - for _,e in ipairs(masterChain.blocks) do - Logger.log('schematic', string.format('%d:%d:%d %s %s:%d', - e.x, e.z, e.y, tostring(e.direction), e.id, e.dmg)) - end - --]] + --[[ + Logger.log('schematic', 'master chain: ') + Logger.log('schematic', masterChain) + Logger.log('schematic', '---------------') + for _,e in ipairs(masterChain.blocks) do + Logger.log('schematic', string.format('%d:%d:%d %s %s:%d', + e.x, e.z, e.y, tostring(e.direction), e.id, e.dmg)) + end + --]] - return masterChain - end + return masterChain + end - -- combine the individual overlapping placement chains into 1 long master chain - local masterChains = {} - for _,group in pairs(groupedChains) do - spinner:spin('chains: ' .. #masterChains) - table.insert(masterChains, mergeChains(group)) - end + -- combine the individual overlapping placement chains into 1 long master chain + local masterChains = {} + for _,group in pairs(groupedChains) do + spinner:spin('chains: ' .. #masterChains) + table.insert(masterChains, mergeChains(group)) + end - local function removeDuplicates(chain) - for k,v in ipairs(chain) do - for i = #chain, k+1, -1 do - if v == chain[i] then + local function removeDuplicates(chain) + for k,v in ipairs(chain) do + for i = #chain, k+1, -1 do + if v == chain[i] then v.info = 'Unplaceable' - table.remove(chain, i) - end - end - end - end + table.remove(chain, i) + end + end + end + end - -- any chains with duplicates cannot be placed correctly - -- there are some cases where a turtle cannot place blocks the same as a player - for _,chain in pairs(masterChains) do - removeDuplicates(chain.blocks) - spinner:spin('chains: ' .. #masterChains) + -- any chains with duplicates cannot be placed correctly + -- there are some cases where a turtle cannot place blocks the same as a player + for _,chain in pairs(masterChains) do + removeDuplicates(chain.blocks) + spinner:spin('chains: ' .. #masterChains) - --[[ - Logger.log('schematic', "MASTER CHAIN") - for _,e in ipairs(chain) do - Logger.log('schematic', string.format('%d:%d:%d %s %d:%d', - e.block.x, e.block.z, e.block.y, tostring(e.block.direction), e.block.id, e.block.dmg)) - end - --]] + --[[ + Logger.log('schematic', "MASTER CHAIN") + for _,e in ipairs(chain) do + Logger.log('schematic', string.format('%d:%d:%d %s %d:%d', + e.block.x, e.block.z, e.block.y, tostring(e.block.direction), e.block.id, e.block.dmg)) + end + --]] - end - term.clearLine() + end + term.clearLine() - -- set dependent blocks for optimize routine - for k,chain in pairs(masterChains) do - spinner:spin('chains: ' .. #masterChains - k) + -- set dependent blocks for optimize routine + for k,chain in pairs(masterChains) do + spinner:spin('chains: ' .. #masterChains - k) - local prev - for k,b in ipairs(chain.blocks) do - b.prev = prev - b.next = chain.blocks[k + 1] - prev = b - end - end + local prev + for k,b in ipairs(chain.blocks) do + b.prev = prev + b.next = chain.blocks[k + 1] + prev = b + end + end - term.clearLine() + term.clearLine() end function Schematic:optimizeRoute(spinner, y) - local function getNearestNeighbor(p, pt, maxDistance) - local key, block, heading - local moves = maxDistance + local function getNearestNeighbor(p, pt, maxDistance) + local key, block, heading + local moves = maxDistance - local function getMoves(b, k) - local distance = math.abs(pt.x - b.x) + math.abs(pt.z - b.z) + local function getMoves(b, k) + local distance = math.abs(pt.x - b.x) + math.abs(pt.z - b.z) - if distance < moves then - -- this operation is expensive - only run if distance is close - local c, h = Point.calculateMoves(pt, b, distance) - if c < moves then - block = b - key = k - moves = c - heading = h - end - end - end + if distance < moves then + -- this operation is expensive - only run if distance is close + local c, h = Point.calculateMoves(pt, b, distance) + if c < moves then + block = b + key = k + moves = c + heading = h + end + end + end - local function blockReady(b) - if b.u then - return false - end - if b.prev and not b.prev.u then - return false - end - return true - end + local function blockReady(b) + if b.u then + return false + end + if b.prev and not b.prev.u then + return false + end + return true + end - local mid = pt.index - local forward = mid + 1 - local backward = mid - 1 - while forward <= #p or backward > 0 do - if forward <= #p then - local b = p[forward] - if blockReady(b) then - getMoves(b, forward) - if moves <= 1 then - break - end - if moves < maxDistance and math.abs(b.z - pt.z) > moves and pt.index > 0 then - forward = #p - end - end - forward = forward + 1 - end - if backward > 0 then - local b = p[backward] - if blockReady(b) then - getMoves(b, backward) - if moves <= 1 then - break - end - if moves < maxDistance and math.abs(pt.z - b.z) > moves then - backward = 0 - end - end - backward = backward - 1 - end - end - pt.x = block.x - pt.z = block.z - pt.y = block.y - pt.heading = heading - pt.index = key - block.u = true - return block - end + local mid = pt.index + local forward = mid + 1 + local backward = mid - 1 + while forward <= #p or backward > 0 do + if forward <= #p then + local b = p[forward] + if blockReady(b) then + getMoves(b, forward) + if moves <= 1 then + break + end + if moves < maxDistance and math.abs(b.z - pt.z) > moves and pt.index > 0 then + forward = #p + end + end + forward = forward + 1 + end + if backward > 0 then + local b = p[backward] + if blockReady(b) then + getMoves(b, backward) + if moves <= 1 then + break + end + if moves < maxDistance and math.abs(pt.z - b.z) > moves then + backward = 0 + end + end + backward = backward - 1 + end + end + pt.x = block.x + pt.z = block.z + pt.y = block.y + pt.heading = heading + pt.index = key + block.u = true + return block + end - local pt = Util.shallowCopy(self.cache[y - 1] or { x = -1, y = 0, z = -1, heading = 0 }) - local t = { } - local ri = self.rowIndex[y] - local blockCount = ri.e - ri.s + 1 + local pt = Util.shallowCopy(self.cache[y - 1] or { x = -1, y = 0, z = -1, heading = 0 }) + local t = { } + local ri = self.rowIndex[y] + local blockCount = ri.e - ri.s + 1 - local function extractPlane() - local t = {} - local dt = {} - for i = ri.s, ri.e do - local b = self.blocks[i] - if b.twoHigh then - b.last = true - while b.next do - b = b.next - b.last = true - end - end - end - for i = ri.s, ri.e do - local b = self.blocks[i] - if b.last then - table.insert(dt, b) - else - table.insert(t, b) - end - end - return t, dt - end + local function extractPlane() + local t = {} + local dt = {} + for i = ri.s, ri.e do + local b = self.blocks[i] + if b.twoHigh then + b.last = true + while b.next do + b = b.next + b.last = true + end + end + end + for i = ri.s, ri.e do + local b = self.blocks[i] + if b.last then + table.insert(dt, b) + else + table.insert(t, b) + end + end + return t, dt + end - local maxDistance = self.width*self.length - local plane, doors = extractPlane(y) - pt.index = 0 - for i = 1, #plane do - local b = getNearestNeighbor(plane, pt, maxDistance) - table.insert(t, b) - local percent = math.floor(#t * 100 / blockCount) .. '%' - spinner:spin(percent .. ' ' .. blockCount - i .. ' ') - end - -- all two high blocks are placed last on each plane - pt.index = 0 - for i = 1, #doors do - local b = getNearestNeighbor(doors, pt, maxDistance) - table.insert(t, b) - local percent = math.floor(#t * 100 / blockCount) .. '%' - spinner:spin(percent .. ' ' .. blockCount - #plane - i .. ' ') - end + local maxDistance = self.width*self.length + local plane, doors = extractPlane(y) + pt.index = 0 + for i = 1, #plane do + local b = getNearestNeighbor(plane, pt, maxDistance) + table.insert(t, b) + local percent = math.floor(#t * 100 / blockCount) .. '%' + spinner:spin(percent .. ' ' .. blockCount - i .. ' ') + end + -- all two high blocks are placed last on each plane + pt.index = 0 + for i = 1, #doors do + local b = getNearestNeighbor(doors, pt, maxDistance) + table.insert(t, b) + local percent = math.floor(#t * 100 / blockCount) .. '%' + spinner:spin(percent .. ' ' .. blockCount - #plane - i .. ' ') + end - self.rowIndex[y].loaded = true - if not self.cache[y] then - self.cache[y] = Util.shallowCopy(pt) - Util.writeTable('usr/builder/' .. self.filename .. '.cache', self.cache) - end + self.rowIndex[y].loaded = true + if not self.cache[y] then + self.cache[y] = Util.shallowCopy(pt) + Util.writeTable('usr/builder/' .. self.filename .. '.cache', self.cache) + end - return t + return t end return Schematic diff --git a/builder/apis/turtle.lua b/builder/apis/turtle.lua index 2b15336..af2c85a 100644 --- a/builder/apis/turtle.lua +++ b/builder/apis/turtle.lua @@ -20,1246 +20,1246 @@ local FUEL_ITEM = { id = 'minecraft:coal', dmg = 0 } local TurtleBuilder = class(Builder) Util.merge(TurtleBuilder, { - slots = { }, + slots = { }, }) -- Temp functions until conversion to new adapters is complete local function convertSingleForward(item) - item.displayName = item.display_name - item.name = item.id - item.damage = item.dmg - item.count = item.qty - item.maxCount = item.max_size - return item + item.displayName = item.display_name + item.name = item.id + item.damage = item.dmg + item.count = item.qty + item.maxCount = item.max_size + return item end local function convertForward(t) - for _,v in pairs(t) do - convertSingleForward(v) - end - return t + for _,v in pairs(t) do + convertSingleForward(v) + end + return t end local function convertSingleBack(item) - if item then - item.id = item.name - item.dmg = item.damage - item.qty = item.count - item.max_size = item.maxCount - item.display_name = item.displayName - end - return item + if item then + item.id = item.name + item.dmg = item.damage + item.qty = item.count + item.max_size = item.maxCount + item.display_name = item.displayName + end + return item end --[[-- SupplyPage --]]-- local supplyPage = UI.Page { - titleBar = UI.TitleBar { - title = 'Waiting for supplies', - previousPage = 'start' - }, - menuBar = UI.MenuBar { - y = 2, - buttons = { - --{ text = 'Refresh', event = 'refresh', help = 'Refresh inventory' }, - { text = 'Continue', event = 'build', help = 'Continue building' }, - { text = 'Menu', event = 'menu', help = 'Return to main menu' }, + titleBar = UI.TitleBar { + title = 'Waiting for supplies', + previousPage = 'start' + }, + menuBar = UI.MenuBar { + y = 2, + buttons = { + --{ text = 'Refresh', event = 'refresh', help = 'Refresh inventory' }, + { text = 'Continue', event = 'build', help = 'Continue building' }, + { text = 'Menu', event = 'menu', help = 'Return to main menu' }, -- { text = 'Force Craft', event = 'craft', help = 'Request crafting (again)' }, - } - }, - grid = UI.Grid { - columns = { - { heading = 'Name', key = 'display_name', width = UI.term.width - 7 }, - { heading = 'Need', key = 'need', width = 4 }, - }, - sortColumn = 'display_name', - y = 3, - width = UI.term.width, - height = UI.term.height - 3 - }, - statusBar = UI.StatusBar { - columns = { - { 'Help', 'help', UI.term.width - 13 }, - { 'Fuel', 'fuel', 11 } - } - }, - accelerators = { - c = 'craft', - r = 'refresh', - b = 'build', - m = 'menu', - }, + } + }, + grid = UI.Grid { + columns = { + { heading = 'Name', key = 'display_name', width = UI.term.width - 7 }, + { heading = 'Need', key = 'need', width = 4 }, + }, + sortColumn = 'display_name', + y = 3, + width = UI.term.width, + height = UI.term.height - 3 + }, + statusBar = UI.StatusBar { + columns = { + { 'Help', 'help', UI.term.width - 13 }, + { 'Fuel', 'fuel', 11 } + } + }, + accelerators = { + c = 'craft', + r = 'refresh', + b = 'build', + m = 'menu', + }, } function supplyPage:eventHandler(event) - if event.type == 'build' then - UI:setPage('start') - self:sync() - self.builder:build() + if event.type == 'build' then + UI:setPage('start') + self:sync() + self.builder:build() - elseif event.type == 'menu' then - self.builder:dumpInventory() - --Builder.status = 'idle' - UI:setPage('start') - turtle.setStatus('waiting') + elseif event.type == 'menu' then + self.builder:dumpInventory() + --Builder.status = 'idle' + UI:setPage('start') + turtle.setStatus('waiting') - elseif event.type == 'grid_focus_row' then - self.statusBar:setValue('help', event.selected.id .. ':' .. event.selected.dmg) - self.statusBar:draw() + elseif event.type == 'grid_focus_row' then + self.statusBar:setValue('help', event.selected.id .. ':' .. event.selected.dmg) + self.statusBar:draw() - elseif event.type == 'focus_change' then - self.statusBar:timedStatus(event.focused.help, 3) - end + elseif event.type == 'focus_change' then + self.statusBar:timedStatus(event.focused.help, 3) + end - return UI.Page.eventHandler(self, event) + return UI.Page.eventHandler(self, event) end function supplyPage:enable(builder) - self.builder = builder - self.grid:setIndex(1) - self.statusBar:setValue('fuel', - string.format('Fuel: %dk', math.floor(turtle.getFuelLevel() / 1024))) + self.builder = builder + self.grid:setIndex(1) + self.statusBar:setValue('fuel', + string.format('Fuel: %dk', math.floor(turtle.getFuelLevel() / 1024))) - self.timer = Event.onInterval(6, function() - if self.enabled then - local s, m = pcall(function() - self.builder:autocraft(self.builder:getSupplies()) - self:refresh() - self.statusBar:timedStatus('Refreshed ', 2) - self:sync() - end) - if not s then -- not sure why it's erroring :( - _G._syslog(m) - end - end - end) - UI.Page.enable(self) + self.timer = Event.onInterval(6, function() + if self.enabled then + local s, m = pcall(function() + self.builder:autocraft(self.builder:getSupplies()) + self:refresh() + self.statusBar:timedStatus('Refreshed ', 2) + self:sync() + end) + if not s then -- not sure why it's erroring :( + _G._syslog(m) + end + end + end) + UI.Page.enable(self) end function supplyPage:disable() - Event.off(self.timer) + Event.off(self.timer) end function supplyPage:setSupplies(supplies) - local t = { } - for _,s in pairs(supplies) do - local key = s.id .. ':' .. s.dmg - local entry = t[key] - if not entry then - entry = Util.shallowCopy(s) - t[key] = entry - else - entry.need = entry.need + s.need - end - entry.need = entry.need - turtle.getItemCount(s.index) - end + local t = { } + for _,s in pairs(supplies) do + local key = s.id .. ':' .. s.dmg + local entry = t[key] + if not entry then + entry = Util.shallowCopy(s) + t[key] = entry + else + entry.need = entry.need + s.need + end + entry.need = entry.need - turtle.getItemCount(s.index) + end - self.grid:setValues(t) + self.grid:setValues(t) end function supplyPage:refresh() - self.statusBar:timedStatus('Refreshed ', 3) - local supplies = self.builder:getSupplies() - if #supplies == 0 then - UI:setPage('blank') - self:sync() - self.builder:build() - else - self:setSupplies(supplies) - self.grid:draw() - end + self.statusBar:timedStatus('Refreshed ', 3) + local supplies = self.builder:getSupplies() + if #supplies == 0 then + UI:setPage('blank') + self:sync() + self.builder:build() + else + self:setSupplies(supplies) + self.grid:draw() + end end --[[-- Builder --]]-- function TurtleBuilder:getBlockCounts() - local blocks = Builder.getBlockCounts(self) + local blocks = Builder.getBlockCounts(self) - -- add a couple essential items to the supply list to allow replacements - local wrench = self.subDB:getSubstitutedItem('SubstituteAWrench', 0) - wrench.qty = 0 - wrench.need = 1 - blocks[wrench.id .. ':' .. wrench.dmg] = wrench + -- add a couple essential items to the supply list to allow replacements + local wrench = self.subDB:getSubstitutedItem('SubstituteAWrench', 0) + wrench.qty = 0 + wrench.need = 1 + blocks[wrench.id .. ':' .. wrench.dmg] = wrench - local fuel = self.subDB:getSubstitutedItem(FUEL_ITEM.id, FUEL_ITEM.dmg) - fuel.qty = 0 - fuel.need = 1 - blocks[fuel.id .. ':' .. fuel.dmg] = fuel + local fuel = self.subDB:getSubstitutedItem(FUEL_ITEM.id, FUEL_ITEM.dmg) + fuel.qty = 0 + fuel.need = 1 + blocks[fuel.id .. ':' .. fuel.dmg] = fuel - blocks['minecraft:piston:0'] = { - id = 'minecraft:piston', - dmg = 0, - qty = 0, - need = 1, - } + blocks['minecraft:piston:0'] = { + id = 'minecraft:piston', + dmg = 0, + qty = 0, + need = 1, + } - return blocks + return blocks end function TurtleBuilder:selectItem(id, dmg) - for k,s in ipairs(self.slots) do - if s.qty > 0 and s.id == id and s.dmg == dmg then - -- check to see if someone pulled items from inventory - -- or we passed over a hopper - if turtle.getItemCount(s.index) > 0 then - if k > 1 and s.qty > 1 then - table.remove(self.slots, k) - table.insert(self.slots, 1, s) - end - turtle.select(s.index) - return s - end - end - end + for k,s in ipairs(self.slots) do + if s.qty > 0 and s.id == id and s.dmg == dmg then + -- check to see if someone pulled items from inventory + -- or we passed over a hopper + if turtle.getItemCount(s.index) > 0 then + if k > 1 and s.qty > 1 then + table.remove(self.slots, k) + table.insert(self.slots, 1, s) + end + turtle.select(s.index) + return s + end + end + end end function TurtleBuilder:getAirResupplyList(blockIndex) - local slots = { } + local slots = { } - if self.mode == 'destroy' then - for i = 1, RESOURCE_SLOTS do - slots[i] = { - qty = 0, - need = 0, - index = i - } - end - else - slots = self:getGenericSupplyList(blockIndex) - end + if self.mode == 'destroy' then + for i = 1, RESOURCE_SLOTS do + slots[i] = { + qty = 0, + need = 0, + index = i + } + end + else + slots = self:getGenericSupplyList(blockIndex) + end - local fuel = self.subDB:getSubstitutedItem(FUEL_ITEM.id, FUEL_ITEM.dmg) + local fuel = self.subDB:getSubstitutedItem(FUEL_ITEM.id, FUEL_ITEM.dmg) - slots[15] = { - id = 'minecraft:chest', - dmg = 0, - qty = 0, - need = 1, - index = 15, - } + slots[15] = { + id = 'minecraft:chest', + dmg = 0, + qty = 0, + need = 1, + index = 15, + } - slots[16] = { - id = fuel.id, - dmg = fuel.dmg, - qty = 0, - need = 64, - index = 16, - } + slots[16] = { + id = fuel.id, + dmg = fuel.dmg, + qty = 0, + need = 64, + index = 16, + } - return slots + return slots end function TurtleBuilder:getSupplyList(blockIndex) - local slots, lastBlock = self:getGenericSupplyList(blockIndex) + local slots, lastBlock = self:getGenericSupplyList(blockIndex) - slots[15] = { - id = 'minecraft:piston', - dmg = 0, - qty = 0, - need = 1, - index = 15, - } + slots[15] = { + id = 'minecraft:piston', + dmg = 0, + qty = 0, + need = 1, + index = 15, + } - local wrench = self.subDB:getSubstitutedItem('SubstituteAWrench', 0) - slots[16] = { - id = wrench.id, - dmg = wrench.dmg, - qty = 0, - need = 1, - index = 16, - } + local wrench = self.subDB:getSubstitutedItem('SubstituteAWrench', 0) + slots[16] = { + id = wrench.id, + dmg = wrench.dmg, + qty = 0, + need = 1, + index = 16, + } - self.slots = slots + self.slots = slots - return lastBlock + return lastBlock end function TurtleBuilder:getGenericSupplyList(blockIndex) - local slots = { } + local slots = { } - for i = 1, RESOURCE_SLOTS do - slots[i] = { - qty = 0, - need = 0, - index = i - } - end + for i = 1, RESOURCE_SLOTS do + slots[i] = { + qty = 0, + need = 0, + index = i + } + end - local function getSlot(id, dmg) - -- find matching slot - local maxStack = itemDB:getMaxCount({ name = id, damage = dmg }) - for _, s in ipairs(slots) do - if s.id == id and s.dmg == dmg and s.need < maxStack then - return s - end - end - -- return first available slot - for _, s in ipairs(slots) do - if not s.id then - s.key = id .. ':' .. dmg - s.id = id - s.dmg = dmg - return s - end - end - end + local function getSlot(id, dmg) + -- find matching slot + local maxStack = itemDB:getMaxCount({ name = id, damage = dmg }) + for _, s in ipairs(slots) do + if s.id == id and s.dmg == dmg and s.need < maxStack then + return s + end + end + -- return first available slot + for _, s in ipairs(slots) do + if not s.id then + s.key = id .. ':' .. dmg + s.id = id + s.dmg = dmg + return s + end + end + end - local lastBlock = blockIndex - for k = blockIndex, #self.schematic.blocks do - lastBlock = k - local b = self.schematic:getComputedBlock(k) + local lastBlock = blockIndex + for k = blockIndex, #self.schematic.blocks do + lastBlock = k + local b = self.schematic:getComputedBlock(k) - if b.id ~= 'minecraft:air' then - local slot = getSlot(b.id, b.dmg) - if not slot then - break - end - slot.need = slot.need + 1 - end - end + if b.id ~= 'minecraft:air' then + local slot = getSlot(b.id, b.dmg) + if not slot then + break + end + slot.need = slot.need + 1 + end + end - for _,s in pairs(slots) do - if s.id then - s.display_name = itemDB:getName({ name = s.id, damage = s.dmg }) - end - end + for _,s in pairs(slots) do + if s.id then + s.display_name = itemDB:getName({ name = s.id, damage = s.dmg }) + end + end - return slots, lastBlock + return slots, lastBlock end function TurtleBuilder:dumpInventory() - local success = true + local success = true - for i = 1, 16 do - local qty = turtle.getItemCount(i) - if qty > 0 then - self.itemAdapter:insert(i, qty) - end - if turtle.getItemCount(i) ~= 0 then - success = false - end - end - turtle.select(1) + for i = 1, 16 do + local qty = turtle.getItemCount(i) + if qty > 0 then + self.itemAdapter:insert(i, qty) + end + if turtle.getItemCount(i) ~= 0 then + success = false + end + end + turtle.select(1) - return success + return success end function TurtleBuilder:dumpInventoryWithCheck() - while not self:dumpInventory() do - print('Storage is full or missing - make space or replace') - print('Press enter to continue') - turtle.setHeading(0) - read() - end + while not self:dumpInventory() do + print('Storage is full or missing - make space or replace') + print('Press enter to continue') + turtle.setHeading(0) + read() + end end function TurtleBuilder:autocraft(supplies) - if self.itemAdapter.craftItems then - local t = { } + if self.itemAdapter.craftItems then + local t = { } - for _,s in pairs(supplies) do - local key = s.id .. ':' .. s.dmg - local item = t[key] - if not item then - item = { - id = s.id, - dmg = s.dmg, - qty = 0, - } - t[key] = item - end - item.qty = item.qty + (s.need - s.qty) - end + for _,s in pairs(supplies) do + local key = s.id .. ':' .. s.dmg + local item = t[key] + if not item then + item = { + id = s.id, + dmg = s.dmg, + qty = 0, + } + t[key] = item + end + item.qty = item.qty + (s.need - s.qty) + end - self.itemAdapter:craftItems(convertForward(t)) - end + self.itemAdapter:craftItems(convertForward(t)) + end end function TurtleBuilder:getSupplies() - self.itemAdapter:refresh() + self.itemAdapter:refresh() - local t = { } - for _,s in ipairs(self.slots) do - if s.need > 0 then - local item = convertSingleBack(self.itemAdapter:getItemInfo({ - name = s.id, - damage = s.dmg, - nbtHash = s.nbt_hash, - })) - if item then - s.display_name = item.display_name + local t = { } + for _,s in ipairs(self.slots) do + if s.need > 0 then + local item = convertSingleBack(self.itemAdapter:getItemInfo({ + name = s.id, + damage = s.dmg, + nbtHash = s.nbt_hash, + })) + if item then + s.display_name = item.display_name - local qty = math.min(s.need - s.qty, item.qty) + local qty = math.min(s.need - s.qty, item.qty) - if qty + s.qty > item.max_size then - qty = item.max_size - s.need = qty - end - if qty > 0 then - self.itemAdapter:provide(convertSingleForward(item), qty, s.index) - s.qty = turtle.getItemCount(s.index) - end - else - s.display_name = itemDB:getName({ name = s.id, damage = s.dmg }) - end - end - if s.qty < s.need then - table.insert(t, s) - end - end + if qty + s.qty > item.max_size then + qty = item.max_size + s.need = qty + end + if qty > 0 then + self.itemAdapter:provide(convertSingleForward(item), qty, s.index) + s.qty = turtle.getItemCount(s.index) + end + else + s.display_name = itemDB:getName({ name = s.id, damage = s.dmg }) + end + end + if s.qty < s.need then + table.insert(t, s) + end + end - return t + return t end function TurtleBuilder:refuel() - while turtle.getFuelLevel() < 4000 do - print('Refueling') - turtle.select(1) + while turtle.getFuelLevel() < 4000 do + print('Refueling') + turtle.select(1) - local fuel = self.subDB:getSubstitutedItem(FUEL_ITEM.id, FUEL_ITEM.dmg) + local fuel = self.subDB:getSubstitutedItem(FUEL_ITEM.id, FUEL_ITEM.dmg) - self.itemAdapter:provide(convertSingleForward(fuel), 64, 1) - if turtle.getItemCount(1) == 0 then - print('Out of fuel, add fuel to chest/ME system') - turtle.setHeading(0) - turtle.setStatus('waiting') - os.sleep(5) - else - turtle.refuel(64) - end - end + self.itemAdapter:provide(convertSingleForward(fuel), 64, 1) + if turtle.getItemCount(1) == 0 then + print('Out of fuel, add fuel to chest/ME system') + turtle.setHeading(0) + turtle.setStatus('waiting') + os.sleep(5) + else + turtle.refuel(64) + end + end end function TurtleBuilder:inAirDropoff() - if not device.wireless_modem then - return false - end + if not device.wireless_modem then + return false + end - self:log('Requesting air supply drop for supply #: ' .. 1) - while true do - Message.broadcast('needSupplies', { point = turtle.getPoint(), uid = 1 }) - local _, _, msg, _ = Message.waitForMessage('gotSupplies', 1) + self:log('Requesting air supply drop for supply #: ' .. 1) + while true do + Message.broadcast('needSupplies', { point = turtle.getPoint(), uid = 1 }) + local _, _, msg, _ = Message.waitForMessage('gotSupplies', 1) - if not msg or not msg.contents then - Message.broadcast('supplyList', { uid = 1, slots = self:getAirResupplyList() }) - return false - end + if not msg or not msg.contents then + Message.broadcast('supplyList', { uid = 1, slots = self:getAirResupplyList() }) + return false + end - turtle.setStatus('waiting') + turtle.setStatus('waiting') - if msg.contents.point then - local pt = msg.contents.point + if msg.contents.point then + local pt = msg.contents.point - self:log('Received supply location') - os.sleep(0) + self:log('Received supply location') + os.sleep(0) - turtle.go(pt) - os.sleep(.1) -- random computer is not connected error + turtle.go(pt) + os.sleep(.1) -- random computer is not connected error - local chestAdapter = Adapter.wrap({ direction = 'down', side = 'top' }) + local chestAdapter = Adapter.wrap({ direction = 'down', side = 'top' }) - if not chestAdapter then - self:log('Chests above is not valid') - return false - end + if not chestAdapter then + self:log('Chests above is not valid') + return false + end - local oldAdapter = self.itemAdapter - self.itemAdapter = chestAdapter + local oldAdapter = self.itemAdapter + self.itemAdapter = chestAdapter - if not self:dumpInventory() then - self:log('Unable to dump inventory') - self.itemAdapter = oldAdapter - return false - end + if not self:dumpInventory() then + self:log('Unable to dump inventory') + self.itemAdapter = oldAdapter + return false + end - self.itemAdapter = oldAdapter + self.itemAdapter = oldAdapter - Message.broadcast('thanks', { }) + Message.broadcast('thanks', { }) - for _ = 1,12 do -- wait til supplier is idle before sending next request - if turtle.detectUp() then - os.sleep(.25) - end - end - os.sleep(.1) + for _ = 1,12 do -- wait til supplier is idle before sending next request + if turtle.detectUp() then + os.sleep(.25) + end + end + os.sleep(.1) - Message.broadcast('supplyList', { uid = 1, slots = self:getAirResupplyList() }) + Message.broadcast('supplyList', { uid = 1, slots = self:getAirResupplyList() }) - return true - end - end + return true + end + end end function TurtleBuilder:inAirResupply() - if not device.wireless_modem then - return false - end + if not device.wireless_modem then + return false + end - local oldAdapter = self.itemAdapter + local oldAdapter = self.itemAdapter - self:log('Requesting air supply drop for supply #: ' .. self.slotUid) - while true do - Message.broadcast('needSupplies', { point = turtle.getPoint(), uid = self.slotUid }) - local _, _, msg, _ = Message.waitForMessage('gotSupplies', 1) + self:log('Requesting air supply drop for supply #: ' .. self.slotUid) + while true do + Message.broadcast('needSupplies', { point = turtle.getPoint(), uid = self.slotUid }) + local _, _, msg, _ = Message.waitForMessage('gotSupplies', 1) - if not msg or not msg.contents then - self.itemAdapter = oldAdapter - return false - end + if not msg or not msg.contents then + self.itemAdapter = oldAdapter + return false + end - turtle.setStatus('waiting') + turtle.setStatus('waiting') - if msg.contents.point then - local pt = msg.contents.point + if msg.contents.point then + local pt = msg.contents.point - self:log('Received supply location') - os.sleep(0) + self:log('Received supply location') + os.sleep(0) - turtle.go(pt) - os.sleep(.1) -- random computer is not connected error + turtle.go(pt) + os.sleep(.1) -- random computer is not connected error - local chestAdapter = Adapter.wrap({ direction = 'down', side = 'top' }) + local chestAdapter = Adapter.wrap({ direction = 'down', side = 'top' }) - if not chestAdapter then - Util.print('not valid') - read() - end + if not chestAdapter then + Util.print('not valid') + read() + end - self.itemAdapter = chestAdapter + self.itemAdapter = chestAdapter - if not self:dumpInventory() then - self.itemAdapter = oldAdapter - return false - end - self:refuel() + if not self:dumpInventory() then + self.itemAdapter = oldAdapter + return false + end + self:refuel() - local lastBlock = self:getSupplyList(self.index) - local supplies = self:getSupplies() + local lastBlock = self:getSupplyList(self.index) + local supplies = self:getSupplies() - Message.broadcast('thanks', { }) + Message.broadcast('thanks', { }) - self.itemAdapter = oldAdapter + self.itemAdapter = oldAdapter - if #supplies == 0 then + if #supplies == 0 then - for _ = 1,12 do -- wait til supplier is idle before sending next request - if turtle.detectUp() then - os.sleep(.25) - end - end - os.sleep(.1) - if lastBlock < #self.schematic.blocks then - self:sendSupplyRequest(lastBlock) - else - Message.broadcast('finished') - end + for _ = 1,12 do -- wait til supplier is idle before sending next request + if turtle.detectUp() then + os.sleep(.25) + end + end + os.sleep(.1) + if lastBlock < #self.schematic.blocks then + self:sendSupplyRequest(lastBlock) + else + Message.broadcast('finished') + end - return true - end - self:log('Missing supplies - manually resupplying') - return false - end - end + return true + end + self:log('Missing supplies - manually resupplying') + return false + end + end end function TurtleBuilder:sendSupplyRequest(lastBlock) - if device.wireless_modem then - local slots = self:getAirResupplyList(lastBlock) - self.slotUid = os.clock() - Message.broadcast('supplyList', { uid = self.slotUid, slots = slots }) - end + if device.wireless_modem then + local slots = self:getAirResupplyList(lastBlock) + self.slotUid = os.clock() + Message.broadcast('supplyList', { uid = self.slotUid, slots = slots }) + end end local function closestEdgePoint(pt, pts, rpt, y) - pt = Point.copy(pt) - pt.heading = rpt.heading + pt = Point.copy(pt) + pt.heading = rpt.heading - local pta = Point.closest(pt, pts) - Util.removeByValue(pts, pta) - local ptb = Point.closest(pt, pts) + local pta = Point.closest(pt, pts) + Util.removeByValue(pts, pta) + local ptb = Point.closest(pt, pts) - local cpt = { } - if rpt.x < math.min(pta.x, ptb.x) then - cpt.x = math.min(pta.x, ptb.x) - elseif rpt.x > math.max(pta.x, ptb.x) then - cpt.x = math.max(pta.x, ptb.x) - else - cpt.x = rpt.x - end + local cpt = { } + if rpt.x < math.min(pta.x, ptb.x) then + cpt.x = math.min(pta.x, ptb.x) + elseif rpt.x > math.max(pta.x, ptb.x) then + cpt.x = math.max(pta.x, ptb.x) + else + cpt.x = rpt.x + end - if rpt.z < math.min(pta.z, ptb.z) then - cpt.z = math.min(pta.z, ptb.z) - elseif rpt.z > math.max(pta.z, ptb.z) then - cpt.z = math.max(pta.z, ptb.z) - else - cpt.z = rpt.z - end + if rpt.z < math.min(pta.z, ptb.z) then + cpt.z = math.min(pta.z, ptb.z) + elseif rpt.z > math.max(pta.z, ptb.z) then + cpt.z = math.max(pta.z, ptb.z) + else + cpt.z = rpt.z + end - cpt.y = y - return cpt + cpt.y = y + return cpt end function TurtleBuilder:getBuildingCorner(y) - local pts = { - { x = -1, z = -1, y = 0 }, - { x = -1, z = self.schematic.length, y = 0 }, - { x = self.schematic.width, z = -1, y = 0 }, - { x = self.schematic.width, z = self.schematic.length, y = 0 }, - } - return closestEdgePoint(self.supplyPoint, pts, turtle.getPoint(), y) + local pts = { + { x = -1, z = -1, y = 0 }, + { x = -1, z = self.schematic.length, y = 0 }, + { x = self.schematic.width, z = -1, y = 0 }, + { x = self.schematic.width, z = self.schematic.length, y = 0 }, + } + return closestEdgePoint(self.supplyPoint, pts, turtle.getPoint(), y) end function TurtleBuilder:gotoSupplyPoint() - if not Point.same(turtle.getPoint(), self.supplyPoint) then - -- so we don't end up pathfinding through a building - -- go to the corner closest to the supplies point - -- pathfind the rest of the way - local pt = self:getBuildingCorner(turtle.point.y) - turtle.go({ x = pt.x, z = pt.z }) - turtle.set({ - digPolicy = 'digNone', - attackPolicy = 'attackNone', - }) - turtle.pathfind(self.supplyPoint) - os.sleep(.1) -- random 'Computer is not connected' error... - end + if not Point.same(turtle.getPoint(), self.supplyPoint) then + -- so we don't end up pathfinding through a building + -- go to the corner closest to the supplies point + -- pathfind the rest of the way + local pt = self:getBuildingCorner(turtle.point.y) + turtle.go({ x = pt.x, z = pt.z }) + turtle.set({ + digPolicy = 'digNone', + attackPolicy = 'attackNone', + }) + turtle.pathfind(self.supplyPoint) + os.sleep(.1) -- random 'Computer is not connected' error... + end end function TurtleBuilder:resupply() - if self.slotUid and self:inAirResupply() then - os.queueEvent('build') - return - end + if self.slotUid and self:inAirResupply() then + os.queueEvent('build') + return + end - turtle.setStatus('resupplying') + turtle.setStatus('resupplying') - self:log('Resupplying') - self:gotoSupplyPoint() - self:dumpInventoryWithCheck() - self:refuel() - local lastBlock = self:getSupplyList(self.index) - if lastBlock < #self.schematic.blocks then - self:sendSupplyRequest(lastBlock) - elseif device.wireless_modem then - Message.broadcast('finished') - end - os.sleep(1) - local supplies = self:getSupplies() + self:log('Resupplying') + self:gotoSupplyPoint() + self:dumpInventoryWithCheck() + self:refuel() + local lastBlock = self:getSupplyList(self.index) + if lastBlock < #self.schematic.blocks then + self:sendSupplyRequest(lastBlock) + elseif device.wireless_modem then + Message.broadcast('finished') + end + os.sleep(1) + local supplies = self:getSupplies() - if #supplies == 0 then - os.queueEvent('build') - else - turtle.setHeading(0) - self:autocraft(supplies) - supplyPage:setSupplies(supplies) - UI:setPage(supplyPage, self) - end + if #supplies == 0 then + os.queueEvent('build') + else + turtle.setHeading(0) + self:autocraft(supplies) + supplyPage:setSupplies(supplies) + UI:setPage(supplyPage, self) + end end function TurtleBuilder:placeDown(slot) - return turtle.placeDown(slot.index) + return turtle.placeDown(slot.index) end function TurtleBuilder:placeUp(slot) - return turtle.placeUp(slot.index) + return turtle.placeUp(slot.index) end function TurtleBuilder:place(slot) - return turtle.place(slot.index) + return turtle.place(slot.index) end function TurtleBuilder:getWrenchSlot() - local wrench = self.subDB:getSubstitutedItem('SubstituteAWrench', 0) - return self:selectItem(wrench.id, wrench.dmg) + local wrench = self.subDB:getSubstitutedItem('SubstituteAWrench', 0) + return self:selectItem(wrench.id, wrench.dmg) end -- figure out our orientation in the world function TurtleBuilder:getTurtleFacing() - local directions = { - [5] = 2, - [3] = 3, - [4] = 0, - [2] = 1, - } + local directions = { + [5] = 2, + [3] = 3, + [4] = 0, + [2] = 1, + } - local function getItem(item) - turtle.select(1) - local msg = false - while true do - self.itemAdapter:provide(item, 1, 1) - if turtle.getItemCount(1) == 1 then - break - end - if not msg then - print('Place ' .. itemDB:getName(item) .. ' in supply chest') - msg = true - end - os.sleep(1) - end - end + local function getItem(item) + turtle.select(1) + local msg = false + while true do + self.itemAdapter:provide(item, 1, 1) + if turtle.getItemCount(1) == 1 then + break + end + if not msg then + print('Place ' .. itemDB:getName(item) .. ' in supply chest') + msg = true + end + os.sleep(1) + end + end - getItem({ name = 'minecraft:piston', damage = 0 }) - turtle.placeUp() - local _, bi = turtle.inspectUp() - turtle.digUp() - self:dumpInventoryWithCheck() + getItem({ name = 'minecraft:piston', damage = 0 }) + turtle.placeUp() + local _, bi = turtle.inspectUp() + turtle.digUp() + self:dumpInventoryWithCheck() - if directions[bi.metadata] then - self.facing = directions[bi.metadata] - return - end + if directions[bi.metadata] then + self.facing = directions[bi.metadata] + return + end - -- if the piston faces up when placed above, then this version - -- has the stair bug - self.stairBug = true + -- if the piston faces up when placed above, then this version + -- has the stair bug + self.stairBug = true - getItem({ name = 'minecraft:chest', damage = 0 }) - turtle.placeUp() - local _, bi2 = turtle.inspectUp() - turtle.digUp() - self:dumpInventoryWithCheck() + getItem({ name = 'minecraft:chest', damage = 0 }) + turtle.placeUp() + local _, bi2 = turtle.inspectUp() + turtle.digUp() + self:dumpInventoryWithCheck() - self.facing = directions[bi2.metadata] + self.facing = directions[bi2.metadata] end function TurtleBuilder:wrenchBlock(side, facing, cache) - local s = self:getWrenchSlot() + local s = self:getWrenchSlot() - if not s then - return false - end + if not s then + return false + end - local key = turtle.getPoint().heading .. '-' .. facing - if cache then - local count = cache[side][key] + local key = turtle.getPoint().heading .. '-' .. facing + if cache then + local count = cache[side][key] - if count then - turtle.select(s.index) - for _ = 1,count do - turtle.getAction(side).place() - end - return true - end - end + if count then + turtle.select(s.index) + for _ = 1,count do + turtle.getAction(side).place() + end + return true + end + end - local directions = { - [5] = 'east', - [3] = 'south', - [4] = 'west', - [2] = 'north', - [0] = 'down', - [1] = 'up', - } + local directions = { + [5] = 'east', + [3] = 'south', + [4] = 'west', + [2] = 'north', + [0] = 'down', + [1] = 'up', + } - if turtle.getHeadingInfo(facing).heading < 4 then - local offsetDirection = (self.facing + - turtle.getHeadingInfo(facing).heading) % 4 - facing = turtle.getHeadingInfo(offsetDirection).direction - end + if turtle.getHeadingInfo(facing).heading < 4 then + local offsetDirection = (self.facing + + turtle.getHeadingInfo(facing).heading) % 4 + facing = turtle.getHeadingInfo(offsetDirection).direction + end - local count = 0 - print('determining wrench count') - for _ = 1, 6 do - local _, bi = turtle.getAction(side).inspect() + local count = 0 + print('determining wrench count') + for _ = 1, 6 do + local _, bi = turtle.getAction(side).inspect() - if facing == directions[bi.metadata] then - if cache then - cache[side][key] = count - end - return true - end - count = count + 1 - turtle.getAction(side).place() - end + if facing == directions[bi.metadata] then + if cache then + cache[side][key] = count + end + return true + end + count = count + 1 + turtle.getAction(side).place() + end - return false + return false end function TurtleBuilder:rotateBlock(side, facing) - if self:getWrenchSlot() then - for _ = 1, facing do - turtle.getAction(side).place() - end - return true - end + if self:getWrenchSlot() then + for _ = 1, facing do + turtle.getAction(side).place() + end + return true + end end -- place piston, wrench piston to face downward, extend, remove piston function TurtleBuilder:placePiston(b) - local ps = self:selectItem('minecraft:piston', 0) - local ws = self:getWrenchSlot() + local ps = self:selectItem('minecraft:piston', 0) + local ws = self:getWrenchSlot() - if not ps or not ws then - b.needResupply = true - -- a hopper may have eaten the piston - return - end + if not ps or not ws then + b.needResupply = true + -- a hopper may have eaten the piston + return + end - if not turtle.place(ps.index) then - return - end + if not turtle.place(ps.index) then + return + end - if self.wrenchSucks then - turtle.turnRight() - turtle.forward() - turtle.turnLeft() - turtle.forward() - turtle.turnLeft() - end + if self.wrenchSucks then + turtle.turnRight() + turtle.forward() + turtle.turnLeft() + turtle.forward() + turtle.turnLeft() + end - --wrench piston to point downwards - local success = self:wrenchBlock('forward', 'down', self.pistonFacings) + --wrench piston to point downwards + local success = self:wrenchBlock('forward', 'down', self.pistonFacings) - rs.setOutput('front', true) - os.sleep(.25) - rs.setOutput('front', false) - os.sleep(.25) - turtle.select(ps.index) - turtle.dig() + rs.setOutput('front', true) + os.sleep(.25) + rs.setOutput('front', false) + os.sleep(.25) + turtle.select(ps.index) + turtle.dig() - if not success and not self.wrenchSucks then - self.wrenchSucks = true - success = self:placePiston(b) - end + if not success and not self.wrenchSucks then + self.wrenchSucks = true + success = self:placePiston(b) + end - return success + return success end function TurtleBuilder:go(x, z, y, heading) - if not turtle.go({ x = x, z = z, y = y, heading = heading }) then - print('stuck') - print('Press enter to continue') - os.sleep(1) - turtle.setStatus('stuck') - read() - end + if not turtle.go({ x = x, z = z, y = y, heading = heading }) then + print('stuck') + print('Press enter to continue') + os.sleep(1) + turtle.setStatus('stuck') + read() + end end -- goto used when turtle could be below travel plane -- if the distance is no more than 1 block, there's no need to pop back to the travel plane function TurtleBuilder:gotoEx(x, z, y, h, travelPlane) - local pt = turtle.getPoint() - local distance = math.abs(pt.x - x) + math.abs(pt.z - z) + local pt = turtle.getPoint() + local distance = math.abs(pt.x - x) + math.abs(pt.z - z) - -- following code could be better - if distance == 0 then - turtle.gotoY(y) - elseif distance == 1 then - if pt.y < y then - turtle.gotoY(y) - end - elseif distance > 1 then - self:gotoTravelPlane(travelPlane) - end - self:go(x, z, y, h) + -- following code could be better + if distance == 0 then + turtle.gotoY(y) + elseif distance == 1 then + if pt.y < y then + turtle.gotoY(y) + end + elseif distance > 1 then + self:gotoTravelPlane(travelPlane) + end + self:go(x, z, y, h) end function TurtleBuilder:placeDirectionalBlock(b, slot, travelPlane) - local d = b.direction + local d = b.direction - local function getAdjacentPoint(pt, direction) - local hi = turtle.getHeadingInfo(direction) - return { x = pt.x + hi.xd, z = pt.z + hi.zd, y = pt.y + hi.yd, heading = (hi.heading + 2) % 4 } - end + local function getAdjacentPoint(pt, direction) + local hi = turtle.getHeadingInfo(direction) + return { x = pt.x + hi.xd, z = pt.z + hi.zd, y = pt.y + hi.yd, heading = (hi.heading + 2) % 4 } + end - local directions = { - [ 'north' ] = 'north', - [ 'south' ] = 'south', - [ 'east' ] = 'east', - [ 'west' ] = 'west', - } - if directions[d] then - self:gotoEx(b.x, b.z, b.y, turtle.getHeadingInfo(directions[d]).heading, travelPlane) - b.placed = self:placeDown(slot) - end + local directions = { + [ 'north' ] = 'north', + [ 'south' ] = 'south', + [ 'east' ] = 'east', + [ 'west' ] = 'west', + } + if directions[d] then + self:gotoEx(b.x, b.z, b.y, turtle.getHeadingInfo(directions[d]).heading, travelPlane) + b.placed = self:placeDown(slot) + end - if d == 'top' then - self:gotoEx(b.x, b.z, b.y+1, nil, travelPlane) - if self:placeDown(slot) then - turtle.goback() - b.placed = self:placePiston(b) - end - end + if d == 'top' then + self:gotoEx(b.x, b.z, b.y+1, nil, travelPlane) + if self:placeDown(slot) then + turtle.goback() + b.placed = self:placePiston(b) + end + end - if d == 'bottom' then - local t = { - [1] = getAdjacentPoint(b, 'east'), - [2] = getAdjacentPoint(b, 'south'), - [3] = getAdjacentPoint(b, 'west'), - [4] = getAdjacentPoint(b, 'north'), - } + if d == 'bottom' then + local t = { + [1] = getAdjacentPoint(b, 'east'), + [2] = getAdjacentPoint(b, 'south'), + [3] = getAdjacentPoint(b, 'west'), + [4] = getAdjacentPoint(b, 'north'), + } - local c = Point.closest(turtle.getPoint(), t) - self:gotoEx(c.x, c.z, c.y, c.heading, travelPlane) + local c = Point.closest(turtle.getPoint(), t) + self:gotoEx(c.x, c.z, c.y, c.heading, travelPlane) - if self:place(slot) then - turtle.up() - b.placed = self:placePiston(b) - end - end + if self:place(slot) then + turtle.up() + b.placed = self:placePiston(b) + end + end - local stairDownDirections = { - [ 'north-down' ] = 'north', - [ 'south-down' ] = 'south', - [ 'east-down' ] = 'east', - [ 'west-down' ] = 'west' - } - if stairDownDirections[d] then - self:gotoEx(b.x, b.z, b.y+1, turtle.getHeadingInfo(stairDownDirections[d]).heading, travelPlane) - if self:placeDown(slot) then - turtle.goback() - b.placed = self:placePiston(b) - end - end + local stairDownDirections = { + [ 'north-down' ] = 'north', + [ 'south-down' ] = 'south', + [ 'east-down' ] = 'east', + [ 'west-down' ] = 'west' + } + if stairDownDirections[d] then + self:gotoEx(b.x, b.z, b.y+1, turtle.getHeadingInfo(stairDownDirections[d]).heading, travelPlane) + if self:placeDown(slot) then + turtle.goback() + b.placed = self:placePiston(b) + end + end - local stairUpDirections = { - [ 'north-up' ] = 'south', - [ 'south-up' ] = 'north', - [ 'east-up' ] = 'west', - [ 'west-up' ] = 'east' - } - if stairUpDirections[d] then - local isSouth = (self.facing + - turtle.getHeadingInfo(stairUpDirections[d]).heading) % 4 == 1 + local stairUpDirections = { + [ 'north-up' ] = 'south', + [ 'south-up' ] = 'north', + [ 'east-up' ] = 'west', + [ 'west-up' ] = 'east' + } + if stairUpDirections[d] then + local isSouth = (self.facing + + turtle.getHeadingInfo(stairUpDirections[d]).heading) % 4 == 1 - if not self.stairBug then - isSouth = false - end + if not self.stairBug then + isSouth = false + end - if isSouth then - -- for some reason, the south facing stair doesn't place correctly - -- jump through some hoops to place it - self:gotoEx(b.x, b.z, b.y, (turtle.getHeadingInfo(stairUpDirections[d]).heading + 2) % 4, travelPlane) - if self:placeUp(slot) then - turtle.goback() - turtle.gotoY(turtle.getPoint().y + 2) - b.placed = self:placePiston(b) - turtle.down() - b.placed = self:placePiston(b) + if isSouth then + -- for some reason, the south facing stair doesn't place correctly + -- jump through some hoops to place it + self:gotoEx(b.x, b.z, b.y, (turtle.getHeadingInfo(stairUpDirections[d]).heading + 2) % 4, travelPlane) + if self:placeUp(slot) then + turtle.goback() + turtle.gotoY(turtle.getPoint().y + 2) + b.placed = self:placePiston(b) + turtle.down() + b.placed = self:placePiston(b) - -- stop debug message below since we are pointing in wrong direction - b.heading = turtle.getPoint().heading - end - else - local hi = turtle.getHeadingInfo(stairUpDirections[d]) - self:gotoEx(b.x - hi.xd, b.z - hi.zd, b.y, hi.heading, travelPlane) - if self:place(slot) then - turtle.up() - b.placed = self:placePiston(b) - end - end - end + -- stop debug message below since we are pointing in wrong direction + b.heading = turtle.getPoint().heading + end + else + local hi = turtle.getHeadingInfo(stairUpDirections[d]) + self:gotoEx(b.x - hi.xd, b.z - hi.zd, b.y, hi.heading, travelPlane) + if self:place(slot) then + turtle.up() + b.placed = self:placePiston(b) + end + end + end - local horizontalDirections = { - [ 'east-west-block' ] = { 'east', 'west' }, - [ 'north-south-block' ] = { 'north', 'south' }, - } - if horizontalDirections[d] then + local horizontalDirections = { + [ 'east-west-block' ] = { 'east', 'west' }, + [ 'north-south-block' ] = { 'north', 'south' }, + } + if horizontalDirections[d] then - local t = { - [1] = getAdjacentPoint(b, horizontalDirections[d][1]), - [2] = getAdjacentPoint(b, horizontalDirections[d][2]), - } + local t = { + [1] = getAdjacentPoint(b, horizontalDirections[d][1]), + [2] = getAdjacentPoint(b, horizontalDirections[d][2]), + } - local c = Point.closest(turtle.getPoint(), t) - self:gotoEx(c.x, c.z, c.y, c.heading, travelPlane) + local c = Point.closest(turtle.getPoint(), t) + self:gotoEx(c.x, c.z, c.y, c.heading, travelPlane) - if self:place(slot) then - turtle.up() - b.placed = self:placePiston(b) - end - end + if self:place(slot) then + turtle.up() + b.placed = self:placePiston(b) + end + end - local pistonDirections = { - [ 'piston-north' ] = 'north', - [ 'piston-south' ] = 'south', - [ 'piston-west' ] = 'west', - [ 'piston-east' ] = 'east', - [ 'piston-down' ] = 'down', - [ 'piston-up' ] = 'up', - } + local pistonDirections = { + [ 'piston-north' ] = 'north', + [ 'piston-south' ] = 'south', + [ 'piston-west' ] = 'west', + [ 'piston-east' ] = 'east', + [ 'piston-down' ] = 'down', + [ 'piston-up' ] = 'up', + } - if pistonDirections[d] then - -- why are pistons so broke in cc 1.7 ?????????????????????? + if pistonDirections[d] then + -- why are pistons so broke in cc 1.7 ?????????????????????? - local ws = self:getWrenchSlot() + local ws = self:getWrenchSlot() - if not ws then - b.needResupply = true - -- a hopper may have eaten the piston - return false - end + if not ws then + b.needResupply = true + -- a hopper may have eaten the piston + return false + end - -- piston turns relative to turtle position :) - local rotatedPistonDirections = { - [ 'piston-east' ] = 0, - [ 'piston-south' ] = 1, - [ 'piston-west' ] = 2, - [ 'piston-north' ] = 3, - } + -- piston turns relative to turtle position :) + local rotatedPistonDirections = { + [ 'piston-east' ] = 0, + [ 'piston-south' ] = 1, + [ 'piston-west' ] = 2, + [ 'piston-north' ] = 3, + } - self:gotoEx(b.x, b.z, b.y, nil, travelPlane) + self:gotoEx(b.x, b.z, b.y, nil, travelPlane) - local heading = rotatedPistonDirections[d] - if heading and turtle.getPoint().heading % 2 ~= heading % 2 then - turtle.setHeading(heading) - end + local heading = rotatedPistonDirections[d] + if heading and turtle.getPoint().heading % 2 ~= heading % 2 then + turtle.setHeading(heading) + end - if self:placeDown(slot) then - b.placed = self:wrenchBlock('down', pistonDirections[d], self.pistonFacings) - end - end + if self:placeDown(slot) then + b.placed = self:wrenchBlock('down', pistonDirections[d], self.pistonFacings) + end + end - local wrenchDirections = { - [ 'wrench-down' ] = 'down', - [ 'wrench-up' ] = 'up', - } + local wrenchDirections = { + [ 'wrench-down' ] = 'down', + [ 'wrench-up' ] = 'up', + } - if wrenchDirections[d] then + if wrenchDirections[d] then - local ws = self:getWrenchSlot() + local ws = self:getWrenchSlot() - if not ws then - b.needResupply = true - -- a hopper may have eaten the piston - return false - end + if not ws then + b.needResupply = true + -- a hopper may have eaten the piston + return false + end - self:gotoEx(b.x, b.z, b.y, nil, travelPlane) + self:gotoEx(b.x, b.z, b.y, nil, travelPlane) - if self:placeDown(slot) then - b.placed = self:wrenchBlock('down', wrenchDirections[d]) - end - end + if self:placeDown(slot) then + b.placed = self:wrenchBlock('down', wrenchDirections[d]) + end + end - local doorDirections = { - [ 'east-door' ] = 'east', - [ 'south-door' ] = 'south', - [ 'west-door' ] = 'west', - [ 'north-door' ] = 'north', - } - if doorDirections[d] then - local hi = turtle.getHeadingInfo(doorDirections[d]) - self:gotoEx(b.x - hi.xd, b.z - hi.zd, b.y - 1, hi.heading, travelPlane) - b.placed = self:place(slot) - end + local doorDirections = { + [ 'east-door' ] = 'east', + [ 'south-door' ] = 'south', + [ 'west-door' ] = 'west', + [ 'north-door' ] = 'north', + } + if doorDirections[d] then + local hi = turtle.getHeadingInfo(doorDirections[d]) + self:gotoEx(b.x - hi.xd, b.z - hi.zd, b.y - 1, hi.heading, travelPlane) + b.placed = self:place(slot) + end - local blockDirections = { - [ 'north-block' ] = 'north', - [ 'south-block' ] = 'south', - [ 'east-block' ] = 'east', - [ 'west-block' ] = 'west', - } - if blockDirections[d] then - local hi = turtle.getHeadingInfo(blockDirections[d]) - self:gotoEx(b.x - hi.xd, b.z - hi.zd, b.y-1, hi.heading, travelPlane) - b.placed = self:place(slot) - end + local blockDirections = { + [ 'north-block' ] = 'north', + [ 'south-block' ] = 'south', + [ 'east-block' ] = 'east', + [ 'west-block' ] = 'west', + } + if blockDirections[d] then + local hi = turtle.getHeadingInfo(blockDirections[d]) + self:gotoEx(b.x - hi.xd, b.z - hi.zd, b.y-1, hi.heading, travelPlane) + b.placed = self:place(slot) + end - if b.facing then - self:rotateBlock('down', b.facing) - end + if b.facing then + self:rotateBlock('down', b.facing) + end -- debug if d ~= 'top' and d ~= 'bottom' and not horizontalDirections[d] and not pistonDirections[d] then - if not b.heading or turtle.getHeading() ~= b.heading then - self:log(d .. ' - ' .. turtle.getHeading() .. ' - ' .. (b.heading or 'nil')) - --read() - end + if not b.heading or turtle.getHeading() ~= b.heading then + self:log(d .. ' - ' .. turtle.getHeading() .. ' - ' .. (b.heading or 'nil')) + --read() + end end - return b.placed + return b.placed end -- find the highest y in the last 2 planes function TurtleBuilder:findTravelPlane(index) - local travelPlane + local travelPlane - for i = index, 1, -1 do - local b = self.schematic.blocks[i] + for i = index, 1, -1 do + local b = self.schematic.blocks[i] - local y = b.y - if b.twoHigh then - y = y + 1 - end - if not travelPlane or y > travelPlane then - travelPlane = y - elseif travelPlane and travelPlane - y > 2 then - break - end - end + local y = b.y + if b.twoHigh then + y = y + 1 + end + if not travelPlane or y > travelPlane then + travelPlane = y + elseif travelPlane and travelPlane - y > 2 then + break + end + end - return travelPlane or 0 + return travelPlane or 0 end function TurtleBuilder:gotoTravelPlane(travelPlane) - if travelPlane > turtle.getPoint().y then - turtle.gotoY(travelPlane) - end + if travelPlane > turtle.getPoint().y then + turtle.gotoY(travelPlane) + end end function TurtleBuilder:build() - local direction = 1 - local last = #self.schematic.blocks - local minFuel = self.schematic.height + self.schematic.width + self.schematic.length + 100 - local throttle = Util.throttle() + local direction = 1 + local last = #self.schematic.blocks + local minFuel = self.schematic.height + self.schematic.width + self.schematic.length + 100 + local throttle = Util.throttle() - if self.mode == 'destroy' then - direction = -1 - last = 1 - turtle.setStatus('destroying') - else - turtle.setStatus('building') - end + if self.mode == 'destroy' then + direction = -1 + last = 1 + turtle.setStatus('destroying') + else + turtle.setStatus('building') + end - local travelPlane = self:findTravelPlane(self.index) + local travelPlane = self:findTravelPlane(self.index) - local pt = self:getBuildingCorner(travelPlane) - turtle.pathfind({ x = pt.x, z = pt.z, y = travelPlane }) - turtle.set({ - digPolicy = 'dig', - attackPolicy = 'attack', - }) + local pt = self:getBuildingCorner(travelPlane) + turtle.pathfind({ x = pt.x, z = pt.z, y = travelPlane }) + turtle.set({ + digPolicy = 'dig', + attackPolicy = 'attack', + }) - for i = self.index, last, direction do - self.index = i + for i = self.index, last, direction do + self.index = i - local b = self.schematic:getComputedBlock(i) + local b = self.schematic:getComputedBlock(i) - if b.id ~= 'minecraft:air' then + if b.id ~= 'minecraft:air' then - if self.mode == 'destroy' then + if self.mode == 'destroy' then - b.heading = nil -- don't make the supplier follow the block heading - self:logBlock(self.index, b) - if b.y ~= turtle.getPoint().y then - turtle.gotoY(b.y) - end - self:go(b.x, b.z, b.y) - turtle.digDown() + b.heading = nil -- don't make the supplier follow the block heading + self:logBlock(self.index, b) + if b.y ~= turtle.getPoint().y then + turtle.gotoY(b.y) + end + self:go(b.x, b.z, b.y) + turtle.digDown() - -- if no supplier, then should fill all slots + -- if no supplier, then should fill all slots - if turtle.getItemCount(RESOURCE_SLOTS) > 0 or turtle.getFuelLevel() < minFuel then - if turtle.getFuelLevel() < minFuel or not self:inAirDropoff() then - self:gotoSupplyPoint() - self:dumpInventoryWithCheck() - self:refuel() - end - turtle.setStatus('destroying') - end + if turtle.getItemCount(RESOURCE_SLOTS) > 0 or turtle.getFuelLevel() < minFuel then + if turtle.getFuelLevel() < minFuel or not self:inAirDropoff() then + self:gotoSupplyPoint() + self:dumpInventoryWithCheck() + self:refuel() + end + turtle.setStatus('destroying') + end - else -- Build mode + else -- Build mode - local slot = self:selectItem(b.id, b.dmg) - if not slot or turtle.getFuelLevel() < minFuel then + local slot = self:selectItem(b.id, b.dmg) + if not slot or turtle.getFuelLevel() < minFuel then - self:gotoTravelPlane(travelPlane) - self:resupply() - return - end - local y = b.y - if b.twoHigh then - y = b.y + 1 - end - if y > travelPlane then - travelPlane = y - end + self:gotoTravelPlane(travelPlane) + self:resupply() + return + end + local y = b.y + if b.twoHigh then + y = b.y + 1 + end + if y > travelPlane then + travelPlane = y + end - self:logBlock(self.index, b) + self:logBlock(self.index, b) - if b.direction then - b.needResupply = false - self:placeDirectionalBlock(b, slot, travelPlane) - if b.needResupply then -- lost our piston in a hopper probably - self:gotoTravelPlane(travelPlane) - self:resupply() - return - end - else - self:gotoTravelPlane(travelPlane) - self:go(b.x, b.z, b.y) - b.placed = self:placeDown(slot) - end + if b.direction then + b.needResupply = false + self:placeDirectionalBlock(b, slot, travelPlane) + if b.needResupply then -- lost our piston in a hopper probably + self:gotoTravelPlane(travelPlane) + self:resupply() + return + end + else + self:gotoTravelPlane(travelPlane) + self:go(b.x, b.z, b.y) + b.placed = self:placeDown(slot) + end - if b.placed then - slot.qty = slot.qty - 1 - else - print('failed to place block') - end - end - if self.mode == 'destroy' then - self:saveProgress(math.max(self.index, 1)) - else - self:saveProgress(self.index + 1) - end - else - throttle() -- sleep in case there are a large # of skipped blocks - end + if b.placed then + slot.qty = slot.qty - 1 + else + print('failed to place block') + end + end + if self.mode == 'destroy' then + self:saveProgress(math.max(self.index, 1)) + else + self:saveProgress(self.index + 1) + end + else + throttle() -- sleep in case there are a large # of skipped blocks + end - if turtle.isAborted() then - turtle.setStatus('aborting') - turtle.abort(false) - self:gotoTravelPlane(travelPlane) - self:gotoSupplyPoint() - turtle.setHeading(0) - self:dumpInventory() - Event.exitPullEvents() - print('Aborted') - return - end - end + if turtle.isAborted() then + turtle.setStatus('aborting') + turtle.abort(false) + self:gotoTravelPlane(travelPlane) + self:gotoSupplyPoint() + turtle.setHeading(0) + self:dumpInventory() + Event.exitPullEvents() + print('Aborted') + return + end + end - if device.wireless_modem then - Message.broadcast('finished') - end + if device.wireless_modem then + Message.broadcast('finished') + end - self:gotoTravelPlane(travelPlane) - self:gotoSupplyPoint() - turtle.setHeading(0) - self:dumpInventory() + self:gotoTravelPlane(travelPlane) + self:gotoSupplyPoint() + turtle.setHeading(0) + self:dumpInventory() - for _ = 1, 4 do - turtle.turnRight() - end + for _ = 1, 4 do + turtle.turnRight() + end - fs.delete(self.schematic.filename .. '.progress') - print('Finished') - Event.exitPullEvents() + fs.delete(self.schematic.filename .. '.progress') + print('Finished') + Event.exitPullEvents() end function TurtleBuilder:begin() - turtle.reset() - self:dumpInventory() - self:refuel() - self:getTurtleFacing() + turtle.reset() + self:dumpInventory() + self:refuel() + self:getTurtleFacing() - if self.loc.x then - self.supplyPoint = { - x = self.loc.x - self.loc.rx - 1, - y = self.loc.y - self.loc.ry, - z = self.loc.z - self.loc.rz - 1, - } - Point.rotate(self.supplyPoint, self.facing) - else - self.supplyPoint = { x = -1, y = 0, z = -1 } - end - turtle.setPoint(self.supplyPoint) + if self.loc.x then + self.supplyPoint = { + x = self.loc.x - self.loc.rx - 1, + y = self.loc.y - self.loc.ry, + z = self.loc.z - self.loc.rz - 1, + } + Point.rotate(self.supplyPoint, self.facing) + else + self.supplyPoint = { x = -1, y = 0, z = -1 } + end + turtle.setPoint(self.supplyPoint) - -- reset piston cache in case wrench was substituted - self.pistonFacings = { - down = { }, - forward = { }, - } + -- reset piston cache in case wrench was substituted + self.pistonFacings = { + down = { }, + forward = { }, + } - if self.mode == 'destroy' then - if device.wireless_modem then - Message.broadcast('supplyList', { uid = 1, slots = self:getAirResupplyList() }) - end - print('Beginning destruction') - self:build() - else - print('Starting build') - self:resupply() - end + if self.mode == 'destroy' then + if device.wireless_modem then + Message.broadcast('supplyList', { uid = 1, slots = self:getAirResupplyList() }) + end + print('Beginning destruction') + self:build() + else + print('Starting build') + self:resupply() + end end return TurtleBuilder diff --git a/builder/builder.lua b/builder/builder.lua index 8edf703..3bc4338 100644 --- a/builder/builder.lua +++ b/builder/builder.lua @@ -1,5 +1,5 @@ if not _G.turtle and not _G.commands then - error('Must be run on a turtle or a command computer') + error('Must be run on a turtle or a command computer') end local Adapter = require('core.inventoryAdapter') @@ -20,467 +20,467 @@ local substitutionPage local Builder if _G.commands then - Builder = require('builder.commands') + Builder = require('builder.commands') else - Builder = require('builder.turtle') + Builder = require('builder.turtle') end Builder = Builder() Builder.schematic = Schematic() local function convertSingleBack(item) - if item then - item.id = item.name - item.dmg = item.damage - item.qty = item.count - item.max_size = item.maxCount - item.display_name = item.displayName - end - return item + if item then + item.id = item.name + item.dmg = item.damage + item.qty = item.count + item.max_size = item.maxCount + item.display_name = item.displayName + end + return item end local function convertBack(t) - for _,v in pairs(t) do - convertSingleBack(v) - end - return t + for _,v in pairs(t) do + convertSingleBack(v) + end + return t end --[[-- SubDB --]]-- local subDB = TableDB({ - fileName = fs.combine(BUILDER_DIR, 'sub.db'), + fileName = fs.combine(BUILDER_DIR, 'sub.db'), }) function subDB:load() - if fs.exists(self.fileName) then - TableDB.load(self) - elseif not Builder.isCommandComputer then - self:seedDB() - end + if fs.exists(self.fileName) then + TableDB.load(self) + elseif not Builder.isCommandComputer then + self:seedDB() + end end function subDB:seedDB() - self.data = { - [ "minecraft:redstone_wire:0" ] = "minecraft:redstone:0", - [ "minecraft:wall_sign:0" ] = "minecraft:sign:0", - [ "minecraft:standing_sign:0" ] = "minecraft:sign:0", - [ "minecraft:potatoes:0" ] = "minecraft:potato:0", - [ "minecraft:unlit_redstone_torch:0" ] = "minecraft:redstone_torch:0", - [ "minecraft:powered_repeater:0" ] = "minecraft:repeater:0", - [ "minecraft:unpowered_repeater:0" ] = "minecraft:repeater:0", - [ "minecraft:carrots:0" ] = "minecraft:carrot:0", - [ "minecraft:cocoa:0" ] = "minecraft:dye:3", - [ "minecraft:unpowered_comparator:0" ] = "minecraft:comparator:0", - [ "minecraft:powered_comparator:0" ] = "minecraft:comparator:0", - [ "minecraft:piston_head:0" ] = "minecraft:air:0", - [ "minecraft:piston_extension:0" ] = "minecraft:air:0", - [ "minecraft:portal:0" ] = "minecraft:air:0", - [ "minecraft:double_wooden_slab:0" ] = "minecraft:planks:0", - [ "minecraft:double_wooden_slab:1" ] = "minecraft:planks:1", - [ "minecraft:double_wooden_slab:2" ] = "minecraft:planks:2", - [ "minecraft:double_wooden_slab:3" ] = "minecraft:planks:3", - [ "minecraft:double_wooden_slab:4" ] = "minecraft:planks:4", - [ "minecraft:double_wooden_slab:5" ] = "minecraft:planks:5", - [ "minecraft:lit_redstone_lamp:0" ] = "minecraft:redstone_lamp:0", - [ "minecraft:double_stone_slab:1" ] = "minecraft:sandstone:0", - [ "minecraft:double_stone_slab:2" ] = "minecraft:planks:0", - [ "minecraft:double_stone_slab:3" ] = "minecraft:cobblestone:0", - [ "minecraft:double_stone_slab:4" ] = "minecraft:brick_block:0", - [ "minecraft:double_stone_slab:5" ] = "minecraft:stonebrick:0", - [ "minecraft:double_stone_slab:6" ] = "minecraft:nether_brick:0", - [ "minecraft:double_stone_slab:7" ] = "minecraft:quartz_block:0", - [ "minecraft:double_stone_slab:9" ] = "minecraft:sandstone:2", - [ "minecraft:double_stone_slab2:0" ] = "minecraft:sandstone:0", - [ "minecraft:stone_slab:2" ] = "minecraft:wooden_slab:0", - [ "minecraft:wheat:0" ] = "minecraft:wheat_seeds:0", - [ "minecraft:flowing_water:0" ] = "minecraft:air:0", - [ "minecraft:lit_furnace:0" ] = "minecraft:furnace:0", - [ "minecraft:wall_banner:0" ] = "minecraft:banner:0", - [ "minecraft:standing_banner:0" ] = "minecraft:banner:0", - [ "minecraft:tripwire:0" ] = "minecraft:string:0", - [ "minecraft:pumpkin_stem:0" ] = "minecraft:pumpkin_seeds:0", - } - self.dirty = true - self:flush() + self.data = { + [ "minecraft:redstone_wire:0" ] = "minecraft:redstone:0", + [ "minecraft:wall_sign:0" ] = "minecraft:sign:0", + [ "minecraft:standing_sign:0" ] = "minecraft:sign:0", + [ "minecraft:potatoes:0" ] = "minecraft:potato:0", + [ "minecraft:unlit_redstone_torch:0" ] = "minecraft:redstone_torch:0", + [ "minecraft:powered_repeater:0" ] = "minecraft:repeater:0", + [ "minecraft:unpowered_repeater:0" ] = "minecraft:repeater:0", + [ "minecraft:carrots:0" ] = "minecraft:carrot:0", + [ "minecraft:cocoa:0" ] = "minecraft:dye:3", + [ "minecraft:unpowered_comparator:0" ] = "minecraft:comparator:0", + [ "minecraft:powered_comparator:0" ] = "minecraft:comparator:0", + [ "minecraft:piston_head:0" ] = "minecraft:air:0", + [ "minecraft:piston_extension:0" ] = "minecraft:air:0", + [ "minecraft:portal:0" ] = "minecraft:air:0", + [ "minecraft:double_wooden_slab:0" ] = "minecraft:planks:0", + [ "minecraft:double_wooden_slab:1" ] = "minecraft:planks:1", + [ "minecraft:double_wooden_slab:2" ] = "minecraft:planks:2", + [ "minecraft:double_wooden_slab:3" ] = "minecraft:planks:3", + [ "minecraft:double_wooden_slab:4" ] = "minecraft:planks:4", + [ "minecraft:double_wooden_slab:5" ] = "minecraft:planks:5", + [ "minecraft:lit_redstone_lamp:0" ] = "minecraft:redstone_lamp:0", + [ "minecraft:double_stone_slab:1" ] = "minecraft:sandstone:0", + [ "minecraft:double_stone_slab:2" ] = "minecraft:planks:0", + [ "minecraft:double_stone_slab:3" ] = "minecraft:cobblestone:0", + [ "minecraft:double_stone_slab:4" ] = "minecraft:brick_block:0", + [ "minecraft:double_stone_slab:5" ] = "minecraft:stonebrick:0", + [ "minecraft:double_stone_slab:6" ] = "minecraft:nether_brick:0", + [ "minecraft:double_stone_slab:7" ] = "minecraft:quartz_block:0", + [ "minecraft:double_stone_slab:9" ] = "minecraft:sandstone:2", + [ "minecraft:double_stone_slab2:0" ] = "minecraft:sandstone:0", + [ "minecraft:stone_slab:2" ] = "minecraft:wooden_slab:0", + [ "minecraft:wheat:0" ] = "minecraft:wheat_seeds:0", + [ "minecraft:flowing_water:0" ] = "minecraft:air:0", + [ "minecraft:lit_furnace:0" ] = "minecraft:furnace:0", + [ "minecraft:wall_banner:0" ] = "minecraft:banner:0", + [ "minecraft:standing_banner:0" ] = "minecraft:banner:0", + [ "minecraft:tripwire:0" ] = "minecraft:string:0", + [ "minecraft:pumpkin_stem:0" ] = "minecraft:pumpkin_seeds:0", + } + self.dirty = true + self:flush() end function subDB:add(s) - TableDB.add(self, { s.id, s.dmg }, table.concat({ s.sid, s.sdmg }, ':')) - self:flush() + TableDB.add(self, { s.id, s.dmg }, table.concat({ s.sid, s.sdmg }, ':')) + self:flush() end function subDB:remove(s) - -- TODO: tableDB.remove should take table key - TableDB.remove(self, s.id .. ':' .. s.dmg) - self:flush() + -- TODO: tableDB.remove should take table key + TableDB.remove(self, s.id .. ':' .. s.dmg) + self:flush() end function subDB:extract(s) - local id, dmg = s:match('(.+):(%d+)') - return id, tonumber(dmg) + local id, dmg = s:match('(.+):(%d+)') + return id, tonumber(dmg) end function subDB:getSubstitutedItem(id, dmg) - local sub = TableDB.get(self, { id, dmg }) - if sub then - id, dmg = self:extract(sub) - end - return { id = id, dmg = dmg } + local sub = TableDB.get(self, { id, dmg }) + if sub then + id, dmg = self:extract(sub) + end + return { id = id, dmg = dmg } end function subDB:lookupBlocksForSub(sid, sdmg) - local t = { } - for k,v in pairs(self.data) do - local id, dmg = self:extract(v) - if id == sid and dmg == sdmg then - id, dmg = self:extract(k) - t[k] = { id = id, dmg = dmg, sid = sid, sdmg = sdmg } - end - end - return t + local t = { } + for k,v in pairs(self.data) do + local id, dmg = self:extract(v) + if id == sid and dmg == sdmg then + id, dmg = self:extract(k) + t[k] = { id = id, dmg = dmg, sid = sid, sdmg = sdmg } + end + end + return t end --[[-- blankPage --]]-- local blankPage = UI.Page() function blankPage:draw() - self:clear(colors.black) - self:setCursorPos(1, 1) + self:clear(colors.black) + self:setCursorPos(1, 1) end function blankPage:enable() - self:sync() - UI.Page.enable(self) + self:sync() + UI.Page.enable(self) end --[[-- selectSubstitutionPage --]]-- local selectSubstitutionPage = UI.Page({ - titleBar = UI.TitleBar({ - title = 'Select a substitution', - previousPage = 'listing' - }), - grid = UI.ScrollingGrid({ - columns = { - { heading = 'id', key = 'id' }, - { heading = 'dmg', key = 'dmg' }, - }, - sortColumn = 'id', - height = UI.term.height-1, - autospace = true, - y = 2, - }), + titleBar = UI.TitleBar({ + title = 'Select a substitution', + previousPage = 'listing' + }), + grid = UI.ScrollingGrid({ + columns = { + { heading = 'id', key = 'id' }, + { heading = 'dmg', key = 'dmg' }, + }, + sortColumn = 'id', + height = UI.term.height-1, + autospace = true, + y = 2, + }), }) function selectSubstitutionPage:enable() - self.grid:adjustWidth() - self.grid:setIndex(1) - UI.Page.enable(self) + self.grid:adjustWidth() + self.grid:setIndex(1) + UI.Page.enable(self) end function selectSubstitutionPage:eventHandler(event) - if event.type == 'grid_select' then - substitutionPage.sub = event.selected - UI:setPage(substitutionPage) - elseif event.type == 'key' and event.key == 'q' then - UI:setPreviousPage() - else - return UI.Page.eventHandler(self, event) - end - return true + if event.type == 'grid_select' then + substitutionPage.sub = event.selected + UI:setPage(substitutionPage) + elseif event.type == 'key' and event.key == 'q' then + UI:setPreviousPage() + else + return UI.Page.eventHandler(self, event) + end + return true end --[[-- substitutionPage --]]-- substitutionPage = UI.Page { - titleBar = UI.TitleBar { - previousPage = true, - title = 'Substitute a block' - }, - menuBar = UI.MenuBar { - y = 2, - buttons = { - { text = 'Accept', event = 'accept', help = 'Accept' }, - { text = 'Revert', event = 'revert', help = 'Restore to original' }, - { text = 'Air', event = 'air', help = 'Air' }, - }, - }, - info = UI.Window { y = 4, width = UI.term.width, height = 3 }, - grid = UI.ScrollingGrid { - columns = { - { heading = 'Name', key = 'display_name', width = UI.term.width-9 }, - { heading = 'Qty', key = 'fQty', width = 5 }, - }, - sortColumn = 'display_name', - height = UI.term.height-7, - y = 7, - }, - throttle = UI.Throttle { }, - statusBar = UI.StatusBar { } + titleBar = UI.TitleBar { + previousPage = true, + title = 'Substitute a block' + }, + menuBar = UI.MenuBar { + y = 2, + buttons = { + { text = 'Accept', event = 'accept', help = 'Accept' }, + { text = 'Revert', event = 'revert', help = 'Restore to original' }, + { text = 'Air', event = 'air', help = 'Air' }, + }, + }, + info = UI.Window { y = 4, width = UI.term.width, height = 3 }, + grid = UI.ScrollingGrid { + columns = { + { heading = 'Name', key = 'display_name', width = UI.term.width-9 }, + { heading = 'Qty', key = 'fQty', width = 5 }, + }, + sortColumn = 'display_name', + height = UI.term.height-7, + y = 7, + }, + throttle = UI.Throttle { }, + statusBar = UI.StatusBar { } } substitutionPage.menuBar:add({ - filterLabel = UI.Text({ - value = 'Search', - x = UI.term.width-14, - }), - filter = UI.TextEntry({ - x = UI.term.width-7, - width = 7, - }) + filterLabel = UI.Text({ + value = 'Search', + x = UI.term.width-14, + }), + filter = UI.TextEntry({ + x = UI.term.width-7, + width = 7, + }) }) function substitutionPage.info:draw() - local sub = self.parent.sub - local inName = itemDB:getName({ name = sub.id, damage = sub.dmg }) - local outName = '' - if sub.sid then - outName = itemDB:getName({ name = sub.sid, damage = sub.sdmg }) - end + local sub = self.parent.sub + local inName = itemDB:getName({ name = sub.id, damage = sub.dmg }) + local outName = '' + if sub.sid then + outName = itemDB:getName({ name = sub.sid, damage = sub.sdmg }) + end - self:clear() - self:setCursorPos(1, 1) - self:print(' Replace ' .. inName .. '\n') - --self:print(' ' .. sub.id .. ':' .. sub.dmg .. '\n', nil, colors.yellow) - self:print(' With ' .. outName) + self:clear() + self:setCursorPos(1, 1) + self:print(' Replace ' .. inName .. '\n') + --self:print(' ' .. sub.id .. ':' .. sub.dmg .. '\n', nil, colors.yellow) + self:print(' With ' .. outName) end function substitutionPage:enable() - self.allItems = convertBack(Builder.itemAdapter:refresh()) - self.grid.values = self.allItems - for _,item in pairs(self.grid.values) do - item.key = item.id .. ':' .. item.dmg - item.lname = string.lower(item.display_name) - item.fQty = Util.toBytes(item.qty) - end - self.grid:update() + self.allItems = convertBack(Builder.itemAdapter:refresh()) + self.grid.values = self.allItems + for _,item in pairs(self.grid.values) do + item.key = item.id .. ':' .. item.dmg + item.lname = string.lower(item.display_name) + item.fQty = Util.toBytes(item.qty) + end + self.grid:update() - self.menuBar.filter:reset() - self:setFocus(self.menuBar.filter) - UI.Page.enable(self) + self.menuBar.filter:reset() + self:setFocus(self.menuBar.filter) + UI.Page.enable(self) end function substitutionPage:applySubstitute(id, dmg) - self.sub.sid = id - self.sub.sdmg = dmg + self.sub.sid = id + self.sub.sdmg = dmg end function substitutionPage:eventHandler(event) - if event.type == 'grid_focus_row' then - local s = string.format('%s:%d', - event.selected.id, - event.selected.dmg) + if event.type == 'grid_focus_row' then + local s = string.format('%s:%d', + event.selected.id, + event.selected.dmg) - self.statusBar:setStatus(s) - self.statusBar:draw() + self.statusBar:setStatus(s) + self.statusBar:draw() - elseif event.type == 'grid_select' then - self:applySubstitute(event.selected.id, event.selected.dmg) - self.info:draw() + elseif event.type == 'grid_select' then + self:applySubstitute(event.selected.id, event.selected.dmg) + self.info:draw() - elseif event.type == 'text_change' then - local text = event.text - if #text == 0 then - self.grid.values = self.allItems - else - self.grid.values = { } - for _,item in pairs(self.allItems) do - if string.find(item.lname, text) then - table.insert(self.grid.values, item) - end - end - end - self.grid:update() - self.grid:setIndex(1) - self.grid:draw() + elseif event.type == 'text_change' then + local text = event.text + if #text == 0 then + self.grid.values = self.allItems + else + self.grid.values = { } + for _,item in pairs(self.allItems) do + if string.find(item.lname, text) then + table.insert(self.grid.values, item) + end + end + end + self.grid:update() + self.grid:setIndex(1) + self.grid:draw() - elseif event.type == 'accept' or event.type == 'air' or event.type == 'revert' then - self.statusBar:setStatus('Saving changes...') - self.statusBar:draw() - self:sync() + elseif event.type == 'accept' or event.type == 'air' or event.type == 'revert' then + self.statusBar:setStatus('Saving changes...') + self.statusBar:draw() + self:sync() - if event.type == 'air' then - self:applySubstitute('minecraft:air', 0) - end + if event.type == 'air' then + self:applySubstitute('minecraft:air', 0) + end - if event.type == 'revert' then - subDB:remove(self.sub) - elseif not self.sub.sid then - self.statusBar:setStatus('Select a substition') - self.statusBar:draw() - return UI.Page.eventHandler(self, event) - else - subDB:add(self.sub) - end + if event.type == 'revert' then + subDB:remove(self.sub) + elseif not self.sub.sid then + self.statusBar:setStatus('Select a substition') + self.statusBar:draw() + return UI.Page.eventHandler(self, event) + else + subDB:add(self.sub) + end - self.throttle:enable() - Builder:reloadSchematic(function() self.throttle:update() end) - self.throttle:disable() - UI:setPage('listing') + self.throttle:enable() + Builder:reloadSchematic(function() self.throttle:update() end) + self.throttle:disable() + UI:setPage('listing') - elseif event.type == 'cancel' then - UI:setPreviousPage() - end + elseif event.type == 'cancel' then + UI:setPreviousPage() + end - return UI.Page.eventHandler(self, event) + return UI.Page.eventHandler(self, event) end --[[-- ListingPage --]]-- local listingPage = UI.Page({ - titleBar = UI.TitleBar({ - title = 'Supply List', - previousPage = 'start' - }), - menuBar = UI.MenuBar({ - y = 2, - buttons = { - { text = 'Craft', event = 'craft', help = 'Request crafting' }, - { text = 'Refresh', event = 'refresh', help = 'Refresh inventory' }, - { text = 'Toggle', event = 'toggle', help = 'Toggles needed blocks' }, - { text = 'Substitute', event = 'edit', help = 'Substitute a block' }, - } - }), - grid = UI.ScrollingGrid({ - columns = { - { heading = 'Name', key = 'display_name', width = UI.term.width - 14 }, - { heading = 'Need', key = 'need', width = 5 }, - { heading = 'Have', key = 'qty', width = 5 }, - }, - sortColumn = 'display_name', - y = 3, - height = UI.term.height-3, - help = 'Set a block type or pick a substitute block' - }), - accelerators = { - q = 'menu', - c = 'craft', - r = 'refresh', - t = 'toggle', - }, - statusBar = UI.StatusBar(), - fullList = true + titleBar = UI.TitleBar({ + title = 'Supply List', + previousPage = 'start' + }), + menuBar = UI.MenuBar({ + y = 2, + buttons = { + { text = 'Craft', event = 'craft', help = 'Request crafting' }, + { text = 'Refresh', event = 'refresh', help = 'Refresh inventory' }, + { text = 'Toggle', event = 'toggle', help = 'Toggles needed blocks' }, + { text = 'Substitute', event = 'edit', help = 'Substitute a block' }, + } + }), + grid = UI.ScrollingGrid({ + columns = { + { heading = 'Name', key = 'display_name', width = UI.term.width - 14 }, + { heading = 'Need', key = 'need', width = 5 }, + { heading = 'Have', key = 'qty', width = 5 }, + }, + sortColumn = 'display_name', + y = 3, + height = UI.term.height-3, + help = 'Set a block type or pick a substitute block' + }), + accelerators = { + q = 'menu', + c = 'craft', + r = 'refresh', + t = 'toggle', + }, + statusBar = UI.StatusBar(), + fullList = true }) function listingPage:enable(throttle) - listingPage:refresh(throttle) - UI.Page.enable(self) + listingPage:refresh(throttle) + UI.Page.enable(self) end function listingPage:eventHandler(event) - if event.type == 'craft' then - local s = self.grid:getSelected() - local item = convertSingleBack(Builder.itemAdapter:getItemInfo({ - name = s.id, - damage = s.dmg, - nbtHash = s.nbt_hash, - })) - if item and item.is_craftable then - local qty = math.max(0, s.need - item.qty) + if event.type == 'craft' then + local s = self.grid:getSelected() + local item = convertSingleBack(Builder.itemAdapter:getItemInfo({ + name = s.id, + damage = s.dmg, + nbtHash = s.nbt_hash, + })) + if item and item.is_craftable then + local qty = math.max(0, s.need - item.qty) - if item and Builder.itemAdapter.craftItems then - Builder.itemAdapter:craftItems({{ name = s.id, damage = s.dmg, nbtHash = s.nbt_hash, count = qty }}) - local name = s.display_name or s.id - self.statusBar:timedStatus('Requested ' .. qty .. ' ' .. name, 3) - end - else - self.statusBar:timedStatus('Unable to craft') - end + if item and Builder.itemAdapter.craftItems then + Builder.itemAdapter:craftItems({{ name = s.id, damage = s.dmg, nbtHash = s.nbt_hash, count = qty }}) + local name = s.display_name or s.id + self.statusBar:timedStatus('Requested ' .. qty .. ' ' .. name, 3) + end + else + self.statusBar:timedStatus('Unable to craft') + end - elseif event.type == 'grid_focus_row' then - self.statusBar:setStatus(event.selected.id .. ':' .. event.selected.dmg) - self.statusBar:draw() + elseif event.type == 'grid_focus_row' then + self.statusBar:setStatus(event.selected.id .. ':' .. event.selected.dmg) + self.statusBar:draw() - elseif event.type == 'refresh' then - self:refresh() - self:draw() - self.statusBar:timedStatus('Refreshed ', 3) + elseif event.type == 'refresh' then + self:refresh() + self:draw() + self.statusBar:timedStatus('Refreshed ', 3) - elseif event.type == 'toggle' then - self.fullList = not self.fullList - self:refresh() - self:draw() + elseif event.type == 'toggle' then + self.fullList = not self.fullList + self:refresh() + self:draw() - elseif event.type == 'menu' then - UI:setPage('start') + elseif event.type == 'menu' then + UI:setPage('start') - elseif event.type == 'edit' or event.type == 'grid_select' then - self:manageBlock(self.grid:getSelected()) + elseif event.type == 'edit' or event.type == 'grid_select' then + self:manageBlock(self.grid:getSelected()) - elseif event.type == 'focus_change' then - if event.focused.help then - self.statusBar:timedStatus(event.focused.help, 3) - end - end + elseif event.type == 'focus_change' then + if event.focused.help then + self.statusBar:timedStatus(event.focused.help, 3) + end + end - return UI.Page.eventHandler(self, event) + return UI.Page.eventHandler(self, event) end function listingPage.grid:getDisplayValues(row) - row = Util.shallowCopy(row) - row.need = Util.toBytes(row.need) - row.qty = Util.toBytes(row.qty) - return row + row = Util.shallowCopy(row) + row.need = Util.toBytes(row.need) + row.qty = Util.toBytes(row.qty) + return row end function listingPage.grid:getRowTextColor(row, selected) - if row.is_craftable then - return colors.yellow - end - return UI.Grid:getRowTextColor(row, selected) + if row.is_craftable then + return colors.yellow + end + return UI.Grid:getRowTextColor(row, selected) end function listingPage:refresh(throttle) - local supplyList = Builder:getBlockCounts() + local supplyList = Builder:getBlockCounts() - Builder.itemAdapter:refresh(throttle) + Builder.itemAdapter:refresh(throttle) - for _,b in pairs(supplyList) do - if b.need > 0 then - local item = convertSingleBack(Builder.itemAdapter:getItemInfo({ - name = b.id, - damage = b.dmg, - nbtHash = b.nbt_hash, - })) + for _,b in pairs(supplyList) do + if b.need > 0 then + local item = convertSingleBack(Builder.itemAdapter:getItemInfo({ + name = b.id, + damage = b.dmg, + nbtHash = b.nbt_hash, + })) - if item then - b.display_name = item.display_name - b.qty = item.qty - b.is_craftable = item.is_craftable - else - b.display_name = itemDB:getName({ name = b.id, damage = b.dmg }) - end - end - if throttle then - throttle() - end - end + if item then + b.display_name = item.display_name + b.qty = item.qty + b.is_craftable = item.is_craftable + else + b.display_name = itemDB:getName({ name = b.id, damage = b.dmg }) + end + end + if throttle then + throttle() + end + end - if self.fullList then - self.grid:setValues(supplyList) - else - local t = {} - for _,b in pairs(supplyList) do - if self.fullList or b.qty < b.need then - table.insert(t, b) - end - end - self.grid:setValues(t) - end - self.grid:setIndex(1) + if self.fullList then + self.grid:setValues(supplyList) + else + local t = {} + for _,b in pairs(supplyList) do + if self.fullList or b.qty < b.need then + table.insert(t, b) + end + end + self.grid:setValues(t) + end + self.grid:setIndex(1) end function listingPage:manageBlock(selected) - local substitutes = subDB:lookupBlocksForSub(selected.id, selected.dmg) + local substitutes = subDB:lookupBlocksForSub(selected.id, selected.dmg) - if Util.empty(substitutes) then - substitutionPage.sub = { id = selected.id, dmg = selected.dmg } - UI:setPage(substitutionPage) - elseif Util.size(substitutes) == 1 then - local _,sub = next(substitutes) - substitutionPage.sub = sub - UI:setPage(substitutionPage) - else - selectSubstitutionPage.selected = selected - selectSubstitutionPage.grid:setValues(substitutes) - UI:setPage(selectSubstitutionPage) - end + if Util.empty(substitutes) then + substitutionPage.sub = { id = selected.id, dmg = selected.dmg } + UI:setPage(substitutionPage) + elseif Util.size(substitutes) == 1 then + local _,sub = next(substitutes) + substitutionPage.sub = sub + UI:setPage(substitutionPage) + else + selectSubstitutionPage.selected = selected + selectSubstitutionPage.grid:setValues(substitutes) + UI:setPage(selectSubstitutionPage) + end end --[[-- startPage --]]-- @@ -488,261 +488,261 @@ local wy = 2 local my = 3 if UI.term.width < 30 then - wy = 9 - my = 2 + wy = 9 + my = 2 end local startPage = UI.Page { - window = UI.Window { - x = UI.term.width-16, - y = wy, - width = 16, - height = 9, - backgroundColor = colors.gray, - grid = UI.Grid { - columns = { - { heading = 'Name', key = 'name', width = 6 }, - { heading = 'Value', key = 'value', width = 7 }, - }, - disableHeader = true, - x = 1, - y = 2, - width = 16, - height = 9, - inactive = true, - backgroundColor = colors.gray - }, - }, - menu = UI.Menu { - x = 2, - y = my, - height = 7, - backgroundColor = UI.Page.defaults.backgroundColor, - menuItems = { - { prompt = 'Set starting level', event = 'startLevel' }, - { prompt = 'Set starting block', event = 'startBlock' }, - { prompt = 'Set starting point', event = 'startPoint' }, - { prompt = 'Supply list', event = 'assignBlocks' }, - { prompt = 'Toggle mode', event = 'toggleMode' }, - { prompt = 'Begin', event = 'begin' }, - { prompt = 'Quit', event = 'quit' } - } - }, - startLevel = UI.Dialog { - title = 'Enter Starting Level', - height = 7, - form = UI.Form { - y = 3, x = 2, height = 4, - event = 'setStartLevel', - cancelEvent = 'slide_hide', - text = UI.Text { - x = 5, y = 1, width = 20, - textColor = colors.gray, - }, - textEntry = UI.TextEntry { - formKey = 'level', - x = 15, y = 1, width = 7, - }, - }, - statusBar = UI.StatusBar(), - }, - startBlock = UI.Dialog { - title = 'Enter Block Number', - height = 7, - form = UI.Form { - y = 3, x = 2, height = 4, - event = 'setStartBlock', - cancelEvent = 'slide_hide', - text = UI.Text { - x = 2, y = 1, width = 20, - textColor = colors.gray, - }, - textEntry = UI.TextEntry { - x = 16, y = 1, - width = 10, limit = 8, - }, - }, - statusBar = UI.StatusBar(), - }, - startPoint = UI.Dialog { - title = 'Set starting point', - height = 11, - form = UI.Form { - y = 2, x = 2, ey = -2, - cancelEvent = 'slide_hide', - text1 = UI.Text { - x = 1, y = 2, value = 'Turtle location' }, - xLoc = UI.TextEntry { - x = 1, y = 3, formKey = 'x', width = 7, limit = 16, shadowText = 'x', required = true }, - yLoc = UI.TextEntry { - x = 9, y = 3, formKey = 'y', width = 7, limit = 16, shadowText = 'y', required = true }, - zLoc = UI.TextEntry { - x = 17, y = 3, formKey = 'z', width = 7, limit = 16, shadowText = 'z', required = true }, - text2 = UI.Text { - x = 1, y = 5, value = 'Starting Point' }, - xrLoc = UI.TextEntry { - x = 1, y = 6, formKey = 'rx', width = 7, limit = 16, shadowText = 'x', required = true }, - yrLoc = UI.TextEntry { - x = 9, y = 6, formKey = 'ry', width = 7, limit = 16, shadowText = 'y', required = true }, - zrLoc = UI.TextEntry { - x = 17, y = 6, formKey = 'rz', width = 7, limit = 16, shadowText = 'z', required = true }, - revert = UI.Button { - x = 1, y = -2, text = 'Revert', event = 'revert' }, - accelerators = { - form_cancel = 'slide_hide', - }, - }, - statusBar = UI.StatusBar({ values = 'Optional start point'}), - }, - throttle = UI.Throttle { }, - accelerators = { - x = 'test', - q = 'quit' - } + window = UI.Window { + x = UI.term.width-16, + y = wy, + width = 16, + height = 9, + backgroundColor = colors.gray, + grid = UI.Grid { + columns = { + { heading = 'Name', key = 'name', width = 6 }, + { heading = 'Value', key = 'value', width = 7 }, + }, + disableHeader = true, + x = 1, + y = 2, + width = 16, + height = 9, + inactive = true, + backgroundColor = colors.gray + }, + }, + menu = UI.Menu { + x = 2, + y = my, + height = 7, + backgroundColor = UI.Page.defaults.backgroundColor, + menuItems = { + { prompt = 'Set starting level', event = 'startLevel' }, + { prompt = 'Set starting block', event = 'startBlock' }, + { prompt = 'Set starting point', event = 'startPoint' }, + { prompt = 'Supply list', event = 'assignBlocks' }, + { prompt = 'Toggle mode', event = 'toggleMode' }, + { prompt = 'Begin', event = 'begin' }, + { prompt = 'Quit', event = 'quit' } + } + }, + startLevel = UI.Dialog { + title = 'Enter Starting Level', + height = 7, + form = UI.Form { + y = 3, x = 2, height = 4, + event = 'setStartLevel', + cancelEvent = 'slide_hide', + text = UI.Text { + x = 5, y = 1, width = 20, + textColor = colors.gray, + }, + textEntry = UI.TextEntry { + formKey = 'level', + x = 15, y = 1, width = 7, + }, + }, + statusBar = UI.StatusBar(), + }, + startBlock = UI.Dialog { + title = 'Enter Block Number', + height = 7, + form = UI.Form { + y = 3, x = 2, height = 4, + event = 'setStartBlock', + cancelEvent = 'slide_hide', + text = UI.Text { + x = 2, y = 1, width = 20, + textColor = colors.gray, + }, + textEntry = UI.TextEntry { + x = 16, y = 1, + width = 10, limit = 8, + }, + }, + statusBar = UI.StatusBar(), + }, + startPoint = UI.Dialog { + title = 'Set starting point', + height = 11, + form = UI.Form { + y = 2, x = 2, ey = -2, + cancelEvent = 'slide_hide', + text1 = UI.Text { + x = 1, y = 2, value = 'Turtle location' }, + xLoc = UI.TextEntry { + x = 1, y = 3, formKey = 'x', width = 7, limit = 16, shadowText = 'x', required = true }, + yLoc = UI.TextEntry { + x = 9, y = 3, formKey = 'y', width = 7, limit = 16, shadowText = 'y', required = true }, + zLoc = UI.TextEntry { + x = 17, y = 3, formKey = 'z', width = 7, limit = 16, shadowText = 'z', required = true }, + text2 = UI.Text { + x = 1, y = 5, value = 'Starting Point' }, + xrLoc = UI.TextEntry { + x = 1, y = 6, formKey = 'rx', width = 7, limit = 16, shadowText = 'x', required = true }, + yrLoc = UI.TextEntry { + x = 9, y = 6, formKey = 'ry', width = 7, limit = 16, shadowText = 'y', required = true }, + zrLoc = UI.TextEntry { + x = 17, y = 6, formKey = 'rz', width = 7, limit = 16, shadowText = 'z', required = true }, + revert = UI.Button { + x = 1, y = -2, text = 'Revert', event = 'revert' }, + accelerators = { + form_cancel = 'slide_hide', + }, + }, + statusBar = UI.StatusBar({ values = 'Optional start point'}), + }, + throttle = UI.Throttle { }, + accelerators = { + x = 'test', + q = 'quit' + } } function startPage:draw() - local t = { - { name = 'mode', value = Builder.mode }, - { name = 'start', value = Builder.index }, - { name = 'blocks', value = #Builder.schematic.blocks }, - { name = 'length', value = Builder.schematic.length }, - { name = 'width', value = Builder.schematic.width }, - { name = 'height', value = Builder.schematic.height }, - } + local t = { + { name = 'mode', value = Builder.mode }, + { name = 'start', value = Builder.index }, + { name = 'blocks', value = #Builder.schematic.blocks }, + { name = 'length', value = Builder.schematic.length }, + { name = 'width', value = Builder.schematic.width }, + { name = 'height', value = Builder.schematic.height }, + } - self.window.grid:setValues(t) - UI.Page.draw(self) + self.window.grid:setValues(t) + UI.Page.draw(self) end function startPage:enable() - self:setFocus(self.menu) - UI.Page.enable(self) + self:setFocus(self.menu) + UI.Page.enable(self) end function startPage.startPoint:eventHandler(event) - if event.type == 'form_complete' then - for k,v in pairs(event.values) do - Builder.loc[k] = tonumber(v) - end - Builder:saveProgress(Builder.index) - self:hide() - elseif event.type == 'revert' then - Builder.loc = { } - Builder:saveProgress(Builder.index) - self:hide() - elseif event.type == 'form_invalid' then - self.statusBar:setStatus(event.message) - elseif event.type == 'form_cancel' or event.type == 'cancel' then - self:hide() - else - return UI.Dialog.eventHandler(self, event) - end - return true + if event.type == 'form_complete' then + for k,v in pairs(event.values) do + Builder.loc[k] = tonumber(v) + end + Builder:saveProgress(Builder.index) + self:hide() + elseif event.type == 'revert' then + Builder.loc = { } + Builder:saveProgress(Builder.index) + self:hide() + elseif event.type == 'form_invalid' then + self.statusBar:setStatus(event.message) + elseif event.type == 'form_cancel' or event.type == 'cancel' then + self:hide() + else + return UI.Dialog.eventHandler(self, event) + end + return true end function startPage:eventHandler(event) - if event.type == 'startLevel' then - self.startLevel.form.text.value = '0 - ' .. Builder.schematic.height - self.startLevel:show() + if event.type == 'startLevel' then + self.startLevel.form.text.value = '0 - ' .. Builder.schematic.height + self.startLevel:show() - elseif event.type == 'setStartLevel' then - local l = tonumber(self.startLevel.form.textEntry.value) - if l and l < Builder.schematic.height and l >= 0 then - for k,v in pairs(Builder.schematic.blocks) do - if v.y >= l then - Builder.index = k - Builder:saveProgress(Builder.index) - break - end - end - self.startLevel:hide() - self:draw() - else - self.startLevel.statusBar:setStatus('Invalid start level') - end + elseif event.type == 'setStartLevel' then + local l = tonumber(self.startLevel.form.textEntry.value) + if l and l < Builder.schematic.height and l >= 0 then + for k,v in pairs(Builder.schematic.blocks) do + if v.y >= l then + Builder.index = k + Builder:saveProgress(Builder.index) + break + end + end + self.startLevel:hide() + self:draw() + else + self.startLevel.statusBar:setStatus('Invalid start level') + end - elseif event.type == 'startBlock' then - self.startBlock.form.text.value = '1 - ' .. #Builder.schematic.blocks - self.startBlock.form.textEntry.value = tostring(Builder.index) - self.startBlock:show() + elseif event.type == 'startBlock' then + self.startBlock.form.text.value = '1 - ' .. #Builder.schematic.blocks + self.startBlock.form.textEntry.value = tostring(Builder.index) + self.startBlock:show() - elseif event.type == 'setStartBlock' then - local bn = tonumber(self.startBlock.form.textEntry.value) - if bn and bn < #Builder.schematic.blocks and bn >= 0 then - Builder.index = bn - Builder:saveProgress(Builder.index) - self.startBlock:hide() - self:draw() - else - self.startLevel.statusBar:setStatus('Invalid start block') - end + elseif event.type == 'setStartBlock' then + local bn = tonumber(self.startBlock.form.textEntry.value) + if bn and bn < #Builder.schematic.blocks and bn >= 0 then + Builder.index = bn + Builder:saveProgress(Builder.index) + self.startBlock:hide() + self:draw() + else + self.startLevel.statusBar:setStatus('Invalid start block') + end - elseif event.type == 'startPoint' then - local loc = Util.shallowCopy(Builder.loc) - if not loc.x then - if _G.turtle then - local pt = GPS.getPoint() - if pt then - loc.x = pt.x - loc.y = pt.y - loc.z = pt.z - end - elseif _G.commands then - loc.x, loc.y, loc.z = _G.commands.getBlockPosition() - end - end + elseif event.type == 'startPoint' then + local loc = Util.shallowCopy(Builder.loc) + if not loc.x then + if _G.turtle then + local pt = GPS.getPoint() + if pt then + loc.x = pt.x + loc.y = pt.y + loc.z = pt.z + end + elseif _G.commands then + loc.x, loc.y, loc.z = _G.commands.getBlockPosition() + end + end - self.startPoint.form:setValues(loc) - self.startPoint:show() + self.startPoint.form:setValues(loc) + self.startPoint:show() - elseif event.type == 'assignBlocks' then - -- this might be an approximation of the blocks needed - -- as the current level's route may or may not have been - -- computed - Builder:dumpInventory() - UI:setPage('listing', function() self.throttle:update() end) - self.throttle:disable() + elseif event.type == 'assignBlocks' then + -- this might be an approximation of the blocks needed + -- as the current level's route may or may not have been + -- computed + Builder:dumpInventory() + UI:setPage('listing', function() self.throttle:update() end) + self.throttle:disable() - elseif event.type == 'toggleMode' then - if Builder.mode == 'build' then - if Builder.index == 1 then - Builder.index = #Builder.schematic.blocks - end - Builder.mode = 'destroy' - else - if Builder.index == #Builder.schematic.blocks then - Builder.index = 1 - end - Builder.mode = 'build' - end - self:draw() + elseif event.type == 'toggleMode' then + if Builder.mode == 'build' then + if Builder.index == 1 then + Builder.index = #Builder.schematic.blocks + end + Builder.mode = 'destroy' + else + if Builder.index == #Builder.schematic.blocks then + Builder.index = 1 + end + Builder.mode = 'build' + end + self:draw() - elseif event.type == 'begin' then - UI:setPage('blank') - self:sync() + elseif event.type == 'begin' then + UI:setPage('blank') + self:sync() - print('Reloading schematic') - Builder:reloadSchematic(Util.throttle()) - Builder:begin() + print('Reloading schematic') + Builder:reloadSchematic(Util.throttle()) + Builder:begin() - elseif event.type == 'quit' then - UI.term:reset() - Event.exitPullEvents() - end + elseif event.type == 'quit' then + UI.term:reset() + Event.exitPullEvents() + end - return UI.Page.eventHandler(self, event) + return UI.Page.eventHandler(self, event) end --[[-- startup logic --]]-- local args = {...} if #args < 1 then - error('supply file name') + error('supply file name') end Builder.itemAdapter = Adapter.wrap({ side = 'bottom', direction = 'up' }) if not Builder.itemAdapter then - error('A chest or ME interface must be below turtle') + error('A chest or ME interface must be below turtle') end subDB:load() @@ -756,19 +756,19 @@ Builder.subDB = subDB Builder:substituteBlocks(Util.throttle()) if not fs.exists(BUILDER_DIR) then - fs.makeDir(BUILDER_DIR) + fs.makeDir(BUILDER_DIR) end Builder:loadProgress(Builder.schematic.filename .. '.progress') Event.on('build', function() - Builder:build() + Builder:build() end) UI:setPages({ - listing = listingPage, - start = startPage, - blank = blankPage + listing = listingPage, + start = startPage, + blank = blankPage }) UI:setPage('start') diff --git a/builder/supplier.lua b/builder/supplier.lua index ee39ba3..0f93b97 100644 --- a/builder/supplier.lua +++ b/builder/supplier.lua @@ -10,390 +10,390 @@ local os = _G.os local turtle = _G.turtle --[[ - A supplier turtle for the builder turtle. For larger builds, use - ender modems. + A supplier turtle for the builder turtle. For larger builds, use + ender modems. - Setup: + Setup: - 1. chest or ME interface at level 0 (bottom of build area) - 2. builder turtle on top facing the build area - 3. If facing the build turtle, the supplier turtle is to the right - pointing at the chest/interface + 1. chest or ME interface at level 0 (bottom of build area) + 2. builder turtle on top facing the build area + 3. If facing the build turtle, the supplier turtle is to the right + pointing at the chest/interface ]]-- local ChestProvider = require('core.chestProvider') if Util.getVersion() == 1.8 then - ChestProvider = require('core.chestProvider18') + ChestProvider = require('core.chestProvider18') end if not device.wireless_modem then - error('No wireless modem detected') + error('No wireless modem detected') end local __BUILDER_ID = 6 local itemInfoDB local Builder = { - version = '1.70', - ccVersion = nil, - slots = { }, - index = 1, - fuelItem = { id = 'minecraft:coal', dmg = 0 }, - resupplying = true, - ready = true, + version = '1.70', + ccVersion = nil, + slots = { }, + index = 1, + fuelItem = { id = 'minecraft:coal', dmg = 0 }, + resupplying = true, + ready = true, } --[[-- maxStackDB --]]-- local maxStackDB = TableDB({ - fileName = 'maxstack.db', - tabledef = { - autokeys = false, - type = 'simple', - columns = { - { label = 'Key', type = 'key', length = 8 }, - { label = 'Quantity', type = 'number', length = 2 } - } - } + fileName = 'maxstack.db', + tabledef = { + autokeys = false, + type = 'simple', + columns = { + { label = 'Key', type = 'key', length = 8 }, + { label = 'Quantity', type = 'number', length = 2 } + } + } }) function maxStackDB:get(id, dmg) - return self.data[id .. ':' .. dmg] or 64 + return self.data[id .. ':' .. dmg] or 64 end function Builder:dumpInventory() - local success = true + local success = true - for i = 1, 16 do - local qty = turtle.getItemCount(i) - if qty > 0 then - self.itemProvider:insert(i, qty) - end - if turtle.getItemCount(i) ~= 0 then - success = false - end - end - turtle.select(1) + for i = 1, 16 do + local qty = turtle.getItemCount(i) + if qty > 0 then + self.itemProvider:insert(i, qty) + end + if turtle.getItemCount(i) ~= 0 then + success = false + end + end + turtle.select(1) - return success + return success end function Builder:dumpInventoryWithCheck() - while not self:dumpInventory() do - Builder:log('Unable to dump inventory') - print('Provider is full or missing - make space or replace') - print('Press enter to continue') - --turtle.setHeading(0) - self.ready = false - _G.read() - end - self.ready = true + while not self:dumpInventory() do + Builder:log('Unable to dump inventory') + print('Provider is full or missing - make space or replace') + print('Press enter to continue') + --turtle.setHeading(0) + self.ready = false + _G.read() + end + self.ready = true end function Builder:autocraft(supplies) - local t = { } + local t = { } - for _,s in pairs(supplies) do - local key = s.id .. ':' .. s.dmg - local item = t[key] - if not item then - item = { - id = s.id, - dmg = s.dmg, - qty = 0, - } - t[key] = item - end - item.qty = item.qty + (s.need-s.qty) - end + for _,s in pairs(supplies) do + local key = s.id .. ':' .. s.dmg + local item = t[key] + if not item then + item = { + id = s.id, + dmg = s.dmg, + qty = 0, + } + t[key] = item + end + item.qty = item.qty + (s.need-s.qty) + end - Builder.itemProvider:craftItems(t) + Builder.itemProvider:craftItems(t) end function Builder:refuel() - while turtle.getFuelLevel() < 4000 and self.fuelItem do - Builder:log('Refueling') - turtle.select(1) - self.itemProvider:provide(self.fuelItem, 64, 1) - if turtle.getItemCount(1) == 0 then - Builder:log('Out of fuel, add coal to chest/ME system') - --turtle.setHeading(0) - os.sleep(5) - else - turtle.refuel(64) - end - end + while turtle.getFuelLevel() < 4000 and self.fuelItem do + Builder:log('Refueling') + turtle.select(1) + self.itemProvider:provide(self.fuelItem, 64, 1) + if turtle.getItemCount(1) == 0 then + Builder:log('Out of fuel, add coal to chest/ME system') + --turtle.setHeading(0) + os.sleep(5) + else + turtle.refuel(64) + end + end end function Builder:log(...) - Util.print(...) + Util.print(...) end function Builder:getSupplies() - Builder.itemProvider:refresh() + Builder.itemProvider:refresh() - local t = { } - for _,s in ipairs(self.slots) do - if s.need > 0 then - local item = Builder.itemProvider:getItemInfo(s) - if item then - if item.name then - s.name = item.name - end + local t = { } + for _,s in ipairs(self.slots) do + if s.need > 0 then + local item = Builder.itemProvider:getItemInfo(s) + if item then + if item.name then + s.name = item.name + end - local qty = math.min(s.need-s.qty, item.qty) + local qty = math.min(s.need-s.qty, item.qty) - if qty + s.qty > item.max_size then - maxStackDB:add({ s.id, s.dmg }, item.max_size) - maxStackDB.dirty = true - maxStackDB:flush() - qty = item.max_size - s.need = qty - end - if qty > 0 then - self.itemProvider:provide(item, qty, s.index) - s.qty = turtle.getItemCount(s.index) - end - end - end - if s.qty < s.need then - table.insert(t, s) - local name = s.name or s.id .. ':' .. s.dmg - local item = itemInfoDB:get({ s.id, s.dmg }) - if item then - name = item.displayName - end + if qty + s.qty > item.max_size then + maxStackDB:add({ s.id, s.dmg }, item.max_size) + maxStackDB.dirty = true + maxStackDB:flush() + qty = item.max_size + s.need = qty + end + if qty > 0 then + self.itemProvider:provide(item, qty, s.index) + s.qty = turtle.getItemCount(s.index) + end + end + end + if s.qty < s.need then + table.insert(t, s) + local name = s.name or s.id .. ':' .. s.dmg + local item = itemInfoDB:get({ s.id, s.dmg }) + if item then + name = item.displayName + end - Builder:log('Need %d %s', s.need - s.qty, name) - end - end + Builder:log('Need %d %s', s.need - s.qty, name) + end + end - return t + return t end local function moveTowardsX(dx) - local direction = dx - turtle.point.x - local move + local direction = dx - turtle.point.x + local move - if direction == 0 then - return false - end + if direction == 0 then + return false + end - if direction > 0 and turtle.point.heading == 0 or - direction < 0 and turtle.point.heading == 2 then - move = turtle.forward - else - move = turtle.back - end + if direction > 0 and turtle.point.heading == 0 or + direction < 0 and turtle.point.heading == 2 then + move = turtle.forward + else + move = turtle.back + end - return move() + return move() end local function moveTowardsZ(dz) - local direction = dz - turtle.point.z - local move + local direction = dz - turtle.point.z + local move - if direction == 0 then - return false - end + if direction == 0 then + return false + end - if direction > 0 and turtle.point.heading == 1 or - direction < 0 and turtle.point.heading == 3 then - move = turtle.forward - else - move = turtle.back - end + if direction > 0 and turtle.point.heading == 1 or + direction < 0 and turtle.point.heading == 3 then + move = turtle.forward + else + move = turtle.back + end - return move() + return move() end function Builder:finish() - Builder.resupplying = true - Builder.ready = false - if turtle.gotoLocation('supplies') then - turtle.setHeading(1) - os.sleep(.1) -- random 'Computer is not connected' error... - Builder:dumpInventory() - Event.exitPullEvents() - print('Finished') - end + Builder.resupplying = true + Builder.ready = false + if turtle.gotoLocation('supplies') then + turtle.setHeading(1) + os.sleep(.1) -- random 'Computer is not connected' error... + Builder:dumpInventory() + Event.exitPullEvents() + print('Finished') + end end function Builder:gotoBuilder() - if Builder.lastPoint then - turtle.setStatus('tracking') - while true do - local pt = Point.copy(Builder.lastPoint) - pt.y = pt.y + 3 - if turtle.point.y ~= pt.y then - turtle.gotoY(pt.y) - else - local distance = Point.turtleDistance(turtle.point, pt) - if distance <= 3 then - Builder:log('Synchronized') - break - end + if Builder.lastPoint then + turtle.setStatus('tracking') + while true do + local pt = Point.copy(Builder.lastPoint) + pt.y = pt.y + 3 + if turtle.point.y ~= pt.y then + turtle.gotoY(pt.y) + else + local distance = Point.turtleDistance(turtle.point, pt) + if distance <= 3 then + Builder:log('Synchronized') + break + end - if turtle.point.heading % 2 == 0 then - if turtle.point.x == pt.x then - turtle.headTowardsZ(pt.z) - moveTowardsZ(pt.z) - else - moveTowardsX(pt.x) - end - elseif turtle.point.z ~= pt.z then - moveTowardsZ(pt.z) - else - turtle.headTowardsX(pt.x) - moveTowardsX(pt.x) - end - end - end - end + if turtle.point.heading % 2 == 0 then + if turtle.point.x == pt.x then + turtle.headTowardsZ(pt.z) + moveTowardsZ(pt.z) + else + moveTowardsX(pt.x) + end + elseif turtle.point.z ~= pt.z then + moveTowardsZ(pt.z) + else + turtle.headTowardsX(pt.x) + moveTowardsX(pt.x) + end + end + end + end end Message.addHandler('builder', - function(_, id, msg) - if not id or id ~= __BUILDER_ID then - return - end + function(_, id, msg) + if not id or id ~= __BUILDER_ID then + return + end - if not Builder.resupplying then - local pt = msg.contents - pt.y = pt.y + 3 + if not Builder.resupplying then + local pt = msg.contents + pt.y = pt.y + 3 - turtle.setStatus('supervising') - turtle.gotoYfirst(pt) - end - end) + turtle.setStatus('supervising') + turtle.gotoYfirst(pt) + end + end) Message.addHandler('supplyList', - function(_, id, msg) - if not id or id ~= __BUILDER_ID then - return - end + function(_, id, msg) + if not id or id ~= __BUILDER_ID then + return + end - turtle.setStatus('resupplying') - Builder.resupplying = true - Builder.slots = msg.contents.slots - Builder.slotUid = msg.contents.uid + turtle.setStatus('resupplying') + Builder.resupplying = true + Builder.slots = msg.contents.slots + Builder.slotUid = msg.contents.uid - Builder:log('Received supply list ' .. Builder.slotUid) + Builder:log('Received supply list ' .. Builder.slotUid) - os.sleep(0) - if not turtle.gotoLocation('supplies') then - Builder:log('Failed to go to supply location') - Builder.ready = false - Event.exitPullEvents() - end - turtle.setHeading(1) - os.sleep(.2) -- random 'Computer is not connected' error... - Builder:dumpInventoryWithCheck() - Builder:refuel() + os.sleep(0) + if not turtle.gotoLocation('supplies') then + Builder:log('Failed to go to supply location') + Builder.ready = false + Event.exitPullEvents() + end + turtle.setHeading(1) + os.sleep(.2) -- random 'Computer is not connected' error... + Builder:dumpInventoryWithCheck() + Builder:refuel() - while true do - local supplies = Builder:getSupplies() - if #supplies == 0 then - break - end - Builder:autocraft(supplies) - turtle.setStatus('waiting') - os.sleep(5) - end - Builder:log('Got all supplies') - os.sleep(0) - Builder:gotoBuilder() - Builder.resupplying = false - end) + while true do + local supplies = Builder:getSupplies() + if #supplies == 0 then + break + end + Builder:autocraft(supplies) + turtle.setStatus('waiting') + os.sleep(5) + end + Builder:log('Got all supplies') + os.sleep(0) + Builder:gotoBuilder() + Builder.resupplying = false + end) Message.addHandler('needSupplies', - function(_, id, msg) - if not id or id ~= __BUILDER_ID then - return - end + function(_, id, msg) + if not id or id ~= __BUILDER_ID then + return + end - if Builder.resupplying or msg.contents.uid ~= Builder.slotUid then - Builder:log('No supplies ready') - Message.send(__BUILDER_ID, 'gotSupplies') - else - turtle.setStatus('supplying') - Builder:log('Supplying') - os.sleep(0) + if Builder.resupplying or msg.contents.uid ~= Builder.slotUid then + Builder:log('No supplies ready') + Message.send(__BUILDER_ID, 'gotSupplies') + else + turtle.setStatus('supplying') + Builder:log('Supplying') + os.sleep(0) - local pt = msg.contents.point - pt.y = turtle.getPoint().y - pt.heading = nil - if not turtle.gotoYfirst(pt) then -- location of builder - Builder.resupplying = true - Message.send(__BUILDER_ID, 'gotSupplies') - os.sleep(0) - if not turtle.gotoLocation('supplies') then - Builder:log('failed to go to supply location') - Event.exitPullEvents() - end - turtle.setHeading(1) - return - end - pt.y = pt.y - 2 -- location where builder should go for the chest to be above + local pt = msg.contents.point + pt.y = turtle.getPoint().y + pt.heading = nil + if not turtle.gotoYfirst(pt) then -- location of builder + Builder.resupplying = true + Message.send(__BUILDER_ID, 'gotSupplies') + os.sleep(0) + if not turtle.gotoLocation('supplies') then + Builder:log('failed to go to supply location') + Event.exitPullEvents() + end + turtle.setHeading(1) + return + end + pt.y = pt.y - 2 -- location where builder should go for the chest to be above - turtle.select(15) - turtle.placeDown() - os.sleep(.1) -- random computer not connected error - local p = ChestProvider({ direction = 'up', wrapSide = 'bottom' }) - for i = 1, 16 do - p:insert(i, 64) - end + turtle.select(15) + turtle.placeDown() + os.sleep(.1) -- random computer not connected error + local p = ChestProvider({ direction = 'up', wrapSide = 'bottom' }) + for i = 1, 16 do + p:insert(i, 64) + end - Message.send(__BUILDER_ID, 'gotSupplies', { supplies = true, point = pt }) + Message.send(__BUILDER_ID, 'gotSupplies', { supplies = true, point = pt }) - Message.waitForMessage('thanks', 5, __BUILDER_ID) - --os.sleep(0) + Message.waitForMessage('thanks', 5, __BUILDER_ID) + --os.sleep(0) - --p.condenseItems() - for i = 1, 16 do - p:extract(i, 64) - end - turtle.digDown() - turtle.setStatus('waiting') - end - end) + --p.condenseItems() + for i = 1, 16 do + p:extract(i, 64) + end + turtle.digDown() + turtle.setStatus('waiting') + end + end) Message.addHandler('finished', - function(_, id) - if not id or id ~= __BUILDER_ID then - return - end - Builder:finish() - end) + function(_, id) + if not id or id ~= __BUILDER_ID then + return + end + Builder:finish() + end) Event.on('turtle_abort', - function() - turtle.abort(false) - turtle.setStatus('aborting') - Builder:finish() - end) + function() + turtle.abort(false) + turtle.setStatus('aborting') + Builder:finish() + end) local function onTheWay() -- parallel routine - while true do - local _, _, _, id, msg, _ = os.pullEvent('modem_message') - if Builder.ready then - if id == __BUILDER_ID and msg and msg.type then - if msg.type == 'needSupplies' then - Message.send(__BUILDER_ID, 'gotSupplies', { supplies = true }) - elseif msg.type == 'builder' then - Builder.lastPoint = msg.contents - end - end - end - end + while true do + local _, _, _, id, msg, _ = os.pullEvent('modem_message') + if Builder.ready then + if id == __BUILDER_ID and msg and msg.type then + if msg.type == 'needSupplies' then + Message.send(__BUILDER_ID, 'gotSupplies', { supplies = true }) + elseif msg.type == 'builder' then + Builder.lastPoint = msg.contents + end + end + end + end end local args = {...} if #args < 2 then - error('syntax: ') + error('syntax: ') end __BUILDER_ID = tonumber(args[1]) @@ -401,30 +401,30 @@ __BUILDER_ID = tonumber(args[1]) maxStackDB:load() itemInfoDB = TableDB({ - fileName = 'items.db' + fileName = 'items.db' }) itemInfoDB:load() Builder.itemProvider = MEProvider({ direction = args[2] }) if not Builder.itemProvider:isValid() then - local sides = { - east = 'west', - west = 'east', - north = 'south', - south = 'north', - } + local sides = { + east = 'west', + west = 'east', + north = 'south', + south = 'north', + } - Builder.itemProvider = ChestProvider({ direction = sides[args[2]], wrapSide = 'front' }) - if not Builder.itemProvider:isValid() then - error('A chest or ME interface must be in front of turtle') - end + Builder.itemProvider = ChestProvider({ direction = sides[args[2]], wrapSide = 'front' }) + if not Builder.itemProvider:isValid() then + error('A chest or ME interface must be in front of turtle') + end end turtle.run(function() - turtle.setPoint({ x = -1, z = -2, y = -1, heading = 1 }) + turtle.setPoint({ x = -1, z = -2, y = -1, heading = 1 }) - turtle.saveLocation('supplies') + turtle.saveLocation('supplies') - Event.pullEvents(onTheWay) + Event.pullEvents(onTheWay) end) diff --git a/builder/viewer.lua b/builder/viewer.lua index b70672e..b59cf59 100644 --- a/builder/viewer.lua +++ b/builder/viewer.lua @@ -13,15 +13,15 @@ local function Syntax(msg) * Entity sensor * Introspection module ]]) - error(msg) + error(msg) end local neural = device['neuralInterface'] or Syntax('Must be run on a neural interface') local function assertModule(module, name) - if not neural.hasModule(module) then - Syntax('Missing: ' .. name) - end + if not neural.hasModule(module) then + Syntax('Missing: ' .. name) + end end assertModule('plethora:glasses', 'Overlay glasses') assertModule('plethora:sensor', 'Entity sensor') @@ -31,102 +31,102 @@ local BUILDER_DIR = 'usr/builder' --[[-- SubDB --]]-- local subDB = TableDB({ - fileName = fs.combine(BUILDER_DIR, 'sub.db'), + fileName = fs.combine(BUILDER_DIR, 'sub.db'), }) function subDB:load() - if fs.exists(self.fileName) then - TableDB.load(self) - elseif not Builder.isCommandComputer then - self:seedDB() - end + if fs.exists(self.fileName) then + TableDB.load(self) + elseif not Builder.isCommandComputer then + self:seedDB() + end end function subDB:seedDB() - self.data = { - [ "minecraft:redstone_wire:0" ] = "minecraft:redstone:0", - [ "minecraft:wall_sign:0" ] = "minecraft:sign:0", - [ "minecraft:standing_sign:0" ] = "minecraft:sign:0", - [ "minecraft:potatoes:0" ] = "minecraft:potato:0", - [ "minecraft:unlit_redstone_torch:0" ] = "minecraft:redstone_torch:0", - [ "minecraft:powered_repeater:0" ] = "minecraft:repeater:0", - [ "minecraft:unpowered_repeater:0" ] = "minecraft:repeater:0", - [ "minecraft:carrots:0" ] = "minecraft:carrot:0", - [ "minecraft:cocoa:0" ] = "minecraft:dye:3", - [ "minecraft:unpowered_comparator:0" ] = "minecraft:comparator:0", - [ "minecraft:powered_comparator:0" ] = "minecraft:comparator:0", - [ "minecraft:piston_head:0" ] = "minecraft:air:0", - [ "minecraft:piston_extension:0" ] = "minecraft:air:0", - [ "minecraft:portal:0" ] = "minecraft:air:0", - [ "minecraft:double_wooden_slab:0" ] = "minecraft:planks:0", - [ "minecraft:double_wooden_slab:1" ] = "minecraft:planks:1", - [ "minecraft:double_wooden_slab:2" ] = "minecraft:planks:2", - [ "minecraft:double_wooden_slab:3" ] = "minecraft:planks:3", - [ "minecraft:double_wooden_slab:4" ] = "minecraft:planks:4", - [ "minecraft:double_wooden_slab:5" ] = "minecraft:planks:5", - [ "minecraft:lit_redstone_lamp:0" ] = "minecraft:redstone_lamp:0", - [ "minecraft:double_stone_slab:1" ] = "minecraft:sandstone:0", - [ "minecraft:double_stone_slab:2" ] = "minecraft:planks:0", - [ "minecraft:double_stone_slab:3" ] = "minecraft:cobblestone:0", - [ "minecraft:double_stone_slab:4" ] = "minecraft:brick_block:0", - [ "minecraft:double_stone_slab:5" ] = "minecraft:stonebrick:0", - [ "minecraft:double_stone_slab:6" ] = "minecraft:nether_brick:0", - [ "minecraft:double_stone_slab:7" ] = "minecraft:quartz_block:0", - [ "minecraft:double_stone_slab:9" ] = "minecraft:sandstone:2", - [ "minecraft:double_stone_slab2:0" ] = "minecraft:sandstone:0", - [ "minecraft:stone_slab:2" ] = "minecraft:wooden_slab:0", - [ "minecraft:wheat:0" ] = "minecraft:wheat_seeds:0", - [ "minecraft:flowing_water:0" ] = "minecraft:air:0", - [ "minecraft:lit_furnace:0" ] = "minecraft:furnace:0", - [ "minecraft:wall_banner:0" ] = "minecraft:banner:0", - [ "minecraft:standing_banner:0" ] = "minecraft:banner:0", - [ "minecraft:tripwire:0" ] = "minecraft:string:0", - [ "minecraft:pumpkin_stem:0" ] = "minecraft:pumpkin_seeds:0", - } - self.dirty = true - self:flush() + self.data = { + [ "minecraft:redstone_wire:0" ] = "minecraft:redstone:0", + [ "minecraft:wall_sign:0" ] = "minecraft:sign:0", + [ "minecraft:standing_sign:0" ] = "minecraft:sign:0", + [ "minecraft:potatoes:0" ] = "minecraft:potato:0", + [ "minecraft:unlit_redstone_torch:0" ] = "minecraft:redstone_torch:0", + [ "minecraft:powered_repeater:0" ] = "minecraft:repeater:0", + [ "minecraft:unpowered_repeater:0" ] = "minecraft:repeater:0", + [ "minecraft:carrots:0" ] = "minecraft:carrot:0", + [ "minecraft:cocoa:0" ] = "minecraft:dye:3", + [ "minecraft:unpowered_comparator:0" ] = "minecraft:comparator:0", + [ "minecraft:powered_comparator:0" ] = "minecraft:comparator:0", + [ "minecraft:piston_head:0" ] = "minecraft:air:0", + [ "minecraft:piston_extension:0" ] = "minecraft:air:0", + [ "minecraft:portal:0" ] = "minecraft:air:0", + [ "minecraft:double_wooden_slab:0" ] = "minecraft:planks:0", + [ "minecraft:double_wooden_slab:1" ] = "minecraft:planks:1", + [ "minecraft:double_wooden_slab:2" ] = "minecraft:planks:2", + [ "minecraft:double_wooden_slab:3" ] = "minecraft:planks:3", + [ "minecraft:double_wooden_slab:4" ] = "minecraft:planks:4", + [ "minecraft:double_wooden_slab:5" ] = "minecraft:planks:5", + [ "minecraft:lit_redstone_lamp:0" ] = "minecraft:redstone_lamp:0", + [ "minecraft:double_stone_slab:1" ] = "minecraft:sandstone:0", + [ "minecraft:double_stone_slab:2" ] = "minecraft:planks:0", + [ "minecraft:double_stone_slab:3" ] = "minecraft:cobblestone:0", + [ "minecraft:double_stone_slab:4" ] = "minecraft:brick_block:0", + [ "minecraft:double_stone_slab:5" ] = "minecraft:stonebrick:0", + [ "minecraft:double_stone_slab:6" ] = "minecraft:nether_brick:0", + [ "minecraft:double_stone_slab:7" ] = "minecraft:quartz_block:0", + [ "minecraft:double_stone_slab:9" ] = "minecraft:sandstone:2", + [ "minecraft:double_stone_slab2:0" ] = "minecraft:sandstone:0", + [ "minecraft:stone_slab:2" ] = "minecraft:wooden_slab:0", + [ "minecraft:wheat:0" ] = "minecraft:wheat_seeds:0", + [ "minecraft:flowing_water:0" ] = "minecraft:air:0", + [ "minecraft:lit_furnace:0" ] = "minecraft:furnace:0", + [ "minecraft:wall_banner:0" ] = "minecraft:banner:0", + [ "minecraft:standing_banner:0" ] = "minecraft:banner:0", + [ "minecraft:tripwire:0" ] = "minecraft:string:0", + [ "minecraft:pumpkin_stem:0" ] = "minecraft:pumpkin_seeds:0", + } + self.dirty = true + self:flush() end function subDB:add(s) - TableDB.add(self, { s.id, s.dmg }, table.concat({ s.sid, s.sdmg }, ':')) - self:flush() + TableDB.add(self, { s.id, s.dmg }, table.concat({ s.sid, s.sdmg }, ':')) + self:flush() end function subDB:remove(s) - -- TODO: tableDB.remove should take table key - TableDB.remove(self, s.id .. ':' .. s.dmg) - self:flush() + -- TODO: tableDB.remove should take table key + TableDB.remove(self, s.id .. ':' .. s.dmg) + self:flush() end function subDB:extract(s) - local id, dmg = s:match('(.+):(%d+)') - return id, tonumber(dmg) + local id, dmg = s:match('(.+):(%d+)') + return id, tonumber(dmg) end function subDB:getSubstitutedItem(id, dmg) - local sub = TableDB.get(self, { id, dmg }) - if sub then - id, dmg = self:extract(sub) - end - return { id = id, dmg = dmg } + local sub = TableDB.get(self, { id, dmg }) + if sub then + id, dmg = self:extract(sub) + end + return { id = id, dmg = dmg } end function subDB:lookupBlocksForSub(sid, sdmg) - local t = { } - for k,v in pairs(self.data) do - local id, dmg = self:extract(v) - if id == sid and dmg == sdmg then - id, dmg = self:extract(k) - t[k] = { id = id, dmg = dmg, sid = sid, sdmg = sdmg } - end - end - return t + local t = { } + for k,v in pairs(self.data) do + local id, dmg = self:extract(v) + if id == sid and dmg == sdmg then + id, dmg = self:extract(k) + t[k] = { id = id, dmg = dmg, sid = sid, sdmg = sdmg } + end + end + return t end --[[-- startup logic --]]-- local args = {...} if #args < 1 then - error('supply file name') + error('supply file name') end subDB:load() @@ -142,18 +142,18 @@ Builder:substituteBlocks(Util.throttle()) local cn = neural.canvas3d().create() local pos = neural.getMetaOwner().withinBlock -cn.recenter({-(pos.x + .5), -(pos.y + 2) + .5, -(pos.z + .5) }) +cn.recenter({-pos.x + .5, -(pos.y + 2) + .5, -pos.z + .5 }) for i = 1, #Builder.schematic.blocks do - local b = Builder.schematic:getComputedBlock(i) - if b.id ~= "minecraft:air" and b.id ~= 'minecraft:water' then - local s, m = pcall(function() - cn.addItem({ b.x, b.y, b.z }, b.id, b.dmg) - end) - if not s and m then - _G.printError(m) - end - end + local b = Builder.schematic:getComputedBlock(i) + if b.id ~= "minecraft:air" and b.id ~= 'minecraft:water' then + local s, m = pcall(function() + cn.addItem({ b.x, b.y, b.z }, b.id, b.dmg) + end) + if not s and m then + _G.printError(m) + end + end end pcall(_G.read) diff --git a/ccemux/autorun/startup.lua b/ccemux/autorun/startup.lua index e1da271..239b695 100644 --- a/ccemux/autorun/startup.lua +++ b/ccemux/autorun/startup.lua @@ -3,15 +3,15 @@ local fs = _G.fs local peripheral = _G.peripheral if ccemux then - -- add a System setup tab - fs.mount('sys/apps/system/ccemux.lua', 'linkfs', 'packages/ccemux/system/ccemux.lua') + -- add a System setup tab + fs.mount('sys/apps/system/ccemux.lua', 'linkfs', 'packages/ccemux/system/ccemux.lua') - local Config = require('config') + local Config = require('config') - for k,v in pairs(Config.load('ccemux')) do - if not peripheral.getType(k) then - ccemux.attach(k, v.type, v.args) - end + for k,v in pairs(Config.load('ccemux')) do + if not peripheral.getType(k) then + ccemux.attach(k, v.type, v.args) + end end end diff --git a/common/Appstore.lua b/common/Appstore.lua index 1387270..69af19f 100644 --- a/common/Appstore.lua +++ b/common/Appstore.lua @@ -15,18 +15,18 @@ local REGISTRY_DIR = 'usr/.registry' -- FIX SOMEDAY local function registerApp(app, key) - app.key = SHA1.sha1(key) - Util.writeTable(fs.combine(REGISTRY_DIR, app.key), app) - os.queueEvent('os_register_app') + app.key = SHA1.sha1(key) + Util.writeTable(fs.combine(REGISTRY_DIR, app.key), app) + os.queueEvent('os_register_app') end local function unregisterApp(key) - local filename = fs.combine(REGISTRY_DIR, SHA1.sha1(key)) - if fs.exists(filename) then - fs.delete(filename) - os.queueEvent('os_register_app') - end + local filename = fs.combine(REGISTRY_DIR, SHA1.sha1(key)) + if fs.exists(filename) then + fs.delete(filename) + os.queueEvent('os_register_app') + end end @@ -40,366 +40,366 @@ local APP_DIR = 'usr/apps' local sources = { - { text = "STD Default", - event = 'source', - url = "http://pastebin.com/raw/zVws7eLq" }, --stock + { text = "STD Default", + event = 'source', + url = "http://pastebin.com/raw/zVws7eLq" }, --stock --[[ - { text = "Discover", - event = 'source', - generateName = true, - url = "http://pastebin.com/raw/9bXfCz6M" }, --owned by dannysmc95 + { text = "Discover", + event = 'source', + generateName = true, + url = "http://pastebin.com/raw/9bXfCz6M" }, --owned by dannysmc95 - { text = "Opus", - event = 'source', - url = "http://pastebin.com/raw/ajQ91Rmn" }, + { text = "Opus", + event = 'source', + url = "http://pastebin.com/raw/ajQ91Rmn" }, ]] } shell.setDir(APP_DIR) local function downloadApp(app) - local h + local h - if type(app.url) == "table" then - h = contextualGet(app.url[1]) - else - h = http.get(app.url) - end + if type(app.url) == "table" then + h = contextualGet(app.url[1]) + else + h = http.get(app.url) + end - if h then - local contents = h.readAll() - h:close() - return contents - end + if h then + local contents = h.readAll() + h:close() + return contents + end end local function runApp(app, checkExists, ...) - local path, fn - local args = { ... } + local path, fn + local args = { ... } - if checkExists and fs.exists(fs.combine(APP_DIR, app.name)) then - path = fs.combine(APP_DIR, app.name) - else - local program = downloadApp(app) + if checkExists and fs.exists(fs.combine(APP_DIR, app.name)) then + path = fs.combine(APP_DIR, app.name) + else + local program = downloadApp(app) - fn = function() + fn = function() - if not program then - error('Failed to download') - end + if not program then + error('Failed to download') + end - local fn = loadstring(program, app.name) + local fn = loadstring(program, app.name) - if not fn then - error('Failed to download') - end + if not fn then + error('Failed to download') + end - setfenv(fn, sandboxEnv) - fn(unpack(args)) - end - end + setfenv(fn, sandboxEnv) + fn(unpack(args)) + end + end - multishell.openTab({ - title = app.name, - env = sandboxEnv, - path = path, - fn = fn, - focused = true, - }) + multishell.openTab({ + title = app.name, + env = sandboxEnv, + path = path, + fn = fn, + focused = true, + }) - return true, 'Running program' + return true, 'Running program' end local installApp = function(app) - local program = downloadApp(app) - if not program then - return false, "Failed to download" - end + local program = downloadApp(app) + if not program then + return false, "Failed to download" + end - local fullPath = fs.combine(APP_DIR, app.name) - Util.writeFile(fullPath, program) - return true, 'Installed as ' .. fullPath + local fullPath = fs.combine(APP_DIR, app.name) + Util.writeFile(fullPath, program) + return true, 'Installed as ' .. fullPath end local viewApp = function(app) - local program = downloadApp(app) - if not program then - return false, "Failed to download" - end + local program = downloadApp(app) + if not program then + return false, "Failed to download" + end - Util.writeFile('/.source', program) - shell.openForegroundTab('edit /.source') - fs.delete('/.source') - return true + Util.writeFile('/.source', program) + shell.openForegroundTab('edit /.source') + fs.delete('/.source') + return true end local getSourceListing = function(source) - local contents = http.get(source.url) - if contents then + local contents = http.get(source.url) + if contents then - local fn = loadstring(contents.readAll(), source.text) - contents.close() + local fn = loadstring(contents.readAll(), source.text) + contents.close() - local env = { std = { } } - setmetatable(env, { __index = _G }) - setfenv(fn, env) - fn() + local env = { std = { } } + setmetatable(env, { __index = _G }) + setfenv(fn, env) + fn() - if env.contextualGet then - contextualGet = env.contextualGet - end + if env.contextualGet then + contextualGet = env.contextualGet + end - source.storeURLs = env.std.storeURLs - source.storeCatagoryNames = env.std.storeCatagoryNames + source.storeURLs = env.std.storeURLs + source.storeCatagoryNames = env.std.storeCatagoryNames - if source.storeURLs and source.storeCatagoryNames then - for k,v in pairs(source.storeURLs) do - if source.generateName then - v.name = v.title:match('(%w+)') - if not v.name or #v.name == 0 then - v.name = tostring(k) - else - v.name = v.name:lower() - end - else - v.name = k - end - v.categoryName = source.storeCatagoryNames[v.catagory] - v.ltitle = v.title:lower() - end - end - end + if source.storeURLs and source.storeCatagoryNames then + for k,v in pairs(source.storeURLs) do + if source.generateName then + v.name = v.title:match('(%w+)') + if not v.name or #v.name == 0 then + v.name = tostring(k) + else + v.name = v.name:lower() + end + else + v.name = k + end + v.categoryName = source.storeCatagoryNames[v.catagory] + v.ltitle = v.title:lower() + end + end + end end local appPage = UI.Page { - menuBar = UI.MenuBar { + menuBar = UI.MenuBar { -- showBackButton = not pocket, - buttons = { - { text = '\027', event = 'back' }, - { text = 'Install', event = 'install' }, - { text = 'Run', event = 'run' }, - { text = 'View', event = 'view' }, - { text = 'Remove', event = 'uninstall', name = 'removeButton' }, - }, - }, - container = UI.Window { - x = 2, y = 3, ex = -2, ey = -3, - viewport = UI.Viewport(), - }, - notification = UI.Notification(), - accelerators = { - q = 'back', - backspace = 'back', - }, + buttons = { + { text = '\027', event = 'back' }, + { text = 'Install', event = 'install' }, + { text = 'Run', event = 'run' }, + { text = 'View', event = 'view' }, + { text = 'Remove', event = 'uninstall', name = 'removeButton' }, + }, + }, + container = UI.Window { + x = 2, y = 3, ex = -2, ey = -3, + viewport = UI.Viewport(), + }, + notification = UI.Notification(), + accelerators = { + q = 'back', + backspace = 'back', + }, } function appPage.container.viewport:draw() - local app = self.parent.parent.app - local str = string.format( - '%s \nBy: %s \nCategory: %s\nFile name: %s\n\n%s', - Ansi.yellow .. app.title .. Ansi.reset, - app.creator, - app.categoryName, app.name, - Ansi.yellow .. app.description .. Ansi.reset) + local app = self.parent.parent.app + local str = string.format( + '%s \nBy: %s \nCategory: %s\nFile name: %s\n\n%s', + Ansi.yellow .. app.title .. Ansi.reset, + app.creator, + app.categoryName, app.name, + Ansi.yellow .. app.description .. Ansi.reset) - self:clear() - self:setCursorPos(1, 1) - self:print(str) - self.ymax = self.cursorY + self:clear() + self:setCursorPos(1, 1) + self:print(str) + self.ymax = self.cursorY - if appPage.notification.enabled then - appPage.notification:draw() - end + if appPage.notification.enabled then + appPage.notification:draw() + end end function appPage:enable(source, app) - self.source = source - self.app = app - UI.Page.enable(self) + self.source = source + self.app = app + UI.Page.enable(self) - self.container.viewport:setScrollPosition(0) - if fs.exists(fs.combine(APP_DIR, app.name)) then - self.menuBar.removeButton:enable('Remove') - else - self.menuBar.removeButton:disable('Remove') - end + self.container.viewport:setScrollPosition(0) + if fs.exists(fs.combine(APP_DIR, app.name)) then + self.menuBar.removeButton:enable('Remove') + else + self.menuBar.removeButton:disable('Remove') + end end function appPage:eventHandler(event) - if event.type == 'back' then - UI:setPreviousPage() + if event.type == 'back' then + UI:setPreviousPage() - elseif event.type == 'run' then - self.notification:info('Running program', 3) - self:sync() - runApp(self.app, true) + elseif event.type == 'run' then + self.notification:info('Running program', 3) + self:sync() + runApp(self.app, true) - elseif event.type == 'view' then - self.notification:info('Downloading program', 3) - self:sync() - viewApp(self.app) + elseif event.type == 'view' then + self.notification:info('Downloading program', 3) + self:sync() + viewApp(self.app) - elseif event.type == 'uninstall' then - if self.app.runOnly then - runApp(self.app, false, 'uninstall') - else - fs.delete(fs.combine(APP_DIR, self.app.name)) - self.notification:success("Uninstalled " .. self.app.name, 3) - self:focusFirst(self) - self.menuBar.removeButton:disable('Remove') - self.menuBar:draw() + elseif event.type == 'uninstall' then + if self.app.runOnly then + runApp(self.app, false, 'uninstall') + else + fs.delete(fs.combine(APP_DIR, self.app.name)) + self.notification:success("Uninstalled " .. self.app.name, 3) + self:focusFirst(self) + self.menuBar.removeButton:disable('Remove') + self.menuBar:draw() - unregisterApp(self.app.creator .. '.' .. self.app.name) - end + unregisterApp(self.app.creator .. '.' .. self.app.name) + end - elseif event.type == 'install' then - self.notification:info("Installing", 3) - self:sync() - local s, m - if self.app.runOnly then - s,m = runApp(self.app, false) - else - s,m = installApp(self.app) - end - if s then - self.notification:success(m, 3) + elseif event.type == 'install' then + self.notification:info("Installing", 3) + self:sync() + local s, m + if self.app.runOnly then + s,m = runApp(self.app, false) + else + s,m = installApp(self.app) + end + if s then + self.notification:success(m, 3) - if not self.app.runOnly then - self.menuBar.removeButton:enable('Remove') - self.menuBar:draw() + if not self.app.runOnly then + self.menuBar.removeButton:enable('Remove') + self.menuBar:draw() - local category = 'Apps' - if self.app.catagoryName == 'Game' then - category = 'Games' - end + local category = 'Apps' + if self.app.catagoryName == 'Game' then + category = 'Games' + end - registerApp({ - run = fs.combine(APP_DIR, self.app.name), - title = self.app.title, - category = category, - icon = self.app.icon, - }, self.app.creator .. '.' .. self.app.name) - end - else - self.notification:error(m, 3) - end - else - return UI.Page.eventHandler(self, event) - end - return true + registerApp({ + run = fs.combine(APP_DIR, self.app.name), + title = self.app.title, + category = category, + icon = self.app.icon, + }, self.app.creator .. '.' .. self.app.name) + end + else + self.notification:error(m, 3) + end + else + return UI.Page.eventHandler(self, event) + end + return true end local categoryPage = UI.Page { - menuBar = UI.MenuBar { - buttons = { - { text = 'Catalog', dropdown = sources }, - { text = 'Category', name = 'categoryButton', dropdown = { } }, - }, - }, - grid = UI.ScrollingGrid { - y = 2, ey = -2, - columns = { - { heading = 'Title', key = 'title' }, - }, - sortColumn = 'title', - }, - statusBar = UI.StatusBar(), - accelerators = { - l = 'lua', - q = 'quit', - }, + menuBar = UI.MenuBar { + buttons = { + { text = 'Catalog', dropdown = sources }, + { text = 'Category', name = 'categoryButton', dropdown = { } }, + }, + }, + grid = UI.ScrollingGrid { + y = 2, ey = -2, + columns = { + { heading = 'Title', key = 'title' }, + }, + sortColumn = 'title', + }, + statusBar = UI.StatusBar(), + accelerators = { + l = 'lua', + q = 'quit', + }, } function categoryPage:setCategory(source, name, index) - self.grid.values = { } - for _,v in pairs(source.storeURLs) do - if index == 0 or index == v.catagory then - table.insert(self.grid.values, v) - end - end - self.statusBar:setStatus(string.format('%s: %s', source.text, name)) - self.grid:update() - self.grid:setIndex(1) + self.grid.values = { } + for _,v in pairs(source.storeURLs) do + if index == 0 or index == v.catagory then + table.insert(self.grid.values, v) + end + end + self.statusBar:setStatus(string.format('%s: %s', source.text, name)) + self.grid:update() + self.grid:setIndex(1) end function categoryPage:setSource(source) - if not source.categoryMenu then + if not source.categoryMenu then - self.statusBar:setStatus('Loading...') - self.statusBar:draw() - self:sync() + self.statusBar:setStatus('Loading...') + self.statusBar:draw() + self:sync() - getSourceListing(source) + getSourceListing(source) - if not source.storeURLs then - error('Unable to download application list') - end + if not source.storeURLs then + error('Unable to download application list') + end - local buttons = { } - for k,v in Util.spairs(source.storeCatagoryNames, - function(a, b) return a:lower() < b:lower() end) do + local buttons = { } + for k,v in Util.spairs(source.storeCatagoryNames, + function(a, b) return a:lower() < b:lower() end) do - if v ~= 'Operating System' then - table.insert(buttons, { - text = v, - event = 'category', - index = k, - }) - end - end + if v ~= 'Operating System' then + table.insert(buttons, { + text = v, + event = 'category', + index = k, + }) + end + end - source.categoryMenu = UI.DropMenu({ - buttons = buttons, - }) - source.index, source.name = Util.first(source.storeCatagoryNames) + source.categoryMenu = UI.DropMenu({ + buttons = buttons, + }) + source.index, source.name = Util.first(source.storeCatagoryNames) - categoryPage.menuBar.categoryButton:add({ - categoryMenu = source.categoryMenu - }) - end + categoryPage.menuBar.categoryButton:add({ + categoryMenu = source.categoryMenu + }) + end - self.source = source - self.menuBar.categoryButton.dropmenu = source.categoryMenu - categoryPage:setCategory(source, source.name, source.index) + self.source = source + self.menuBar.categoryButton.dropmenu = source.categoryMenu + categoryPage:setCategory(source, source.name, source.index) end function categoryPage.grid:sortCompare(a, b) - return a.ltitle < b.ltitle + return a.ltitle < b.ltitle end function categoryPage.grid:getRowTextColor(row, selected) - if fs.exists(fs.combine(APP_DIR, row.name)) then - return colors.orange - end - return UI.Grid:getRowTextColor(row, selected) + if fs.exists(fs.combine(APP_DIR, row.name)) then + return colors.orange + end + return UI.Grid:getRowTextColor(row, selected) end function categoryPage:eventHandler(event) - if event.type == 'grid_select' or event.type == 'select' then - UI:setPage(appPage, self.source, self.grid:getSelected()) + if event.type == 'grid_select' or event.type == 'select' then + UI:setPage(appPage, self.source, self.grid:getSelected()) - elseif event.type == 'category' then - self:setCategory(self.source, event.button.text, event.button.index) - self:setFocus(self.grid) - self:draw() + elseif event.type == 'category' then + self:setCategory(self.source, event.button.text, event.button.index) + self:setFocus(self.grid) + self:draw() - elseif event.type == 'source' then - self:setFocus(self.grid) - self:setSource(event.button) - self:draw() + elseif event.type == 'source' then + self:setFocus(self.grid) + self:setSource(event.button) + self:draw() - elseif event.type == 'quit' then - UI:exitPullEvents() + elseif event.type == 'quit' then + UI:exitPullEvents() - else - return UI.Page.eventHandler(self, event) - end - return true + else + return UI.Page.eventHandler(self, event) + end + return true end print("Retrieving catalog list") diff --git a/common/Devices.lua b/common/Devices.lua index db83f37..ccc8159 100644 --- a/common/Devices.lua +++ b/common/Devices.lua @@ -10,196 +10,196 @@ local peripheral = _G.peripheral --[[ -- PeripheralsPage -- ]] -- local peripheralsPage = UI.Page { - grid = UI.ScrollingGrid { - ey = -2, - columns = { - { heading = 'Type', key = 'type' }, - { heading = 'Side', key = 'side' }, - }, - sortColumn = 'type', - autospace = true, - }, - statusBar = UI.StatusBar { - values = 'Select peripheral', - }, - accelerators = { - q = 'quit', - }, + grid = UI.ScrollingGrid { + ey = -2, + columns = { + { heading = 'Type', key = 'type' }, + { heading = 'Side', key = 'side' }, + }, + sortColumn = 'type', + autospace = true, + }, + statusBar = UI.StatusBar { + values = 'Select peripheral', + }, + accelerators = { + q = 'quit', + }, } function peripheralsPage.grid:draw() - local sides = peripheral.getNames() + local sides = peripheral.getNames() - Util.clear(self.values) - for _,side in pairs(sides) do - table.insert(self.values, { - type = peripheral.getType(side), - side = side - }) - end - self:update() - self:adjustWidth() - UI.Grid.draw(self) + Util.clear(self.values) + for _,side in pairs(sides) do + table.insert(self.values, { + type = peripheral.getType(side), + side = side + }) + end + self:update() + self:adjustWidth() + UI.Grid.draw(self) end function peripheralsPage:updatePeripherals() - if UI:getCurrentPage() == self then - self.grid:draw() - self:sync() - end + if UI:getCurrentPage() == self then + self.grid:draw() + self:sync() + end end function peripheralsPage:eventHandler(event) - if event.type == 'quit' then - Event.exitPullEvents() + if event.type == 'quit' then + Event.exitPullEvents() - elseif event.type == 'grid_select' then - UI:setPage('methods', event.selected) + elseif event.type == 'grid_select' then + UI:setPage('methods', event.selected) - end - return UI.Page.eventHandler(self, event) + end + return UI.Page.eventHandler(self, event) end --[[ -- MethodsPage -- ]] -- local methodsPage = UI.Page { - backgroundColor = colors.black, - doc = UI.TextArea { - backgroundColor = colors.black, - x = 2, y = 2, ex = -1, ey = -7, - }, - grid = UI.ScrollingGrid { - y = -6, ey = -2, - columns = { - { heading = 'Name', key = 'name', width = UI.term.width } - }, - sortColumn = 'name', - }, - statusBar = UI.StatusBar { - status = 'q to return', - }, - accelerators = { - q = 'back', - backspace = 'back', - }, + backgroundColor = colors.black, + doc = UI.TextArea { + backgroundColor = colors.black, + x = 2, y = 2, ex = -1, ey = -7, + }, + grid = UI.ScrollingGrid { + y = -6, ey = -2, + columns = { + { heading = 'Name', key = 'name', width = UI.term.width } + }, + sortColumn = 'name', + }, + statusBar = UI.StatusBar { + status = 'q to return', + }, + accelerators = { + q = 'back', + backspace = 'back', + }, } function methodsPage:enable(p) - self.peripheral = p or self.peripheral + self.peripheral = p or self.peripheral - p = peripheral.wrap(self.peripheral.side) - if p.getDocs then - -- plethora - self.grid.values = { } - for k,v in pairs(p.getDocs()) do - table.insert(self.grid.values, { - name = k, - doc = v, - }) - end - elseif not p.getAdvancedMethodsData then - -- computercraft - self.grid.values = { } - for name in pairs(p) do - table.insert(self.grid.values, { - name = name, - noext = true, - }) - end - else - -- open peripherals - self.grid.values = p.getAdvancedMethodsData() - for name,f in pairs(self.grid.values) do - f.name = name - end - end + p = peripheral.wrap(self.peripheral.side) + if p.getDocs then + -- plethora + self.grid.values = { } + for k,v in pairs(p.getDocs()) do + table.insert(self.grid.values, { + name = k, + doc = v, + }) + end + elseif not p.getAdvancedMethodsData then + -- computercraft + self.grid.values = { } + for name in pairs(p) do + table.insert(self.grid.values, { + name = name, + noext = true, + }) + end + else + -- open peripherals + self.grid.values = p.getAdvancedMethodsData() + for name,f in pairs(self.grid.values) do + f.name = name + end + end - self.grid:update() - self.grid:setIndex(1) + self.grid:update() + self.grid:setIndex(1) - self.doc:setText(self:getDocumentation()) + self.doc:setText(self:getDocumentation()) - self.statusBar:setStatus(self.peripheral.type) - UI.Page.enable(self) + self.statusBar:setStatus(self.peripheral.type) + UI.Page.enable(self) - self:setFocus(self.grid) + self:setFocus(self.grid) end function methodsPage:eventHandler(event) - if event.type == 'back' then - UI:setPage(peripheralsPage) - return true - elseif event.type == 'grid_focus_row' then - self.doc:setText(self:getDocumentation()) - end - return UI.Page.eventHandler(self, event) + if event.type == 'back' then + UI:setPage(peripheralsPage) + return true + elseif event.type == 'grid_focus_row' then + self.doc:setText(self:getDocumentation()) + end + return UI.Page.eventHandler(self, event) end function methodsPage:getDocumentation() - local method = self.grid:getSelected() + local method = self.grid:getSelected() - if method.noext then -- computercraft docs - return 'No documentation' - end + if method.noext then -- computercraft docs + return 'No documentation' + end - if method.doc then -- plethora docs - return Ansi.yellow .. method.doc - end + if method.doc then -- plethora docs + return Ansi.yellow .. method.doc + end - -- open peripherals docs - local sb = { } - if method.description then - table.insert(sb, method.description .. '\n\n') - end + -- open peripherals docs + local sb = { } + if method.description then + table.insert(sb, method.description .. '\n\n') + end - if method.returnTypes ~= '()' then - table.insert(sb, Ansi.yellow .. method.returnTypes .. ' ') - end - table.insert(sb, Ansi.blue .. method.name .. Ansi.reset .. '(') + if method.returnTypes ~= '()' then + table.insert(sb, Ansi.yellow .. method.returnTypes .. ' ') + end + table.insert(sb, Ansi.blue .. method.name .. Ansi.reset .. '(') - for k,arg in ipairs(method.args) do - if arg.optional then - table.insert(sb, Ansi.orange .. string.format('[%s]', arg.name)) - else - table.insert(sb, Ansi.green .. arg.name) - end - if k < #method.args then - table.insert(sb, ',') - end - end - table.insert(sb, Ansi.reset .. ')') + for k,arg in ipairs(method.args) do + if arg.optional then + table.insert(sb, Ansi.orange .. string.format('[%s]', arg.name)) + else + table.insert(sb, Ansi.green .. arg.name) + end + if k < #method.args then + table.insert(sb, ',') + end + end + table.insert(sb, Ansi.reset .. ')') - Util.filterInplace(method.args, function(a) return #a.description > 0 end) - if #method.args > 0 then - table.insert(sb, '\n\n') - for k,arg in ipairs(method.args) do - if arg.optional then - table.insert(sb, Ansi.orange) - else - table.insert(sb, Ansi.green) - end - table.insert(sb, arg.name .. Ansi.reset .. ': ' .. arg.description) - if k ~= #method.args then - table.insert(sb, '\n\n') - end - end - end - return table.concat(sb) + Util.filterInplace(method.args, function(a) return #a.description > 0 end) + if #method.args > 0 then + table.insert(sb, '\n\n') + for k,arg in ipairs(method.args) do + if arg.optional then + table.insert(sb, Ansi.orange) + else + table.insert(sb, Ansi.green) + end + table.insert(sb, arg.name .. Ansi.reset .. ': ' .. arg.description) + if k ~= #method.args then + table.insert(sb, '\n\n') + end + end + end + return table.concat(sb) end Event.on('peripheral', function() - peripheralsPage:updatePeripherals() + peripheralsPage:updatePeripherals() end) Event.on('peripheral_detach', function() - peripheralsPage:updatePeripherals() + peripheralsPage:updatePeripherals() end) UI:setPage(peripheralsPage) UI:setPages({ - methods = methodsPage, + methods = methodsPage, }) UI:pullEvents() diff --git a/common/DiskCopy.lua b/common/DiskCopy.lua index c59976f..ce17f2b 100644 --- a/common/DiskCopy.lua +++ b/common/DiskCopy.lua @@ -10,230 +10,230 @@ local peripheral = _G.peripheral local drives = { } peripheral.find('drive', function(n, v) - if not drives.left then - drives.left = Util.shallowCopy(v) - drives.left.name = n - else - drives.right = Util.shallowCopy(v) - drives.right.name = n - end + if not drives.left then + drives.left = Util.shallowCopy(v) + drives.left.name = n + else + drives.right = Util.shallowCopy(v) + drives.right.name = n + end end) if not (drives.left and drives.right) then - error('Two drives are required') + error('Two drives are required') end local COPY_LEFT = 1 local COPY_RIGHT = 2 local directions = { - [ COPY_LEFT ] = { text = '-->>' }, - [ COPY_RIGHT ] = { text = '<<--' }, + [ COPY_LEFT ] = { text = '-->>' }, + [ COPY_RIGHT ] = { text = '<<--' }, } local config = Config.load('DiskCopy', { - eject = true, - automatic = false, - copyDir = COPY_LEFT + eject = true, + automatic = false, + copyDir = COPY_LEFT }) local page = UI.Page { - linfo = UI.Window { - x = 2, y = 2, ey = 5, width = 18, - }, - rinfo = UI.Window { - x = -19, y = 2, ey = 5, width = 18, - }, - dir = UI.Button { - x = 17, y = 7, width = 6, - event = 'change_dir', - }, - progress = UI.ProgressBar { - x = 2, ex = -2, y = -4, - backgroundColor = colors.black, - }, - ejectText = UI.Text { - x = 2, y = -2, - value = 'Eject' - }, - eject = UI.Checkbox { - x = 8, y = -2, - }, - automaticText = UI.Text { - x = 12, y = -2, - value = 'Copy automatically' - }, - automatic = UI.Checkbox { - x = 31, y = -2, - }, - copyButton = UI.Button { - x = -7, y = -2, - text = 'Copy', - event = 'copy', - inactive = true, - }, - warning = UI.Text { - x = 2, ex = -2, y = -5, - align = 'center', - textColor = colors.orange, - }, - notification = UI.Notification { }, + linfo = UI.Window { + x = 2, y = 2, ey = 5, width = 18, + }, + rinfo = UI.Window { + x = -19, y = 2, ey = 5, width = 18, + }, + dir = UI.Button { + x = 17, y = 7, width = 6, + event = 'change_dir', + }, + progress = UI.ProgressBar { + x = 2, ex = -2, y = -4, + backgroundColor = colors.black, + }, + ejectText = UI.Text { + x = 2, y = -2, + value = 'Eject' + }, + eject = UI.Checkbox { + x = 8, y = -2, + }, + automaticText = UI.Text { + x = 12, y = -2, + value = 'Copy automatically' + }, + automatic = UI.Checkbox { + x = 31, y = -2, + }, + copyButton = UI.Button { + x = -7, y = -2, + text = 'Copy', + event = 'copy', + inactive = true, + }, + warning = UI.Text { + x = 2, ex = -2, y = -5, + align = 'center', + textColor = colors.orange, + }, + notification = UI.Notification { }, } function page:enable() - Util.merge(self.dir, directions[config.copyDir]) + Util.merge(self.dir, directions[config.copyDir]) - self.eject.value = config.eject - self.automatic.value = config.automatic + self.eject.value = config.eject + self.automatic.value = config.automatic - self.dir.x = math.floor((self.width / 2) - 3) + 1 + self.dir.x = math.floor((self.width / 2) - 3) + 1 - UI.Page.enable(self) + UI.Page.enable(self) end local function isValid(drive) - return drive.isDiskPresent() and drive.getMountPath() + return drive.isDiskPresent() and drive.getMountPath() end local function needsLabel(drive) - return drive.isDiskPresent() and not drive.getMountPath() and not drive.getAudioTitle() + return drive.isDiskPresent() and not drive.getMountPath() and not drive.getAudioTitle() end function page:drawInfo(drive, textArea) - local function getLabel() - return not drive.isDiskPresent() and 'empty' or - not drive.getMountPath() and 'invalid' or - drive.getDiskLabel() or 'unlabeled' - end + local function getLabel() + return not drive.isDiskPresent() and 'empty' or + not drive.getMountPath() and 'invalid' or + drive.getDiskLabel() or 'unlabeled' + end - local function getUsed() - return isValid(drive) and fs.getSize(drive.getMountPath(), true) or 0 - end + local function getUsed() + return isValid(drive) and fs.getSize(drive.getMountPath(), true) or 0 + end - local function getFree() - return isValid(drive) and fs.getFreeSpace(drive.getMountPath()) or 0 - end + local function getFree() + return isValid(drive) and fs.getFreeSpace(drive.getMountPath()) or 0 + end - textArea:setCursorPos(1, 1) - textArea:print(string.format('Drive: %s%s%s\nLabel: %s%s%s\nUsed: %s%s%s\nFree: %s%s%s', - Ansi.yellow, drive.name, Ansi.reset, - isValid(drive) and Ansi.yellow or Ansi.orange, getLabel():sub(1, 10), Ansi.reset, - Ansi.yellow, Util.toBytes(getUsed()), Ansi.reset, - Ansi.yellow, Util.toBytes(getFree()), Ansi.reset)) + textArea:setCursorPos(1, 1) + textArea:print(string.format('Drive: %s%s%s\nLabel: %s%s%s\nUsed: %s%s%s\nFree: %s%s%s', + Ansi.yellow, drive.name, Ansi.reset, + isValid(drive) and Ansi.yellow or Ansi.orange, getLabel():sub(1, 10), Ansi.reset, + Ansi.yellow, Util.toBytes(getUsed()), Ansi.reset, + Ansi.yellow, Util.toBytes(getFree()), Ansi.reset)) end function page:scan() - local showWarning = needsLabel(drives.left) or needsLabel(drives.right) - local valid = isValid(drives.left) and isValid(drives.right) + local showWarning = needsLabel(drives.left) or needsLabel(drives.right) + local valid = isValid(drives.left) and isValid(drives.right) - self.warning.value = showWarning and 'Computers must be labeled' - self.copyButton.inactive = not valid + self.warning.value = showWarning and 'Computers must be labeled' + self.copyButton.inactive = not valid - self:draw() - self.progress:centeredWrite(1, 'Analyzing Disks..') - self.progress:sync() + self:draw() + self.progress:centeredWrite(1, 'Analyzing Disks..') + self.progress:sync() - self:drawInfo(drives.left, self.linfo) - self:drawInfo(drives.right, self.rinfo) + self:drawInfo(drives.left, self.linfo) + self:drawInfo(drives.right, self.rinfo) - self.progress:clear() + self.progress:clear() end function page:copy() - local sdrive = config.copyDir == COPY_LEFT and drives.left or drives.right - local tdrive = config.copyDir == COPY_LEFT and drives.right or drives.left + local sdrive = config.copyDir == COPY_LEFT and drives.left or drives.right + local tdrive = config.copyDir == COPY_LEFT and drives.right or drives.left - local throttle = Util.throttle() - local sourceFiles, targetFiles = { }, { } + local throttle = Util.throttle() + local sourceFiles, targetFiles = { }, { } - local function getListing(mountPath, path, files) - for _,f in pairs(fs.list(path)) do - local file = fs.combine(path, f) - if not fs.isReadOnly(file) then - files[string.sub(file, #mountPath + 1)] = true - if fs.isDir(file) then - getListing(mountPath, file, files) - end - end - end - throttle() - end + local function getListing(mountPath, path, files) + for _,f in pairs(fs.list(path)) do + local file = fs.combine(path, f) + if not fs.isReadOnly(file) then + files[string.sub(file, #mountPath + 1)] = true + if fs.isDir(file) then + getListing(mountPath, file, files) + end + end + end + throttle() + end - self.progress:centeredWrite(1, 'Computing..') - self.progress:sync() + self.progress:centeredWrite(1, 'Computing..') + self.progress:sync() - getListing(sdrive.getMountPath(), sdrive.getMountPath(), sourceFiles) - getListing(tdrive.getMountPath(), tdrive.getMountPath(), targetFiles) + getListing(sdrive.getMountPath(), sdrive.getMountPath(), sourceFiles) + getListing(tdrive.getMountPath(), tdrive.getMountPath(), targetFiles) - local copied = 0 - local totalFiles = Util.size(sourceFiles) + local copied = 0 + local totalFiles = Util.size(sourceFiles) - local function rawCopy(source, target) - if fs.isDir(source) then - copied = copied + 1 - if not fs.exists(target) then - fs.makeDir(target) - end - for _,f in pairs(fs.list(source)) do - rawCopy(fs.combine(source, f), fs.combine(target, f)) - end + local function rawCopy(source, target) + if fs.isDir(source) then + copied = copied + 1 + if not fs.exists(target) then + fs.makeDir(target) + end + for _,f in pairs(fs.list(source)) do + rawCopy(fs.combine(source, f), fs.combine(target, f)) + end - else - if fs.exists(target) then - fs.delete(target) - end + else + if fs.exists(target) then + fs.delete(target) + end - fs.copy(source, target) - copied = copied + 1 - self.progress.value = copied * 100 / totalFiles - self.progress:draw() - self.progress:sync() - end - throttle() - end + fs.copy(source, target) + copied = copied + 1 + self.progress.value = copied * 100 / totalFiles + self.progress:draw() + self.progress:sync() + end + throttle() + end - local function cleanup() - for k in pairs(targetFiles) do - if not sourceFiles[k] then - fs.delete(fs.combine(tdrive.getMountPath(), k)) - end - end - end + local function cleanup() + for k in pairs(targetFiles) do + if not sourceFiles[k] then + fs.delete(fs.combine(tdrive.getMountPath(), k)) + end + end + end - self.progress:clear() - rawCopy(sdrive.getMountPath(), tdrive.getMountPath()) - cleanup() - self.progress:centeredWrite(1, 'Copy Complete', colors.lime, colors.black) - self.progress:sync() + self.progress:clear() + rawCopy(sdrive.getMountPath(), tdrive.getMountPath()) + cleanup() + self.progress:centeredWrite(1, 'Copy Complete', colors.lime, colors.black) + self.progress:sync() - self.progress.value = 0 - self.progress:clear() + self.progress.value = 0 + self.progress:clear() - self:scan() + self:scan() - if config.eject then - tdrive.ejectDisk() - end + if config.eject then + tdrive.ejectDisk() + end end function page:eventHandler(event) - if event.type == 'change_dir' then - config.copyDir = (config.copyDir) % 2 + 1 - Util.merge(self.dir, directions[config.copyDir]) - Config.update('DiskCopy', config) - self.dir:draw() + if event.type == 'change_dir' then + config.copyDir = (config.copyDir) % 2 + 1 + Util.merge(self.dir, directions[config.copyDir]) + Config.update('DiskCopy', config) + self.dir:draw() - elseif event.type == 'copy' then - self:copy() + elseif event.type == 'copy' then + self:copy() - elseif event.type == 'checkbox_change' then - if event.element == self.eject then - config.eject = not not event.checked - elseif event.element == self.automatic then - config.automatic = not not event.checked - end + elseif event.type == 'checkbox_change' then + if event.element == self.eject then + config.eject = not not event.checked + elseif event.element == self.automatic then + config.automatic = not not event.checked + end - Config.update('DiskCopy', config) - event.element:draw() + Config.update('DiskCopy', config) + event.element:draw() else return UI.Page.eventHandler(self, event) @@ -242,22 +242,22 @@ function page:eventHandler(event) end Event.on("disk", function() - page:scan() - page:sync() + page:scan() + page:sync() - if config.automatic and not page.copyButton.inactive then - page:copy() - end + if config.automatic and not page.copyButton.inactive then + page:copy() + end end) Event.on("disk_eject", function() - page:scan() - page:sync() + page:scan() + page:sync() end) Event.onTimeout(.2, function() - page:scan() - page:sync() + page:scan() + page:sync() end) UI:setPage(page) diff --git a/common/Events.lua b/common/Events.lua index 59fabaa..ac9d8ba 100644 --- a/common/Events.lua +++ b/common/Events.lua @@ -8,131 +8,131 @@ local os = _G.os UI:configure('Events', ...) local page = UI.Page { - menuBar = UI.MenuBar { - buttons = { - { text = 'Filter', event = 'filter' }, - { text = 'Reset', event = 'reset' }, - { text = 'Pause ', event = 'toggle', name = 'pauseButton' }, - }, - }, - grid = UI.Grid { - y = 2, - columns = { - { key = 'event' }, - { key = 'p1' }, - { key = 'p2' }, - { key = 'p3' }, - { key = 'p4' }, - { key = 'p5' }, - }, - autospace = true, - disableHeader = true, - }, - accelerators = { - f = 'filter', - p = 'toggle', - r = 'reset', - c = 'clear', - q = 'quit', - }, - filtered = { }, + menuBar = UI.MenuBar { + buttons = { + { text = 'Filter', event = 'filter' }, + { text = 'Reset', event = 'reset' }, + { text = 'Pause ', event = 'toggle', name = 'pauseButton' }, + }, + }, + grid = UI.Grid { + y = 2, + columns = { + { key = 'event' }, + { key = 'p1' }, + { key = 'p2' }, + { key = 'p3' }, + { key = 'p4' }, + { key = 'p5' }, + }, + autospace = true, + disableHeader = true, + }, + accelerators = { + f = 'filter', + p = 'toggle', + r = 'reset', + c = 'clear', + q = 'quit', + }, + filtered = { }, } function page:eventHandler(event) - if event.type == 'filter' then - local entry = self.grid:getSelected() - self.filtered[entry.event] = true + if event.type == 'filter' then + local entry = self.grid:getSelected() + self.filtered[entry.event] = true - elseif event.type == 'toggle' then - self.paused = not self.paused - if self.paused then - self.menuBar.pauseButton.text = 'Resume' - else - self.menuBar.pauseButton.text = 'Pause ' - end - self.menuBar:draw() + elseif event.type == 'toggle' then + self.paused = not self.paused + if self.paused then + self.menuBar.pauseButton.text = 'Resume' + else + self.menuBar.pauseButton.text = 'Pause ' + end + self.menuBar:draw() - elseif event.type == 'grid_select' then - multishell.openTab({ - path = 'sys/apps/Lua.lua', - args = { event.selected }, - focused = true, - }) + elseif event.type == 'grid_select' then + multishell.openTab({ + path = 'sys/apps/Lua.lua', + args = { event.selected }, + focused = true, + }) - elseif event.type == 'reset' then - self.filtered = { } - self.grid:setValues({ }) - self.grid:draw() - if self.paused then - self:emit({ type = 'toggle' }) - end + elseif event.type == 'reset' then + self.filtered = { } + self.grid:setValues({ }) + self.grid:draw() + if self.paused then + self:emit({ type = 'toggle' }) + end - elseif event.type == 'clear' then - self.grid:setValues({ }) - self.grid:draw() + elseif event.type == 'clear' then + self.grid:setValues({ }) + self.grid:draw() - elseif event.type == 'quit' then - UI:exitPullEvents() + elseif event.type == 'quit' then + UI:exitPullEvents() - --[[ - elseif event.type == 'focus_change' then - if event.focused == self.grid then - if not self.paused then - self:emit({ type = 'toggle' }) - end - end - --]] + --[[ + elseif event.type == 'focus_change' then + if event.focused == self.grid then + if not self.paused then + self:emit({ type = 'toggle' }) + end + end + --]] - else - return UI.Page.eventHandler(self, event) - end - return true + else + return UI.Page.eventHandler(self, event) + end + return true end function page.grid:getDisplayValues(row) - row = Util.shallowCopy(row) + row = Util.shallowCopy(row) - local function tovalue(s) - if type(s) == 'table' then - return 'table' - end - return s - end + local function tovalue(s) + if type(s) == 'table' then + return 'table' + end + return s + end - for k,v in pairs(row) do - row[k] = tovalue(v) - end + for k,v in pairs(row) do + row[k] = tovalue(v) + end - return row + return row end function page.grid:draw() - self:adjustWidth() - UI.Grid.draw(self) + self:adjustWidth() + UI.Grid.draw(self) end Event.addRoutine(function() - while true do - local e = { os.pullEvent() } - if not page.paused and not page.filtered[e[1]] then - table.insert(page.grid.values, 1, { - event = e[1], - p1 = e[2], - p2 = e[3], - p3 = e[4], - p4 = e[5], - p5 = e[6], - }) - if #page.grid.values > page.grid.height then - table.remove(page.grid.values, #page.grid.values) - end - page.grid:update() - page.grid:draw() - page:sync() - end - end + while true do + local e = { os.pullEvent() } + if not page.paused and not page.filtered[e[1]] then + table.insert(page.grid.values, 1, { + event = e[1], + p1 = e[2], + p2 = e[3], + p3 = e[4], + p4 = e[5], + p5 = e[6], + }) + if #page.grid.values > page.grid.height then + table.remove(page.grid.values, #page.grid.values) + end + page.grid:update() + page.grid:draw() + page:sync() + end + end end) UI:setPage(page) diff --git a/common/Follow.lua b/common/Follow.lua index db63c07..41554d3 100644 --- a/common/Follow.lua +++ b/common/Follow.lua @@ -15,32 +15,64 @@ local gpt = GPS.getPoint() or error('GPS not found') local pts, blocks local page = UI.Page { - mode = UI.Chooser { - x = 13, y = -1, - choices = { - { name = 'No breaking', value = 'digNone' }, - { name = 'Destructive', value = 'turtleSafe' }, - }, - value = 'digNone', - }, - grid = UI.ScrollingGrid { - y = 2, ey = -2, - columns = { - { heading = 'Label', key = 'label' }, - { heading = 'Dist', key = 'distance' }, - { heading = 'Status', key = 'status' }, - { heading = 'Fuel', key = 'fuel' }, - }, - sortColumn = 'distance', - autospace = true, - }, + menuBar = UI.MenuBar { + buttons = { + { text = 'Range', event = 'range' }, + { text = 'Stop', event = 'stop' }, + }, + mode = UI.Chooser { + x = -16, + choices = { + { name = 'No breaking', value = 'digNone' }, + { name = 'Destructive', value = 'turtleSafe' }, + }, + value = 'digNone', + }, + }, + grid = UI.ScrollingGrid { + y = 2, ey = -2, + columns = { + { heading = 'Label', key = 'label' }, + { heading = 'Dist', key = 'distance' }, + { heading = 'Status', key = 'status' }, + { heading = 'Fuel', key = 'fuel' }, + }, + sortColumn = 'distance', + autospace = true, + }, + range = UI.SlideOut { + y = -7, height = 7, + backgroundColor = colors.cyan, + titleBar = UI.TitleBar { + event = 'cancel', + title = 'Enter range', + }, + notice = UI.TextArea { + x = 2, ex = -2, y = 3, ey = 4, + value = +[[Select all turtles within a specified range]], + }, + entry = UI.TextEntry { + y = 6, x = 2, ex = 10, + limit = 4, + shadowText = 'range', + accelerators = { + enter = 'select_range', + }, + }, + button = UI.Button { + x = 12, y = 6, + text = 'Apply', + event = 'select_range', + } + }, } function page.grid:getRowTextColor(row, selected) - if swarm.pool[row.id] then - return colors.yellow - end - return UI.ScrollingGrid.getRowTextColor(self, row, selected) + if swarm.pool[row.id] then + return colors.yellow + end + return UI.ScrollingGrid.getRowTextColor(self, row, selected) end function page.grid:getDisplayValues(row) @@ -55,130 +87,155 @@ function page.grid:getDisplayValues(row) end function page:enable() - local function update() - local t = { } - for _,v in pairs(network) do - if v.fuel and v.active and v.fuel > 0 and v.distance then - table.insert(t, v) - end - end - self.grid:setValues(t) - end + local function update() + local t = { } + for _,v in pairs(network) do + if v.fuel and v.active and v.fuel > 0 and v.distance then + table.insert(t, v) + end + end + self.grid:setValues(t) + end - Event.onInterval(3, function() - update() - self.grid:draw() - self:sync() - end) + Event.onInterval(3, function() + update() + self.grid:draw() + self:sync() + end) - update() + update() - UI.Page.enable(self) + UI.Page.enable(self) end local function follow(member) - local turtle = member.turtle - turtle.reset() - turtle.set({ - digPolicy = page.mode.value, - status = 'Following', - }) + local turtle = member.turtle + turtle.reset() + turtle.set({ + digPolicy = page.menuBar.mode.value, + status = 'Following', + }) - if not turtle.enableGPS(nil, true) then - error('turtle: No GPS found') - end + if not turtle.enableGPS(nil, true) then + error('turtle: No GPS found') + end - member.snmp = Socket.connect(member.id, 161) - member.snmp.co = coroutine.running() + member.snmp = Socket.connect(member.id, 161) + member.snmp.co = coroutine.running() - local pt + local pt - while true do - while pt and Point.same(gpt, pt) do - os.sleep(.5) - end - pt = Point.copy(gpt) + while true do + while pt and Point.same(gpt, pt) do + os.sleep(.5) + end + pt = Point.copy(gpt) - local cpt = Point.closest(turtle.getPoint(), pts) + local cpt = Point.closest(turtle.getPoint(), pts) - turtle.abort(false) - if turtle.pathfind(cpt, { blocks = blocks }) then - turtle.headTowards(pt) - end - end + turtle.abort(false) + if turtle.pathfind(cpt, { blocks = blocks }) then + turtle.headTowards(pt) + end + end end function swarm:onRemove(member, status, message) - if member.socket then - pcall(function() - member.turtle.set({ status = 'idle' }) - member.turtle.abort(true) - end) - end - if member.snmp then - member.snmp:close() - member.snmp = nil - end - if not status then - _G._syslog(message) - end + if member.socket then + pcall(function() + member.turtle.set({ status = 'idle' }) + member.turtle.abort(true) + end) + end + if member.snmp then + member.snmp:close() + member.snmp = nil + end + if not status then + _G._syslog(message) + end end function page:eventHandler(event) - if event.type == 'grid_select' then - if not swarm.pool[event.selected.id] then - swarm:add(event.selected.id) - swarm:run(follow) - else - swarm:remove(event.selected.id) - end - self.grid:draw() + if event.type == 'grid_select' then + if not swarm.pool[event.selected.id] then + swarm:add(event.selected.id) + swarm:run(follow) + else + swarm:remove(event.selected.id) + end + self.grid:draw() - elseif event.type == 'choice_change' then - local script = string.format('turtle.set({ digPolicy = "%s"})', event.value) - for _, member in pairs(swarm.pool) do - member.snmp:write({ type = 'scriptEx', args = script }) - end + elseif event.type == 'choice_change' then + local script = string.format('turtle.set({ digPolicy = "%s"})', event.value) + for _, member in pairs(swarm.pool) do + member.snmp:write({ type = 'scriptEx', args = script }) + end - else - return UI.Page.eventHandler(self, event) - end - return true + elseif event.type == 'stop' then + for id in pairs(swarm.pool) do + swarm:remove(id) + end + + elseif event.type == 'range' then + self.range:show() + + elseif event.type == 'cancel' then + self.range:hide() + + elseif event.type == 'select_range' then + local range = tonumber(self.range.entry.value) + + if range and range > 0 then + for id, v in pairs(network) do + if not swarm.pool[id] then + if v.fuel and v.active and v.fuel > 0 and v.distance and v.distance <= range then + swarm:add(id) + end + end + end + swarm:run(follow) + self.range:hide() + end + else + return UI.Page.eventHandler(self, event) + end + return true end Event.addRoutine(function() - while true do - local pt = GPS.getPoint() - if not pts or (pt and not Point.same(pt, gpt)) then - gpt = pt - pts = { - { x = pt.x + 2, z = pt.z, y = pt.y }, - { x = pt.x - 2, z = pt.z, y = pt.y }, - { x = pt.x, z = pt.z + 2, y = pt.y }, - { x = pt.x, z = pt.z - 2, y = pt.y }, - } - blocks = { } + while true do + local pt = GPS.getPoint() + if not pts or (pt and not Point.same(pt, gpt)) then + gpt = pt + pts = { + { x = pt.x + 2, z = pt.z, y = pt.y }, + { x = pt.x - 2, z = pt.z, y = pt.y }, + { x = pt.x, z = pt.z + 2, y = pt.y }, + { x = pt.x, z = pt.z - 2, y = pt.y }, + } + blocks = { } - local function addBlocks(tpt) - table.insert(blocks, tpt) - local apts = Point.adjacentPoints(tpt) - for _,apt in pairs(apts) do - table.insert(blocks, apt) - end - end + local function addBlocks(tpt) + table.insert(blocks, tpt) + local apts = Point.adjacentPoints(tpt) + for _,apt in pairs(apts) do + table.insert(blocks, apt) + end + end - -- don't run into player - addBlocks(pt) - addBlocks(Point.above(pt)) + -- don't run into player + addBlocks(pt) + addBlocks(Point.above(pt)) - for _, member in pairs(swarm.pool) do - if member.snmp then - member.snmp:write({ type = 'scriptEx', args = 'turtle.abort(true)' }) - end - end - end - os.sleep(1) - end + for _, member in pairs(swarm.pool) do + if member.snmp then + member.snmp:write({ type = 'scriptEx', args = 'turtle.abort(true)' }) + end + end + end + os.sleep(1) + end end) UI:setPage(page) diff --git a/common/Turtles.lua b/common/Turtles.lua index 0940f45..42b9a3f 100644 --- a/common/Turtles.lua +++ b/common/Turtles.lua @@ -21,12 +21,12 @@ local config = { } Config.load('Turtles', config) local options = { - turtle = { arg = 'i', type = 'number', value = config.id or -1, - desc = 'Turtle ID' }, - tab = { arg = 's', type = 'string', value = config.tab or 'Sel', - desc = 'Selected tab to display' }, - help = { arg = 'h', type = 'flag', value = false, - desc = 'Displays the options' }, + turtle = { arg = 'i', type = 'number', value = config.id or -1, + desc = 'Turtle ID' }, + tab = { arg = 's', type = 'string', value = config.tab or 'Sel', + desc = 'Selected tab to display' }, + help = { arg = 'h', type = 'flag', value = false, + desc = 'Displays the options' }, } local SCRIPTS_PATH = 'packages/common/etc/scripts' @@ -35,343 +35,343 @@ local nullTerm = Terminal.getNullTerm(term.current()) local socket local page = UI.Page { - coords = UI.Window { - backgroundColor = colors.black, - height = 3, - }, - tabs = UI.Tabs { - x = 1, y = 4, ey = -2, - scripts = UI.ScrollingGrid { - tabTitle = 'Run', - backgroundColor = colors.cyan, - columns = { - { heading = '', key = 'label' }, - }, - disableHeader = true, - sortColumn = 'label', - autospace = true, - }, - turtles = UI.ScrollingGrid { - tabTitle = 'Select', - backgroundColor = colors.cyan, - columns = { - { heading = 'label', key = 'label' }, - { heading = 'Dist', key = 'distance' }, - { heading = 'Status', key = 'status' }, - { heading = 'Fuel', key = 'fuel' }, - }, - disableHeader = true, - sortColumn = 'label', - autospace = true, - }, - inventory = UI.ScrollingGrid { - backgroundColor = colors.cyan, - tabTitle = 'Inv', - columns = { - { heading = '', key = 'index', width = 2 }, - { heading = '', key = 'qty', width = 2 }, - { heading = 'Inventory', key = 'id', width = UI.term.width - 7 }, - }, - disableHeader = true, - sortColumn = 'index', - }, + coords = UI.Window { + backgroundColor = colors.black, + height = 3, + }, + tabs = UI.Tabs { + x = 1, y = 4, ey = -2, + scripts = UI.ScrollingGrid { + tabTitle = 'Run', + backgroundColor = colors.cyan, + columns = { + { heading = '', key = 'label' }, + }, + disableHeader = true, + sortColumn = 'label', + autospace = true, + }, + turtles = UI.ScrollingGrid { + tabTitle = 'Select', + backgroundColor = colors.cyan, + columns = { + { heading = 'label', key = 'label' }, + { heading = 'Dist', key = 'distance' }, + { heading = 'Status', key = 'status' }, + { heading = 'Fuel', key = 'fuel' }, + }, + disableHeader = true, + sortColumn = 'label', + autospace = true, + }, + inventory = UI.ScrollingGrid { + backgroundColor = colors.cyan, + tabTitle = 'Inv', + columns = { + { heading = '', key = 'index', width = 2 }, + { heading = '', key = 'qty', width = 2 }, + { heading = 'Inventory', key = 'id', width = UI.term.width - 7 }, + }, + disableHeader = true, + sortColumn = 'index', + }, --[[ - policy = UI.ScrollingGrid { - tabTitle = 'Mod', - backgroundColor = UI.TabBar.defaults.selectedBackgroundColor, - columns = { - { heading = 'label', key = 'label' }, - }, - values = policies, - disableHeader = true, - sortColumn = 'label', - autospace = true, - }, - ]] - action = UI.Window { - tabTitle = 'Action', - backgroundColor = colors.cyan, - moveUp = UI.Button { - x = 5, y = 2, - text = 'up', - fn = 'turtle.up', - }, - moveDown = UI.Button { - x = 5, y = 4, - text = 'dn', - fn = 'turtle.down', - }, - moveForward = UI.Button { - x = 9, y = 3, - text = 'f', - fn = 'turtle.forward', - }, - moveBack = UI.Button { - x = 2, y = 3, - text = 'b', - fn = 'turtle.back', - }, - turnLeft = UI.Button { - x = 2, y = 6, - text = 'lt', - fn = 'turtle.turnLeft', - }, - turnRight = UI.Button { - x = 8, y = 6, - text = 'rt', - fn = 'turtle.turnRight', - }, - info = UI.TextArea { - x = 15, y = 2, - inactive = true, - } - }, - }, - statusBar = UI.StatusBar { - values = { }, - columns = { - { key = 'status' }, - { key = 'distance', width = 6 }, - { key = 'fuel', width = 6 }, - }, - }, - notification = UI.Notification(), - accelerators = { - q = 'quit', - }, + policy = UI.ScrollingGrid { + tabTitle = 'Mod', + backgroundColor = UI.TabBar.defaults.selectedBackgroundColor, + columns = { + { heading = 'label', key = 'label' }, + }, + values = policies, + disableHeader = true, + sortColumn = 'label', + autospace = true, + }, + ]] + action = UI.Window { + tabTitle = 'Action', + backgroundColor = colors.cyan, + moveUp = UI.Button { + x = 5, y = 2, + text = 'up', + fn = 'turtle.up', + }, + moveDown = UI.Button { + x = 5, y = 4, + text = 'dn', + fn = 'turtle.down', + }, + moveForward = UI.Button { + x = 9, y = 3, + text = 'f', + fn = 'turtle.forward', + }, + moveBack = UI.Button { + x = 2, y = 3, + text = 'b', + fn = 'turtle.back', + }, + turnLeft = UI.Button { + x = 2, y = 6, + text = 'lt', + fn = 'turtle.turnLeft', + }, + turnRight = UI.Button { + x = 8, y = 6, + text = 'rt', + fn = 'turtle.turnRight', + }, + info = UI.TextArea { + x = 15, y = 2, + inactive = true, + } + }, + }, + statusBar = UI.StatusBar { + values = { }, + columns = { + { key = 'status' }, + { key = 'distance', width = 6 }, + { key = 'fuel', width = 6 }, + }, + }, + notification = UI.Notification(), + accelerators = { + q = 'quit', + }, } function page:enable(turtle) - self.turtle = turtle - UI.Page.enable(self) + self.turtle = turtle + UI.Page.enable(self) end function page:runFunction(script, nowrap) - for _ = 1, 2 do - if not socket then - socket = Socket.connect(self.turtle.id, 161) - end + for _ = 1, 2 do + if not socket then + socket = Socket.connect(self.turtle.id, 161) + end - if socket then - if not nowrap then - script = 'turtle.run(' .. script .. ')' - end - if socket:write({ type = 'scriptEx', args = script }) then - local t = socket:read(3) - if t then - return table.unpack(t) - end - return false, 'Socket timeout' - end - end - socket = nil - end - self.notification:error('Unable to connect') + if socket then + if not nowrap then + script = 'turtle.run(' .. script .. ')' + end + if socket:write({ type = 'scriptEx', args = script }) then + local t = socket:read(3) + if t then + return table.unpack(t) + end + return false, 'Socket timeout' + end + end + socket = nil + end + self.notification:error('Unable to connect') end function page:runScript(scriptName) - if self.turtle then - self.notification:info('Connecting') - self:sync() + if self.turtle then + self.notification:info('Connecting') + self:sync() - local cmd = string.format('Script %d %s', self.turtle.id, scriptName) - local ot = term.redirect(nullTerm) - pcall(function() shell.run(cmd) end) - term.redirect(ot) - self.notification:success('Sent') - end + local cmd = string.format('Script %d %s', self.turtle.id, scriptName) + local ot = term.redirect(nullTerm) + pcall(function() shell.run(cmd) end) + term.redirect(ot) + self.notification:success('Sent') + end end function page.coords:draw() - local t = self.parent.turtle - self:clear() - if t then - self:setCursorPos(2, 2) - local ind = 'GPS' - if not t.point.gps then - ind = 'REL' - end - self:print(string.format('%s : %d,%d,%d', - ind, t.point.x, t.point.y, t.point.z)) - end + local t = self.parent.turtle + self:clear() + if t then + self:setCursorPos(2, 2) + local ind = 'GPS' + if not t.point.gps then + ind = 'REL' + end + self:print(string.format('%s : %d,%d,%d', + ind, t.point.x, t.point.y, t.point.z)) + end end --[[ Inventory Tab ]]-- function page.tabs.inventory:getRowTextColor(row, selected) - if page.turtle and row.selected then - return colors.yellow - end - return UI.ScrollingGrid.getRowTextColor(self, row, selected) + if page.turtle and row.selected then + return colors.yellow + end + return UI.ScrollingGrid.getRowTextColor(self, row, selected) end function page.tabs.inventory:draw() - local t = page.turtle - Util.clear(self.values) - if t then - for _,v in ipairs(t.inventory) do - if v.qty > 0 then - table.insert(self.values, v) - if v.index == t.slotIndex then - v.selected = true - end - if v.id then - v.id = itemDB:getName(v) - end - end - end - end - self:adjustWidth() - self:update() - UI.ScrollingGrid.draw(self) + local t = page.turtle + Util.clear(self.values) + if t then + for _,v in ipairs(t.inventory) do + if v.qty > 0 then + table.insert(self.values, v) + if v.index == t.slotIndex then + v.selected = true + end + if v.id then + v.id = itemDB:getName(v) + end + end + end + end + self:adjustWidth() + self:update() + UI.ScrollingGrid.draw(self) end function page.tabs.inventory:eventHandler(event) - if event.type == 'grid_select' then - local fn = string.format('turtle.select(%d)', event.selected.index) - page:runFunction(fn) - else - return UI.ScrollingGrid.eventHandler(self, event) - end - return true + if event.type == 'grid_select' then + local fn = string.format('turtle.select(%d)', event.selected.index) + page:runFunction(fn) + else + return UI.ScrollingGrid.eventHandler(self, event) + end + return true end function page.tabs.scripts:draw() - Util.clear(self.values) - local files = fs.list(SCRIPTS_PATH) - for _,path in pairs(files) do - table.insert(self.values, { label = path, path = fs.combine(SCRIPTS_PATH, path) }) - end - self:update() - UI.ScrollingGrid.draw(self) + Util.clear(self.values) + local files = fs.list(SCRIPTS_PATH) + for _,path in pairs(files) do + table.insert(self.values, { label = path, path = fs.combine(SCRIPTS_PATH, path) }) + end + self:update() + 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.ScrollingGrid.eventHandler(self, event) - end - return true + if event.type == 'grid_select' then + page:runScript(event.selected.label) + else + return UI.ScrollingGrid.eventHandler(self, event) + end + return true end function page.tabs.turtles:getDisplayValues(row) - row = Util.shallowCopy(row) - if row.fuel then - row.fuel = Util.toBytes(row.fuel) - end - if row.distance then - row.distance = Util.round(row.distance, 1) - end - return row + row = Util.shallowCopy(row) + if row.fuel then + row.fuel = Util.toBytes(row.fuel) + end + if row.distance then + row.distance = Util.round(row.distance, 1) + end + return row end function page.tabs.turtles:draw() - Util.clear(self.values) - for _,v in pairs(network) do - if v.fuel then - table.insert(self.values, v) - end - end - self:update() - UI.ScrollingGrid.draw(self) + Util.clear(self.values) + for _,v in pairs(network) do + if v.fuel then + table.insert(self.values, v) + end + end + self:update() + UI.ScrollingGrid.draw(self) end function page.tabs.turtles:eventHandler(event) - if event.type == 'grid_select' then - page.turtle = event.selected - config.id = event.selected.id - Config.update('Turtles', config) - multishell.setTitle(multishell.getCurrent(), page.turtle.label) - if socket then - socket:close() - socket = nil - end - else - return UI.ScrollingGrid.eventHandler(self, event) - end - return true + if event.type == 'grid_select' then + page.turtle = event.selected + config.id = event.selected.id + Config.update('Turtles', config) + multishell.setTitle(multishell.getCurrent(), page.turtle.label) + if socket then + socket:close() + socket = nil + end + else + return UI.ScrollingGrid.eventHandler(self, event) + end + return true end function page.statusBar:draw() - local t = self.parent.turtle - if t then - self.values.status = t.status - self.values.distance = Util.round(t.distance, 2) - self.values.fuel = Util.toBytes(t.fuel) - end - UI.StatusBar.draw(self) + local t = self.parent.turtle + if t then + self.values.status = t.status + self.values.distance = Util.round(t.distance, 2) + self.values.fuel = Util.toBytes(t.fuel) + end + UI.StatusBar.draw(self) end function page:showBlocks() - local script = [[ - local function inspect(direction) - local s,b = turtle['inspect' .. (direction or '')]() - if not s then - return 'minecraft:air:0' - end - return string.format('%s:%d', b.name, b.metadata) - end + local script = [[ + local function inspect(direction) + local s,b = turtle['inspect' .. (direction or '')]() + if not s then + return 'minecraft:air:0' + end + return string.format('%s:%d', b.name, b.metadata) + end - local bu, bf, bd = inspect('Up'), inspect(), inspect('Down') - return string.format('%s\n%s\n%s', bu, bf, bd) - ]] + local bu, bf, bd = inspect('Up'), inspect(), inspect('Down') + return string.format('%s\n%s\n%s', bu, bf, bd) + ]] - local s, m = self:runFunction(script, true) - self.tabs.action.info:setText(s or m) + local s, m = self:runFunction(script, true) + self.tabs.action.info:setText(s or m) end function page:eventHandler(event) - if event.type == 'quit' then - UI:exitPullEvents() + if event.type == 'quit' then + UI:exitPullEvents() - elseif event.type == 'tab_select' then - config.tab = event.button.text - Config.update('Turtles', config) + elseif event.type == 'tab_select' then + config.tab = event.button.text + Config.update('Turtles', config) - elseif event.type == 'button_press' then - if event.button.fn then - self:runFunction(event.button.fn, event.button.nowrap) - self:showBlocks() - elseif event.button.script then - self:runScript(event.button.script) - end - else - return UI.Page.eventHandler(self, event) - end - return true + elseif event.type == 'button_press' then + if event.button.fn then + self:runFunction(event.button.fn, event.button.nowrap) + self:showBlocks() + elseif event.button.script then + self:runScript(event.button.script) + end + else + return UI.Page.eventHandler(self, event) + end + return true end function page:enable() - UI.Page.enable(self) + UI.Page.enable(self) -- self.tabs:activateTab(page.tabs.turtles) end if not Util.getOptions(options, { ... }, true) then - return + return end if options.turtle.value >= 0 then - for _ = 1, 10 do - page.turtle = _G.network[options.turtle.value] - if page.turtle then - break - end - os.sleep(1) - end + for _ = 1, 10 do + page.turtle = _G.network[options.turtle.value] + if page.turtle then + break + end + os.sleep(1) + end end Event.onInterval(1, function() - if page.turtle then - local t = _G.network[page.turtle.id] - page.turtle = t - page:draw() - page:sync() - end + if page.turtle then + local t = _G.network[page.turtle.id] + page.turtle = t + page:draw() + page:sync() + end end) if config.tab then - page.tabs.tabBar:selectTab(config.tab) + page.tabs.tabBar:selectTab(config.tab) end UI:setPage(page) diff --git a/common/autorun/common.lua b/common/autorun/common.lua index d67d732..fc4e2bb 100644 --- a/common/autorun/common.lua +++ b/common/autorun/common.lua @@ -1,7 +1,7 @@ local c = function(shell, nIndex, sText) - if nIndex == 1 then - return _G.fs.complete(sText, shell.dir(), true, false) - end + if nIndex == 1 then + return _G.fs.complete(sText, shell.dir(), true, false) + end end _ENV.shell.setCompletionFunction("packages/common/edit.lua", c) diff --git a/common/canvasClient.lua b/common/canvasClient.lua new file mode 100644 index 0000000..3f94393 --- /dev/null +++ b/common/canvasClient.lua @@ -0,0 +1,45 @@ +local Point = require('point') +local Util = require('util') + +local device = _G.device +local os = _G.os +local turtle = _G.turtle + +local function convert(blocks, reference) + if not reference then + return blocks + end + + local rotated = { + [0] = 0, + [1] = 3, + [2] = 2, + [3] = 1, + } + return Util.reduce(blocks, function(acc, b) + local c = Util.shallowCopy(b) + Point.rotate(c, rotated[reference.heading]) + c.x = c.x + reference.x + c.y = c.y + reference.y + c.z = c.z + reference.z + table.insert(acc, c) + return acc + end, { }) +end + +local function broadcast(blocks, displayType, source) + if device.wireless_modem then + device.wireless_modem.transmit(3773, os.getComputerID(), { + type = displayType, + data = convert(blocks, source), + }) + end +end + +while true do + local _, msg = os.pullEvent('canvas') + + local reference = turtle and turtle.getState().reference + + broadcast(msg.data, msg.type, reference) +end diff --git a/common/edit.lua b/common/edit.lua index 4484f9a..9a4e398 100644 --- a/common/edit.lua +++ b/common/edit.lua @@ -10,25 +10,25 @@ local term = _G.term local textutils = _G.textutils shell.setCompletionFunction(shell.getRunningProgram(), function(_, index, text) - if index == 1 then - return fs.complete(text, shell.dir(), true, false) - end + if index == 1 then + return fs.complete(text, shell.dir(), true, false) + end end) local tArgs = { ... } if #tArgs == 0 then - error( "Usage: edit " ) + error( "Usage: edit " ) end -- Error checking local sPath = shell.resolve(tArgs[1]) local bReadOnly = fs.isReadOnly(sPath) if fs.exists(sPath) and fs.isDir(sPath) then - error( "Cannot edit a directory." ) + error( "Cannot edit a directory." ) end if multishell then - multishell.setTitle(multishell.getCurrent(), fs.getName(sPath)) + multishell.setTitle(multishell.getCurrent(), fs.getName(sPath)) end local x, y = 1, 1 @@ -52,1053 +52,1053 @@ local clipboard -- do we need a clipboard shim if not multishell or not _G.kernel then -- is this OpusOS ? - if _G.clipboard then -- has it been installed already - clipboard = _G.clipboard - else - clipboard = { } + if _G.clipboard then -- has it been installed already + clipboard = _G.clipboard + else + clipboard = { } - function clipboard.setData(data) - clipboard.data = data - end + function clipboard.setData(data) + clipboard.data = data + end - function clipboard.getText() - if clipboard.data then - return tostring(clipboard.data) - end - end + function clipboard.getText() + if clipboard.data then + return tostring(clipboard.data) + end + end - _G.clipboard = clipboard - end + _G.clipboard = clipboard + end end local color = { - textColor = '0', - keywordColor = '4', - commentColor = 'd', - stringColor = 'e', - bgColor = colors.black, - highlightColor = colors.orange, - cursorColor = colors.lime, - errorBackground = colors.red, + textColor = '0', + keywordColor = '4', + commentColor = 'd', + stringColor = 'e', + bgColor = colors.black, + highlightColor = colors.orange, + cursorColor = colors.lime, + errorBackground = colors.red, } if not term.isColor() then - color = { - textColor = '0', - keywordColor = '8', - commentColor = '8', - stringColor = '8', - bgColor = colors.black, - highlightColor = colors.lightGray, - cursorColor = colors.white, - errorBackground = colors.gray, - } + color = { + textColor = '0', + keywordColor = '8', + commentColor = '8', + stringColor = '8', + bgColor = colors.black, + highlightColor = colors.lightGray, + cursorColor = colors.white, + errorBackground = colors.gray, + } end local keyMapping = { - -- movement - up = 'up', - down = 'down', - left = 'left', - right = 'right', - pageUp = 'pageUp', - [ 'control-b' ] = 'pageUp', - pageDown = 'pageDown', + -- movement + up = 'up', + down = 'down', + left = 'left', + right = 'right', + pageUp = 'pageUp', + [ 'control-b' ] = 'pageUp', + pageDown = 'pageDown', -- [ 'control-f' ] = 'pageDown', - home = 'home', - [ 'end' ] = 'toend', - [ 'control-home' ] = 'top', - [ 'control-end' ] = 'bottom', - [ 'control-right' ] = 'word', - [ 'control-left' ] = 'backword', - [ 'scroll_up' ] = 'scroll_up', - [ 'control-up' ] = 'scroll_up', - [ 'scroll_down' ] = 'scroll_down', - [ 'control-down' ] = 'scroll_down', - [ 'mouse_click' ] = 'go_to', - [ 'control-l' ] = 'goto_line', + home = 'home', + [ 'end' ] = 'toend', + [ 'control-home' ] = 'top', + [ 'control-end' ] = 'bottom', + [ 'control-right' ] = 'word', + [ 'control-left' ] = 'backword', + [ 'scroll_up' ] = 'scroll_up', + [ 'control-up' ] = 'scroll_up', + [ 'scroll_down' ] = 'scroll_down', + [ 'control-down' ] = 'scroll_down', + [ 'mouse_click' ] = 'go_to', + [ 'control-l' ] = 'goto_line', - -- marking - [ 'shift-up' ] = 'mark_up', - [ 'shift-down' ] = 'mark_down', - [ 'shift-left' ] = 'mark_left', - [ 'shift-right' ] = 'mark_right', - [ 'mouse_drag' ] = 'mark_to', - [ 'shift-mouse_click' ] = 'mark_to', - [ 'control-a' ] = 'mark_all', - [ 'control-shift-right' ] = 'mark_word', - [ 'control-shift-left' ] = 'mark_backword', - [ 'shift-end' ] = 'mark_end', - [ 'shift-home' ] = 'mark_home', - [ 'mouse_down' ] = 'mark_anchor', + -- marking + [ 'shift-up' ] = 'mark_up', + [ 'shift-down' ] = 'mark_down', + [ 'shift-left' ] = 'mark_left', + [ 'shift-right' ] = 'mark_right', + [ 'mouse_drag' ] = 'mark_to', + [ 'shift-mouse_click' ] = 'mark_to', + [ 'control-a' ] = 'mark_all', + [ 'control-shift-right' ] = 'mark_word', + [ 'control-shift-left' ] = 'mark_backword', + [ 'shift-end' ] = 'mark_end', + [ 'shift-home' ] = 'mark_home', + [ 'mouse_down' ] = 'mark_anchor', - -- editing - delete = 'delete', - backspace = 'backspace', - enter = 'enter', - char = 'char', - paste = 'paste', - tab = 'tab', - [ 'control-z' ] = 'undo', - [ 'control-space' ] = 'autocomplete', + -- editing + delete = 'delete', + backspace = 'backspace', + enter = 'enter', + char = 'char', + paste = 'paste', + tab = 'tab', + [ 'control-z' ] = 'undo', + [ 'control-space' ] = 'autocomplete', - -- copy/paste - [ 'control-x' ] = 'cut', - [ 'control-c' ] = 'copy', - [ 'control-shift-paste' ] = 'paste_internal', + -- copy/paste + [ 'control-x' ] = 'cut', + [ 'control-c' ] = 'copy', + [ 'control-shift-paste' ] = 'paste_internal', - -- file - [ 'control-s' ] = 'save', - [ 'control-q' ] = 'exit', - [ 'control-enter' ] = 'run', + -- file + [ 'control-s' ] = 'save', + [ 'control-q' ] = 'exit', + [ 'control-enter' ] = 'run', - -- search - [ 'control-f' ] = 'find_prompt', - [ 'control-slash' ] = 'find_prompt', - [ 'control-n' ] = 'find_next', + -- search + [ 'control-f' ] = 'find_prompt', + [ 'control-slash' ] = 'find_prompt', + [ 'control-n' ] = 'find_next', - -- misc - [ 'control-g' ] = 'status', - [ 'control-r' ] = 'refresh', - [ 'control' ] = 'menu', + -- misc + [ 'control-g' ] = 'status', + [ 'control-r' ] = 'refresh', + [ 'control' ] = 'menu', } local messages = { - menu = '^s: save, ^q: quit, ^enter: run', - wrapped = 'search hit BOTTOM, continuing at TOP', + menu = '^s: save, ^q: quit, ^enter: run', + wrapped = 'search hit BOTTOM, continuing at TOP', } if w < 32 then - messages = { - menu = '^s = save, ^q = quit', - wrapped = 'search wrapped', - } + messages = { + menu = '^s = save, ^q = quit', + wrapped = 'search wrapped', + } end local function getFileInfo(path) - local abspath = shell.resolve(path) + local abspath = shell.resolve(path) - local fi = { - abspath = abspath, - path = path, - isNew = not fs.exists(abspath), - dirExists = fs.exists(fs.getDir(abspath)), - modified = false, - } - if fi.isDir then - fi.isReadOnly = true - else - fi.isReadOnly = fs.isReadOnly(fi.abspath) - end + local fi = { + abspath = abspath, + path = path, + isNew = not fs.exists(abspath), + dirExists = fs.exists(fs.getDir(abspath)), + modified = false, + } + if fi.isDir then + fi.isReadOnly = true + else + fi.isReadOnly = fs.isReadOnly(fi.abspath) + end - return fi + return fi end local function setStatus(pattern, ...) - sStatus = string.format(pattern, ...) + sStatus = string.format(pattern, ...) end local function setError(pattern, ...) - setStatus(pattern, ...) - isError = true + setStatus(pattern, ...) + isError = true end local function load(path) - tLines = {} - if fs.exists(path) then - local file = io.open(path, "r") - local sLine = file:read() - while sLine do - table.insert(tLines, sLine) - sLine = file:read() - end - file:close() - end + tLines = {} + if fs.exists(path) then + local file = io.open(path, "r") + local sLine = file:read() + while sLine do + table.insert(tLines, sLine) + sLine = file:read() + end + file:close() + end - if #tLines == 0 then - table.insert(tLines, '') - end + if #tLines == 0 then + table.insert(tLines, '') + end - fileInfo = getFileInfo(tArgs[1]) + fileInfo = getFileInfo(tArgs[1]) - local name = fileInfo.path - if w < 32 then - name = fs.getName(fileInfo.path) - end - if fileInfo.isNew then - if not fileInfo.dirExists then - setStatus('"%s" [New DIRECTORY]', name) - else - setStatus('"%s" [New File]', name) - end - elseif fileInfo.isReadOnly then - setStatus('"%s" [readonly] %dL, %dC', - name, #tLines, fs.getSize(fileInfo.abspath)) - else - setStatus('"%s" %dL, %dC', - name, #tLines, fs.getSize(fileInfo.abspath)) - end + local name = fileInfo.path + if w < 32 then + name = fs.getName(fileInfo.path) + end + if fileInfo.isNew then + if not fileInfo.dirExists then + setStatus('"%s" [New DIRECTORY]', name) + else + setStatus('"%s" [New File]', name) + end + elseif fileInfo.isReadOnly then + setStatus('"%s" [readonly] %dL, %dC', + name, #tLines, fs.getSize(fileInfo.abspath)) + else + setStatus('"%s" %dL, %dC', + name, #tLines, fs.getSize(fileInfo.abspath)) + end end local function save( _sPath ) - -- Create intervening folder - local sDir = _sPath:sub(1, _sPath:len() - fs.getName(_sPath):len() ) - if not fs.exists( sDir ) then - fs.makeDir( sDir ) - end + -- Create intervening folder + local sDir = _sPath:sub(1, _sPath:len() - fs.getName(_sPath):len() ) + if not fs.exists( sDir ) then + fs.makeDir( sDir ) + end - -- Save - local file = nil - local function innerSave() - file = fs.open( _sPath, "w" ) - if file then - for _,sLine in ipairs( tLines ) do - file.write(sLine .. "\n") - end - else - error( "Failed to open ".._sPath ) - end - end + -- Save + local file = nil + local function innerSave() + file = fs.open( _sPath, "w" ) + if file then + for _,sLine in ipairs( tLines ) do + file.write(sLine .. "\n") + end + else + error( "Failed to open ".._sPath ) + end + end - local ok, err = pcall( innerSave ) - if file then - file.close() - end - return ok, err + local ok, err = pcall( innerSave ) + if file then + file.close() + end + return ok, err end local function split(str, pattern) - pattern = pattern or "(.-)\n" - local t = {} - local function helper(line) table.insert(t, line) return "" end - helper((str:gsub(pattern, helper))) - return t + pattern = pattern or "(.-)\n" + local t = {} + local function helper(line) table.insert(t, line) return "" end + helper((str:gsub(pattern, helper))) + return t end local tKeywords = { - ["and"] = true, - ["break"] = true, - ["do"] = true, - ["else"] = true, - ["elseif"] = true, - ["end"] = true, - ["false"] = true, - ["for"] = true, - ["function"] = true, - ["if"] = true, - ["in"] = true, - ["local"] = true, - ["nil"] = true, - ["not"] = true, - ["or"] = true, - ["repeat"] = true, - ["return"] = true, - ["then"] = true, - ["true"] = true, - ["until"]= true, - ["while"] = true, + ["and"] = true, + ["break"] = true, + ["do"] = true, + ["else"] = true, + ["elseif"] = true, + ["end"] = true, + ["false"] = true, + ["for"] = true, + ["function"] = true, + ["if"] = true, + ["in"] = true, + ["local"] = true, + ["nil"] = true, + ["not"] = true, + ["or"] = true, + ["repeat"] = true, + ["return"] = true, + ["then"] = true, + ["true"] = true, + ["until"]= true, + ["while"] = true, } local function writeHighlighted(sLine, ny) - local buffer = { - fg = '', - text = '', - } + local buffer = { + fg = '', + text = '', + } - local function tryWrite(line, regex, fgcolor) - local match = line:match(regex) - if match then - local fg - if type(fgcolor) == "string" then - fg = fgcolor - else - fg = fgcolor(match) - end - buffer.text = buffer.text .. match - buffer.fg = buffer.fg .. string.rep(fg, #match) - return line:sub(#match + 1) - end - return nil - end + local function tryWrite(line, regex, fgcolor) + local match = line:match(regex) + if match then + local fg + if type(fgcolor) == "string" then + fg = fgcolor + else + fg = fgcolor(match) + end + buffer.text = buffer.text .. match + buffer.fg = buffer.fg .. string.rep(fg, #match) + return line:sub(#match + 1) + end + return nil + end - while #sLine > 0 do - sLine = - tryWrite(sLine, "^%-%-%[%[.-%]%]", color.commentColor ) or - tryWrite(sLine, "^%-%-.*", color.commentColor ) or - tryWrite(sLine, "^\".-[^\\]\"", color.stringColor ) or - tryWrite(sLine, "^\'.-[^\\]\'", color.stringColor ) or - tryWrite(sLine, "^%[%[.-%]%]", color.stringColor ) or - tryWrite(sLine, "^[%w_]+", function(match) - if tKeywords[match] then - return color.keywordColor - end - return color.textColor - end) or - tryWrite(sLine, "^[^%w_]", color.textColor) - end + while #sLine > 0 do + sLine = + tryWrite(sLine, "^%-%-%[%[.-%]%]", color.commentColor ) or + tryWrite(sLine, "^%-%-.*", color.commentColor ) or + tryWrite(sLine, "^\".-[^\\]\"", color.stringColor ) or + tryWrite(sLine, "^\'.-[^\\]\'", color.stringColor ) or + tryWrite(sLine, "^%[%[.-%]%]", color.stringColor ) or + tryWrite(sLine, "^[%w_]+", function(match) + if tKeywords[match] then + return color.keywordColor + end + return color.textColor + end) or + tryWrite(sLine, "^[^%w_]", color.textColor) + end - buffer.fg = buffer.fg .. '7' - buffer.text = buffer.text .. '.' + buffer.fg = buffer.fg .. '7' + buffer.text = buffer.text .. '.' - if mark.active and ny >= mark.y and ny <= mark.ey then - local sx = 1 - if ny == mark.y then - sx = mark.x - end - local ex = #buffer.text - if ny == mark.ey then - ex = mark.ex - end - buffer.bg = string.rep('f', sx - 1) .. - string.rep('7', ex - sx) .. - string.rep('f', #buffer.text - ex + 1) + if mark.active and ny >= mark.y and ny <= mark.ey then + local sx = 1 + if ny == mark.y then + sx = mark.x + end + local ex = #buffer.text + if ny == mark.ey then + ex = mark.ex + end + buffer.bg = string.rep('f', sx - 1) .. + string.rep('7', ex - sx) .. + string.rep('f', #buffer.text - ex + 1) - else - buffer.bg = string.rep('f', #buffer.text) - end + else + buffer.bg = string.rep('f', #buffer.text) + end - term.blit(buffer.text, buffer.fg, buffer.bg) + term.blit(buffer.text, buffer.fg, buffer.bg) end local function redraw() - if dirty.y > 0 then - term.setBackgroundColor(color.bgColor) - for dy = 1, h do + if dirty.y > 0 then + term.setBackgroundColor(color.bgColor) + for dy = 1, h do - local sLine = tLines[dy + scrollY] - if sLine ~= nil then - if dy + scrollY >= dirty.y and dy + scrollY <= dirty.ey then - term.setCursorPos(1 - scrollX, dy) - term.clearLine() - writeHighlighted(sLine, dy + scrollY) - end - else - term.setCursorPos(1 - scrollX, dy) - term.clearLine() - end - end - end + local sLine = tLines[dy + scrollY] + if sLine ~= nil then + if dy + scrollY >= dirty.y and dy + scrollY <= dirty.ey then + term.setCursorPos(1 - scrollX, dy) + term.clearLine() + writeHighlighted(sLine, dy + scrollY) + end + else + term.setCursorPos(1 - scrollX, dy) + term.clearLine() + end + end + end - -- Draw status - if #sStatus > 0 then - if isError then - term.setTextColor(colors.white) - term.setBackgroundColor(color.errorBackground) - else - term.setTextColor(color.highlightColor) - term.setBackgroundColor(colors.gray) - end - term.setCursorPos(1, h) - term.clearLine() - term.write(string.format(' %s ', sStatus)) - end + -- Draw status + if #sStatus > 0 then + if isError then + term.setTextColor(colors.white) + term.setBackgroundColor(color.errorBackground) + else + term.setTextColor(color.highlightColor) + term.setBackgroundColor(colors.gray) + end + term.setCursorPos(1, h) + term.clearLine() + term.write(string.format(' %s ', sStatus)) + end - if not (w < 32 and #sStatus > 0) then - local modifiedIndicator = ' ' - if undo.chain[1] then - modifiedIndicator = '*' - end + if not (w < 32 and #sStatus > 0) then + local modifiedIndicator = ' ' + if undo.chain[1] then + modifiedIndicator = '*' + end - local str = string.format(' %d:%d %s', - y, x, modifiedIndicator) - term.setTextColor(color.highlightColor) - term.setBackgroundColor(colors.gray) - term.setCursorPos(w - #str + 1, h) - term.write(str) - end + local str = string.format(' %d:%d %s', + y, x, modifiedIndicator) + term.setTextColor(color.highlightColor) + term.setBackgroundColor(colors.gray) + term.setCursorPos(w - #str + 1, h) + term.write(str) + end - term.setTextColor(color.cursorColor) - term.setCursorPos(x - scrollX, y - scrollY) + term.setTextColor(color.cursorColor) + term.setCursorPos(x - scrollX, y - scrollY) - dirty.y, dirty.ey = 0, 0 - if #sStatus > 0 then - sStatus = '' - dirty.y = scrollY + h - dirty.ey = dirty.y - end - isError = false + dirty.y, dirty.ey = 0, 0 + if #sStatus > 0 then + sStatus = '' + dirty.y = scrollY + h + dirty.ey = dirty.y + end + isError = false end local function nextWord(line, cx) - local result = { line:find("(%w+)", cx) } - if #result > 1 and result[2] > cx then - return result[2] + 1 - elseif #result > 0 and result[1] == cx then - result = { line:find("(%w+)", result[2] + 1) } - if #result > 0 then - return result[1] - end - end + local result = { line:find("(%w+)", cx) } + if #result > 1 and result[2] > cx then + return result[2] + 1 + elseif #result > 0 and result[1] == cx then + result = { line:find("(%w+)", result[2] + 1) } + if #result > 0 then + return result[1] + end + end end local function hacky_read() - local _oldSetCursorPos = term.setCursorPos - local _oldGetCursorPos = term.getCursorPos + local _oldSetCursorPos = term.setCursorPos + local _oldGetCursorPos = term.getCursorPos - term.setCursorPos = function(cx) - return _oldSetCursorPos(cx, h) - end - term.getCursorPos = function() - local cx = _oldGetCursorPos() - return cx, 1 - end + term.setCursorPos = function(cx) + return _oldSetCursorPos(cx, h) + end + term.getCursorPos = function() + local cx = _oldGetCursorPos() + return cx, 1 + end - local s, m = pcall(function() return _G.read() end) - term.setCursorPos = _oldSetCursorPos - term.getCursorPos = _oldGetCursorPos - if s then - return m - end - if m == 'Terminated' then - bRunning = false - end - return '' + local s, m = pcall(function() return _G.read() end) + term.setCursorPos = _oldSetCursorPos + term.getCursorPos = _oldGetCursorPos + if s then + return m + end + if m == 'Terminated' then + bRunning = false + end + return '' end local actions local __actions = { - input = function(prompt) - term.setTextColor(color.highlightColor) - term.setBackgroundColor(colors.gray) - term.setCursorPos(1, h) - term.clearLine() - term.write(prompt) - local str = hacky_read() - term.setCursorBlink(true) - input:reset() - term.setCursorPos(x - scrollX, y - scrollY) - actions.dirty_line(scrollY + h) - return str - end, - - undo = function() - local last = table.remove(undo.chain) - if last then - undo.active = true - actions[last.action](unpack(last.args)) - undo.active = false - else - setStatus('Already at oldest change') - end - end, - - addUndo = function(entry) - local last = undo.chain[#undo.chain] - if last and last.action == entry.action then - if last.action == 'deleteText' then - if last.args[3] == entry.args[1] and - last.args[4] == entry.args[2] then - last.args = { - last.args[1], last.args[2], entry.args[3], entry.args[4], - last.args[5] .. entry.args[5] - } - else - table.insert(undo.chain, entry) - end - else - -- insertText (need to finish) - table.insert(undo.chain, entry) - end - else - table.insert(undo.chain, entry) - end - end, - - autocomplete = function() - if lastAction ~= 'autocomplete' or not complete.results then - local sLine = tLines[y]:sub(1, x - 1) - local nStartPos = sLine:find("[a-zA-Z0-9_%.]+$") - if nStartPos then - sLine = sLine:sub(nStartPos) - end - if #sLine > 0 then - complete.results = textutils.complete(sLine) - else - complete.results = { } - end - complete.index = 0 - complete.x = x - end - - if #complete.results == 0 then - setError('No completions available') - - elseif #complete.results == 1 then - actions.insertText(x, y, complete.results[1]) - complete.results = nil - - elseif #complete.results > 1 then - local prefix = complete.results[1] - for n = 1, #complete.results do - local result = complete.results[n] - while #prefix > 0 do - if result:find(prefix, 1, true) == 1 then - break - end - prefix = prefix:sub(1, #prefix - 1) - end - end - if #prefix > 0 then - actions.insertText(x, y, prefix) - complete.results = nil - else - if complete.index > 0 then - actions.deleteText(complete.x, y, complete.x + #complete.results[complete.index], y) - end - complete.index = complete.index + 1 - if complete.index > #complete.results then - complete.index = 1 - end - actions.insertText(complete.x, y, complete.results[complete.index]) - end - end - end, - - refresh = function() - actions.dirty_all() - mark.continue = mark.active - setStatus('refreshed') - end, - - menu = function() - setStatus(messages.menu) - mark.continue = mark.active - end, - - goto_line = function() - local lineNo = tonumber(actions.input('Line: ')) - if lineNo then - actions.go_to(1, lineNo) - else - setStatus('Invalid line number') - end - end, - - find = function(pattern, sx) - local nLines = #tLines - for i = 1, nLines + 1 do - local ny = y + i - 1 - if ny > nLines then - ny = ny - nLines - end - local nx = tLines[ny]:lower():find(pattern, sx) - if nx then - if ny < y or ny == y and nx <= x then - setStatus(messages.wrapped) - end - actions.go_to(nx, ny) - actions.mark_to(nx + #pattern, ny) - actions.go_to(nx, ny) - return - end - sx = 1 - end - setError('Pattern not found') - end, - - find_next = function() - if searchPattern then - actions.unmark() - actions.find(searchPattern, x + 1) - end - end, - - find_prompt = function() - local text = actions.input('/') - if #text > 0 then - searchPattern = text:lower() - if searchPattern then - actions.unmark() - actions.find(searchPattern, x) - end - end - end, - - save = function() - if bReadOnly then - setError("Access denied") - else - local ok = save(sPath) - if ok then - setStatus('"%s" %dL, %dC written', - fileInfo.path, #tLines, fs.getSize(fileInfo.abspath)) - else - setError("Error saving to %s", sPath) - end - end - end, - - exit = function() - bRunning = false - end, - - run = function() - input:reset() - local sTempPath = "/.temp" - local ok = save(sTempPath) - if ok then - local nTask = shell.openTab(sTempPath) - if nTask then - shell.switchTab(nTask) - else - setError("Error starting Task") - end - os.sleep(0) - fs.delete(sTempPath) - else - setError("Error saving to %s", sTempPath) - end - end, - - status = function() - local modified = '' - if undo.chain[1] then - modified = '[Modified] ' - end - setStatus('"%s" %s%d lines --%d%%--', - fileInfo.path, modified, #tLines, - math.floor((y - 1) / (#tLines - 1) * 100)) - end, - - dirty_line = function(dy) - if dirty.y == 0 then - dirty.y = dy - dirty.ey = dy - else - dirty.y = math.min(dirty.y, dy) - dirty.ey = math.max(dirty.ey, dy) - end - end, - - dirty_range = function(dy, dey) - actions.dirty_line(dy) - actions.dirty_line(dey or #tLines) - end, - - dirty = function() - actions.dirty_line(y) - end, - - dirty_all = function() - actions.dirty_line(1) - actions.dirty_line(#tLines) - end, - - mark_begin = function() - actions.dirty() - if not mark.active then - mark.active = true - mark.anchor = { x = x, y = y } - end - end, - - mark_finish = function() - if y == mark.anchor.y then - if x == mark.anchor.x then - mark.active = false - else - mark.x = math.min(mark.anchor.x, x) - mark.y = y - mark.ex = math.max(mark.anchor.x, x) - mark.ey = y - end - elseif y < mark.anchor.y then - mark.x = x - mark.y = y - mark.ex = mark.anchor.x - mark.ey = mark.anchor.y - else - mark.x = mark.anchor.x - mark.y = mark.anchor.y - mark.ex = x - mark.ey = y - end - actions.dirty() - mark.continue = mark.active - end, - - unmark = function() - if mark.active then - actions.dirty_range(mark.y, mark.ey) - mark.active = false - end - end, - - mark_anchor = function(nx, ny) - actions.go_to(nx, ny) - actions.unmark() - actions.mark_begin() - actions.mark_finish() - end, - - mark_to = function(nx, ny) - actions.mark_begin() - actions.go_to(nx, ny) - actions.mark_finish() - end, - - mark_up = function() - actions.mark_begin() - actions.up() - actions.mark_finish() - end, - - mark_right = function() - actions.mark_begin() - actions.right() - actions.mark_finish() - end, - - mark_down = function() - actions.mark_begin() - actions.down() - actions.mark_finish() - end, - - mark_left = function() - actions.mark_begin() - actions.left() - actions.mark_finish() - end, - - mark_word = function() - actions.mark_begin() - actions.word() - actions.mark_finish() - end, - - mark_backword = function() - actions.mark_begin() - actions.backword() - actions.mark_finish() - end, - - mark_home = function() - actions.mark_begin() - actions.home() - actions.mark_finish() - end, - - mark_end = function() - actions.mark_begin() - actions.toend() - actions.mark_finish() - end, - - mark_all = function() - mark.anchor = { x = 1, y = 1 } - mark.active = true - mark.continue = true - mark.x = 1 - mark.y = 1 - mark.ey = #tLines - mark.ex = #tLines[mark.ey] + 1 - actions.dirty_all() - end, - - setCursor = function() - lastPos.x = x - lastPos.y = y - - local screenX = x - scrollX - local screenY = y - scrollY - - if screenX < 1 then - scrollX = x - 1 - actions.dirty_all() - elseif screenX > w then - scrollX = x - w - actions.dirty_all() - end - - if screenY < 1 then - scrollY = y - 1 - actions.dirty_all() - elseif screenY > h - 1 then - scrollY = y - (h - 1) - actions.dirty_all() - end - end, - - top = function() - actions.go_to(1, 1) - end, - - bottom = function() - y = #tLines - x = #tLines[y] + 1 - end, - - up = function() - if y > 1 then - x = math.min(x, #tLines[y - 1] + 1) - y = y - 1 - end - end, - - down = function() - if y < #tLines then - x = math.min(x, #tLines[y + 1] + 1) - y = y + 1 - end - end, - - tab = function() - if mark.active then - actions.delete() - end - actions.insertText(x, y, ' ') - end, - - pageUp = function() - actions.go_to(x, y - (h - 1)) - end, - - pageDown = function() - actions.go_to(x, y + (h - 1)) - end, - - home = function() - x = 1 - end, - - toend = function() - x = #tLines[y] + 1 - end, - - left = function() - if x > 1 then - x = x - 1 - elseif y > 1 then - x = #tLines[y - 1] + 1 - y = y - 1 - else - return false - end - return true - end, - - right = function() - if x < #tLines[y] + 1 then - x = x + 1 - elseif y < #tLines then - x = 1 - y = y + 1 - end - end, - - word = function() - local nx = nextWord(tLines[y], x) - if nx then - x = nx - elseif x < #tLines[y] + 1 then - x = #tLines[y] + 1 - elseif y < #tLines then - x = 1 - y = y + 1 - end - end, - - backword = function() - if x == 1 then - actions.left() - else - local sLine = tLines[y] - local lx = 1 - while true do - local nx = nextWord(sLine, lx) - if not nx or nx >= x then - break - end - lx = nx - end - if not lx then - x = 1 - else - x = lx - end - end - end, - - insertText = function(sx, sy, text) - x = sx - y = sy - local sLine = tLines[y] - - if not text:find('\n') then - tLines[y] = sLine:sub(1, x - 1) .. text .. sLine:sub(x) - actions.dirty_line(y) - x = x + #text - else - local lines = split(text) - local remainder = sLine:sub(x) - tLines[y] = sLine:sub(1, x - 1) .. lines[1] - actions.dirty_range(y, #tLines + #lines) - x = x + #lines[1] - for k = 2, #lines do - y = y + 1 - table.insert(tLines, y, lines[k]) - x = #lines[k] + 1 - end - tLines[y] = tLines[y]:sub(1, x) .. remainder - end - - if not undo.active then - actions.addUndo( - { action = 'deleteText', args = { sx, sy, x, y, text } }) - end - end, - - deleteText = function(sx, sy, ex, ey) - x = sx - y = sy - - if not undo.active then - local text = actions.copyText(sx, sy, ex, ey) - actions.addUndo( - { action = 'insertText', args = { sx, sy, text } }) - end - - local front = tLines[sy]:sub(1, sx - 1) - local back = tLines[ey]:sub(ex, #tLines[ey]) - for _ = 2, ey - sy + 1 do - table.remove(tLines, y + 1) - end - tLines[y] = front .. back - if sy ~= ey then - actions.dirty_range(y) - else - actions.dirty() - end - end, - - copyText = function(csx, csy, cex, cey) - local count = 0 - local lines = { } - - for cy = csy, cey do - local line = tLines[cy] - if line then - local cx = 1 - local ex = #line - if cy == csy then - cx = csx - end - if cy == cey then - ex = cex - 1 - end - local str = line:sub(cx, ex) - count = count + #str - table.insert(lines, str) - end - end - return table.concat(lines, '\n'), count - end, - - delete = function() - if mark.active then - actions.deleteText(mark.x, mark.y, mark.ex, mark.ey) - else - local nLimit = #tLines[y] + 1 - if x < nLimit then - actions.deleteText(x, y, x + 1, y) - elseif y < #tLines then - actions.deleteText(x, y, 1, y + 1) - end - end - end, - - backspace = function() - if mark.active then - actions.delete() - elseif actions.left() then - actions.delete() - end - end, - - enter = function() - local sLine = tLines[y] - local _,spaces = sLine:find("^[ ]+") - if not spaces then - spaces = 0 - end - spaces = math.min(spaces, x - 1) - if mark.active then - actions.delete() - end - actions.insertText(x, y, '\n' .. string.rep(' ', spaces)) - end, - - char = function(ch) - if mark.active then - actions.delete() - end - actions.insertText(x, y, ch) - end, - - copy_marked = function() - local text = actions.copyText(mark.x, mark.y, mark.ex, mark.ey) - if clipboard then - clipboard.setData(text) - else - os.queueEvent('clipboard_copy', text) - end - setStatus('shift-^v to paste') - end, - - cut = function() - if mark.active then - actions.copy_marked() - actions.delete() - end - end, - - copy = function() - if mark.active then - actions.copy_marked() - mark.continue = true - end - end, - - paste = function(text) - if mark.active then - actions.delete() - end - if text then - actions.insertText(x, y, text) - setStatus('%d chars added', #text) - else - setStatus('Clipboard empty') - end - end, - - paste_internal = function() - if clipboard then - actions.paste(clipboard.getText()) - end - end, - - go_to = function(cx, cy) - y = math.min(math.max(cy, 1), #tLines) - x = math.min(math.max(cx, 1), #tLines[y] + 1) - end, - - scroll_up = function() - if scrollY > 0 then - scrollY = scrollY - 1 - actions.dirty_all() - end - mark.continue = mark.active - end, - - scroll_down = function() - local nMaxScroll = #tLines - (h-1) - if scrollY < nMaxScroll then - scrollY = scrollY + 1 - actions.dirty_all() - end - mark.continue = mark.active - end, + input = function(prompt) + term.setTextColor(color.highlightColor) + term.setBackgroundColor(colors.gray) + term.setCursorPos(1, h) + term.clearLine() + term.write(prompt) + local str = hacky_read() + term.setCursorBlink(true) + input:reset() + term.setCursorPos(x - scrollX, y - scrollY) + actions.dirty_line(scrollY + h) + return str + end, + + undo = function() + local last = table.remove(undo.chain) + if last then + undo.active = true + actions[last.action](unpack(last.args)) + undo.active = false + else + setStatus('Already at oldest change') + end + end, + + addUndo = function(entry) + local last = undo.chain[#undo.chain] + if last and last.action == entry.action then + if last.action == 'deleteText' then + if last.args[3] == entry.args[1] and + last.args[4] == entry.args[2] then + last.args = { + last.args[1], last.args[2], entry.args[3], entry.args[4], + last.args[5] .. entry.args[5] + } + else + table.insert(undo.chain, entry) + end + else + -- insertText (need to finish) + table.insert(undo.chain, entry) + end + else + table.insert(undo.chain, entry) + end + end, + + autocomplete = function() + if lastAction ~= 'autocomplete' or not complete.results then + local sLine = tLines[y]:sub(1, x - 1) + local nStartPos = sLine:find("[a-zA-Z0-9_%.]+$") + if nStartPos then + sLine = sLine:sub(nStartPos) + end + if #sLine > 0 then + complete.results = textutils.complete(sLine) + else + complete.results = { } + end + complete.index = 0 + complete.x = x + end + + if #complete.results == 0 then + setError('No completions available') + + elseif #complete.results == 1 then + actions.insertText(x, y, complete.results[1]) + complete.results = nil + + elseif #complete.results > 1 then + local prefix = complete.results[1] + for n = 1, #complete.results do + local result = complete.results[n] + while #prefix > 0 do + if result:find(prefix, 1, true) == 1 then + break + end + prefix = prefix:sub(1, #prefix - 1) + end + end + if #prefix > 0 then + actions.insertText(x, y, prefix) + complete.results = nil + else + if complete.index > 0 then + actions.deleteText(complete.x, y, complete.x + #complete.results[complete.index], y) + end + complete.index = complete.index + 1 + if complete.index > #complete.results then + complete.index = 1 + end + actions.insertText(complete.x, y, complete.results[complete.index]) + end + end + end, + + refresh = function() + actions.dirty_all() + mark.continue = mark.active + setStatus('refreshed') + end, + + menu = function() + setStatus(messages.menu) + mark.continue = mark.active + end, + + goto_line = function() + local lineNo = tonumber(actions.input('Line: ')) + if lineNo then + actions.go_to(1, lineNo) + else + setStatus('Invalid line number') + end + end, + + find = function(pattern, sx) + local nLines = #tLines + for i = 1, nLines + 1 do + local ny = y + i - 1 + if ny > nLines then + ny = ny - nLines + end + local nx = tLines[ny]:lower():find(pattern, sx) + if nx then + if ny < y or ny == y and nx <= x then + setStatus(messages.wrapped) + end + actions.go_to(nx, ny) + actions.mark_to(nx + #pattern, ny) + actions.go_to(nx, ny) + return + end + sx = 1 + end + setError('Pattern not found') + end, + + find_next = function() + if searchPattern then + actions.unmark() + actions.find(searchPattern, x + 1) + end + end, + + find_prompt = function() + local text = actions.input('/') + if #text > 0 then + searchPattern = text:lower() + if searchPattern then + actions.unmark() + actions.find(searchPattern, x) + end + end + end, + + save = function() + if bReadOnly then + setError("Access denied") + else + local ok = save(sPath) + if ok then + setStatus('"%s" %dL, %dC written', + fileInfo.path, #tLines, fs.getSize(fileInfo.abspath)) + else + setError("Error saving to %s", sPath) + end + end + end, + + exit = function() + bRunning = false + end, + + run = function() + input:reset() + local sTempPath = "/.temp" + local ok = save(sTempPath) + if ok then + local nTask = shell.openTab(sTempPath) + if nTask then + shell.switchTab(nTask) + else + setError("Error starting Task") + end + os.sleep(0) + fs.delete(sTempPath) + else + setError("Error saving to %s", sTempPath) + end + end, + + status = function() + local modified = '' + if undo.chain[1] then + modified = '[Modified] ' + end + setStatus('"%s" %s%d lines --%d%%--', + fileInfo.path, modified, #tLines, + math.floor((y - 1) / (#tLines - 1) * 100)) + end, + + dirty_line = function(dy) + if dirty.y == 0 then + dirty.y = dy + dirty.ey = dy + else + dirty.y = math.min(dirty.y, dy) + dirty.ey = math.max(dirty.ey, dy) + end + end, + + dirty_range = function(dy, dey) + actions.dirty_line(dy) + actions.dirty_line(dey or #tLines) + end, + + dirty = function() + actions.dirty_line(y) + end, + + dirty_all = function() + actions.dirty_line(1) + actions.dirty_line(#tLines) + end, + + mark_begin = function() + actions.dirty() + if not mark.active then + mark.active = true + mark.anchor = { x = x, y = y } + end + end, + + mark_finish = function() + if y == mark.anchor.y then + if x == mark.anchor.x then + mark.active = false + else + mark.x = math.min(mark.anchor.x, x) + mark.y = y + mark.ex = math.max(mark.anchor.x, x) + mark.ey = y + end + elseif y < mark.anchor.y then + mark.x = x + mark.y = y + mark.ex = mark.anchor.x + mark.ey = mark.anchor.y + else + mark.x = mark.anchor.x + mark.y = mark.anchor.y + mark.ex = x + mark.ey = y + end + actions.dirty() + mark.continue = mark.active + end, + + unmark = function() + if mark.active then + actions.dirty_range(mark.y, mark.ey) + mark.active = false + end + end, + + mark_anchor = function(nx, ny) + actions.go_to(nx, ny) + actions.unmark() + actions.mark_begin() + actions.mark_finish() + end, + + mark_to = function(nx, ny) + actions.mark_begin() + actions.go_to(nx, ny) + actions.mark_finish() + end, + + mark_up = function() + actions.mark_begin() + actions.up() + actions.mark_finish() + end, + + mark_right = function() + actions.mark_begin() + actions.right() + actions.mark_finish() + end, + + mark_down = function() + actions.mark_begin() + actions.down() + actions.mark_finish() + end, + + mark_left = function() + actions.mark_begin() + actions.left() + actions.mark_finish() + end, + + mark_word = function() + actions.mark_begin() + actions.word() + actions.mark_finish() + end, + + mark_backword = function() + actions.mark_begin() + actions.backword() + actions.mark_finish() + end, + + mark_home = function() + actions.mark_begin() + actions.home() + actions.mark_finish() + end, + + mark_end = function() + actions.mark_begin() + actions.toend() + actions.mark_finish() + end, + + mark_all = function() + mark.anchor = { x = 1, y = 1 } + mark.active = true + mark.continue = true + mark.x = 1 + mark.y = 1 + mark.ey = #tLines + mark.ex = #tLines[mark.ey] + 1 + actions.dirty_all() + end, + + setCursor = function() + lastPos.x = x + lastPos.y = y + + local screenX = x - scrollX + local screenY = y - scrollY + + if screenX < 1 then + scrollX = x - 1 + actions.dirty_all() + elseif screenX > w then + scrollX = x - w + actions.dirty_all() + end + + if screenY < 1 then + scrollY = y - 1 + actions.dirty_all() + elseif screenY > h - 1 then + scrollY = y - (h - 1) + actions.dirty_all() + end + end, + + top = function() + actions.go_to(1, 1) + end, + + bottom = function() + y = #tLines + x = #tLines[y] + 1 + end, + + up = function() + if y > 1 then + x = math.min(x, #tLines[y - 1] + 1) + y = y - 1 + end + end, + + down = function() + if y < #tLines then + x = math.min(x, #tLines[y + 1] + 1) + y = y + 1 + end + end, + + tab = function() + if mark.active then + actions.delete() + end + actions.insertText(x, y, ' ') + end, + + pageUp = function() + actions.go_to(x, y - (h - 1)) + end, + + pageDown = function() + actions.go_to(x, y + (h - 1)) + end, + + home = function() + x = 1 + end, + + toend = function() + x = #tLines[y] + 1 + end, + + left = function() + if x > 1 then + x = x - 1 + elseif y > 1 then + x = #tLines[y - 1] + 1 + y = y - 1 + else + return false + end + return true + end, + + right = function() + if x < #tLines[y] + 1 then + x = x + 1 + elseif y < #tLines then + x = 1 + y = y + 1 + end + end, + + word = function() + local nx = nextWord(tLines[y], x) + if nx then + x = nx + elseif x < #tLines[y] + 1 then + x = #tLines[y] + 1 + elseif y < #tLines then + x = 1 + y = y + 1 + end + end, + + backword = function() + if x == 1 then + actions.left() + else + local sLine = tLines[y] + local lx = 1 + while true do + local nx = nextWord(sLine, lx) + if not nx or nx >= x then + break + end + lx = nx + end + if not lx then + x = 1 + else + x = lx + end + end + end, + + insertText = function(sx, sy, text) + x = sx + y = sy + local sLine = tLines[y] + + if not text:find('\n') then + tLines[y] = sLine:sub(1, x - 1) .. text .. sLine:sub(x) + actions.dirty_line(y) + x = x + #text + else + local lines = split(text) + local remainder = sLine:sub(x) + tLines[y] = sLine:sub(1, x - 1) .. lines[1] + actions.dirty_range(y, #tLines + #lines) + x = x + #lines[1] + for k = 2, #lines do + y = y + 1 + table.insert(tLines, y, lines[k]) + x = #lines[k] + 1 + end + tLines[y] = tLines[y]:sub(1, x) .. remainder + end + + if not undo.active then + actions.addUndo( + { action = 'deleteText', args = { sx, sy, x, y, text } }) + end + end, + + deleteText = function(sx, sy, ex, ey) + x = sx + y = sy + + if not undo.active then + local text = actions.copyText(sx, sy, ex, ey) + actions.addUndo( + { action = 'insertText', args = { sx, sy, text } }) + end + + local front = tLines[sy]:sub(1, sx - 1) + local back = tLines[ey]:sub(ex, #tLines[ey]) + for _ = 2, ey - sy + 1 do + table.remove(tLines, y + 1) + end + tLines[y] = front .. back + if sy ~= ey then + actions.dirty_range(y) + else + actions.dirty() + end + end, + + copyText = function(csx, csy, cex, cey) + local count = 0 + local lines = { } + + for cy = csy, cey do + local line = tLines[cy] + if line then + local cx = 1 + local ex = #line + if cy == csy then + cx = csx + end + if cy == cey then + ex = cex - 1 + end + local str = line:sub(cx, ex) + count = count + #str + table.insert(lines, str) + end + end + return table.concat(lines, '\n'), count + end, + + delete = function() + if mark.active then + actions.deleteText(mark.x, mark.y, mark.ex, mark.ey) + else + local nLimit = #tLines[y] + 1 + if x < nLimit then + actions.deleteText(x, y, x + 1, y) + elseif y < #tLines then + actions.deleteText(x, y, 1, y + 1) + end + end + end, + + backspace = function() + if mark.active then + actions.delete() + elseif actions.left() then + actions.delete() + end + end, + + enter = function() + local sLine = tLines[y] + local _,spaces = sLine:find("^[ ]+") + if not spaces then + spaces = 0 + end + spaces = math.min(spaces, x - 1) + if mark.active then + actions.delete() + end + actions.insertText(x, y, '\n' .. string.rep(' ', spaces)) + end, + + char = function(ch) + if mark.active then + actions.delete() + end + actions.insertText(x, y, ch) + end, + + copy_marked = function() + local text = actions.copyText(mark.x, mark.y, mark.ex, mark.ey) + if clipboard then + clipboard.setData(text) + else + os.queueEvent('clipboard_copy', text) + end + setStatus('shift-^v to paste') + end, + + cut = function() + if mark.active then + actions.copy_marked() + actions.delete() + end + end, + + copy = function() + if mark.active then + actions.copy_marked() + mark.continue = true + end + end, + + paste = function(text) + if mark.active then + actions.delete() + end + if text then + actions.insertText(x, y, text) + setStatus('%d chars added', #text) + else + setStatus('Clipboard empty') + end + end, + + paste_internal = function() + if clipboard then + actions.paste(clipboard.getText()) + end + end, + + go_to = function(cx, cy) + y = math.min(math.max(cy, 1), #tLines) + x = math.min(math.max(cx, 1), #tLines[y] + 1) + end, + + scroll_up = function() + if scrollY > 0 then + scrollY = scrollY - 1 + actions.dirty_all() + end + mark.continue = mark.active + end, + + scroll_down = function() + local nMaxScroll = #tLines - (h-1) + if scrollY < nMaxScroll then + scrollY = scrollY + 1 + actions.dirty_all() + end + mark.continue = mark.active + end, } actions = __actions @@ -1108,65 +1108,65 @@ term.setCursorBlink(true) redraw() while bRunning do - local sEvent, param, param2, param3 = os.pullEventRaw() - local action + local sEvent, param, param2, param3 = os.pullEventRaw() + local action - if sEvent == 'terminate' then - action = 'exit' - elseif sEvent == 'multishell_focus' then -- opus only event - input:reset() - elseif sEvent == "mouse_click" or - sEvent == 'mouse_drag' or - sEvent == 'mouse_up' or - sEvent == 'mouse_down' then - local ie = input:translate(sEvent, param, param2, param3) - if param3 < h or sEvent == 'mouse_drag' then - if ie.code then - action = keyMapping[ie.code] - param = param2 + scrollX - param2 = param3 + scrollY - end - end - else - local ie = input:translate(sEvent, param, param2) - if ie then - if ie.ch and #ie.ch == 1 then - action = keyMapping.char - param = ie.ch - else - action = keyMapping[ie.code] - end - end - end + if sEvent == 'terminate' then + action = 'exit' + elseif sEvent == 'multishell_focus' then -- opus only event + input:reset() + elseif sEvent == "mouse_click" or + sEvent == 'mouse_drag' or + sEvent == 'mouse_up' or + sEvent == 'mouse_down' then + local ie = input:translate(sEvent, param, param2, param3) + if param3 < h or sEvent == 'mouse_drag' then + if ie.code then + action = keyMapping[ie.code] + param = param2 + scrollX + param2 = param3 + scrollY + end + end + else + local ie = input:translate(sEvent, param, param2) + if ie then + if ie.ch and #ie.ch == 1 then + action = keyMapping.char + param = ie.ch + else + action = keyMapping[ie.code] + end + end + end - if action then - if not actions[action] then - error('Invaid action: ' .. action) - end + if action then + if not actions[action] then + error('Invaid action: ' .. action) + end - local wasMarking = mark.continue - mark.continue = false + local wasMarking = mark.continue + mark.continue = false - actions[action](param, param2) - if action ~= 'menu' then - lastAction = action - end + actions[action](param, param2) + if action ~= 'menu' then + lastAction = action + end - if x ~= lastPos.x or y ~= lastPos.y then - actions.setCursor() - end - if not mark.continue and wasMarking then - actions.unmark() - end + if x ~= lastPos.x or y ~= lastPos.y then + actions.setCursor() + end + if not mark.continue and wasMarking then + actions.unmark() + end - redraw() + redraw() - elseif sEvent == "term_resize" then - w,h = term.getSize() - actions.setCursor(x, y) - actions.dirty_all() - redraw() - end + elseif sEvent == "term_resize" then + w,h = term.getSize() + actions.setCursor(x, y) + actions.dirty_all() + redraw() + end end -- Cleanup diff --git a/common/multiMiner.lua b/common/multiMiner.lua index e1a829c..b816954 100644 --- a/common/multiMiner.lua +++ b/common/multiMiner.lua @@ -22,37 +22,37 @@ local scanner = device['plethora:scanner'] or -- hud local canvas = glasses and glasses.canvas() if canvas then - local lh + local lh - local function addText(x, y, text, color) - local th = canvas.group.addText({ x, y }, text, color or 0xa0a0a0FF) - lh = lh or th.getLineHeight() - th.setShadow(true) - th.setScale(.75) - return th - end + local function addText(x, y, text, color) + local th = canvas.group.addText({ x, y }, text, color or 0xa0a0a0FF) + lh = lh or th.getLineHeight() + th.setShadow(true) + th.setScale(.75) + return th + end - canvas.group = canvas.addGroup({ 4, 90 }) - canvas.group.bg = canvas.group.addRectangle(0, 0, 80, 10, 0x40404080) - canvas.group.addLines( - { 0, 0 }, - { 80, 0 }, - { 80, 10 }, - { 0, 10 }, - { 0, 0 }, - 0x202020FF, - 2) - addText(20, 2, 'Swarm Miner', 0xc0c0c0FF) + canvas.group = canvas.addGroup({ 4, 90 }) + canvas.group.bg = canvas.group.addRectangle(0, 0, 80, 10, 0x40404080) + canvas.group.addLines( + { 0, 0 }, + { 80, 0 }, + { 80, 10 }, + { 0, 10 }, + { 0, 0 }, + 0x202020FF, + 2) + addText(20, 2, 'Swarm Miner', 0xc0c0c0FF) - local y = 15 - addText(3, y, 'Turtles') - canvas.turtles = addText(60, y, '') - canvas.group.addLine({ 0, y + lh - 2 }, { 80, y + lh - 2 }, 0x404040FF, 4) + local y = 15 + addText(3, y, 'Turtles') + canvas.turtles = addText(60, y, '') + canvas.group.addLine({ 0, y + lh - 2 }, { 80, y + lh - 2 }, 0x404040FF, 4) - y = y + lh + 5 - addText(3, y, 'Queue') - canvas.queue = addText(60, y, '') - canvas.group.addLine({ 0, y + lh - 2 }, { 80, y + lh - 2 }, 0x404040FF, 4) + y = y + lh + 5 + addText(3, y, 'Queue') + canvas.queue = addText(60, y, '') + canvas.group.addLine({ 0, y + lh - 2 }, { 80, y + lh - 2 }, 0x404040FF, 4) end -- container @@ -62,19 +62,19 @@ local box, offset local paused = false local function inBox(pt) - if not box or not box.ex then - return true - end - return Point.inBox(pt, box) + if not box or not box.ex then + return true + end + return Point.inBox(pt, box) end local function locate() - for _ = 1, 3 do - local pt = GPS.getPoint() - if pt then - return pt - end - end + for _ = 1, 3 do + local pt = GPS.getPoint() + if pt then + return pt + end + end end local spt = GPS.getPoint() or error('GPS failure') @@ -88,7 +88,7 @@ local abort local function hijackTurtle(remoteId) local socket, msg = Socket.connect(remoteId, 188) - if not socket then + if not socket then error(msg) end @@ -111,240 +111,240 @@ local function hijackTurtle(remoteId) end local function getNextPoint(turtle) - if not paused then - local pt = Point.closest(turtle.getPoint(), queue) - if pt then - turtle.pt = pt - queue[pt.pkey] = nil - return pt - end - end + if not paused then + local pt = Point.closest(turtle.getPoint(), queue) + if pt then + turtle.pt = pt + queue[pt.pkey] = nil + return pt + end + end end local function run(member, point) - Event.addRoutine(function() - local turtle, socket - local _, m = pcall(function() - member.active = true - turtle, socket = hijackTurtle(member.id) + Event.addRoutine(function() + local turtle, socket + local _, m = pcall(function() + member.active = true + turtle, socket = hijackTurtle(member.id) - local function emptySlots(retain, pt) - local slots = turtle.getFilledSlots() - for _,slot in pairs(slots) do - if not retain[slot.key] then - turtle.select(slot.index) - if pt then - turtle.dropAt(pt, 64) - else - turtle.dropUp(64) - end - end - end - end + local function emptySlots(retain, pt) + local slots = turtle.getFilledSlots() + for _,slot in pairs(slots) do + if not retain[slot.key] then + turtle.select(slot.index) + if pt then + turtle.dropAt(pt, 64) + else + turtle.dropUp(64) + end + end + end + end - local function dropOff() - -- go to 2 above chest - local topPoint = Point.copy(chestPoint) - topPoint.y = topPoint.y + 2 - turtle.gotoY(topPoint.y) - while not turtle.go(topPoint) do - os.sleep(.5) - end + local function dropOff() + -- go to 2 above chest + local topPoint = Point.copy(chestPoint) + topPoint.y = topPoint.y + 2 + turtle.gotoY(topPoint.y) + while not turtle.go(topPoint) do + os.sleep(.5) + end - -- path to chest - local box = Point.makeBox( - { x = chestPoint.x - 3, y = chestPoint.y + 3, z = chestPoint.z - 3 }, - { x = chestPoint.x + 3, y = chestPoint.y, z = chestPoint.z + 3 } - ) - turtle.set({ - movementStrategy = 'pathing', - pathingBox = Point.normalizeBox(box), - digPolicy = 'digNone', - }) - while not turtle.moveAgainst(chestPoint) do - os.sleep(.5) - end - emptySlots({ }, chestPoint) + -- path to chest + local box = Point.makeBox( + { x = chestPoint.x - 3, y = chestPoint.y + 3, z = chestPoint.z - 3 }, + { x = chestPoint.x + 3, y = chestPoint.y, z = chestPoint.z + 3 } + ) + turtle.set({ + movementStrategy = 'pathing', + pathingBox = Point.normalizeBox(box), + digPolicy = 'digNone', + }) + while not turtle.moveAgainst(chestPoint) do + os.sleep(.5) + end + emptySlots({ }, chestPoint) - -- path to 3 above chest - turtle.pathfind(Point.above(topPoint)) - turtle.set({ - movementStrategy = 'goto', - digPolicy = 'blacklist', - }) - end + -- path to 3 above chest + turtle.pathfind(Point.above(topPoint)) + turtle.set({ + movementStrategy = 'goto', + digPolicy = 'blacklist', + }) + end - if turtle then - turtles[member.id] = turtle + if turtle then + turtles[member.id] = turtle - turtle.reset() - turtle.set({ - attackPolicy = 'attack', - digPolicy = 'blacklist', - blacklist = { - 'turtle', - 'chest', - 'shulker', - }, - movementStrategy = 'goto', - point = point, - }) - turtle.select(1) + turtle.reset() + turtle.set({ + attackPolicy = 'attack', + digPolicy = 'blacklist', + blacklist = { + 'turtle', + 'chest', + 'shulker', + }, + movementStrategy = 'goto', + point = point, + }) + turtle.select(1) - repeat - local pt = getNextPoint(turtle) - if pt then - member.status = 'digging' + repeat + local pt = getNextPoint(turtle) + if pt then + member.status = 'digging' - if blockTypes[pt.key] == true then - if turtle.moveAgainst(pt) then - local index = turtle.selectOpenSlot() - if turtle.digAt(pt, pt.name) then - local slot = turtle.getSlot(index) - if slot.count > 0 then - blockTypes[pt.key] = slot.key - if slot.key ~= pt.key then - blockTypes[slot.key] = true - end - end - end - end - turtle.select(1) - else - turtle.digAt(pt, pt.name) - end + if blockTypes[pt.key] == true then + if turtle.moveAgainst(pt) then + local index = turtle.selectOpenSlot() + if turtle.digAt(pt, pt.name) then + local slot = turtle.getSlot(index) + if slot.count > 0 then + blockTypes[pt.key] = slot.key + if slot.key ~= pt.key then + blockTypes[slot.key] = true + end + end + end + end + turtle.select(1) + else + turtle.digAt(pt, pt.name) + end - if turtle.getItemCount(15) > 0 then - member.status = 'ejecting trash' - emptySlots(blockTypes) - turtle.condense() - if turtle.getItemCount(15) > 0 then - member.status = 'dropping off' - if not chestPoint then - member.abort = true - member.status = 'full' - else - dropOff() - end - end - turtle.select(1) - end - else - member.status = 'waiting' - os.sleep(1) - end - if member.fuel < 100 then - member.status = 'out of fuel' - break - end - until member.abort - end + if turtle.getItemCount(15) > 0 then + member.status = 'ejecting trash' + emptySlots(blockTypes) + turtle.condense() + if turtle.getItemCount(15) > 0 then + member.status = 'dropping off' + if not chestPoint then + member.abort = true + member.status = 'full' + else + dropOff() + end + end + turtle.select(1) + end + else + member.status = 'waiting' + os.sleep(1) + end + if member.fuel < 100 then + member.status = 'out of fuel' + break + end + until member.abort + end - emptySlots(blockTypes) + emptySlots(blockTypes) - if chestPoint then - dropOff() - while not turtle.go(Point.above(spt)) do - os.sleep(.5) - end - --if turtle.selectSlotWithQuantity(0) then - --turtle.set({ digPolicy = 'dig' }) - --end - while not turtle.go(spt) do - os.sleep(.5) - end - else - turtle.gotoY(spt.y) - while not turtle.go(spt) do - os.sleep(.5) - end - end - end) + if chestPoint then + dropOff() + while not turtle.go(Point.above(spt)) do + os.sleep(.5) + end + --if turtle.selectSlotWithQuantity(0) then + --turtle.set({ digPolicy = 'dig' }) + --end + while not turtle.go(spt) do + os.sleep(.5) + end + else + turtle.gotoY(spt.y) + while not turtle.go(spt) do + os.sleep(.5) + end + end + end) - turtles[member.id] = nil - member.status = m - member.active = false - if socket then - socket:close() - end - end) + turtles[member.id] = nil + member.status = m + member.active = false + if socket then + socket:close() + end + end) end local function drawContainer(pos) - if canvas3d then - canvas3d.clear() + if canvas3d then + canvas3d.clear() - local function addBox(b) - canvas3d.addBox( - b.x - offset.x + .25, - b.y - offset.y + .25 , - b.z - offset.z + .25 , - .5, .5, .5).setDepthTested(false) - end - if box and box.ex then - addBox({ x = box.x, y = box.y, z = box.z }) - addBox({ x = box.x, y = box.y, z = box.ez }) - addBox({ x = box.ex, y = box.y, z = box.z }) - addBox({ x = box.ex, y = box.y, z = box.ez }) - addBox({ x = box.x, y = box.ey, z = box.z }) - addBox({ x = box.x, y = box.ey, z = box.ez }) - addBox({ x = box.ex, y = box.ey, z = box.z }) - addBox({ x = box.ex, y = box.ey, z = box.ez }) - elseif box then - canvas3d.recenter({ -(pos.x % 1), -(pos.y % 1), -(pos.z % 1) }) - addBox(box) - end - end + local function addBox(b) + canvas3d.addBox( + b.x - offset.x + .25, + b.y - offset.y + .25 , + b.z - offset.z + .25 , + .5, .5, .5).setDepthTested(false) + end + if box and box.ex then + addBox({ x = box.x, y = box.y, z = box.z }) + addBox({ x = box.x, y = box.y, z = box.ez }) + addBox({ x = box.ex, y = box.y, z = box.z }) + addBox({ x = box.ex, y = box.y, z = box.ez }) + addBox({ x = box.x, y = box.ey, z = box.z }) + addBox({ x = box.x, y = box.ey, z = box.ez }) + addBox({ x = box.ex, y = box.ey, z = box.z }) + addBox({ x = box.ex, y = box.ey, z = box.ez }) + elseif box then + canvas3d.recenter({ -(pos.x % 1), -(pos.y % 1), -(pos.z % 1) }) + addBox(box) + end + end end local pauseResume = { - { text = 'Pause', event = 'pause' }, - { text = 'Resume', event = 'resume' }, + { text = 'Pause', event = 'pause' }, + { text = 'Resume', event = 'resume' }, } local containerText = { - [[Set a corner to contain mining area]], - [[Set ending corner]], - [[Set again to clear]], + [[Set a corner to contain mining area]], + [[Set ending corner]], + [[Set again to clear]], } local containTab = UI.Tab { - tabTitle = 'Contain', - button = UI.Button { - x = 2, y = 2, - text = 'Set corner', - event = 'contain' - }, - textArea = UI.TextArea { - x = 2, y = 4, - value = containerText[1], - }, + tabTitle = 'Contain', + button = UI.Button { + x = 2, y = 2, + text = 'Set corner', + event = 'contain' + }, + textArea = UI.TextArea { + x = 2, y = 4, + value = containerText[1], + }, } local blocksTab = UI.Tab { - tabTitle = 'Blocks', - grid = UI.ScrollingGrid { - y = 1, - columns = { - { heading = 'Count', key = 'count', width = 6, align = 'right' }, - { heading = 'Name', key = 'displayName' }, - }, - sortColumn = 'displayName', - }, + tabTitle = 'Blocks', + grid = UI.ScrollingGrid { + y = 1, + columns = { + { heading = 'Count', key = 'count', width = 6, align = 'right' }, + { heading = 'Name', key = 'displayName' }, + }, + sortColumn = 'displayName', + }, } local turtlesTab = UI.Tab { - tabTitle = 'Turtles', - grid = UI.ScrollingGrid { - y = 1, - values = pool, - columns = { - { heading = 'ID', key = 'id', width = 5, }, - { heading = ' Fuel', key = 'fuel', width = 5, align = 'right' }, - { heading = ' Dist', key = 'distance', width = 5, align = 'right' }, - { heading = 'Status', key = 'status' }, - }, - sortColumn = 'label', - }, + tabTitle = 'Turtles', + grid = UI.ScrollingGrid { + y = 1, + values = pool, + columns = { + { heading = 'ID', key = 'id', width = 5, }, + { heading = ' Fuel', key = 'fuel', width = 5, align = 'right' }, + { heading = ' Dist', key = 'distance', width = 5, align = 'right' }, + { heading = 'Status', key = 'status' }, + }, + sortColumn = 'label', + }, } local page = UI.Page { @@ -354,264 +354,264 @@ local page = UI.Page { { text = 'Abort', event = 'abort' }, pauseResume[1], }, - }, - tabs = UI.Tabs { - y = 2, ey = -2, - [1] = blocksTab, - [2] = turtlesTab, - [3] = containTab, - }, - info = UI.Window { - y = -1, - backgroundColor = colors.blue, - } + }, + tabs = UI.Tabs { + y = 2, ey = -2, + [1] = blocksTab, + [2] = turtlesTab, + [3] = containTab, + }, + info = UI.Window { + y = -1, + backgroundColor = colors.blue, + } } function page.info:draw() - self:clear() - self:write(2, 1, 'Turtles: ' .. Util.size(turtles)) - if not chestPoint then - self:write(16, 1, 'No chest') - end - self:write(28, 1, 'Queue: ' .. Util.size(queue)) + self:clear() + self:write(2, 1, 'Turtles: ' .. Util.size(turtles)) + if not chestPoint then + self:write(16, 1, 'No chest') + end + self:write(28, 1, 'Queue: ' .. Util.size(queue)) end function turtlesTab.grid:getDisplayValues(row) row = Util.shallowCopy(row) - row.distance = row.distance and Util.round(row.distance, 1) - row.fuel = row.fuel and row.fuel > 0 and Util.toBytes(row.fuel) or '' - return row + row.distance = row.distance and Util.round(row.distance, 1) + row.fuel = row.fuel and row.fuel > 0 and Util.toBytes(row.fuel) or '' + return row end function page:scan() - local gpt = GPS.getPoint() - if not gpt then - return - end - local rawBlocks = scanner:scan() - local candidates = { } + local gpt = GPS.getPoint() + if not gpt then + return + end + local rawBlocks = scanner:scan() + local candidates = { } - self.totals = Util.reduce(rawBlocks, - function(acc, b) - b.key = table.concat({ b.name, b.metadata }, ':') - local entry = acc[b.key] - if not entry then - b.displayName = itemDB:getName(b.key) - b.count = 1 - acc[b.key] = b + self.totals = Util.reduce(rawBlocks, + function(acc, b) + b.key = table.concat({ b.name, b.metadata }, ':') + local entry = acc[b.key] + if not entry then + b.displayName = itemDB:getName(b.key) + b.count = 1 + acc[b.key] = b else - entry.count = entry.count + 1 - end + entry.count = entry.count + 1 + end - if b.name == 'computercraft:turtle_advanced' or - b.name == 'computercraft:turtle_expanded' or - b.name == 'computercraft:turtle' then - table.insert(candidates, b) - end + if b.name == 'computercraft:turtle_advanced' or + b.name == 'computercraft:turtle_expanded' or + b.name == 'computercraft:turtle' then + table.insert(candidates, b) + end - if b.name == 'minecraft:chest' or b.name:find('shulker') then - chestPoint = b - end + if b.name == 'minecraft:chest' or b.name:find('shulker') then + chestPoint = b + end - -- add relevant blocks to queue - b.x = gpt.x + b.x - b.y = gpt.y + b.y - b.z = gpt.z + b.z - b.pkey = table.concat({ b.x, b.y, b.z }, ':') - if blockTypes[b.key] and inBox(b) then - if not Util.any(turtles, function(t) - return t.pt and t.pt.pkey == b.pkey - end) then - queue[b.pkey] = b - end - else - queue[b.pkey] = nil - end - return acc + -- add relevant blocks to queue + b.x = gpt.x + b.x + b.y = gpt.y + b.y + b.z = gpt.z + b.z + b.pkey = table.concat({ b.x, b.y, b.z }, ':') + if blockTypes[b.key] and inBox(b) then + if not Util.any(turtles, function(t) + return t.pt and t.pt.pkey == b.pkey + end) then + queue[b.pkey] = b + end + else + queue[b.pkey] = nil + end + return acc end, - { }) + { }) - for _, b in pairs(candidates) do - local v = scanner.getBlockMeta(b.x - gpt.x, b.y - gpt.y, b.z - gpt.z) - if v and v.computer then - local member = pool[v.computer.id] - if not member then - member = { - id = v.computer.id, - label = v.computer.label, - } - pool[v.computer.id] = member - end + for _, b in pairs(candidates) do + local v = scanner.getBlockMeta(b.x - gpt.x, b.y - gpt.y, b.z - gpt.z) + if v and v.computer then + local member = pool[v.computer.id] + if not member then + member = { + id = v.computer.id, + label = v.computer.label, + } + pool[v.computer.id] = member + end - member.fuel = v.turtle.fuel - member.distance = 0 + member.fuel = v.turtle.fuel + member.distance = 0 - if not v.computer.isOn then - member.status = 'Powered off' - elseif v.turtle.fuel < 100 and not member.active then - member.status = 'Not enough fuel' - elseif not member.active and not member.abort then - local pt = Point.copy(b) - pt.heading = Point.facings[v.state.facing].heading - run(member, pt) - end - end - end + if not v.computer.isOn then + member.status = 'Powered off' + elseif v.turtle.fuel < 100 and not member.active then + member.status = 'Not enough fuel' + elseif not member.active and not member.abort then + local pt = Point.copy(b) + pt.heading = Point.facings[v.state.facing].heading + run(member, pt) + end + end + end end function blocksTab.grid:getDisplayValues(row) row = Util.shallowCopy(row) row.count = Util.toBytes(row.count) .. ' ' - return row + return row end function blocksTab.grid:getRowTextColor(row, selected) - return blockTypes[row.key] and - colors.yellow or - UI.Grid.getRowTextColor(self, row, selected) + return blockTypes[row.key] and + colors.yellow or + UI.Grid.getRowTextColor(self, row, selected) end function blocksTab:eventHandler(event) if event.type == 'grid_select' then - local key = event.selected.key - if blockTypes[key] then - for k,v in pairs(queue) do - if v.key == key then - queue[k] = nil - end - end - blockTypes[key] = nil - else - blockTypes[key] = true - end - self.grid:draw() - end + local key = event.selected.key + if blockTypes[key] then + for k,v in pairs(queue) do + if v.key == key then + queue[k] = nil + end + end + blockTypes[key] = nil + else + blockTypes[key] = true + end + self.grid:draw() + end end function page:eventHandler(event) - if event.type == 'scan' then - blocksTab.grid:setValues(self.totals) - blocksTab.grid:draw() - self.tabs:selectTab(blocksTab) + if event.type == 'scan' then + blocksTab.grid:setValues(self.totals) + blocksTab.grid:draw() + self.tabs:selectTab(blocksTab) - elseif event.type == 'pause' then - paused = true - Util.merge(event.button, pauseResume[2]) - event.button:draw() + elseif event.type == 'pause' then + paused = true + Util.merge(event.button, pauseResume[2]) + event.button:draw() - elseif event.type == 'resume' then - paused = false - Util.merge(event.button, pauseResume[1]) - event.button:draw() + elseif event.type == 'resume' then + paused = false + Util.merge(event.button, pauseResume[1]) + event.button:draw() - elseif event.type == 'contain' then - local pt = { gps.locate() } - local pos = { - x = pt[1], - y = pt[2], - z = pt[3], - } + elseif event.type == 'contain' then + local pt = { gps.locate() } + local pos = { + x = pt[1], + y = pt[2], + z = pt[3], + } - if not box then - offset = { - x = math.floor(pos.x), - y = math.floor(pos.y), - z = math.floor(pos.z), - } - box = { - x = math.floor(pos.x), - y = math.floor(pos.y) - 1, - z = math.floor(pos.z), - } - containTab.textArea.value = containerText[2] - elseif not box.ex then - box.ex = math.floor(pos.x) - box.ey = math.floor(pos.y) - 1 - box.ez = math.floor(pos.z) - box = Point.normalizeBox(box) - containTab.textArea.value = containerText[3] - else - box = nil - containTab.textArea.value = containerText[1] - end + if not box then + offset = { + x = math.floor(pos.x), + y = math.floor(pos.y), + z = math.floor(pos.z), + } + box = { + x = math.floor(pos.x), + y = math.floor(pos.y) - 1, + z = math.floor(pos.z), + } + containTab.textArea.value = containerText[2] + elseif not box.ex then + box.ex = math.floor(pos.x) + box.ey = math.floor(pos.y) - 1 + box.ez = math.floor(pos.z) + box = Point.normalizeBox(box) + containTab.textArea.value = containerText[3] + else + box = nil + containTab.textArea.value = containerText[1] + end - containTab.textArea:draw() - drawContainer(pos) + containTab.textArea:draw() + drawContainer(pos) - elseif event.type == 'abort' then - for _, v in pairs(pool) do - v.abort = true - v.status = 'aborting' - end - spt = Point.above(locate()) - abort = true - end + elseif event.type == 'abort' then + for _, v in pairs(pool) do + v.abort = true + v.status = 'aborting' + end + spt = Point.above(locate()) + abort = true + end UI.Page.eventHandler(self, event) end Event.onInterval(5, function() - if not abort and not paused then + if not abort and not paused then - --local meta = scanner.getMetaOwner() - --if meta.isSneaking then - page:scan() - -- Sound.play('entity.bobber.throw', .6) - --end - end + --local meta = scanner.getMetaOwner() + --if meta.isSneaking then + page:scan() + -- Sound.play('entity.bobber.throw', .6) + --end + end end) Event.onInterval(1, function() - for id,v in pairs(network) do - if v.fuel then - if pool[id] then - pool[id].fuel = v.fuel - pool[id].distance = v.distance - end - end - end + for id,v in pairs(network) do + if v.fuel then + if pool[id] then + pool[id].fuel = v.fuel + pool[id].distance = v.distance + end + end + end - if abort and Util.size(turtles) == 0 then - Event.exitPullEvents() - end + if abort and Util.size(turtles) == 0 then + Event.exitPullEvents() + end - if turtlesTab.enabled then - turtlesTab.grid:update() - turtlesTab.grid:draw() - end + if turtlesTab.enabled then + turtlesTab.grid:update() + turtlesTab.grid:draw() + end - page.info:draw() - page.info:sync() + page.info:draw() + page.info:sync() - if canvas then - canvas.turtles.setText(tostring(Util.size(turtles))) - canvas.queue.setText(tostring(Util.size(queue))) - end + if canvas then + canvas.turtles.setText(tostring(Util.size(turtles))) + canvas.queue.setText(tostring(Util.size(queue))) + end end) Event.onTimeout(.1, function() - page:scan() - blocksTab.grid:setValues(page.totals) - blocksTab.grid:draw() - page:sync() + page:scan() + blocksTab.grid:setValues(page.totals) + blocksTab.grid:draw() + page:sync() end) UI:setPage(page) --[[ Event.onTerminate(function() - spt = Point.above(locate()) - for _, v in pairs(pool) do - v.status = 'aborting' - v.abort = true - end - abort = true + spt = Point.above(locate()) + for _, v in pairs(pool) do + v.status = 'aborting' + v.abort = true + end + abort = true end) ]] Event.pullEvents() if canvas then - canvas3d.clear() - canvas.group.remove() + canvas3d.clear() + canvas.group.remove() end diff --git a/core/apis/chestAdapter.lua b/core/apis/chestAdapter.lua index 39b4e2a..c503cad 100644 --- a/core/apis/chestAdapter.lua +++ b/core/apis/chestAdapter.lua @@ -8,154 +8,154 @@ local os = _G.os local ChestAdapter = class() local convertNames = { - name = 'id', - damage = 'dmg', - maxCount = 'max_size', - count = 'qty', - displayName = 'display_name', - maxDamage = 'max_dmg', - nbtHash = 'nbt_hash', + name = 'id', + damage = 'dmg', + maxCount = 'max_size', + count = 'qty', + displayName = 'display_name', + maxDamage = 'max_dmg', + nbtHash = 'nbt_hash', } -- Strip off color prefix local function safeString(text) - local val = text:byte(1) + local val = text:byte(1) - if val < 32 or val > 128 then + if val < 32 or val > 128 then - local newText = {} - for i = 4, #text do - val = text:byte(i) - newText[i - 3] = (val > 31 and val < 127) and val or 63 - end - return string.char(unpack(newText)) - end + local newText = {} + for i = 4, #text do + val = text:byte(i) + newText[i - 3] = (val > 31 and val < 127) and val or 63 + end + return string.char(unpack(newText)) + end - return text + return text end local function convertItem(item) - for k,v in pairs(convertNames) do - item[k] = item[v] - item[v] = nil - end - item.displayName = safeString(item.displayName) + for k,v in pairs(convertNames) do + item[k] = item[v] + item[v] = nil + end + item.displayName = safeString(item.displayName) end function ChestAdapter:init(args) - local defaults = { - name = 'chest', - } - Util.merge(self, defaults) - Util.merge(self, args) + local defaults = { + name = 'chest', + } + Util.merge(self, defaults) + Util.merge(self, args) - local chest - if not self.side then - chest = Peripheral.getByMethod('getAllStacks') - else - chest = Peripheral.getBySide(self.side) - if chest and not chest.getAllStacks then - chest = nil - end - end + local chest + if not self.side then + chest = Peripheral.getByMethod('getAllStacks') + else + chest = Peripheral.getBySide(self.side) + if chest and not chest.getAllStacks then + chest = nil + end + end - if chest then - Util.merge(self, chest) + if chest then + Util.merge(self, chest) - if chest.listAvailableItems then - self.list = chest.listAvailableItems - end - end + if chest.listAvailableItems then + self.list = chest.listAvailableItems + end + end end function ChestAdapter:isValid() - return not not self.getAllStacks + return not not self.getAllStacks end function ChestAdapter:refresh(throttle) - return self:listItems(throttle) + return self:listItems(throttle) end -- provide a consolidated list of items function ChestAdapter:listItems(throttle) - local cache = { } - local items = { } - throttle = throttle or Util.throttle() + local cache = { } + local items = { } + throttle = throttle or Util.throttle() - -- getAllStacks sometimes fails - local s, m = pcall(function() - for _,v in pairs(self.getAllStacks(false)) do - if v.qty > 0 then - convertItem(v) - local key = table.concat({ v.name, v.damage, v.nbtHash }, ':') + -- getAllStacks sometimes fails + local s, m = pcall(function() + for _,v in pairs(self.getAllStacks(false)) do + if v.qty > 0 then + convertItem(v) + local key = table.concat({ v.name, v.damage, v.nbtHash }, ':') - local entry = cache[key] - if not entry then - entry = itemDB:get(v) or itemDB:add(v) - entry = Util.shallowCopy(entry) - entry.count = 0 - cache[key] = entry - table.insert(items, entry) - end - entry.count = entry.count + v.count - throttle() - end - itemDB:flush() - end - end) - if s then - if not Util.empty(items) then - self.cache = cache - return items - end - else - _G._syslog(m) - end + local entry = cache[key] + if not entry then + entry = itemDB:get(v) or itemDB:add(v) + entry = Util.shallowCopy(entry) + entry.count = 0 + cache[key] = entry + table.insert(items, entry) + end + entry.count = entry.count + v.count + throttle() + end + itemDB:flush() + end + end) + if s then + if not Util.empty(items) then + self.cache = cache + return items + end + else + _G._syslog(m) + end end function ChestAdapter:getItemInfo(item) - if not self.cache then - self:listItems() - end - local key = table.concat({ item.name, item.damage, item.nbtHash }, ':') - return self.cache[key] + if not self.cache then + self:listItems() + end + local key = table.concat({ item.name, item.damage, item.nbtHash }, ':') + return self.cache[key] end function ChestAdapter:provide(item, qty, slot, direction) - pcall(function() - for key,stack in Util.rpairs(self.getAllStacks(false)) do - if stack.id == item.name and - (not item.damage or stack.dmg == item.damage) and - (not item.nbtHash or stack.nbt_hash == item.nbtHash) then - local amount = math.min(qty, stack.qty) - if amount > 0 then - self.pushItemIntoSlot(direction or self.direction, key, amount, slot) - end - qty = qty - amount - if qty <= 0 then - break - end - end - end - end) + pcall(function() + for key,stack in Util.rpairs(self.getAllStacks(false)) do + if stack.id == item.name and + (not item.damage or stack.dmg == item.damage) and + (not item.nbtHash or stack.nbt_hash == item.nbtHash) then + local amount = math.min(qty, stack.qty) + if amount > 0 then + self.pushItemIntoSlot(direction or self.direction, key, amount, slot) + end + qty = qty - amount + if qty <= 0 then + break + end + end + end + end) end function ChestAdapter:extract(slot, qty, toSlot) - if toSlot then - self.pushItemIntoSlot(self.direction, slot, qty, toSlot) - else - self.pushItem(self.direction, slot, qty) - end + if toSlot then + self.pushItemIntoSlot(self.direction, slot, qty, toSlot) + else + self.pushItem(self.direction, slot, qty) + end end function ChestAdapter:insert(slot, qty, toSlot) - -- toSlot not tested ... - local s, m = pcall(self.pullItem, self.direction, slot, qty, toSlot) - if not s and m then - os.sleep(1) - pcall(self.pullItem, self.direction, slot, qty, toSlot) - end + -- toSlot not tested ... + local s, m = pcall(self.pullItem, self.direction, slot, qty, toSlot) + if not s and m then + os.sleep(1) + pcall(self.pullItem, self.direction, slot, qty, toSlot) + end end return ChestAdapter diff --git a/core/apis/chestAdapter18.lua b/core/apis/chestAdapter18.lua index 6176c5c..e1c02ef 100644 --- a/core/apis/chestAdapter18.lua +++ b/core/apis/chestAdapter18.lua @@ -6,159 +6,159 @@ local Peripheral = require('peripheral') local ChestAdapter = class() function ChestAdapter:init(args) - local defaults = { - name = 'chest', - adapter = 'ChestAdapter18' - } - Util.merge(self, defaults) - Util.merge(self, args) + local defaults = { + name = 'chest', + adapter = 'ChestAdapter18' + } + Util.merge(self, defaults) + Util.merge(self, args) - local chest - if not self.side then - chest = Peripheral.getByMethod('list') or - Peripheral.getByMethod('listAvailableItems') - else - chest = Peripheral.getBySide(self.side) - if chest and not chest.list and not chest.listAvailableItems then - chest = nil - end - end + local chest + if not self.side then + chest = Peripheral.getByMethod('list') or + Peripheral.getByMethod('listAvailableItems') + else + chest = Peripheral.getBySide(self.side) + if chest and not chest.list and not chest.listAvailableItems then + chest = nil + end + end - if chest then - Util.merge(self, chest) + if chest then + Util.merge(self, chest) - if chest.listAvailableItems then - self.list = chest.listAvailableItems - end - end + if chest.listAvailableItems then + self.list = chest.listAvailableItems + end + end end function ChestAdapter:isValid() - return not not self.list + return not not self.list end -- handle both AE/RS and generic inventory function ChestAdapter:getItemDetails(index, item) - if self.getItemMeta then - local s, detail = pcall(self.getItemMeta, index) - if not s or not detail or detail.name ~= item.name then - return - end - return detail - else - local detail = self.findItems(item) - if detail and #detail > 0 then - return detail[1].getMetadata() - end - end + if self.getItemMeta then + local s, detail = pcall(self.getItemMeta, index) + if not s or not detail or detail.name ~= item.name then + return + end + return detail + else + local detail = self.findItems(item) + if detail and #detail > 0 then + return detail[1].getMetadata() + end + end end function ChestAdapter:getCachedItemDetails(item, k) - local cached = itemDB:get(item) - if cached then - return cached - end + local cached = itemDB:get(item) + if cached then + return cached + end - local detail = self:getItemDetails(k, item) - if detail then - return itemDB:add(detail) - end + local detail = self:getItemDetails(k, item) + if detail then + return itemDB:add(detail) + end end function ChestAdapter:refresh(throttle) - return self:listItems(throttle) + return self:listItems(throttle) 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) + 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() + local cache = { } + local items = { } + 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 }, ':') + 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 - entry = self:getCachedItemDetails(v, k) - if not entry then - return -- Inventory has changed - end - entry = Util.shallowCopy(entry) - entry.count = 0 - cache[key] = entry - table.insert(items, entry) - end + local entry = cache[key] + if not entry then + entry = self:getCachedItemDetails(v, k) + if not entry then + return -- Inventory has changed + end + entry = Util.shallowCopy(entry) + entry.count = 0 + cache[key] = entry + table.insert(items, entry) + end - if entry then - entry.count = entry.count + v.count - end - throttle() - end - end - itemDB:flush() + if entry then + entry.count = entry.count + v.count + end + throttle() + end + end + itemDB:flush() - self.cache = cache - return items + self.cache = cache + return items end function ChestAdapter: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] + 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 ChestAdapter:getPercentUsed() - if self.cache and self.getDrawerCount then - return math.floor(Util.size(self.cache) / self.getDrawerCount() * 100) - end - return 0 + if self.cache and self.getDrawerCount then + return math.floor(Util.size(self.cache) / self.getDrawerCount() * 100) + end + return 0 end function ChestAdapter:provide(item, qty, slot, direction) - local total = 0 + local total = 0 - local _, m = pcall(function() - local stacks = self.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 - 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 total, m + local _, m = pcall(function() + local stacks = self.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 + 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 total, m end function ChestAdapter:extract(slot, qty, toSlot, direction) - return self.pushItems(direction or self.direction, slot, qty, toSlot) + return self.pushItems(direction or self.direction, slot, qty, toSlot) end function ChestAdapter:insert(slot, qty, toSlot, direction) - return self.pullItems(direction or self.direction, slot, qty, toSlot) + return self.pullItems(direction or self.direction, slot, qty, toSlot) end return ChestAdapter diff --git a/core/apis/itemDB.lua b/core/apis/itemDB.lua index d486499..7091854 100644 --- a/core/apis/itemDB.lua +++ b/core/apis/itemDB.lua @@ -6,277 +6,277 @@ local Util = require('util') local itemDB = TableDB({ fileName = 'usr/config/items.db' }) local function safeString(text) - local val = text:byte(1) + local val = text:byte(1) - if val < 32 or val > 128 then + if val < 32 or val > 128 then - local newText = { } - local skip = 0 - for i = 1, #text do - val = text:byte(i) - if val == 167 then - skip = 2 - end - if skip > 0 then - skip = skip - 1 - else - if val >= 32 and val <= 128 then - newText[#newText + 1] = val - end - end - end - return string.char(unpack(newText)) - end + local newText = { } + local skip = 0 + for i = 1, #text do + val = text:byte(i) + if val == 167 then + skip = 2 + end + if skip > 0 then + skip = skip - 1 + else + if val >= 32 and val <= 128 then + newText[#newText + 1] = val + end + end + end + return string.char(unpack(newText)) + end - return text + return 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 }, ':') + if not item then error('itemDB:makeKey: item is required', 2) end + return table.concat({ item.name, item.damage or '*', item.nbtHash }, ':') end function itemDB:splitKey(key, item) - item = item or { } + item = item or { } - local t = Util.split(key, '(.-):') - if #t[#t] > 8 then - item.nbtHash = table.remove(t) - end - local damage = table.remove(t) - if damage ~= '*' then - item.damage = tonumber(damage) - end - item.name = table.concat(t, ':') + local t = Util.split(key, '(.-):') + if #t[#t] > 8 then + item.nbtHash = table.remove(t) + end + local damage = table.remove(t) + if damage ~= '*' then + item.damage = tonumber(damage) + end + item.name = table.concat(t, ':') - return item + return item end function itemDB:get(key, populateFn) - if not key then error('itemDB:get: key is required', 2) end - if type(key) == 'string' then - key = self:splitKey(key) - else - key = Util.shallowCopy(key) - end + if not key then error('itemDB:get: key is required', 2) end + if type(key) == 'string' then + key = self:splitKey(key) + else + key = Util.shallowCopy(key) + end - local item = self:_get(key) - if not item and populateFn then - item = populateFn() - if item then - item = self:add(item) - end - end + local item = self:_get(key) + if not item and populateFn then + item = populateFn() + if item then + item = self:add(item) + end + end - return item and Util.merge(key, item) + return item and Util.merge(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 + if not key then error('itemDB:get: key is required', 2) end + if type(key) == 'string' then + key = self:splitKey(key) + end - local item = TableDB.get(self, self:makeKey(key)) - if item then - return item - end + local item = TableDB.get(self, self:makeKey(key)) + if item then + return item + end - -- try finding an item that has damage values - if type(key.damage) == 'number' then - item = TableDB.get(self, self:makeKey({ name = key.name, nbtHash = key.nbtHash })) - if item and item.maxDamage then - item = Util.shallowCopy(item) - item.damage = key.damage - if item.maxDamage > 0 and type(item.damage) == 'number' and item.damage > 0 then - item.displayName = string.format('%s (damage: %s)', item.displayName, item.damage) - end - return item - end - else - for k,item in pairs(self.data) do - if key.name == item.name and - key.nbtHash == key.nbtHash and - item.maxDamage > 0 then - item = Util.shallowCopy(item) - item.nbtHash = key.nbtHash - return item - end - end - end + -- try finding an item that has damage values + if type(key.damage) == 'number' then + item = TableDB.get(self, self:makeKey({ name = key.name, nbtHash = key.nbtHash })) + if item and item.maxDamage then + item = Util.shallowCopy(item) + item.damage = key.damage + if item.maxDamage > 0 and type(item.damage) == 'number' and item.damage > 0 then + item.displayName = string.format('%s (damage: %s)', item.displayName, item.damage) + end + return item + end + else + for k,item in pairs(self.data) do + if key.name == item.name and + key.nbtHash == key.nbtHash and + item.maxDamage > 0 then + item = Util.shallowCopy(item) + item.nbtHash = key.nbtHash + return item + end + end + end - if key.nbtHash then - item = self:get({ name = key.name, damage = key.damage }) + if key.nbtHash then + item = self:get({ name = key.name, damage = key.damage }) - if item and item.ignoreNBT then - item = Util.shallowCopy(item) - item.nbtHash = key.nbtHash - item.damage = key.damage - return item - end - end + if item and item.ignoreNBT then + item = Util.shallowCopy(item) + item.nbtHash = key.nbtHash + item.damage = key.damage + return item + end + end end local function formatTime(t) - local m = math.floor(t/60) - local s = t % 60 - if s < 10 then - s = '0' .. s - end + local m = math.floor(t/60) + local s = t % 60 + if s < 10 then + s = '0' .. s + end - return m .. ':' .. s + return m .. ':' .. s end --[[ - If the base item contains an NBT hash, then the NBT hash uniquely - identifies this item. + If the base item contains an NBT hash, then the NBT hash uniquely + identifies this item. ]]-- function itemDB:add(baseItem) - local nItem = { - name = baseItem.name, - damage = baseItem.damage, - nbtHash = baseItem.nbtHash, - } + local nItem = { + name = baseItem.name, + damage = baseItem.damage, + nbtHash = baseItem.nbtHash, + } -- if detail.maxDamage > 0 then -- nItem.damage = '*' -- end - nItem.displayName = safeString(baseItem.displayName) - nItem.maxCount = baseItem.maxCount - nItem.maxDamage = baseItem.maxDamage + nItem.displayName = safeString(baseItem.displayName) + nItem.maxCount = baseItem.maxCount + nItem.maxDamage = baseItem.maxDamage - -- enchanted items - if baseItem.enchantments then - 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 + -- enchanted items + if baseItem.enchantments then + 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 - -- turtles / computers / etc - elseif baseItem.computer then - -- a turtle's NBT is updated constantly - -- update the cache with the new NBT - if baseItem.computer.id then - Map.removeMatches(self.data, { name = nItem.name, displayName = nItem.displayName }) - end - nItem.displayName = baseItem.computer.label or baseItem.displayName + -- turtles / computers / etc + elseif baseItem.computer then + -- a turtle's NBT is updated constantly + -- update the cache with the new NBT + if baseItem.computer.id then + Map.removeMatches(self.data, { name = nItem.name, displayName = nItem.displayName }) + end + nItem.displayName = baseItem.computer.label or baseItem.displayName - -- 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 + -- 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 - 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 + -- 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 + else + 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 + 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 - TableDB.add(self, self:makeKey(nItem), nItem) - nItem = Util.shallowCopy(nItem) - nItem.damage = baseItem.damage - nItem.nbtHash = baseItem.nbtHash + TableDB.add(self, self:makeKey(nItem), nItem) + nItem = Util.shallowCopy(nItem) + nItem.damage = baseItem.damage + nItem.nbtHash = baseItem.nbtHash - return nItem + return nItem end -- Accepts: "minecraft:stick:0" or { name = 'minecraft:stick', damage = 0 } function itemDB:getName(item) - if type(item) == 'string' then - item = self:splitKey(item) - end + if type(item) == 'string' then + item = self:splitKey(item) + end - local detail = self:get(item) - if detail then - return detail.displayName - end + local detail = self:get(item) + if detail then + return detail.displayName + end - -- fallback to nameDB - local strId = self:makeKey(item) - local name = nameDB.data[strId] - if not name and not item.damage then - name = nameDB.data[self:makeKey({ name = item.name, damage = 0, nbtHash = item.nbtHash })] - end - return name or strId + -- fallback to nameDB + local strId = self:makeKey(item) + local name = nameDB.data[strId] + if not name and not item.damage then + name = nameDB.data[self:makeKey({ name = item.name, damage = 0, nbtHash = item.nbtHash })] + end + return name or strId end function itemDB:getMaxCount(item) - local detail = self:get(item) - return detail and detail.maxCount or 64 + local detail = self:get(item) + return detail and detail.maxCount or 64 end function itemDB:load() - TableDB.load(self) + TableDB.load(self) - for key,item in pairs(self.data) do - self:splitKey(key, item) - item.maxDamage = item.maxDamage or 0 - item.maxCount = item.maxCount or 64 - end + for key,item in pairs(self.data) do + self:splitKey(key, item) + item.maxDamage = item.maxDamage or 0 + item.maxCount = item.maxCount or 64 + end end function itemDB:flush() - if self.dirty then + if self.dirty then - local t = { } - for k,v in pairs(self.data) do - v = Util.shallowCopy(v) - v.name = nil - v.damage = nil - v.nbtHash = nil + local t = { } + for k,v in pairs(self.data) do + v = Util.shallowCopy(v) + v.name = nil + v.damage = nil + v.nbtHash = nil v.count = nil -- wipe out previously saved counts - temporary - if v.maxDamage == 0 then - v.maxDamage = nil - end - if v.maxCount == 64 then - v.maxCount = nil - end - t[k] = v - end + if v.maxDamage == 0 then + v.maxDamage = nil + end + if v.maxCount == 64 then + v.maxCount = nil + end + t[k] = v + end - Util.writeTable(self.fileName, t) - self.dirty = false - end + Util.writeTable(self.fileName, t) + self.dirty = false + end end itemDB:load() diff --git a/core/apis/meAdapter.lua b/core/apis/meAdapter.lua index 0263752..e2914d1 100644 --- a/core/apis/meAdapter.lua +++ b/core/apis/meAdapter.lua @@ -6,249 +6,249 @@ local Util = require('util') local os = _G.os local convertNames = { - name = 'id', - damage = 'dmg', - maxCount = 'max_size', - count = 'qty', - displayName = 'display_name', - maxDamage = 'max_dmg', - nbtHash = 'nbt_hash', + name = 'id', + damage = 'dmg', + maxCount = 'max_size', + count = 'qty', + displayName = 'display_name', + maxDamage = 'max_dmg', + nbtHash = 'nbt_hash', } -- Strip off color prefix local function safeString(text) - local val = text:byte(1) + local val = text:byte(1) - if val < 32 or val > 128 then + if val < 32 or val > 128 then - local newText = {} - for i = 4, #text do - val = text:byte(i) - newText[i - 3] = (val > 31 and val < 127) and val or 63 - end - return string.char(unpack(newText)) - end + local newText = {} + for i = 4, #text do + val = text:byte(i) + newText[i - 3] = (val > 31 and val < 127) and val or 63 + end + return string.char(unpack(newText)) + end - return text + return text end local function convertItem(item) - for k,v in pairs(convertNames) do - item[k] = item[v] - item[v] = nil - end - item.displayName = safeString(item.displayName) + for k,v in pairs(convertNames) do + item[k] = item[v] + item[v] = nil + end + item.displayName = safeString(item.displayName) end local MEAdapter = class() function MEAdapter:init(args) - local defaults = { - items = { }, - name = 'ME', - jobList = { }, - } - Util.merge(self, defaults) - Util.merge(self, args) + local defaults = { + items = { }, + name = 'ME', + jobList = { }, + } + Util.merge(self, defaults) + Util.merge(self, args) - local chest + local chest - if not self.side then - chest = Peripheral.getByMethod('getAvailableItems') - else - chest = Peripheral.getBySide(self.side) - if chest and not chest.getAvailableItems then - chest = nil - end - end + if not self.side then + chest = Peripheral.getByMethod('getAvailableItems') + else + chest = Peripheral.getBySide(self.side) + if chest and not chest.getAvailableItems then + chest = nil + end + end - if chest then - Util.merge(self, chest) - end + if chest then + Util.merge(self, chest) + end end function MEAdapter:isValid() - return self.getAvailableItems and self.getAvailableItems() + return self.getAvailableItems and self.getAvailableItems() end function MEAdapter:refresh() - self.items = nil - local hasItems, failed + self.items = nil + local hasItems, failed - local s, m = pcall(function() - self.items = self.getAvailableItems('all') - for _,v in pairs(self.items) do - Util.merge(v, v.item) - convertItem(v) + local s, m = pcall(function() + self.items = self.getAvailableItems('all') + for _,v in pairs(self.items) do + Util.merge(v, v.item) + convertItem(v) - -- if power has been interrupted, the list will still be returned - -- but all items will have a 0 quantity - -- ensure that the list is valid - if not hasItems then - hasItems = v.count > 0 - end + -- if power has been interrupted, the list will still be returned + -- but all items will have a 0 quantity + -- ensure that the list is valid + if not hasItems then + hasItems = v.count > 0 + end - if not v.fingerprint then - failed = true - break - end + if not v.fingerprint then + failed = true + break + end - if not itemDB:get(v) then - itemDB:add(v, v) - end - end - end) - itemDB:flush() + if not itemDB:get(v) then + itemDB:add(v, v) + end + end + end) + itemDB:flush() - if not s and m then - _G._syslog(m) - end + if not s and m then + _G._syslog(m) + end - if s and not failed and hasItems and self.items and not Util.empty(self.items) then - return self.items - end - self.items = nil + if s and not failed and hasItems and self.items and not Util.empty(self.items) then + return self.items + end + self.items = nil end function MEAdapter:listItems() - self:refresh() - return self.items + self:refresh() + return self.items end function MEAdapter:getItemInfo(item) - for _,i in pairs(self.items) do - if item.name == i.name and - item.damage == i.damage and - item.nbtHash == i.nbtHash then - return i - end - end + for _,i in pairs(self.items) do + if item.name == i.name and + item.damage == i.damage and + item.nbtHash == i.nbtHash then + return i + end + end end function MEAdapter:isCPUAvailable() - local cpus = self.getCraftingCPUs() or { } - local available = false + local cpus = self.getCraftingCPUs() or { } + local available = false - for cpu,v in pairs(cpus) do - if not v.busy then - available = true - elseif not self.jobList[cpu] then -- something else is crafting something (don't know what) - return false -- return false since we are in an unknown state - end - end - return available + for cpu,v in pairs(cpus) do + if not v.busy then + available = true + elseif not self.jobList[cpu] then -- something else is crafting something (don't know what) + return false -- return false since we are in an unknown state + end + end + return available end function MEAdapter:craft(item, count) - if not self:isCPUAvailable() then - return false - end + if not self:isCPUAvailable() then + return false + end - self:refresh() + self:refresh() - item = self:getItemInfo(item) - if item and item.is_craftable then + item = self:getItemInfo(item) + if item and item.is_craftable then - local cpus = self.getCraftingCPUs() or { } - for cpu,v in pairs(cpus) do - if not v.busy then - self.requestCrafting({ - id = item.name, - dmg = item.damage, - nbt_hash = item.nbtHash, - }, - count or 1, - v.name -- CPUs must be named ! use anvil - ) + local cpus = self.getCraftingCPUs() or { } + for cpu,v in pairs(cpus) do + if not v.busy then + self.requestCrafting({ + id = item.name, + dmg = item.damage, + nbt_hash = item.nbtHash, + }, + count or 1, + v.name -- CPUs must be named ! use anvil + ) - os.sleep(0) -- needed ? - cpus = self.getCraftingCPUs() or { } + os.sleep(0) -- needed ? + cpus = self.getCraftingCPUs() or { } - if cpus[cpu].busy then - self.jobList[cpu] = { - name = item.name, - damage = item.damage, - nbtHash = item.nbtHash, - count = count, - } - return true - end - break -- only need to try the first available cpu - end - end - return false - end + if cpus[cpu].busy then + self.jobList[cpu] = { + name = item.name, + damage = item.damage, + nbtHash = item.nbtHash, + count = count, + } + return true + end + break -- only need to try the first available cpu + end + end + return false + end end function MEAdapter:getJobList() - local cpus = self.getCraftingCPUs() or { } - for cpu,v in pairs(cpus) do - if not v.busy then - self.jobList[cpu] = nil - end - end + local cpus = self.getCraftingCPUs() or { } + for cpu,v in pairs(cpus) do + if not v.busy then + self.jobList[cpu] = nil + end + end - return self.jobList + return self.jobList end function MEAdapter:isCrafting(item) - for _,v in pairs(self:getJobList()) do - if v.name == item.name and - v.damage == item.damage and - v.nbtHash == item.nbtHash then - return true - end - end + for _,v in pairs(self:getJobList()) do + if v.name == item.name and + v.damage == item.damage and + v.nbtHash == item.nbtHash then + return true + end + end end function MEAdapter:craftItems(items) - local cpus = self.getCraftingCPUs() or { } - local count = 0 + local cpus = self.getCraftingCPUs() or { } + local count = 0 - for _,cpu in pairs(cpus) do - if cpu.busy then - return - end - end + for _,cpu in pairs(cpus) do + if cpu.busy then + return + end + end - for _,item in pairs(items) do - if count >= #cpus then - break - end - if not self:isCrafting(item) then - if self:craft(item, item.count) then - count = count + 1 - end - end - end + for _,item in pairs(items) do + if count >= #cpus then + break + end + if not self:isCrafting(item) then + if self:craft(item, item.count) then + count = count + 1 + end + end + end end function MEAdapter:provide(item, qty, slot, direction) - return pcall(function() - for _,stack in pairs(self.getAvailableItems('all')) do - if stack.item.id == item.name and - (not item.damage or stack.item.dmg == item.damage) and - (not item.nbtHash or stack.item.nbt_hash == item.nbtHash) then - local amount = math.min(qty, stack.item.qty) - if amount > 0 then - self.exportItem(stack.item, direction or self.direction, amount, slot) - end - qty = qty - amount - if qty <= 0 then - break - end - end - end - end) + return pcall(function() + for _,stack in pairs(self.getAvailableItems('all')) do + if stack.item.id == item.name and + (not item.damage or stack.item.dmg == item.damage) and + (not item.nbtHash or stack.item.nbt_hash == item.nbtHash) then + local amount = math.min(qty, stack.item.qty) + if amount > 0 then + self.exportItem(stack.item, direction or self.direction, amount, slot) + end + qty = qty - amount + if qty <= 0 then + break + end + end + end + end) end function MEAdapter:insert(slot, qty, toSlot) - local s, m = pcall(self.pullItem, self.direction, slot, qty, toSlot) - if not s and m then - os.sleep(1) - pcall(self.pullItem, self.direction, slot, qty, toSlot) - end + local s, m = pcall(self.pullItem, self.direction, slot, qty, toSlot) + if not s and m then + os.sleep(1) + pcall(self.pullItem, self.direction, slot, qty, toSlot) + end end return MEAdapter diff --git a/core/apis/meAdapter18.lua b/core/apis/meAdapter18.lua index cbc3f44..95d3bc5 100644 --- a/core/apis/meAdapter18.lua +++ b/core/apis/meAdapter18.lua @@ -8,83 +8,83 @@ local MEAdapter = class(RSAdapter) local DEVICE_TYPE = 'appliedenergistics2:interface' function MEAdapter:init(args) - local defaults = { - name = 'appliedEnergistics', - jobList = { }, - } - Util.merge(self, defaults) - Util.merge(self, args) + local defaults = { + name = 'appliedEnergistics', + jobList = { }, + } + Util.merge(self, defaults) + Util.merge(self, args) - local controller - if not self.side then - controller = Peripheral.getByType(DEVICE_TYPE) - else - controller = Peripheral.getBySide(self.side) - end + local controller + if not self.side then + controller = Peripheral.getByType(DEVICE_TYPE) + else + controller = Peripheral.getBySide(self.side) + end - if controller then - Util.merge(self, controller) - end + if controller then + Util.merge(self, controller) + end end function MEAdapter:isValid() - return self.type == DEVICE_TYPE and not not self.findItems + return self.type == DEVICE_TYPE and not not self.findItems end function MEAdapter:clearFinished() - for _,key in pairs(Util.keys(self.jobList)) do - local job = self.jobList[key] - if job.info.status() == 'finished' then - self.jobList[key] = nil - end - end + for _,key in pairs(Util.keys(self.jobList)) do + local job = self.jobList[key] + if job.info.status() == 'finished' then + self.jobList[key] = nil + end + end end function MEAdapter:isCPUAvailable() - local cpus = self.getCraftingCPUs() or { } - local busy = 0 + local cpus = self.getCraftingCPUs() or { } + local busy = 0 - for _,cpu in pairs(cpus) do - if cpu.busy then - busy = busy + 1 - end - end - self:clearFinished() - return busy == Util.size(self.jobList) and busy < #cpus + for _,cpu in pairs(cpus) do + if cpu.busy then + busy = busy + 1 + end + end + self:clearFinished() + return busy == Util.size(self.jobList) and busy < #cpus end function MEAdapter:craft(item, count) - if not self:isCPUAvailable() then - return false - end + if not self:isCPUAvailable() then + return false + end - local detail = self.findItem(item) - if detail and detail.craft then - local info = detail.craft(count or 1) - if info.status() == 'unknown' then - self.jobList[info.getId()] = { - name = item.name, - damage = item.damage, - nbtHash = item.nbtHash, - info = info, - } - return true - end - return false - end + local detail = self.findItem(item) + if detail and detail.craft then + local info = detail.craft(count or 1) + if info.status() == 'unknown' then + self.jobList[info.getId()] = { + name = item.name, + damage = item.damage, + nbtHash = item.nbtHash, + info = info, + } + return true + end + return false + end end function MEAdapter:isCrafting(item) - self:clearFinished() + self:clearFinished() _G._p = self.jobList - for _,job in pairs(self.jobList) do - if job.name == item.name and - job.damage == item.damage and - job.nbtHash == item.nbtHash then - return true - end - end - return false + for _,job in pairs(self.jobList) do + if job.name == item.name and + job.damage == item.damage and + job.nbtHash == item.nbtHash then + return true + end + end + return false end return MEAdapter diff --git a/core/apis/message.lua b/core/apis/message.lua index babe3fb..afeb26d 100644 --- a/core/apis/message.lua +++ b/core/apis/message.lua @@ -5,91 +5,91 @@ local Message = { } local messageHandlers = {} function Message.enable() - if not device.wireless_modem.isOpen(os.getComputerID()) then - device.wireless_modem.open(os.getComputerID()) - end - if not device.wireless_modem.isOpen(60000) then - device.wireless_modem.open(60000) - end + if not device.wireless_modem.isOpen(os.getComputerID()) then + device.wireless_modem.open(os.getComputerID()) + end + if not device.wireless_modem.isOpen(60000) then + device.wireless_modem.open(60000) + end end if device and device.wireless_modem then - Message.enable() + Message.enable() end Event.on('device_attach', function(event, deviceName) - if deviceName == 'wireless_modem' then - Message.enable() - end + if deviceName == 'wireless_modem' then + Message.enable() + end end) function Message.addHandler(type, f) - table.insert(messageHandlers, { - type = type, - f = f, - enabled = true - }) + table.insert(messageHandlers, { + type = type, + f = f, + enabled = true + }) end function Message.removeHandler(h) - for k,v in pairs(messageHandlers) do + for k,v in pairs(messageHandlers) do if v == h then - messageHandlers[k] = nil - break - end - end + messageHandlers[k] = nil + break + end + end end Event.on('modem_message', - function(event, side, sendChannel, replyChannel, msg, distance) - if msg and msg.type then -- filter out messages from other systems - local id = replyChannel - for k,h in pairs(messageHandlers) do - if h.type == msg.type then + function(event, side, sendChannel, replyChannel, msg, distance) + if msg and msg.type then -- filter out messages from other systems + local id = replyChannel + for k,h in pairs(messageHandlers) do + if h.type == msg.type then -- should provide msg.contents instead of message - type is already known - h.f(h, id, msg, distance) - end - end - end - end + h.f(h, id, msg, distance) + end + end + end + end ) function Message.send(id, msgType, contents) - if not device.wireless_modem then - error('No modem attached', 2) - end + if not device.wireless_modem then + error('No modem attached', 2) + end - if id then - device.wireless_modem.transmit(id, os.getComputerID(), { - type = msgType, contents = contents - }) - else - device.wireless_modem.transmit(60000, os.getComputerID(), { - type = msgType, contents = contents - }) - end + if id then + device.wireless_modem.transmit(id, os.getComputerID(), { + type = msgType, contents = contents + }) + else + device.wireless_modem.transmit(60000, os.getComputerID(), { + type = msgType, contents = contents + }) + end end function Message.broadcast(t, contents) - if not device.wireless_modem then - error('No modem attached', 2) - end + if not device.wireless_modem then + error('No modem attached', 2) + end - Message.send(nil, t, contents) + Message.send(nil, t, contents) end function Message.waitForMessage(msgType, timeout, fromId) - local timerId = os.startTimer(timeout) - repeat - local e, side, _id, id, msg, distance = os.pullEvent() - if e == 'modem_message' then - if msg and msg.type and msg.type == msgType then - if not fromId or id == fromId then - return e, id, msg, distance - end - end - end - until e == 'timer' and side == timerId + local timerId = os.startTimer(timeout) + repeat + local e, side, _id, id, msg, distance = os.pullEvent() + if e == 'modem_message' then + if msg and msg.type and msg.type == msgType then + if not fromId or id == fromId then + return e, id, msg, distance + end + end + end + until e == 'timer' and side == timerId end return Message \ No newline at end of file diff --git a/core/apis/nameDB.lua b/core/apis/nameDB.lua index 4724c42..76d0852 100644 --- a/core/apis/nameDB.lua +++ b/core/apis/nameDB.lua @@ -10,50 +10,50 @@ local USER_DIR = '/usr/etc/names' local nameDB = TableDB() function nameDB:loadDirectory(directory) - if fs.exists(directory) then - local files = fs.list(directory) - table.sort(files) + if fs.exists(directory) then + local files = fs.list(directory) + table.sort(files) - for _,file in ipairs(files) do - local mod = file:match('(%S+).json') - if mod then - local blocks = JSON.decodeFromFile(fs.combine(directory, file)) + for _,file in ipairs(files) do + local mod = file:match('(%S+).json') + if mod then + local blocks = JSON.decodeFromFile(fs.combine(directory, file)) - if not blocks then - error('Unable to read ' .. fs.combine(directory, file)) - end + if not blocks then + error('Unable to read ' .. fs.combine(directory, file)) + end - for strId, block in pairs(blocks) do - strId = string.format('%s:%s', mod, strId) - if type(block.name) == 'string' then - self.data[strId .. ':0'] = block.name - else - for nid,name in pairs(block.name) do - self.data[strId .. ':' .. (nid-1)] = name - end - end - end + for strId, block in pairs(blocks) do + strId = string.format('%s:%s', mod, strId) + if type(block.name) == 'string' then + self.data[strId .. ':0'] = block.name + else + for nid,name in pairs(block.name) do + self.data[strId .. ':' .. (nid-1)] = name + end + end + end - elseif file:match('(%S+).db') then - local names = Util.readTable(fs.combine(directory, file)) - if not names then - error('Unable to read ' .. fs.combine(directory, file)) - end - for key,name in pairs(names) do - self.data[key] = name - end - end - end - end + elseif file:match('(%S+).db') then + local names = Util.readTable(fs.combine(directory, file)) + if not names then + error('Unable to read ' .. fs.combine(directory, file)) + end + for key,name in pairs(names) do + self.data[key] = name + end + end + end + end end function nameDB:load() - self:loadDirectory(CORE_DIR) - self:loadDirectory(USER_DIR) + self:loadDirectory(CORE_DIR) + self:loadDirectory(USER_DIR) end function nameDB:getName(strId) - return self.data[strId] or strId + return self.data[strId] or strId end nameDB:load() diff --git a/core/apis/proxy.lua b/core/apis/proxy.lua new file mode 100644 index 0000000..eb3d819 --- /dev/null +++ b/core/apis/proxy.lua @@ -0,0 +1,32 @@ +local Socket = require('socket') + +local Proxy = { } + +function Proxy.create(remoteId, uri) + local socket, msg = Socket.connect(remoteId, 188) + + if not socket then + error(msg) + end + + socket.co = coroutine.running() + + socket:write(uri) + local methods = socket:read(2) or error('Timed out') + + local hijack = { } + for _,method in pairs(methods) do + hijack[method] = function(...) + socket:write({ method, ... }) + local resp = socket:read() + if not resp then + error('timed out: ' .. method) + end + return table.unpack(resp) + end + end + + return hijack, socket +end + +return Proxy diff --git a/core/apis/refinedAdapter.lua b/core/apis/refinedAdapter.lua index 5c8a1b2..3f5cead 100644 --- a/core/apis/refinedAdapter.lua +++ b/core/apis/refinedAdapter.lua @@ -6,134 +6,134 @@ local Util = require('util') local RefinedAdapter = class() function RefinedAdapter:init(args) - local defaults = { - name = 'refinedStorage', - } - Util.merge(self, defaults) - Util.merge(self, args) + local defaults = { + name = 'refinedStorage', + } + Util.merge(self, defaults) + Util.merge(self, args) - local controller - if not self.side then - controller = Peripheral.getByMethod('getCraftingTasks') - else - controller = Peripheral.getBySide(self.side) - end + local controller + if not self.side then + controller = Peripheral.getByMethod('getCraftingTasks') + else + controller = Peripheral.getBySide(self.side) + end - if controller then - Util.merge(self, controller) - end + if controller then + Util.merge(self, controller) + end end function RefinedAdapter:isValid() - return not not self.getCraftingTasks + return not not self.getCraftingTasks end function RefinedAdapter:getItemDetails(item) - local detail = self.findItems(item) - if detail and #detail > 0 then - return detail[1].getMetadata() - end + local detail = self.findItems(item) + if detail and #detail > 0 then + return detail[1].getMetadata() + end end function RefinedAdapter:getCachedItemDetails(item) - local cached = itemDB:get(item) - if cached then - return cached - end + local cached = itemDB:get(item) + if cached then + return cached + end - local detail = self:getItemDetails(item) - if detail then - return itemDB:add(detail) - end + local detail = self:getItemDetails(item) + if detail then + return itemDB:add(detail) + end end function RefinedAdapter:refresh(throttle) - return self:listItems(throttle) + return self:listItems(throttle) end function RefinedAdapter:listItems(throttle) - local items = { } - throttle = throttle or Util.throttle() + local items = { } + throttle = throttle or Util.throttle() - local s, m = pcall(function() - for _,v in pairs(self.listAvailableItems()) do - --if v.count > 0 then - local item = self:getCachedItemDetails(v) - if item then - item = Util.shallowCopy(item) - item.count = v.count - table.insert(items, item) - end - --end - throttle() - end - end) + local s, m = pcall(function() + for _,v in pairs(self.listAvailableItems()) do + --if v.count > 0 then + local item = self:getCachedItemDetails(v) + if item then + item = Util.shallowCopy(item) + item.count = v.count + table.insert(items, item) + end + --end + throttle() + end + end) - if not s and m then - _G._syslog(m) - end + if not s and m then + _G._syslog(m) + end - itemDB:flush() - if not Util.empty(items) then - return items - end + itemDB:flush() + if not Util.empty(items) then + return items + end end function RefinedAdapter:getItemInfo(item) - return self:getItemDetails(item) + return self:getItemDetails(item) end function RefinedAdapter:isCPUAvailable() - return true + return true end function RefinedAdapter:craft(item, qty) - local detail = self.findItem(item) - if detail then - return detail.craft(qty) - end + local detail = self.findItem(item) + if detail then + return detail.craft(qty) + end end function RefinedAdapter:isCrafting(item) - for _,task in pairs(self.getCraftingTasks()) do - local output = task.getPattern().outputs[1] - if output.name == item.name and - output.damage == item.damage and - output.nbtHash == item.nbtHash then - return true - end - end - return false + for _,task in pairs(self.getCraftingTasks()) do + local output = task.getPattern().outputs[1] + if output.name == item.name and + output.damage == item.damage and + output.nbtHash == item.nbtHash then + return true + end + end + return false end function RefinedAdapter:provide(item, qty, slot, direction) - return pcall(function() - for _,stack in pairs(self.listAvailableItems()) 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 - local amount = math.min(qty, stack.count) - if amount > 0 then - local detail = self.findItem(item) - if detail then - return detail.export(direction or self.direction, amount, slot) - end - end - qty = qty - amount - if qty <= 0 then - break - end - end - end - end) + return pcall(function() + for _,stack in pairs(self.listAvailableItems()) 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 + local amount = math.min(qty, stack.count) + if amount > 0 then + local detail = self.findItem(item) + if detail then + return detail.export(direction or self.direction, amount, slot) + end + end + qty = qty - amount + if qty <= 0 then + break + end + end + end + end) end function RefinedAdapter:extract(slot, qty, toSlot) - self.pushItems(self.direction, slot, qty, toSlot) + self.pushItems(self.direction, slot, qty, toSlot) end function RefinedAdapter:insert(slot, qty, toSlot) - self.pullItems(self.direction, slot, qty, toSlot) + self.pullItems(self.direction, slot, qty, toSlot) end return RefinedAdapter diff --git a/core/apis/swarm.lua b/core/apis/swarm.lua index bd9b47b..1eb7d44 100644 --- a/core/apis/swarm.lua +++ b/core/apis/swarm.lua @@ -1,66 +1,66 @@ local class = require('class') local Event = require('event') local Map = require('map') -local Proxy = require('proxy') +local Proxy = require('core.proxy') local Swarm = class() function Swarm:init(args) - self.pool = { } - Map.merge(self, args) + self.pool = { } + Map.merge(self, args) end function Swarm:add(id, args) - local member = Map.shallowCopy(args or { }) - member.id = id - self.pool[id] = member + local member = Map.shallowCopy(args or { }) + member.id = id + self.pool[id] = member end function Swarm:remove(id, s, m) - local member = self.pool[id] - if member then - self.pool[id] = nil - self:onRemove(member, s, m) - if member.socket then - member.socket:close() - member.socket = nil - end - if member.handler then - member.handler:terminate() - member.handler = nil - end - end + local member = self.pool[id] + if member then + self.pool[id] = nil + self:onRemove(member, s, m) + if member.socket then + member.socket:close() + member.socket = nil + end + if member.handler then + member.handler:terminate() + member.handler = nil + end + end end function Swarm:run(fn) - for id, member in pairs(self.pool) do - if not member.socket then - member.handler = Event.addRoutine(function() - local s, m = pcall(function() - member.turtle, member.socket = Proxy.create(id, 'turtle') + for id, member in pairs(self.pool) do + if not member.socket then + member.handler = Event.addRoutine(function() + local s, m = pcall(function() + member.turtle, member.socket = Proxy.create(id, 'turtle') - fn(member) - end) - self:remove(id, s, m) - end) - end - end + fn(member) + end) + self:remove(id, s, m) + end) + end + end end function Swarm:stop() - for _, member in pairs(self.pool) do - if member.socket then - member.socket:close() - member.socket = nil - end - end + for _, member in pairs(self.pool) do + if member.socket then + member.socket:close() + member.socket = nil + end + end end -- Override function Swarm:onRemove(member, success, msg) - print('removed from pool: ' .. member.id) - if not success then - _G.printError(msg) - end + print('removed from pool: ' .. member.id) + if not success then + _G.printError(msg) + end end return Swarm diff --git a/core/apis/tableDB.lua b/core/apis/tableDB.lua index a985522..fd059bf 100644 --- a/core/apis/tableDB.lua +++ b/core/apis/tableDB.lua @@ -3,47 +3,47 @@ local Util = require('util') local TableDB = class() function TableDB:init(args) - local defaults = { - fileName = '', - dirty = false, - data = { }, - } - Util.merge(defaults, args) - Util.merge(self, defaults) + local defaults = { + fileName = '', + dirty = false, + data = { }, + } + Util.merge(defaults, args) + Util.merge(self, defaults) end function TableDB:load() - local t = Util.readTable(self.fileName) - if t then - self.data = t.data or t - end + local t = Util.readTable(self.fileName) + if t then + self.data = t.data or t + end end function TableDB:add(key, entry) - if type(key) == 'table' then - key = table.concat(key, ':') - end - self.data[key] = entry - self.dirty = true + if type(key) == 'table' then + key = table.concat(key, ':') + end + self.data[key] = entry + self.dirty = true end function TableDB:get(key) - if type(key) == 'table' then - key = table.concat(key, ':') - end - return self.data[key] + if type(key) == 'table' then + key = table.concat(key, ':') + end + return self.data[key] end function TableDB:remove(key) - self.data[key] = nil - self.dirty = true + self.data[key] = nil + self.dirty = true end function TableDB:flush() - if self.dirty then - Util.writeTable(self.fileName, self.data) - self.dirty = false - end + if self.dirty then + Util.writeTable(self.fileName, self.data) + self.dirty = false + end end return TableDB diff --git a/core/etc/names/minecraft.json b/core/etc/names/minecraft.json index c16efae..99db93e 100644 --- a/core/etc/names/minecraft.json +++ b/core/etc/names/minecraft.json @@ -1,2100 +1,2100 @@ { - "air": { - "id": 0, - "name": "Air", - }, - "stone": { - "id": 1, - "name": ["Stone", - "Granite", - "Polished Granite", - "Diorite", - "Polished Diorite", - "Andesite", - "Polished Andesite"], - }, - "grass": { - "id": 2, - "name": "Grass Block", - }, - "dirt": { - "id": 3, - "name": ["Dirt", - "Coarse Dirt", - "Podzol"], - }, - "cobblestone": { - "id": 4, - "name": "Cobblestone", - }, - "planks": { - "id": 5, - "name": ["Oak Wood Planks", - "Spruce Wood Planks", - "Birch Wood Planks", - "Jungle Wood Planks", - "Acacia Wood Planks", - "Dark Oak Wood Planks"], - }, - "sapling": { - "id": 6, - "name": ["Oak Sapling", - "Spruce Sapling", - "Birch Sapling", - "Jungle Sapling", - "Acacia Sapling", - "Dark Oak Sapling"], - "place": "sapling", - }, - "bedrock": { - "id": 7, - "name": "Bedrock", - }, - "flowing_water": { - "id": 8, - "name": "Water", - "place": "flatten", - }, - "water": { - "id": 9, - "name": "Water", - "place": "flatten", - }, - "flowing_lava": { - "id": 10, - "name": "Lava", - }, - "lava": { - "id": 11, - "name": "Lava", - }, - "sand": { - "id": 12, - "name": ["Sand", - "Red Sand"], - }, - "gravel": { - "id": 13, - "name": "Gravel", - }, - "gold_ore": { - "id": 14, - "name": "Gold Ore", - }, - "iron_ore": { - "id": 15, - "name": "Iron Ore", - }, - "coal_ore": { - "id": 16, - "name": "Coal Ore", - }, - "log": { - "id": 17, - "name": ["Oak Wood", - "Spruce Wood", - "Birch Wood", - "Jungle Wood"], - "place": "wood", - }, - "leaves": { - "id": 18, - "name": ["Oak Leaves", - "Spruce Leaves", - "Birch Leaves", - "Jungle Leaves"], - "place": "leaves", - }, - "sponge": { - "id": 19, - "name": ["Sponge", - "Wet Sponge"], - }, - "glass": { - "id": 20, - "name": "Glass", - }, - "lapis_ore": { - "id": 21, - "name": "Lapis Lazuli Ore", - }, - "lapis_block": { - "id": 22, - "name": "Lapis Lazuli Block", - }, - "dispenser": { - "id": 23, - "name": "Dispenser", - "place": "dispenser", - }, - "sandstone": { - "id": 24, - "name": ["Sandstone", - "Chiseled Sandstone", - "Smooth Sandstone"], - }, - "noteblock": { - "id": 25, - "name": "Note Block", - }, - "bed": { - "id": 26, - "name": "Bed", - "place": "bed", - }, - "golden_rail": { - "id": 27, - "name": "Powered Rail", - "place": "adp-rail", - }, - "detector_rail": { - "id": 28, - "name": "Detector Rail", - "place": "adp-rail", - }, - "sticky_piston": { - "id": 29, - "name": "Sticky Piston", - "place": "piston", - }, - "web": { - "id": 30, - "name": "Cobweb", - }, - "tallgrass": { - "id": 31, - "name": ["Shrub", - "Grass", - "Fern"], - }, - "deadbush": { - "id": 32, - "name": "Dead Bush", - }, - "piston": { - "id": 33, - "name": "Piston", - "place": "piston", - }, - "piston_head": { - "id": 34, - "name": "Piston Extension", - "place": "flatten", - }, - "wool": { - "id": 35, - "name": ["White Wool", - "Orange Wool", - "Magenta Wool", - "Light Blue Wool", - "Yellow Wool", - "Lime Wool", - "Pink Wool", - "Gray Wool", - "Light Gray Wool", - "Cyan Wool", - "Purple Wool", - "Blue Wool", - "Brown Wool", - "Green Wool", - "Red Wool", - "Black Wool"], - }, - "piston_extension": { - "id": 36, - "name": "Block moved by Piston", - "place": "flatten", - }, - "yellow_flower": { - "id": 37, - "name": "Dandelion", - }, - "red_flower": { - "id": 38, - "name": ["Poppy", - "Blue Orchid", - "Allium", - "Azure Bluet", - "Red Tulip", - "Orange Tulip", - "White Tulip", - "Pink Tulip", - "Oxeye Daisy"], - }, - "brown_mushroom": { - "id": 39, - "name": "Brown Mushroom", - }, - "red_mushroom": { - "id": 40, - "name": "Red Mushroom", - }, - "gold_block": { - "id": 41, - "name": "Block of Gold", - }, - "iron_block": { - "id": 42, - "name": "Block of Iron", - }, - "double_stone_slab": { - "id": 43, - "name": "Double Stone Slab", - }, - "stone_slab": { - "id": 44, - "name": ["Stone Slab", - "Sandstone Slab", - "Wooden Slab", - "Cobblestone Slab", - "Bricks Slab", - "Stone Bricks Slab", - "Nether Brick Slab", - "Quartz Slab"], - "place": "slab", - }, - "brick_block": { - "id": 45, - "name": "Bricks", - }, - "tnt": { - "id": 46, - "name": "TNT", - }, - "bookshelf": { - "id": 47, - "name": "Bookshelf", - }, - "mossy_cobblestone": { - "id": 48, - "name": "Moss Stone", - }, - "obsidian": { - "id": 49, - "name": "Obsidian", - }, - "torch": { - "id": 50, - "name": "Torch", - "place": "torch", - }, - "fire": { - "id": 51, - "name": "Fire", - "place": "flatten", - }, - "mob_spawner": { - "id": 52, - "name": "Monster Spawner", - }, - "oak_stairs": { - "id": 53, - "name": "Oak Wood Stairs", - "place": "stairs", - }, - "chest": { - "id": 54, - "name": "Chest", - "place": "chest-furnace", - }, - "redstone_wire": { - "id": 55, - "name": "Redstone Wire", - "place": "flatten", - }, - "diamond_ore": { - "id": 56, - "name": "Diamond Ore", - }, - "diamond_block": { - "id": 57, - "name": "Block of Diamond", - }, - "crafting_table": { - "id": 58, - "name": "Crafting Table", - }, - "wheat": { - "id": 59, - "name": "Wheat", - "place": "flatten", - }, - "farmland": { - "id": 60, - "name": "Farmland", - "place": "flatten", - }, - "furnace": { - "id": 61, - "name": "Furnace", - "place": "chest-furnace", - }, - "lit_furnace": { - "id": 62, - "name": "Burning Furnace", - "place": "chest-furnace", - }, - "standing_sign": { - "id": 63, - "name": "Sign", - "place": "signpost", - }, - "wooden_door": { - "id": 64, - "name": "Oak Door", - "place": "door", - }, - "ladder": { - "id": 65, - "name": "Ladder", - "place": "wallsign-ladder", - }, - "rail": { - "id": 66, - "name": "Rail", - "place": "rail", - }, - "stone_stairs": { - "id": 67, - "name": "Cobblestone Stairs", - "place": "stairs", - }, - "wall_sign": { - "id": 68, - "name": "Sign", - "place": "wallsign-ladder", - }, - "lever": { - "id": 69, - "name": "Lever", - "place": "lever", - }, - "stone_pressure_plate": { - "id": 70, - "name": "Stone Pressure Plate", - }, - "iron_door": { - "id": 71, - "name": "Iron Door", - "place": "door", - }, - "wooden_pressure_plate": { - "id": 72, - "name": "Wooden Pressure Plate", - }, - "redstone_ore": { - "id": 73, - "name": "Redstone Ore", - }, - "lit_redstone_ore": { - "id": 74, - "name": "Redstone Ore", - }, - "unlit_redstone_torch": { - "id": 75, - "name": "Redstone Torch (inactive)", - "place": "torch", - }, - "redstone_torch": { - "id": 76, - "name": "Redstone Torch (active)", - "place": "torch", - }, - "stone_button": { - "id": 77, - "name": "Stone Button", - "place": "button", - }, - "snow_layer": { - "id": 78, - "name": "Snow", - "place": "flatten", - }, - "ice": { - "id": 79, - "name": "Ice", - }, - "snow": { - "id": 80, - "name": "Snow", - }, - "cactus": { - "id": 81, - "name": "Cactus", - "place": "flatten", - }, - "clay": { - "id": 82, - "name": "Clay", - }, - "reeds": { - "id": 83, - "name": "Sugar Cane", - "place": "flatten", - }, - "jukebox": { - "id": 84, - "name": "Jukebox", - "place": "flatten", - }, - "fence": { - "id": 85, - "name": "Fence", - }, - "pumpkin": { - "id": 86, - "name": "Pumpkin", - "place": "pumpkin", - }, - "netherrack": { - "id": 87, - "name": "Netherrack", - }, - "soul_sand": { - "id": 88, - "name": "Soul Sand", - }, - "glowstone": { - "id": 89, - "name": "Glowstone", - }, - "portal": { - "id": 90, - "name": "Portal", - "place": "flatten", - }, - "lit_pumpkin": { - "id": 91, - "name": "Jack o'Lantern", - "place": "pumpkin", - }, - "cake": { - "id": 92, - "name": "Cake", - "place": "flatten", - }, - "unpowered_repeater": { - "id": 93, - "name": "Redstone Repeater (inactive)", - "place": "repeater", - }, - "powered_repeater": { - "id": 94, - "name": "Redstone Repeater (active)", - "place": "repeater", - }, - "stained_glass": { - "id": 95, - "name": ["White Stained Glass", - "Orange Stained Glass", - "Magenta Stained Glass", - "Light Blue Stained Glass", - "Yellow Stained Glass", - "Lime Stained Glass", - "Pink Stained Glass", - "Gray Stained Glass", - "Light Gray Stained Glass", - "Cyan Stained Glass", - "Purple Stained Glass", - "Blue Stained Glass", - "Brown Stained Glass", - "Green Stained Glass", - "Red Stained Glass", - "Black Stained Glass"], - }, - "trapdoor": { - "id": 96, - "name": "Trapdoor", - "place": "trapdoor", - }, - "monster_egg": { - "id": 97, - "name": ["Stone Monster Egg", - "Cobblestone Monster Egg", - "Stone Brick Monster Egg", - "Mossy Stone Brick Monster Egg", - "Cracked Stone Brick Monster Egg", - "Chiseled Stone Brick Monster Egg"], - }, - "stonebrick": { - "id": 98, - "name": ["Stone Bricks", - "Mossy Stone Bricks", - "Cracked Stone Bricks", - "Chiseled Stone Bricks"], - }, - "brown_mushroom_block": { - "id": 99, - "name": "Brown Mushroom (block)", - "place": "flatten", - }, - "red_mushroom_block": { - "id": 100, - "name": "Red Mushroom (block)", - "place": "flatten", - }, - "iron_bars": { - "id": 101, - "name": "Iron Bars", - }, - "glass_pane": { - "id": 102, - "name": "Glass Pane", - }, - "melon_block": { - "id": 103, - "name": "Melon", - }, - "pumpkin_stem": { - "id": 104, - "name": "Pumpkin Stem", - }, - "melon_stem": { - "id": 105, - "name": "Melon Stem", - }, - "vine": { - "id": 106, - "name": "Vines", - "place": "vine", - }, - "fence_gate": { - "id": 107, - "name": "Fence Gate", - "place": "gate", - }, - "brick_stairs": { - "id": 108, - "name": "Brick Stairs", - "place": "stairs", - }, - "stone_brick_stairs": { - "id": 109, - "name": "Stone Brick Stairs", - "place": "stairs", - }, - "mycelium": { - "id": 110, - "name": "Mycelium", - }, - "waterlily": { - "id": 111, - "name": "Lily Pad", - }, - "nether_brick": { - "id": 112, - "name": "Nether Brick", - }, - "nether_brick_fence": { - "id": 113, - "name": "Nether Brick Fence", - }, - "nether_brick_stairs": { - "id": 114, - "name": "Nether Brick Stairs", - "place": "stairs", - }, - "nether_wart": { - "id": 115, - "name": "Nether Wart", - "place": "flatten", - }, - "enchanting_table": { - "id": 116, - "name": "Enchantment Table", - }, - "brewing_stand": { - "id": 117, - "name": "Brewing Stand", - "place": "flatten", - }, - "cauldron": { - "id": 118, - "name": "Cauldron", - "place": "cauldron", - }, - "end_portal": { - "id": 119, - "name": "End Portal", - }, - "end_portal_frame": { - "id": 120, - "name": "End Portal Block", - "place": "flatten", - }, - "end_stone": { - "id": 121, - "name": "End Stone", - }, - "dragon_egg": { - "id": 122, - "name": "Dragon Egg", - }, - "redstone_lamp": { - "id": 123, - "name": "Redstone Lamp (inactive)", - }, - "lit_redstone_lamp": { - "id": 124, - "name": "Redstone Lamp (active)", - }, - "double_wooden_slab": { - "id": 125, - "name": "Double Wooden Slab", - }, - "wooden_slab": { - "id": 126, - "name": ["Oak Wood Slab", - "Spruce Wood Slab", - "Birch Wood Slab", - "Jungle Wood Slab", - "Acacia Wood Slab", - "Dark Oak Wood Slab"], - "place": "slab", - }, - "cocoa": { - "id": 127, - "name": "Cocoa", - "place": "cocoa", - }, - "sandstone_stairs": { - "id": 128, - "name": "Sandstone Stairs", - "place": "stairs", - }, - "emerald_ore": { - "id": 129, - "name": "Emerald Ore", - }, - "ender_chest": { - "id": 130, - "name": "Ender Chest", - "place": "chest-furnace", - }, - "tripwire_hook": { - "id": 131, - "name": "Tripwire Hook", - "place": "tripwire", - }, - "tripwire": { - "id": 132, - "name": "Tripwire", - "place": "flatten", - }, - "emerald_block": { - "id": 133, - "name": "Block of Emerald", - }, - "spruce_stairs": { - "id": 134, - "name": "Spruce Wood Stairs", - "place": "stairs", - }, - "birch_stairs": { - "id": 135, - "name": "Birch Wood Stairs", - "place": "stairs", - }, - "jungle_stairs": { - "id": 136, - "name": "Jungle Wood Stairs", - "place": "stairs", - }, - "command_block": { - "id": 137, - "name": "Command Block", - }, - "beacon": { - "id": 138, - "name": "Beacon", - }, - "cobblestone_wall": { - "id": 139, - "name": ["Cobblestone Wall", - "Mossy Cobblestone Wall"], - }, - "flower_pot": { - "id": 140, - "name": "Flower Pot", - "place": "flatten", - }, - "carrots": { - "id": 141, - "name": "Carrot", - "place": "flatten", - }, - "potatoes": { - "id": 142, - "name": "Potato", - "place": "flatten", - }, - "wooden_button": { - "id": 143, - "name": "Wooden Button", - "place": "button", - }, - "skull": { - "id": 144, - "name": ["Skeleton Skull", - "Wither Skeleton Skull", - "Zombie Head", - "Human Head", - "Creeper Head", - "Dragon Head"] - "place": "mobhead", - }, - "anvil": { - "id": 145, - "name": ["Anvil", - "Slightly Damaged Anvil", - "Very Damaged Anvil"], - "place": "anvil", - }, - "trapped_chest": { - "id": 146, - "name": "Trapped Chest", - "place": "chest-furnace", - }, - "light_weighted_pressure_plate": { - "id": 147, - "name": "Weighted Pressure Plate", - }, - "heavy_weighted_pressure_plate": { - "id": 148, - "name": "Weighted Pressure Plate", - }, - "unpowered_comparator": { - "id": 149, - "name": "Redstone Comparator", - "place": "comparator", - }, - "powered_comparator": { - "id": 150, - "name": "Redstone Comparator", - }, - "daylight_detector": { - "id": 151, - "name": "Daylight Sensor", - "place": "flatten", - }, - "redstone_block": { - "id": 152, - "name": "Block of Redstone", - }, - "quartz_ore": { - "id": 153, - "name": "Nether Quartz Ore", - }, - "hopper": { - "id": 154, - "name": "Hopper", - "place": "hopper", - }, - "quartz_block": { - "id": 155, - "name": ["Block of Quartz", - "Chiseled Quartz Block", - "Pillar Quartz Block"], - }, - "quartz_stairs": { - "id": 156, - "name": "Quartz Stairs", - "place": "stairs", - }, - "activator_rail": { - "id": 157, - "name": "Activator Rail", - "place": "adp-rail", - }, - "dropper": { - "id": 158, - "name": "Dropper", - "place": "dispenser", - }, - "stained_hardened_clay": { - "id": 159, - "name": ["White Stained Clay", - "Orange Stained Clay", - "Magenta Stained Clay", - "Light Blue Stained Clay", - "Yellow Stained Clay", - "Lime Stained Clay", - "Pink Stained Clay", - "Gray Stained Clay", - "Light Gray Stained Clay", - "Cyan Stained Clay", - "Purple Stained Clay", - "Blue Stained Clay", - "Brown Stained Clay", - "Green Stained Clay", - "Red Stained Clay", - "Black Stained Clay"], - }, - "stained_glass_pane": { - "id": 160, - "name": ["White Stained Glass Pane", - "Orange Stained Glass Pane", - "Magenta Stained Glass Pane", - "Light Blue Stained Glass Pane", - "Yellow Stained Glass Pane", - "Lime Stained Glass Pane", - "Pink Stained Glass Pane", - "Gray Stained Glass Pane", - "Light Gray Stained Glass Pane", - "Cyan Stained Glass Pane", - "Purple Stained Glass Pane", - "Blue Stained Glass Pane", - "Brown Stained Glass Pane", - "Green Stained Glass Pane", - "Red Stained Glass Pane", - "Black Stained Glass Pane"], - }, - "leaves2": { - "id": 161, - "name": ["Acacia Leaves", - "Dark Oak Leaves"], - "place": "leaves", - }, - "log2": { - "id": 162, - "name": ["Acacia Wood", - "Dark Oak Wood"], - "place": "wood", - }, - "acacia_stairs": { - "id": 163, - "name": "Acacia Wood Stairs", - "place": "stairs", - }, - "dark_oak_stairs": { - "id": 164, - "name": "Dark Oak Wood Stairs", - "place": "stairs", - }, - "slime": { - "id": 165, - "name": "Slime Block", - }, - "barrier": { - "id": 166, - "name": "Barrier", - }, - "iron_trapdoor": { - "id": 167, - "name": "Iron Trapdoor", - "place": "trapdoor", - }, - "prismarine": { - "id": 168, - "name": ["Prismarine", - "Prismarine Bricks", - "Dark Prismarine"], - }, - "sea_lantern": { - "id": 169, - "name": "Sea Lantern", - }, - "hay_block": { - "id": 170, - "name": "Hay Block", - "place": "hay-bale", - }, - "carpet": { - "id": 171, - "name": ["Carpet", - "Orange Carpet", - "Magenta Carpet", - "Light Blue Carpet", - "Yellow Carpet", - "Lime Carpet", - "Pink Carpet", - "Gray Carpet", - "Light Gray Carpet", - "Cyan Carpet", - "Purple Carpet", - "Blue Carpet", - "Brown Carpet", - "Green Carpet", - "Red Carpet", - "Black Carpet"], - }, - "hardened_clay": { - "id": 172, - "name": "Hardened Clay", - }, - "coal_block": { - "id": 173, - "name": "Block of Coal", - }, - "packed_ice": { - "id": 174, - "name": "Packed Ice", - }, - "double_plant": { - "id": 175, - "name": ["Sunflower", - "Lilac", - "Double Tallgrass", - "Large Fern", - "Rose Bush", - "Peony"], - "place": "largeplant", - }, - "standing_banner": { - "id": 176, - "name": "Banner", - "place": "signpost", - }, - "wall_banner": { - "id": 177, - "name": "Banner", - "place": "wallsign-ladder", - }, - "daylight_detector_inverted": { - "id": 178, - "name": "Inverted Daylight Sensor", - "place": "flatten", - }, - "red_sandstone": { - "id": 179, - "name": ["Red Sandstone", - "Chiseled Red Sandstone", - "Smooth Red Sandstone"], - }, - "red_sandstone_stairs": { - "id": 180, - "name": "Red Sandstone Stairs", - "place": "stairs", - }, - "double_stone_slab2": { - "id": 181, - "name": "Double Red Sandstone Slab", - }, - "stone_slab2": { - "id": 182, - "name": "Red Sandstone Slab", - "place": "slab", - }, - "spruce_fence_gate": { - "id": 183, - "name": "Spruce Fence Gate", - "place": "gate", - }, - "birch_fence_gate": { - "id": 184, - "name": "Birch Fence Gate", - "place": "gate", - }, - "jungle_fence_gate": { - "id": 185, - "name": "Jungle Fence Gate", - "place": "gate", - }, - "dark_oak_fence_gate": { - "id": 186, - "name": "Dark Oak Fence Gate", - "place": "gate", - }, - "acacia_fence_gate": { - "id": 187, - "name": "Acacia Fence Gate", - "place": "gate", - }, - "spruce_fence": { - "id": 188, - "name": "Spruce Fence", - }, - "birch_fence": { - "id": 189, - "name": "Birch Fence", - }, - "jungle_fence": { - "id": 190, - "name": "Jungle Fence", - }, - "dark_oak_fence": { - "id": 191, - "name": "Dark Oak Fence", - }, - "acacia_fence": { - "id": 192, - "name": "Acacia Fence", - }, - "spruce_door": { - "id": 193, - "name": "Spruce Door", - "place": "door", - }, - "birch_door": { - "id": 194, - "name": "Birch Door", - "place": "door", - }, - "jungle_door": { - "id": 195, - "name": "Jungle Door", - "place": "door", - }, - "acacia_door": { - "id": 196, - "name": "Acacia Door", - "place": "door", - }, - "dark_oak_door": { - "id": 197, - "name": "Dark Oak Door", - "place": "door", - }, - "end_rod": { - "id": 198 - "name": "End Rod", - "place": "end_rod", - }, - "chorus_plant": { - "id": 199 - "name": "Chorus Plant", - }, - "chorus_flower": { - "id": 200 - "name": "Chorus Flower", - }, - "purpur_block": { - "id": 201 - "name": "Purpur Block", - }, - "purpur_pillar": { - "id": 202 - "name": "Purpur Pillar", - }, - "purpur_stairs": { - "id": 203 - "name": "Purpur Stairs", - }, - "purpur_double_slab": { - "id": 204 - "name": "Double Purpur Slabs", - }, - "purpur_slab": { - "name": "Purpur Slab", - "id": 205, - "place": "slab", - }, - "end_bricks": { - "name": "End Stone Bricks", - "id": 206 - }, - "beetroots": { - "name": "Beetroot", - "id": 207 - }, - "grass_path": { - "name": "Path", - "id": 208 - }, - "end_gateway": { - "id": 209, - "name": "End Gateway" - }, - "repeating_command_block": { - "name": "Repeating Command Block", - "id": 210, - "place": "flatten", - }, - "chain_command_block": { - "name": "Chain Command Block", - "id": 211 - }, - "frosted_ice": { - "name": "Frosted Ice", - "id": 212 - }, - "magma": { - "id": 213, - "name": "Magma Block", - }, - "nether_wart_block": { - "id": 214, - "name": "Nether Wart Block", - }, - "red_nether_brick": { - "id": 215, - "name": "Red Nether Brick", - }, - "bone_block": { - "id": 216, - "name": "Bone Block", - }, - "structure_void": { - "id": 217, - "name": "Structure Void", - }, - "observer": { - "name": "Observer", - "id": 218 - }, - "white_shulker_box": { - "name": "White Shulker Box", - "id": 219 - }, - "orange_shulker_box": { - "name": "Orange Shulker Box", - "id": 220 - }, - "magenta_shulker_box": { - "name": "Magenta Shulker Box", - "id": 221 - }, - "light_blue_shulker_box": { - "name": "Light Blue Shulker Box", - "id": 222 - }, - "yellow_shulker_box": { - "name": "Yellow Shulker Box", - "id": 223 - }, - "lime_shulker_box": { - "name": "Lime Shulker Box", - "id": 224 - }, - "pink_shulker_box": { - "name": "Pink Shulker Box", - "id": 225 - }, - "gray_shulker_box": { - "name": "Gray Shulker Box", - "id": 226 - }, - "silver_shulker_box": { - "name": "Light Gray Shulker Box", - "id": 227 - }, - "cyan_shulker_box": { - "name": "Cyan Shulker Box", - "id": 228 - }, - "purple_shulker_box": { - "name": "Purple Shulker Box", - "id": 229 - }, - "blue_shulker_box": { - "name": "Blue Shulker Box", - "id": 230 - }, - "brown_shulker_box": { - "name": "Brown Shulker Box", - "id": 231 - }, - "green_shulker_box": { - "name": "Green Shulker Box", - "id": 232 - }, - "red_shulker_box": { - "name": "Red Shulker Box", - "id": 233 - }, - "black_shulker_box": { - "name": "Black Shulker Box", - "id": 234 - }, - "white_glazed_terracotta": { - "id": 235, - "name": "White glazed terracotta", - }, - "orange_glazed_terracotta": { - "id": 236, - "name": "Orange glazed terracotta", - }, - "magenta_glazed_terracotta": { - "id": 237, - "name": "Magenta glazed terracotta", - }, - "light_blue_glazed_terracotta": { - "id": 238, - "name": "Light blue glazed terracotta", - }, - "yellow_glazed_terracotta": { - "id": 239, - "name": "Yellow glazed terracotta", - }, - "lime_glazed_terracotta": { - "id": 240, - "name": "Lime glazed terracotta", - }, - "pink_glazed_terracotta": { - "id": 241, - "name": "Pink glazed terracotta", - }, - "gray_glazed_terracotta": { - "id": 242, - "name": "Gray glazed terracotta", - }, - "light_gray_glazed_terracotta": { - "id": 243, - "name": "Light gray glazed terracotta", - }, - "cyan_glazed_terracotta": { - "id": 244, - "name": "Cyan glazed terracotta", - }, - "purple_glazed_terracotta": { - "id": 245, - "name": "Purple glazed terracotta", - }, - "blue_glazed_terracotta": { - "id": 246, - "name": "Blue glazed terracotta", - }, - "brown_glazed_terracotta": { - "id": 247, - "name": "Brown glazed terracotta", - }, - "green_glazed_terracotta": { - "id": 248, - "name": "Green glazed terracotta", - }, - "red_glazed_terracotta": { - "id": 249, - "name": "Red glazed terracotta", - }, - "black_glazed_terracotta": { - "id": 250, - "name": "Black glazed terracotta", - }, - "concrete": { - "id": 251, - "name": ["White concrete", - "Orange concrete", - "Magenta concrete", - "Light blue concrete", - "Yellow concrete", - "Lime concrete", - "Pink concrete", - "Gray concrete", - "Silver concrete", - "Cyan concrete", - "Purple concrete", - "Blue concrete", - "Brown concrete", - "Green concrete", - "Red concrete", - "Black concrete"], - }, - "concrete_powder": { - "id": 252, - "name": ["White concrete powder", - "Orange concrete powder", - "Magenta concrete powder", - "Light blue concrete powder", - "Yellow concrete powder", - "Lime concrete powder", - "Pink concrete powder", - "Gray concrete powder", - "Silver concrete powder", - "Cyan concrete powder", - "Purple concrete powder", - "Blue concrete powder", - "Brown concrete powder", - "Green concrete powder", - "Red concrete powder", - "Black concrete powder"], - }, - "structure_block": { - "name": ["Structure Block (Save)", - "Structure Block (Load)", - "Structure Block (Corner)", - "Structure Block (Data)"], - "id": 255 - }, - "iron_shovel": { - "id": 256, - "name": "Iron Shovel" - }, - "iron_pickaxe": { - "id": 257, - "name": "Iron Pickaxe" - }, - "iron_axe": { - "id": 258, - "name": "Iron Axe" - }, - "flint_and_steel": { - "id": 259, - "name": "Flint and Steel" - }, - "apple": { - "id": 260, - "name": "Apple" - }, - "bow": { - "id": 261, - "name": "Bow" - }, - "arrow": { - "id": 262, - "name": "Arrow" - }, - "coal": { - "name": ["Coal", - "Charcoal"], - "id": 263 - }, - "diamond": { - "id": 264, - "name": "Diamond" - }, - "iron_ingot": { - "id": 265, - "name": "Iron Ingot" - }, - "gold_ingot": { - "id": 266, - "name": "Gold Ingot" - }, - "iron_sword": { - "id": 267, - "name": "Iron Sword" - }, - "wooden_sword": { - "id": 268, - "name": "Wooden Sword" - }, - "wooden_shovel": { - "id": 269, - "name": "Wooden Shovel" - }, - "wooden_pickaxe": { - "id": 270, - "name": "Wooden Pickaxe" - }, - "wooden_axe": { - "id": 271, - "name": "Wooden Axe" - }, - "stone_sword": { - "id": 272, - "name": "Stone Sword" - }, - "stone_shovel": { - "id": 273, - "name": "Stone Shovel" - }, - "stone_pickaxe": { - "id": 274, - "name": "Stone Pickaxe" - }, - "stone_axe": { - "id": 275, - "name": "Stone Axe" - }, - "diamond_sword": { - "id": 276, - "name": "Diamond Sword" - }, - "diamond_shovel": { - "id": 277, - "name": "Diamond Shovel" - }, - "diamond_pickaxe": { - "id": 278, - "name": "Diamond Pickaxe" - }, - "diamond_axe": { - "id": 279, - "name": "Diamond Axe" - }, - "stick": { - "id": 280, - "name": "Stick" - }, - "bowl": { - "id": 281, - "name": "Bowl" - }, - "mushroom_stew": { - "id": 282, - "name": "Mushroom Stew" - }, - "golden_sword": { - "id": 283, - "name": "Golden Sword" - }, - "golden_shovel": { - "id": 284, - "name": "Golden Shovel" - }, - "golden_pickaxe": { - "id": 285, - "name": "Golden Pickaxe" - }, - "golden_axe": { - "id": 286, - "name": "Golden Axe" - }, - "string": { - "id": 287, - "name": "String" - }, - "feather": { - "id": 288, - "name": "Feather" - }, - "gunpowder": { - "id": 289, - "name": "Gunpowder" - }, - "wooden_hoe": { - "id": 290, - "name": "Wooden Hoe" - }, - "stone_hoe": { - "id": 291, - "name": "Stone Hoe" - }, - "iron_hoe": { - "id": 292, - "name": "Iron Hoe" - }, - "diamond_hoe": { - "id": 293, - "name": "Diamond Hoe" - }, - "golden_hoe": { - "id": 294, - "name": "Golden Hoe" - }, - "wheat_seeds": { - "id": 295, - "name": "Wheat Seeds", - }, - "bread": { - "id": 297, - "name": "Bread" - }, - "leather_helmet": { - "id": 298, - "name": "Leather Helmet" - }, - "leather_chestplate": { - "id": 299, - "name": "Leather Tunic" - }, - "leather_leggings": { - "id": 300, - "name": "Leather Pants" - }, - "leather_boots": { - "id": 301, - "name": "Leather Boots" - }, - "chainmail_helmet": { - "id": 302, - "name": "Chainmail Helmet" - }, - "chainmail_chestplate": { - "id": 303, - "name": "Chainmail Chestplate" - }, - "chainmail_leggings": { - "id": 304, - "name": "Chainmail Leggings" - }, - "chainmail_boots": { - "id": 305, - "name": "Chainmail Boots" - }, - "iron_helmet": { - "id": 306, - "name": "Iron Helmet" - }, - "iron_chestplate": { - "id": 307, - "name": "Iron Chestplate" - }, - "iron_leggings": { - "id": 308, - "name": "Iron Leggings" - }, - "iron_boots": { - "id": 309, - "name": "Iron Boots" - }, - "diamond_helmet": { - "id": 310, - "name": "Diamond Helmet" - }, - "diamond_chestplate": { - "id": 311, - "name": "Diamond Chestplate" - }, - "diamond_leggings": { - "id": 312, - "name": "Diamond Leggings" - }, - "diamond_boots": { - "id": 313, - "name": "Diamond Boots" - }, - "golden_helmet": { - "id": 314, - "name": "Golden Helmet" - }, - "golden_chestplate": { - "id": 315, - "name": "Golden Chestplate" - }, - "golden_leggings": { - "id": 316, - "name": "Golden Leggings" - }, - "golden_boots": { - "id": 317, - "name": "Golden Boots" - }, - "flint": { - "id": 318, - "name": "Flint" - }, - "porkchop": { - "id": 319, - "name": "Raw Porkchop" - }, - "cooked_porkchop": { - "id": 320, - "name": "Cooked Porkchop" - }, - "painting": { - "id": 321, - "name": "Painting" - }, - "golden_apple": { - "id": 322, - "name": ["Enchanted Golden Apple", - "Enchanted Golden Apple" ] - }, - "sign": { - "id": 323, - "name": "Sign", - }, - "bucket": { - "id": 325, - "name": "Bucket" - }, - "water_bucket": { - "id": 326, - "name": "Water Bucket" - }, - "lava_bucket": { - "id": 327, - "name": "Lava Bucket" - }, - "minecart": { - "id": 328, - "name": "Minecart" - }, - "saddle": { - "id": 329, - "name": "Saddle" - }, - "redstone": { - "id": 331, - "name": "Redstone Dust", - }, - "snowball": { - "id": 332, - "name": "Snowball" - }, - "boat": { - "id": 333, - "name": "Oak Boat" - }, - "leather": { - "id": 334, - "name": "Leather" - }, - "milk_bucket": { - "id": 335, - "name": "Milk Bucket" - }, - "brick": { - "id": 336, - "name": "Brick" - }, - "clay_ball": { - "id": 337, - "name": "Clay" - }, - "paper": { - "id": 339, - "name": "Paper" - }, - "book": { - "id": 340, - "name": "Book" - }, - "slime_ball": { - "id": 341, - "name": "Slimeball" - }, - "chest_minecart": { - "id": 342, - "name": "Minecart with Chest" - }, - "furnace_minecart": { - "id": 343, - "name": "Minecart with Furnace" - }, - "egg": { - "id": 344, - "name": "Egg" - }, - "compass": { - "id": 345, - "name": "Compass" - }, - "fishing_rod": { - "id": 346, - "name": "Fishing Rod" - }, - "clock": { - "id": 347, - "name": "Clock" - }, - "glowstone_dust": { - "id": 348, - "name": "Glowstone Dust" - }, - "fish": { - "id": 349, - "name": ["Raw Fish", - "Raw Salmon", - "Clownfish", - "Pufferfish"] - }, - "cooked_fish": { - "id": 350, - "name": ["Cooked Fish", - "Cooked Salmon"] - }, - "dye": { - "id": 351, - "name": ["Ink Sack", - "Rose Red", - "Cactus Green", - "Cocoa Bean", - "Lapis Lazuli", - "Purple Dye", - "Cyan Dye", - "Light Gray Dye", - "Gray Dye", - "Pink Dye", - "Lime Dye", - "Dandelion Yellow", - "Light Blue Dye", - "Magenta Dye", - "Orange Dye", - "Bone Meal"] - }, - "bone": { - "id": 352, - "name": "Bone" - }, - "sugar": { - "id": 353, - "name": "Sugar" - }, - "bed-block": { - "id": 355, - "name": "Bed", - "place": "bed", - }, - "repeater": { - "id": 356, - "name": "Redstone Repeater", - "place": "repeater", - }, - "cookie": { - "id": 357, - "name": "Cookie" - }, - "filled_map": { - "id": 358, - "name": "Map" - }, - "shears": { - "id": 359, - "name": "Shears" - }, - "melon": { - "id": 360, - "name": "Melon" - }, - "pumpkin_seeds": { - "id": 361, - "name": "Pumpkin Seeds" - }, - "melon_seeds": { - "id": 362, - "name": "Melon Seeds" - }, - "beef": { - "id": 363, - "name": "Raw Beef" - }, - "cooked_beef": { - "id": 364, - "name": "Steak" - }, - "chicken": { - "id": 365, - "name": "Raw Chicken" - }, - "cooked_chicken": { - "id": 366, - "name": "Cooked Chicken" - }, - "rotten_flesh": { - "id": 367, - "name": "Rotten Flesh" - }, - "ender_pearl": { - "id": 368, - "name": "Ender Pearl" - }, - "blaze_rod": { - "id": 369, - "name": "Blaze Rod" - }, - "ghast_tear": { - "id": 370, - "name": "Ghast Tear" - }, - "gold_nugget": { - "id": 371, - "name": "Gold Nugget" - }, - "potion": { - "id": 373, - "name": "Potion" - }, - "glass_bottle": { - "id": 374, - "name": "Glass Bottle" - }, - "spider_eye": { - "id": 375, - "name": "Spider Eye" - }, - "fermented_spider_eye": { - "id": 376, - "name": "Fermented Spider Eye" - }, - "blaze_powder": { - "id": 377, - "name": "Blaze Powder" - }, - "magma_cream": { - "id": 378, - "name": "Magma Cream" - }, - "ender_eye": { - "id": 381, - "name": "Eye of Ender" - }, - "speckled_melon": { - "id": 382, - "name": "Glistering Melon" - }, - "spawn_egg": { - "id": 383, - "name": "Spawn Egg" - }, - "experience_bottle": { - "id": 384, - "name": "Bottle o' Enchanting" - }, - "fire_charge": { - "id": 385, - "name": "Fire Charge" - }, - "writable_book": { - "id": 386, - "name": "Book and Quill" - }, - "written_book": { - "id": 387, - "name": "Written Book" - }, - "emerald": { - "id": 388, - "name": "Emerald" - }, - "item_frame": { - "id": 389, - "name": "Item Frame" - }, - "carrot": { - "id": 391, - "name": "Carrot", - }, - "potato": { - "id": 392, - "name": "Potato", - }, - "baked_potato": { - "id": 393, - "name": "Baked Potato" - }, - "poisonous_potato": { - "id": 394, - "name": "Poisonous Potato" - }, - "map": { - "id": 395, - "name": "Empty Map" - }, - "golden_carrot": { - "id": 396, - "name": "Golden Carrot" - }, - "carrot_on_a_stick": { - "id": 398, - "name": "Carrot on a Stick" - }, - "nether_star": { - "id": 399, - "name": "Nether Star" - }, - "pumpkin_pie": { - "id": 400, - "name": "Pumpkin Pie" - }, - "fireworks": { - "id": 401, - "name": "Firework Rocket" - }, - "firework_charge": { - "id": 402, - "name": "Firework Star" - }, - "enchanted_book": { - "id": 403, - "name": "Enchanted Book" - }, - "comparator": { - "id": 404, - "name": "Redstone Comparator", - "place": "comparator", - }, - "netherbrick": { - "id": 405, - "name": "Nether Brick" - }, - "quartz": { - "id": 406, - "name": "Nether Quartz" - }, - "tnt_minecart": { - "id": 407, - "name": "Minecart with TNT" - }, - "hopper_minecart": { - "id": 408, - "name": "Minecart with Hopper" - }, - "prismarine_shard": { - "id": 409, - "name": "Prismarine Shard" - }, - "prismarine_crystals": { - "id": 410, - "name": "Prismarine Crystals" - }, - "rabbit": { - "id": 411, - "name": "Raw Rabbit" - }, - "cooked_rabbit": { - "id": 412, - "name": "Cooked Rabbit" - }, - "rabbit_stew": { - "id": 413, - "name": "Rabbit Stew" - }, - "rabbit_foot": { - "id": 414, - "name": "Rabbit's Foot" - }, - "rabbit_hide": { - "id": 415, - "name": "Rabbit Hide" - }, - "armor_stand": { - "id": 416, - "name": "Armor Stand" - }, - "iron_horse_armor": { - "id": 417, - "name": "Iron Horse Armor" - }, - "golden_horse_armor": { - "id": 418, - "name": "Golden Horse Armor" - }, - "diamond_horse_armor": { - "id": 419, - "name": "Diamond Horse Armor" - }, - "lead": { - "id": 420, - "name": "Lead" - }, - "name_tag": { - "id": 421, - "name": "Name Tag" - }, - "command_block_minecart": { - "id": 422, - "name": "Minecart with Command Block" - }, - "mutton": { - "id": 423, - "name": "Raw Mutton" - }, - "cooked_mutton": { - "id": 424, - "name": "Cooked Mutton" - }, - "banner": { - "id": 425, - "name": "Banner", - }, - "chorus_fruit": { - "id": 432, - "name": "Chorus Fruit" - }, - "popped_chorus_fruit": { - "id": 433, - "name": "Popped Chorus Fruit" - }, - "beetroot": { - "id": 434, - "name": "Beetroot" - }, - "beetroot_seeds": { - "id": 435, - "name": "Beetroot Seeds" - }, - "beetroot_soup": { - "id": 436, - "name": "Beetroot Soup" - }, - "dragon_breath": { - "id": 437, - "name": "Dragon's Breath" - }, - "splash_potion": { - "id": 438, - "name": "Splash Potion" - }, - "spectral_arrow": { - "id": 439, - "name": "Spectral Arrow" - }, - "tipped_arrow": { - "id": 440, - "name": "Tipped Arrow" - }, - "lingering_potion": { - "id": 441, - "name": "Lingering Potion" - }, - "shield": { - "id": 442, - "name": "Shield" - }, - "elytra": { - "id": 443, - "name": "Elytra" - }, - "spruce_boat": { - "id": 444, - "name": "Spruce Boat" - }, - "birch_boat": { - "id": 445, - "name": "Birch Boat" - }, - "jungle_boat": { - "id": 446, - "name": "Jungle Boat" - }, - "acacia_boat": { - "id": 447, - "name": "Acacia Boat" - }, - "dark_oak_boat": { - "id": 448, - "name": "Dark Oak Boat" - }, - "totem_of_undying": { - "id": 449, - "name": "Totem of Undying" - }, - "shulker_shell": { - "id": 450, - "name": "Shulker Shell" - }, - "iron_nugget": { - "id": 452, - "name": "Iron Nugget" - }, - "record_13": { - "id": 2256, - "name": "13 Disc" - }, - "record_cat": { - "id": 2257, - "name": "Cat Disc" - } - "record_blocks": { - "id": 2258, - "name": "Blocks Disc" - }, - "record_chirp": { - "id": 2259, - "name": "Chirp Disc" - }, - "record_far": { - "id": 2260, - "name": "Far Disc" - }, - "record_mall": { - "id": 2261, - "name": "Mall Disc" - }, - "record_mellohi": { - "id": 2262, - "name": "Mellohi Disc" - }, - "record_stal": { - "id": 2263, - "name": "Stal Disc" - }, - "record_strad": { - "id": 2264, - "name": "Strad Disc" - }, - "record_ward": { - "id": 2265, - "name": "Ward Disc" - }, - "record_11": { - "id": 2266, - "name": "11 Disc" - }, - "record_wait": { - "id": 2267, - "name": "Wait Disc" - }, + "air": { + "id": 0, + "name": "Air", + }, + "stone": { + "id": 1, + "name": ["Stone", + "Granite", + "Polished Granite", + "Diorite", + "Polished Diorite", + "Andesite", + "Polished Andesite"], + }, + "grass": { + "id": 2, + "name": "Grass Block", + }, + "dirt": { + "id": 3, + "name": ["Dirt", + "Coarse Dirt", + "Podzol"], + }, + "cobblestone": { + "id": 4, + "name": "Cobblestone", + }, + "planks": { + "id": 5, + "name": ["Oak Wood Planks", + "Spruce Wood Planks", + "Birch Wood Planks", + "Jungle Wood Planks", + "Acacia Wood Planks", + "Dark Oak Wood Planks"], + }, + "sapling": { + "id": 6, + "name": ["Oak Sapling", + "Spruce Sapling", + "Birch Sapling", + "Jungle Sapling", + "Acacia Sapling", + "Dark Oak Sapling"], + "place": "sapling", + }, + "bedrock": { + "id": 7, + "name": "Bedrock", + }, + "flowing_water": { + "id": 8, + "name": "Water", + "place": "flatten", + }, + "water": { + "id": 9, + "name": "Water", + "place": "flatten", + }, + "flowing_lava": { + "id": 10, + "name": "Lava", + }, + "lava": { + "id": 11, + "name": "Lava", + }, + "sand": { + "id": 12, + "name": ["Sand", + "Red Sand"], + }, + "gravel": { + "id": 13, + "name": "Gravel", + }, + "gold_ore": { + "id": 14, + "name": "Gold Ore", + }, + "iron_ore": { + "id": 15, + "name": "Iron Ore", + }, + "coal_ore": { + "id": 16, + "name": "Coal Ore", + }, + "log": { + "id": 17, + "name": ["Oak Wood", + "Spruce Wood", + "Birch Wood", + "Jungle Wood"], + "place": "wood", + }, + "leaves": { + "id": 18, + "name": ["Oak Leaves", + "Spruce Leaves", + "Birch Leaves", + "Jungle Leaves"], + "place": "leaves", + }, + "sponge": { + "id": 19, + "name": ["Sponge", + "Wet Sponge"], + }, + "glass": { + "id": 20, + "name": "Glass", + }, + "lapis_ore": { + "id": 21, + "name": "Lapis Lazuli Ore", + }, + "lapis_block": { + "id": 22, + "name": "Lapis Lazuli Block", + }, + "dispenser": { + "id": 23, + "name": "Dispenser", + "place": "dispenser", + }, + "sandstone": { + "id": 24, + "name": ["Sandstone", + "Chiseled Sandstone", + "Smooth Sandstone"], + }, + "noteblock": { + "id": 25, + "name": "Note Block", + }, + "bed": { + "id": 26, + "name": "Bed", + "place": "bed", + }, + "golden_rail": { + "id": 27, + "name": "Powered Rail", + "place": "adp-rail", + }, + "detector_rail": { + "id": 28, + "name": "Detector Rail", + "place": "adp-rail", + }, + "sticky_piston": { + "id": 29, + "name": "Sticky Piston", + "place": "piston", + }, + "web": { + "id": 30, + "name": "Cobweb", + }, + "tallgrass": { + "id": 31, + "name": ["Shrub", + "Grass", + "Fern"], + }, + "deadbush": { + "id": 32, + "name": "Dead Bush", + }, + "piston": { + "id": 33, + "name": "Piston", + "place": "piston", + }, + "piston_head": { + "id": 34, + "name": "Piston Extension", + "place": "flatten", + }, + "wool": { + "id": 35, + "name": ["White Wool", + "Orange Wool", + "Magenta Wool", + "Light Blue Wool", + "Yellow Wool", + "Lime Wool", + "Pink Wool", + "Gray Wool", + "Light Gray Wool", + "Cyan Wool", + "Purple Wool", + "Blue Wool", + "Brown Wool", + "Green Wool", + "Red Wool", + "Black Wool"], + }, + "piston_extension": { + "id": 36, + "name": "Block moved by Piston", + "place": "flatten", + }, + "yellow_flower": { + "id": 37, + "name": "Dandelion", + }, + "red_flower": { + "id": 38, + "name": ["Poppy", + "Blue Orchid", + "Allium", + "Azure Bluet", + "Red Tulip", + "Orange Tulip", + "White Tulip", + "Pink Tulip", + "Oxeye Daisy"], + }, + "brown_mushroom": { + "id": 39, + "name": "Brown Mushroom", + }, + "red_mushroom": { + "id": 40, + "name": "Red Mushroom", + }, + "gold_block": { + "id": 41, + "name": "Block of Gold", + }, + "iron_block": { + "id": 42, + "name": "Block of Iron", + }, + "double_stone_slab": { + "id": 43, + "name": "Double Stone Slab", + }, + "stone_slab": { + "id": 44, + "name": ["Stone Slab", + "Sandstone Slab", + "Wooden Slab", + "Cobblestone Slab", + "Bricks Slab", + "Stone Bricks Slab", + "Nether Brick Slab", + "Quartz Slab"], + "place": "slab", + }, + "brick_block": { + "id": 45, + "name": "Bricks", + }, + "tnt": { + "id": 46, + "name": "TNT", + }, + "bookshelf": { + "id": 47, + "name": "Bookshelf", + }, + "mossy_cobblestone": { + "id": 48, + "name": "Moss Stone", + }, + "obsidian": { + "id": 49, + "name": "Obsidian", + }, + "torch": { + "id": 50, + "name": "Torch", + "place": "torch", + }, + "fire": { + "id": 51, + "name": "Fire", + "place": "flatten", + }, + "mob_spawner": { + "id": 52, + "name": "Monster Spawner", + }, + "oak_stairs": { + "id": 53, + "name": "Oak Wood Stairs", + "place": "stairs", + }, + "chest": { + "id": 54, + "name": "Chest", + "place": "chest-furnace", + }, + "redstone_wire": { + "id": 55, + "name": "Redstone Wire", + "place": "flatten", + }, + "diamond_ore": { + "id": 56, + "name": "Diamond Ore", + }, + "diamond_block": { + "id": 57, + "name": "Block of Diamond", + }, + "crafting_table": { + "id": 58, + "name": "Crafting Table", + }, + "wheat": { + "id": 59, + "name": "Wheat", + "place": "flatten", + }, + "farmland": { + "id": 60, + "name": "Farmland", + "place": "flatten", + }, + "furnace": { + "id": 61, + "name": "Furnace", + "place": "chest-furnace", + }, + "lit_furnace": { + "id": 62, + "name": "Burning Furnace", + "place": "chest-furnace", + }, + "standing_sign": { + "id": 63, + "name": "Sign", + "place": "signpost", + }, + "wooden_door": { + "id": 64, + "name": "Oak Door", + "place": "door", + }, + "ladder": { + "id": 65, + "name": "Ladder", + "place": "wallsign-ladder", + }, + "rail": { + "id": 66, + "name": "Rail", + "place": "rail", + }, + "stone_stairs": { + "id": 67, + "name": "Cobblestone Stairs", + "place": "stairs", + }, + "wall_sign": { + "id": 68, + "name": "Sign", + "place": "wallsign-ladder", + }, + "lever": { + "id": 69, + "name": "Lever", + "place": "lever", + }, + "stone_pressure_plate": { + "id": 70, + "name": "Stone Pressure Plate", + }, + "iron_door": { + "id": 71, + "name": "Iron Door", + "place": "door", + }, + "wooden_pressure_plate": { + "id": 72, + "name": "Wooden Pressure Plate", + }, + "redstone_ore": { + "id": 73, + "name": "Redstone Ore", + }, + "lit_redstone_ore": { + "id": 74, + "name": "Redstone Ore", + }, + "unlit_redstone_torch": { + "id": 75, + "name": "Redstone Torch (inactive)", + "place": "torch", + }, + "redstone_torch": { + "id": 76, + "name": "Redstone Torch (active)", + "place": "torch", + }, + "stone_button": { + "id": 77, + "name": "Stone Button", + "place": "button", + }, + "snow_layer": { + "id": 78, + "name": "Snow", + "place": "flatten", + }, + "ice": { + "id": 79, + "name": "Ice", + }, + "snow": { + "id": 80, + "name": "Snow", + }, + "cactus": { + "id": 81, + "name": "Cactus", + "place": "flatten", + }, + "clay": { + "id": 82, + "name": "Clay", + }, + "reeds": { + "id": 83, + "name": "Sugar Cane", + "place": "flatten", + }, + "jukebox": { + "id": 84, + "name": "Jukebox", + "place": "flatten", + }, + "fence": { + "id": 85, + "name": "Fence", + }, + "pumpkin": { + "id": 86, + "name": "Pumpkin", + "place": "pumpkin", + }, + "netherrack": { + "id": 87, + "name": "Netherrack", + }, + "soul_sand": { + "id": 88, + "name": "Soul Sand", + }, + "glowstone": { + "id": 89, + "name": "Glowstone", + }, + "portal": { + "id": 90, + "name": "Portal", + "place": "flatten", + }, + "lit_pumpkin": { + "id": 91, + "name": "Jack o'Lantern", + "place": "pumpkin", + }, + "cake": { + "id": 92, + "name": "Cake", + "place": "flatten", + }, + "unpowered_repeater": { + "id": 93, + "name": "Redstone Repeater (inactive)", + "place": "repeater", + }, + "powered_repeater": { + "id": 94, + "name": "Redstone Repeater (active)", + "place": "repeater", + }, + "stained_glass": { + "id": 95, + "name": ["White Stained Glass", + "Orange Stained Glass", + "Magenta Stained Glass", + "Light Blue Stained Glass", + "Yellow Stained Glass", + "Lime Stained Glass", + "Pink Stained Glass", + "Gray Stained Glass", + "Light Gray Stained Glass", + "Cyan Stained Glass", + "Purple Stained Glass", + "Blue Stained Glass", + "Brown Stained Glass", + "Green Stained Glass", + "Red Stained Glass", + "Black Stained Glass"], + }, + "trapdoor": { + "id": 96, + "name": "Trapdoor", + "place": "trapdoor", + }, + "monster_egg": { + "id": 97, + "name": ["Stone Monster Egg", + "Cobblestone Monster Egg", + "Stone Brick Monster Egg", + "Mossy Stone Brick Monster Egg", + "Cracked Stone Brick Monster Egg", + "Chiseled Stone Brick Monster Egg"], + }, + "stonebrick": { + "id": 98, + "name": ["Stone Bricks", + "Mossy Stone Bricks", + "Cracked Stone Bricks", + "Chiseled Stone Bricks"], + }, + "brown_mushroom_block": { + "id": 99, + "name": "Brown Mushroom (block)", + "place": "flatten", + }, + "red_mushroom_block": { + "id": 100, + "name": "Red Mushroom (block)", + "place": "flatten", + }, + "iron_bars": { + "id": 101, + "name": "Iron Bars", + }, + "glass_pane": { + "id": 102, + "name": "Glass Pane", + }, + "melon_block": { + "id": 103, + "name": "Melon", + }, + "pumpkin_stem": { + "id": 104, + "name": "Pumpkin Stem", + }, + "melon_stem": { + "id": 105, + "name": "Melon Stem", + }, + "vine": { + "id": 106, + "name": "Vines", + "place": "vine", + }, + "fence_gate": { + "id": 107, + "name": "Fence Gate", + "place": "gate", + }, + "brick_stairs": { + "id": 108, + "name": "Brick Stairs", + "place": "stairs", + }, + "stone_brick_stairs": { + "id": 109, + "name": "Stone Brick Stairs", + "place": "stairs", + }, + "mycelium": { + "id": 110, + "name": "Mycelium", + }, + "waterlily": { + "id": 111, + "name": "Lily Pad", + }, + "nether_brick": { + "id": 112, + "name": "Nether Brick", + }, + "nether_brick_fence": { + "id": 113, + "name": "Nether Brick Fence", + }, + "nether_brick_stairs": { + "id": 114, + "name": "Nether Brick Stairs", + "place": "stairs", + }, + "nether_wart": { + "id": 115, + "name": "Nether Wart", + "place": "flatten", + }, + "enchanting_table": { + "id": 116, + "name": "Enchantment Table", + }, + "brewing_stand": { + "id": 117, + "name": "Brewing Stand", + "place": "flatten", + }, + "cauldron": { + "id": 118, + "name": "Cauldron", + "place": "cauldron", + }, + "end_portal": { + "id": 119, + "name": "End Portal", + }, + "end_portal_frame": { + "id": 120, + "name": "End Portal Block", + "place": "flatten", + }, + "end_stone": { + "id": 121, + "name": "End Stone", + }, + "dragon_egg": { + "id": 122, + "name": "Dragon Egg", + }, + "redstone_lamp": { + "id": 123, + "name": "Redstone Lamp (inactive)", + }, + "lit_redstone_lamp": { + "id": 124, + "name": "Redstone Lamp (active)", + }, + "double_wooden_slab": { + "id": 125, + "name": "Double Wooden Slab", + }, + "wooden_slab": { + "id": 126, + "name": ["Oak Wood Slab", + "Spruce Wood Slab", + "Birch Wood Slab", + "Jungle Wood Slab", + "Acacia Wood Slab", + "Dark Oak Wood Slab"], + "place": "slab", + }, + "cocoa": { + "id": 127, + "name": "Cocoa", + "place": "cocoa", + }, + "sandstone_stairs": { + "id": 128, + "name": "Sandstone Stairs", + "place": "stairs", + }, + "emerald_ore": { + "id": 129, + "name": "Emerald Ore", + }, + "ender_chest": { + "id": 130, + "name": "Ender Chest", + "place": "chest-furnace", + }, + "tripwire_hook": { + "id": 131, + "name": "Tripwire Hook", + "place": "tripwire", + }, + "tripwire": { + "id": 132, + "name": "Tripwire", + "place": "flatten", + }, + "emerald_block": { + "id": 133, + "name": "Block of Emerald", + }, + "spruce_stairs": { + "id": 134, + "name": "Spruce Wood Stairs", + "place": "stairs", + }, + "birch_stairs": { + "id": 135, + "name": "Birch Wood Stairs", + "place": "stairs", + }, + "jungle_stairs": { + "id": 136, + "name": "Jungle Wood Stairs", + "place": "stairs", + }, + "command_block": { + "id": 137, + "name": "Command Block", + }, + "beacon": { + "id": 138, + "name": "Beacon", + }, + "cobblestone_wall": { + "id": 139, + "name": ["Cobblestone Wall", + "Mossy Cobblestone Wall"], + }, + "flower_pot": { + "id": 140, + "name": "Flower Pot", + "place": "flatten", + }, + "carrots": { + "id": 141, + "name": "Carrot", + "place": "flatten", + }, + "potatoes": { + "id": 142, + "name": "Potato", + "place": "flatten", + }, + "wooden_button": { + "id": 143, + "name": "Wooden Button", + "place": "button", + }, + "skull": { + "id": 144, + "name": ["Skeleton Skull", + "Wither Skeleton Skull", + "Zombie Head", + "Human Head", + "Creeper Head", + "Dragon Head"] + "place": "mobhead", + }, + "anvil": { + "id": 145, + "name": ["Anvil", + "Slightly Damaged Anvil", + "Very Damaged Anvil"], + "place": "anvil", + }, + "trapped_chest": { + "id": 146, + "name": "Trapped Chest", + "place": "chest-furnace", + }, + "light_weighted_pressure_plate": { + "id": 147, + "name": "Weighted Pressure Plate", + }, + "heavy_weighted_pressure_plate": { + "id": 148, + "name": "Weighted Pressure Plate", + }, + "unpowered_comparator": { + "id": 149, + "name": "Redstone Comparator", + "place": "comparator", + }, + "powered_comparator": { + "id": 150, + "name": "Redstone Comparator", + }, + "daylight_detector": { + "id": 151, + "name": "Daylight Sensor", + "place": "flatten", + }, + "redstone_block": { + "id": 152, + "name": "Block of Redstone", + }, + "quartz_ore": { + "id": 153, + "name": "Nether Quartz Ore", + }, + "hopper": { + "id": 154, + "name": "Hopper", + "place": "hopper", + }, + "quartz_block": { + "id": 155, + "name": ["Block of Quartz", + "Chiseled Quartz Block", + "Pillar Quartz Block"], + }, + "quartz_stairs": { + "id": 156, + "name": "Quartz Stairs", + "place": "stairs", + }, + "activator_rail": { + "id": 157, + "name": "Activator Rail", + "place": "adp-rail", + }, + "dropper": { + "id": 158, + "name": "Dropper", + "place": "dispenser", + }, + "stained_hardened_clay": { + "id": 159, + "name": ["White Stained Clay", + "Orange Stained Clay", + "Magenta Stained Clay", + "Light Blue Stained Clay", + "Yellow Stained Clay", + "Lime Stained Clay", + "Pink Stained Clay", + "Gray Stained Clay", + "Light Gray Stained Clay", + "Cyan Stained Clay", + "Purple Stained Clay", + "Blue Stained Clay", + "Brown Stained Clay", + "Green Stained Clay", + "Red Stained Clay", + "Black Stained Clay"], + }, + "stained_glass_pane": { + "id": 160, + "name": ["White Stained Glass Pane", + "Orange Stained Glass Pane", + "Magenta Stained Glass Pane", + "Light Blue Stained Glass Pane", + "Yellow Stained Glass Pane", + "Lime Stained Glass Pane", + "Pink Stained Glass Pane", + "Gray Stained Glass Pane", + "Light Gray Stained Glass Pane", + "Cyan Stained Glass Pane", + "Purple Stained Glass Pane", + "Blue Stained Glass Pane", + "Brown Stained Glass Pane", + "Green Stained Glass Pane", + "Red Stained Glass Pane", + "Black Stained Glass Pane"], + }, + "leaves2": { + "id": 161, + "name": ["Acacia Leaves", + "Dark Oak Leaves"], + "place": "leaves", + }, + "log2": { + "id": 162, + "name": ["Acacia Wood", + "Dark Oak Wood"], + "place": "wood", + }, + "acacia_stairs": { + "id": 163, + "name": "Acacia Wood Stairs", + "place": "stairs", + }, + "dark_oak_stairs": { + "id": 164, + "name": "Dark Oak Wood Stairs", + "place": "stairs", + }, + "slime": { + "id": 165, + "name": "Slime Block", + }, + "barrier": { + "id": 166, + "name": "Barrier", + }, + "iron_trapdoor": { + "id": 167, + "name": "Iron Trapdoor", + "place": "trapdoor", + }, + "prismarine": { + "id": 168, + "name": ["Prismarine", + "Prismarine Bricks", + "Dark Prismarine"], + }, + "sea_lantern": { + "id": 169, + "name": "Sea Lantern", + }, + "hay_block": { + "id": 170, + "name": "Hay Block", + "place": "hay-bale", + }, + "carpet": { + "id": 171, + "name": ["Carpet", + "Orange Carpet", + "Magenta Carpet", + "Light Blue Carpet", + "Yellow Carpet", + "Lime Carpet", + "Pink Carpet", + "Gray Carpet", + "Light Gray Carpet", + "Cyan Carpet", + "Purple Carpet", + "Blue Carpet", + "Brown Carpet", + "Green Carpet", + "Red Carpet", + "Black Carpet"], + }, + "hardened_clay": { + "id": 172, + "name": "Hardened Clay", + }, + "coal_block": { + "id": 173, + "name": "Block of Coal", + }, + "packed_ice": { + "id": 174, + "name": "Packed Ice", + }, + "double_plant": { + "id": 175, + "name": ["Sunflower", + "Lilac", + "Double Tallgrass", + "Large Fern", + "Rose Bush", + "Peony"], + "place": "largeplant", + }, + "standing_banner": { + "id": 176, + "name": "Banner", + "place": "signpost", + }, + "wall_banner": { + "id": 177, + "name": "Banner", + "place": "wallsign-ladder", + }, + "daylight_detector_inverted": { + "id": 178, + "name": "Inverted Daylight Sensor", + "place": "flatten", + }, + "red_sandstone": { + "id": 179, + "name": ["Red Sandstone", + "Chiseled Red Sandstone", + "Smooth Red Sandstone"], + }, + "red_sandstone_stairs": { + "id": 180, + "name": "Red Sandstone Stairs", + "place": "stairs", + }, + "double_stone_slab2": { + "id": 181, + "name": "Double Red Sandstone Slab", + }, + "stone_slab2": { + "id": 182, + "name": "Red Sandstone Slab", + "place": "slab", + }, + "spruce_fence_gate": { + "id": 183, + "name": "Spruce Fence Gate", + "place": "gate", + }, + "birch_fence_gate": { + "id": 184, + "name": "Birch Fence Gate", + "place": "gate", + }, + "jungle_fence_gate": { + "id": 185, + "name": "Jungle Fence Gate", + "place": "gate", + }, + "dark_oak_fence_gate": { + "id": 186, + "name": "Dark Oak Fence Gate", + "place": "gate", + }, + "acacia_fence_gate": { + "id": 187, + "name": "Acacia Fence Gate", + "place": "gate", + }, + "spruce_fence": { + "id": 188, + "name": "Spruce Fence", + }, + "birch_fence": { + "id": 189, + "name": "Birch Fence", + }, + "jungle_fence": { + "id": 190, + "name": "Jungle Fence", + }, + "dark_oak_fence": { + "id": 191, + "name": "Dark Oak Fence", + }, + "acacia_fence": { + "id": 192, + "name": "Acacia Fence", + }, + "spruce_door": { + "id": 193, + "name": "Spruce Door", + "place": "door", + }, + "birch_door": { + "id": 194, + "name": "Birch Door", + "place": "door", + }, + "jungle_door": { + "id": 195, + "name": "Jungle Door", + "place": "door", + }, + "acacia_door": { + "id": 196, + "name": "Acacia Door", + "place": "door", + }, + "dark_oak_door": { + "id": 197, + "name": "Dark Oak Door", + "place": "door", + }, + "end_rod": { + "id": 198 + "name": "End Rod", + "place": "end_rod", + }, + "chorus_plant": { + "id": 199 + "name": "Chorus Plant", + }, + "chorus_flower": { + "id": 200 + "name": "Chorus Flower", + }, + "purpur_block": { + "id": 201 + "name": "Purpur Block", + }, + "purpur_pillar": { + "id": 202 + "name": "Purpur Pillar", + }, + "purpur_stairs": { + "id": 203 + "name": "Purpur Stairs", + }, + "purpur_double_slab": { + "id": 204 + "name": "Double Purpur Slabs", + }, + "purpur_slab": { + "name": "Purpur Slab", + "id": 205, + "place": "slab", + }, + "end_bricks": { + "name": "End Stone Bricks", + "id": 206 + }, + "beetroots": { + "name": "Beetroot", + "id": 207 + }, + "grass_path": { + "name": "Path", + "id": 208 + }, + "end_gateway": { + "id": 209, + "name": "End Gateway" + }, + "repeating_command_block": { + "name": "Repeating Command Block", + "id": 210, + "place": "flatten", + }, + "chain_command_block": { + "name": "Chain Command Block", + "id": 211 + }, + "frosted_ice": { + "name": "Frosted Ice", + "id": 212 + }, + "magma": { + "id": 213, + "name": "Magma Block", + }, + "nether_wart_block": { + "id": 214, + "name": "Nether Wart Block", + }, + "red_nether_brick": { + "id": 215, + "name": "Red Nether Brick", + }, + "bone_block": { + "id": 216, + "name": "Bone Block", + }, + "structure_void": { + "id": 217, + "name": "Structure Void", + }, + "observer": { + "name": "Observer", + "id": 218 + }, + "white_shulker_box": { + "name": "White Shulker Box", + "id": 219 + }, + "orange_shulker_box": { + "name": "Orange Shulker Box", + "id": 220 + }, + "magenta_shulker_box": { + "name": "Magenta Shulker Box", + "id": 221 + }, + "light_blue_shulker_box": { + "name": "Light Blue Shulker Box", + "id": 222 + }, + "yellow_shulker_box": { + "name": "Yellow Shulker Box", + "id": 223 + }, + "lime_shulker_box": { + "name": "Lime Shulker Box", + "id": 224 + }, + "pink_shulker_box": { + "name": "Pink Shulker Box", + "id": 225 + }, + "gray_shulker_box": { + "name": "Gray Shulker Box", + "id": 226 + }, + "silver_shulker_box": { + "name": "Light Gray Shulker Box", + "id": 227 + }, + "cyan_shulker_box": { + "name": "Cyan Shulker Box", + "id": 228 + }, + "purple_shulker_box": { + "name": "Purple Shulker Box", + "id": 229 + }, + "blue_shulker_box": { + "name": "Blue Shulker Box", + "id": 230 + }, + "brown_shulker_box": { + "name": "Brown Shulker Box", + "id": 231 + }, + "green_shulker_box": { + "name": "Green Shulker Box", + "id": 232 + }, + "red_shulker_box": { + "name": "Red Shulker Box", + "id": 233 + }, + "black_shulker_box": { + "name": "Black Shulker Box", + "id": 234 + }, + "white_glazed_terracotta": { + "id": 235, + "name": "White glazed terracotta", + }, + "orange_glazed_terracotta": { + "id": 236, + "name": "Orange glazed terracotta", + }, + "magenta_glazed_terracotta": { + "id": 237, + "name": "Magenta glazed terracotta", + }, + "light_blue_glazed_terracotta": { + "id": 238, + "name": "Light blue glazed terracotta", + }, + "yellow_glazed_terracotta": { + "id": 239, + "name": "Yellow glazed terracotta", + }, + "lime_glazed_terracotta": { + "id": 240, + "name": "Lime glazed terracotta", + }, + "pink_glazed_terracotta": { + "id": 241, + "name": "Pink glazed terracotta", + }, + "gray_glazed_terracotta": { + "id": 242, + "name": "Gray glazed terracotta", + }, + "light_gray_glazed_terracotta": { + "id": 243, + "name": "Light gray glazed terracotta", + }, + "cyan_glazed_terracotta": { + "id": 244, + "name": "Cyan glazed terracotta", + }, + "purple_glazed_terracotta": { + "id": 245, + "name": "Purple glazed terracotta", + }, + "blue_glazed_terracotta": { + "id": 246, + "name": "Blue glazed terracotta", + }, + "brown_glazed_terracotta": { + "id": 247, + "name": "Brown glazed terracotta", + }, + "green_glazed_terracotta": { + "id": 248, + "name": "Green glazed terracotta", + }, + "red_glazed_terracotta": { + "id": 249, + "name": "Red glazed terracotta", + }, + "black_glazed_terracotta": { + "id": 250, + "name": "Black glazed terracotta", + }, + "concrete": { + "id": 251, + "name": ["White concrete", + "Orange concrete", + "Magenta concrete", + "Light blue concrete", + "Yellow concrete", + "Lime concrete", + "Pink concrete", + "Gray concrete", + "Silver concrete", + "Cyan concrete", + "Purple concrete", + "Blue concrete", + "Brown concrete", + "Green concrete", + "Red concrete", + "Black concrete"], + }, + "concrete_powder": { + "id": 252, + "name": ["White concrete powder", + "Orange concrete powder", + "Magenta concrete powder", + "Light blue concrete powder", + "Yellow concrete powder", + "Lime concrete powder", + "Pink concrete powder", + "Gray concrete powder", + "Silver concrete powder", + "Cyan concrete powder", + "Purple concrete powder", + "Blue concrete powder", + "Brown concrete powder", + "Green concrete powder", + "Red concrete powder", + "Black concrete powder"], + }, + "structure_block": { + "name": ["Structure Block (Save)", + "Structure Block (Load)", + "Structure Block (Corner)", + "Structure Block (Data)"], + "id": 255 + }, + "iron_shovel": { + "id": 256, + "name": "Iron Shovel" + }, + "iron_pickaxe": { + "id": 257, + "name": "Iron Pickaxe" + }, + "iron_axe": { + "id": 258, + "name": "Iron Axe" + }, + "flint_and_steel": { + "id": 259, + "name": "Flint and Steel" + }, + "apple": { + "id": 260, + "name": "Apple" + }, + "bow": { + "id": 261, + "name": "Bow" + }, + "arrow": { + "id": 262, + "name": "Arrow" + }, + "coal": { + "name": ["Coal", + "Charcoal"], + "id": 263 + }, + "diamond": { + "id": 264, + "name": "Diamond" + }, + "iron_ingot": { + "id": 265, + "name": "Iron Ingot" + }, + "gold_ingot": { + "id": 266, + "name": "Gold Ingot" + }, + "iron_sword": { + "id": 267, + "name": "Iron Sword" + }, + "wooden_sword": { + "id": 268, + "name": "Wooden Sword" + }, + "wooden_shovel": { + "id": 269, + "name": "Wooden Shovel" + }, + "wooden_pickaxe": { + "id": 270, + "name": "Wooden Pickaxe" + }, + "wooden_axe": { + "id": 271, + "name": "Wooden Axe" + }, + "stone_sword": { + "id": 272, + "name": "Stone Sword" + }, + "stone_shovel": { + "id": 273, + "name": "Stone Shovel" + }, + "stone_pickaxe": { + "id": 274, + "name": "Stone Pickaxe" + }, + "stone_axe": { + "id": 275, + "name": "Stone Axe" + }, + "diamond_sword": { + "id": 276, + "name": "Diamond Sword" + }, + "diamond_shovel": { + "id": 277, + "name": "Diamond Shovel" + }, + "diamond_pickaxe": { + "id": 278, + "name": "Diamond Pickaxe" + }, + "diamond_axe": { + "id": 279, + "name": "Diamond Axe" + }, + "stick": { + "id": 280, + "name": "Stick" + }, + "bowl": { + "id": 281, + "name": "Bowl" + }, + "mushroom_stew": { + "id": 282, + "name": "Mushroom Stew" + }, + "golden_sword": { + "id": 283, + "name": "Golden Sword" + }, + "golden_shovel": { + "id": 284, + "name": "Golden Shovel" + }, + "golden_pickaxe": { + "id": 285, + "name": "Golden Pickaxe" + }, + "golden_axe": { + "id": 286, + "name": "Golden Axe" + }, + "string": { + "id": 287, + "name": "String" + }, + "feather": { + "id": 288, + "name": "Feather" + }, + "gunpowder": { + "id": 289, + "name": "Gunpowder" + }, + "wooden_hoe": { + "id": 290, + "name": "Wooden Hoe" + }, + "stone_hoe": { + "id": 291, + "name": "Stone Hoe" + }, + "iron_hoe": { + "id": 292, + "name": "Iron Hoe" + }, + "diamond_hoe": { + "id": 293, + "name": "Diamond Hoe" + }, + "golden_hoe": { + "id": 294, + "name": "Golden Hoe" + }, + "wheat_seeds": { + "id": 295, + "name": "Wheat Seeds", + }, + "bread": { + "id": 297, + "name": "Bread" + }, + "leather_helmet": { + "id": 298, + "name": "Leather Helmet" + }, + "leather_chestplate": { + "id": 299, + "name": "Leather Tunic" + }, + "leather_leggings": { + "id": 300, + "name": "Leather Pants" + }, + "leather_boots": { + "id": 301, + "name": "Leather Boots" + }, + "chainmail_helmet": { + "id": 302, + "name": "Chainmail Helmet" + }, + "chainmail_chestplate": { + "id": 303, + "name": "Chainmail Chestplate" + }, + "chainmail_leggings": { + "id": 304, + "name": "Chainmail Leggings" + }, + "chainmail_boots": { + "id": 305, + "name": "Chainmail Boots" + }, + "iron_helmet": { + "id": 306, + "name": "Iron Helmet" + }, + "iron_chestplate": { + "id": 307, + "name": "Iron Chestplate" + }, + "iron_leggings": { + "id": 308, + "name": "Iron Leggings" + }, + "iron_boots": { + "id": 309, + "name": "Iron Boots" + }, + "diamond_helmet": { + "id": 310, + "name": "Diamond Helmet" + }, + "diamond_chestplate": { + "id": 311, + "name": "Diamond Chestplate" + }, + "diamond_leggings": { + "id": 312, + "name": "Diamond Leggings" + }, + "diamond_boots": { + "id": 313, + "name": "Diamond Boots" + }, + "golden_helmet": { + "id": 314, + "name": "Golden Helmet" + }, + "golden_chestplate": { + "id": 315, + "name": "Golden Chestplate" + }, + "golden_leggings": { + "id": 316, + "name": "Golden Leggings" + }, + "golden_boots": { + "id": 317, + "name": "Golden Boots" + }, + "flint": { + "id": 318, + "name": "Flint" + }, + "porkchop": { + "id": 319, + "name": "Raw Porkchop" + }, + "cooked_porkchop": { + "id": 320, + "name": "Cooked Porkchop" + }, + "painting": { + "id": 321, + "name": "Painting" + }, + "golden_apple": { + "id": 322, + "name": ["Enchanted Golden Apple", + "Enchanted Golden Apple" ] + }, + "sign": { + "id": 323, + "name": "Sign", + }, + "bucket": { + "id": 325, + "name": "Bucket" + }, + "water_bucket": { + "id": 326, + "name": "Water Bucket" + }, + "lava_bucket": { + "id": 327, + "name": "Lava Bucket" + }, + "minecart": { + "id": 328, + "name": "Minecart" + }, + "saddle": { + "id": 329, + "name": "Saddle" + }, + "redstone": { + "id": 331, + "name": "Redstone Dust", + }, + "snowball": { + "id": 332, + "name": "Snowball" + }, + "boat": { + "id": 333, + "name": "Oak Boat" + }, + "leather": { + "id": 334, + "name": "Leather" + }, + "milk_bucket": { + "id": 335, + "name": "Milk Bucket" + }, + "brick": { + "id": 336, + "name": "Brick" + }, + "clay_ball": { + "id": 337, + "name": "Clay" + }, + "paper": { + "id": 339, + "name": "Paper" + }, + "book": { + "id": 340, + "name": "Book" + }, + "slime_ball": { + "id": 341, + "name": "Slimeball" + }, + "chest_minecart": { + "id": 342, + "name": "Minecart with Chest" + }, + "furnace_minecart": { + "id": 343, + "name": "Minecart with Furnace" + }, + "egg": { + "id": 344, + "name": "Egg" + }, + "compass": { + "id": 345, + "name": "Compass" + }, + "fishing_rod": { + "id": 346, + "name": "Fishing Rod" + }, + "clock": { + "id": 347, + "name": "Clock" + }, + "glowstone_dust": { + "id": 348, + "name": "Glowstone Dust" + }, + "fish": { + "id": 349, + "name": ["Raw Fish", + "Raw Salmon", + "Clownfish", + "Pufferfish"] + }, + "cooked_fish": { + "id": 350, + "name": ["Cooked Fish", + "Cooked Salmon"] + }, + "dye": { + "id": 351, + "name": ["Ink Sack", + "Rose Red", + "Cactus Green", + "Cocoa Bean", + "Lapis Lazuli", + "Purple Dye", + "Cyan Dye", + "Light Gray Dye", + "Gray Dye", + "Pink Dye", + "Lime Dye", + "Dandelion Yellow", + "Light Blue Dye", + "Magenta Dye", + "Orange Dye", + "Bone Meal"] + }, + "bone": { + "id": 352, + "name": "Bone" + }, + "sugar": { + "id": 353, + "name": "Sugar" + }, + "bed-block": { + "id": 355, + "name": "Bed", + "place": "bed", + }, + "repeater": { + "id": 356, + "name": "Redstone Repeater", + "place": "repeater", + }, + "cookie": { + "id": 357, + "name": "Cookie" + }, + "filled_map": { + "id": 358, + "name": "Map" + }, + "shears": { + "id": 359, + "name": "Shears" + }, + "melon": { + "id": 360, + "name": "Melon" + }, + "pumpkin_seeds": { + "id": 361, + "name": "Pumpkin Seeds" + }, + "melon_seeds": { + "id": 362, + "name": "Melon Seeds" + }, + "beef": { + "id": 363, + "name": "Raw Beef" + }, + "cooked_beef": { + "id": 364, + "name": "Steak" + }, + "chicken": { + "id": 365, + "name": "Raw Chicken" + }, + "cooked_chicken": { + "id": 366, + "name": "Cooked Chicken" + }, + "rotten_flesh": { + "id": 367, + "name": "Rotten Flesh" + }, + "ender_pearl": { + "id": 368, + "name": "Ender Pearl" + }, + "blaze_rod": { + "id": 369, + "name": "Blaze Rod" + }, + "ghast_tear": { + "id": 370, + "name": "Ghast Tear" + }, + "gold_nugget": { + "id": 371, + "name": "Gold Nugget" + }, + "potion": { + "id": 373, + "name": "Potion" + }, + "glass_bottle": { + "id": 374, + "name": "Glass Bottle" + }, + "spider_eye": { + "id": 375, + "name": "Spider Eye" + }, + "fermented_spider_eye": { + "id": 376, + "name": "Fermented Spider Eye" + }, + "blaze_powder": { + "id": 377, + "name": "Blaze Powder" + }, + "magma_cream": { + "id": 378, + "name": "Magma Cream" + }, + "ender_eye": { + "id": 381, + "name": "Eye of Ender" + }, + "speckled_melon": { + "id": 382, + "name": "Glistering Melon" + }, + "spawn_egg": { + "id": 383, + "name": "Spawn Egg" + }, + "experience_bottle": { + "id": 384, + "name": "Bottle o' Enchanting" + }, + "fire_charge": { + "id": 385, + "name": "Fire Charge" + }, + "writable_book": { + "id": 386, + "name": "Book and Quill" + }, + "written_book": { + "id": 387, + "name": "Written Book" + }, + "emerald": { + "id": 388, + "name": "Emerald" + }, + "item_frame": { + "id": 389, + "name": "Item Frame" + }, + "carrot": { + "id": 391, + "name": "Carrot", + }, + "potato": { + "id": 392, + "name": "Potato", + }, + "baked_potato": { + "id": 393, + "name": "Baked Potato" + }, + "poisonous_potato": { + "id": 394, + "name": "Poisonous Potato" + }, + "map": { + "id": 395, + "name": "Empty Map" + }, + "golden_carrot": { + "id": 396, + "name": "Golden Carrot" + }, + "carrot_on_a_stick": { + "id": 398, + "name": "Carrot on a Stick" + }, + "nether_star": { + "id": 399, + "name": "Nether Star" + }, + "pumpkin_pie": { + "id": 400, + "name": "Pumpkin Pie" + }, + "fireworks": { + "id": 401, + "name": "Firework Rocket" + }, + "firework_charge": { + "id": 402, + "name": "Firework Star" + }, + "enchanted_book": { + "id": 403, + "name": "Enchanted Book" + }, + "comparator": { + "id": 404, + "name": "Redstone Comparator", + "place": "comparator", + }, + "netherbrick": { + "id": 405, + "name": "Nether Brick" + }, + "quartz": { + "id": 406, + "name": "Nether Quartz" + }, + "tnt_minecart": { + "id": 407, + "name": "Minecart with TNT" + }, + "hopper_minecart": { + "id": 408, + "name": "Minecart with Hopper" + }, + "prismarine_shard": { + "id": 409, + "name": "Prismarine Shard" + }, + "prismarine_crystals": { + "id": 410, + "name": "Prismarine Crystals" + }, + "rabbit": { + "id": 411, + "name": "Raw Rabbit" + }, + "cooked_rabbit": { + "id": 412, + "name": "Cooked Rabbit" + }, + "rabbit_stew": { + "id": 413, + "name": "Rabbit Stew" + }, + "rabbit_foot": { + "id": 414, + "name": "Rabbit's Foot" + }, + "rabbit_hide": { + "id": 415, + "name": "Rabbit Hide" + }, + "armor_stand": { + "id": 416, + "name": "Armor Stand" + }, + "iron_horse_armor": { + "id": 417, + "name": "Iron Horse Armor" + }, + "golden_horse_armor": { + "id": 418, + "name": "Golden Horse Armor" + }, + "diamond_horse_armor": { + "id": 419, + "name": "Diamond Horse Armor" + }, + "lead": { + "id": 420, + "name": "Lead" + }, + "name_tag": { + "id": 421, + "name": "Name Tag" + }, + "command_block_minecart": { + "id": 422, + "name": "Minecart with Command Block" + }, + "mutton": { + "id": 423, + "name": "Raw Mutton" + }, + "cooked_mutton": { + "id": 424, + "name": "Cooked Mutton" + }, + "banner": { + "id": 425, + "name": "Banner", + }, + "chorus_fruit": { + "id": 432, + "name": "Chorus Fruit" + }, + "popped_chorus_fruit": { + "id": 433, + "name": "Popped Chorus Fruit" + }, + "beetroot": { + "id": 434, + "name": "Beetroot" + }, + "beetroot_seeds": { + "id": 435, + "name": "Beetroot Seeds" + }, + "beetroot_soup": { + "id": 436, + "name": "Beetroot Soup" + }, + "dragon_breath": { + "id": 437, + "name": "Dragon's Breath" + }, + "splash_potion": { + "id": 438, + "name": "Splash Potion" + }, + "spectral_arrow": { + "id": 439, + "name": "Spectral Arrow" + }, + "tipped_arrow": { + "id": 440, + "name": "Tipped Arrow" + }, + "lingering_potion": { + "id": 441, + "name": "Lingering Potion" + }, + "shield": { + "id": 442, + "name": "Shield" + }, + "elytra": { + "id": 443, + "name": "Elytra" + }, + "spruce_boat": { + "id": 444, + "name": "Spruce Boat" + }, + "birch_boat": { + "id": 445, + "name": "Birch Boat" + }, + "jungle_boat": { + "id": 446, + "name": "Jungle Boat" + }, + "acacia_boat": { + "id": 447, + "name": "Acacia Boat" + }, + "dark_oak_boat": { + "id": 448, + "name": "Dark Oak Boat" + }, + "totem_of_undying": { + "id": 449, + "name": "Totem of Undying" + }, + "shulker_shell": { + "id": 450, + "name": "Shulker Shell" + }, + "iron_nugget": { + "id": 452, + "name": "Iron Nugget" + }, + "record_13": { + "id": 2256, + "name": "13 Disc" + }, + "record_cat": { + "id": 2257, + "name": "Cat Disc" + } + "record_blocks": { + "id": 2258, + "name": "Blocks Disc" + }, + "record_chirp": { + "id": 2259, + "name": "Chirp Disc" + }, + "record_far": { + "id": 2260, + "name": "Far Disc" + }, + "record_mall": { + "id": 2261, + "name": "Mall Disc" + }, + "record_mellohi": { + "id": 2262, + "name": "Mellohi Disc" + }, + "record_stal": { + "id": 2263, + "name": "Stal Disc" + }, + "record_strad": { + "id": 2264, + "name": "Strad Disc" + }, + "record_ward": { + "id": 2265, + "name": "Ward Disc" + }, + "record_11": { + "id": 2266, + "name": "11 Disc" + }, + "record_wait": { + "id": 2267, + "name": "Wait Disc" + }, } diff --git a/farms/apis/crafting.lua b/farms/apis/crafting.lua index 07725a5..266b210 100644 --- a/farms/apis/crafting.lua +++ b/farms/apis/crafting.lua @@ -6,60 +6,60 @@ local turtle = _G.turtle local CRAFTING_TABLE = 'minecraft:crafting_table' local function clearGrid(inventory) - for i = 1, 16 do - local count = turtle.getItemCount(i) - if count > 0 then - inventory:insert(i, count) - if turtle.getItemCount(i) ~= 0 then - return false - end - end - end - return true + for i = 1, 16 do + local count = turtle.getItemCount(i) + if count > 0 then + inventory:insert(i, count) + if turtle.getItemCount(i) ~= 0 then + return false + end + end + end + return true end function turtle.craftItem(item, count, inventoryInfo) - local success, msg + local success, msg - local inventory = Adapter.wrap(inventoryInfo) - if not inventory then - return false, 'Invalid inventory' - end + local inventory = Adapter.wrap(inventoryInfo) + if not inventory then + return false, 'Invalid inventory' + end - local equipped, side - if not turtle.isEquipped('workbench') then - local modemSide = turtle.isEquipped('modem') or 'right' - local osides = { left = 'right', right = 'left' } - side = osides[modemSide] - if not turtle.select(CRAFTING_TABLE) then - clearGrid(inventory) - if not turtle.selectOpenSlot() then - return false, 'Inventory is full' - end - if not inventory:provide({ name = CRAFTING_TABLE, damage = 0 }, 1) then - return false, 'Missing crafting table' - end - end + local equipped, side + if not turtle.isEquipped('workbench') then + local modemSide = turtle.isEquipped('modem') or 'right' + local osides = { left = 'right', right = 'left' } + side = osides[modemSide] + if not turtle.select(CRAFTING_TABLE) then + clearGrid(inventory) + if not turtle.selectOpenSlot() then + return false, 'Inventory is full' + end + if not inventory:provide({ name = CRAFTING_TABLE, damage = 0 }, 1) then + return false, 'Missing crafting table' + end + end - local slot = turtle.select(CRAFTING_TABLE) - turtle.equip(side, CRAFTING_TABLE) - equipped = turtle.getItemDetail(slot.index) - end + local slot = turtle.select(CRAFTING_TABLE) + turtle.equip(side, CRAFTING_TABLE) + equipped = turtle.getItemDetail(slot.index) + end - clearGrid(inventory) - success, msg = Craft.craftRecipe(item, count or 1, inventory) + clearGrid(inventory) + success, msg = Craft.craftRecipe(item, count or 1, inventory) - if equipped then - turtle.selectOpenSlot() - inventory:provide({ name = equipped.name, damage = equipped.damage }, 1) - turtle.equip(side, equipped.name .. ':' .. equipped.damage) - end + if equipped then + turtle.selectOpenSlot() + inventory:provide({ name = equipped.name, damage = equipped.damage }, 1) + turtle.equip(side, equipped.name .. ':' .. equipped.damage) + end - return success, msg + return success, msg end function turtle.canCraft(item, count, items) - return Craft.canCraft(item, count, items) + return Craft.canCraft(item, count, items) end return true diff --git a/farms/apis/level.lua b/farms/apis/level.lua index 1d315be..05eb8b8 100644 --- a/farms/apis/level.lua +++ b/farms/apis/level.lua @@ -9,167 +9,167 @@ local box = { } local oldCallback local function toKey(pt) - return table.concat({ pt.x, pt.y, pt.z }, ':') + return table.concat({ pt.x, pt.y, pt.z }, ':') end local function addNode(node) - for i = 0, 5 do - local hi = turtle.getHeadingInfo(i) - local testNode = { x = node.x + hi.xd, y = node.y + hi.yd, z = node.z + hi.zd } + for i = 0, 5 do + local hi = turtle.getHeadingInfo(i) + local testNode = { x = node.x + hi.xd, y = node.y + hi.yd, z = node.z + hi.zd } - if Point.inBox(testNode, box) then - local key = toKey(testNode) - if not checkedNodes[key] then - nodes[key] = testNode - end - end - end + if Point.inBox(testNode, box) then + local key = toKey(testNode) + if not checkedNodes[key] then + nodes[key] = testNode + end + end + end end local function dig(action) - local directions = { - top = 'up', - bottom = 'down', - } + local directions = { + top = 'up', + bottom = 'down', + } - -- convert to up, down, north, south, east, west - local direction = directions[action.side] or - turtle.getHeadingInfo(turtle.point.heading).direction + -- convert to up, down, north, south, east, west + local direction = directions[action.side] or + turtle.getHeadingInfo(turtle.point.heading).direction - local hi = turtle.getHeadingInfo(direction) - local node = { x = turtle.point.x + hi.xd, y = turtle.point.y + hi.yd, z = turtle.point.z + hi.zd } + local hi = turtle.getHeadingInfo(direction) + local node = { x = turtle.point.x + hi.xd, y = turtle.point.y + hi.yd, z = turtle.point.z + hi.zd } - if Point.inBox(node, box) then + if Point.inBox(node, box) then - local key = toKey(node) - checkedNodes[key] = true - nodes[key] = nil + local key = toKey(node) + checkedNodes[key] = true + nodes[key] = nil - if action.dig() then - addNode(node) - repeat until not action.dig() -- sand, etc - return true - end - end + if action.dig() then + addNode(node) + repeat until not action.dig() -- sand, etc + return true + end + end end local function move(action) - if action == 'turn' then - dig(turtle.getAction('forward')) - elseif action == 'up' then - dig(turtle.getAction('up')) - dig(turtle.getAction('forward')) - elseif action == 'down' then - dig(turtle.getAction('down')) - dig(turtle.getAction('forward')) - elseif action == 'back' then - dig(turtle.getAction('up')) - dig(turtle.getAction('down')) - end + if action == 'turn' then + dig(turtle.getAction('forward')) + elseif action == 'up' then + dig(turtle.getAction('up')) + dig(turtle.getAction('forward')) + elseif action == 'down' then + dig(turtle.getAction('down')) + dig(turtle.getAction('forward')) + elseif action == 'back' then + dig(turtle.getAction('up')) + dig(turtle.getAction('down')) + end - if oldCallback then - oldCallback(action) - end + if oldCallback then + oldCallback(action) + end end -- find the closest block -- * favor same plane -- * going backwards only if the dest is above or below local function closestPoint(reference, pts) - local lpt, lm -- lowest - for _,pt in pairs(pts) do - local m = Point.turtleDistance(reference, pt) - local h = Point.calculateHeading(reference, pt) - local t = Point.calculateTurns(reference.heading, h) - if pt.y ~= reference.y then -- try and stay on same plane - m = m + .01 - end - if t ~= 2 or pt.y == reference.y then - m = m + t - if t > 0 then - m = m + .01 - end - end - if not lm or m < lm then - lpt = pt - lm = m - end - end - return lpt + local lpt, lm -- lowest + for _,pt in pairs(pts) do + local m = Point.turtleDistance(reference, pt) + local h = Point.calculateHeading(reference, pt) + local t = Point.calculateTurns(reference.heading, h) + if pt.y ~= reference.y then -- try and stay on same plane + m = m + .01 + end + if t ~= 2 or pt.y == reference.y then + m = m + t + if t > 0 then + m = m + .01 + end + end + if not lm or m < lm then + lpt = pt + lm = m + end + end + return lpt end local function getAdjacentPoint(pt) - local t = { } - table.insert(t, pt) - for i = 0, 5 do - local hi = turtle.getHeadingInfo(i) - local heading - if i < 4 then - heading = (hi.heading + 2) % 4 - end - table.insert(t, { x = pt.x + hi.xd, z = pt.z + hi.zd, y = pt.y + hi.yd, heading = heading }) - end + local t = { } + table.insert(t, pt) + for i = 0, 5 do + local hi = turtle.getHeadingInfo(i) + local heading + if i < 4 then + heading = (hi.heading + 2) % 4 + end + table.insert(t, { x = pt.x + hi.xd, z = pt.z + hi.zd, y = pt.y + hi.yd, heading = heading }) + end - return closestPoint(turtle.getPoint(), t) + return closestPoint(turtle.getPoint(), t) end function turtle.level(startPt, endPt, firstPt, verbose) - checkedNodes = { } - nodes = { } - box = { } + checkedNodes = { } + nodes = { } + box = { } - box.x = math.min(startPt.x, endPt.x) - box.y = math.min(startPt.y, endPt.y) - box.z = math.min(startPt.z, endPt.z) - box.ex = math.max(startPt.x, endPt.x) - box.ey = math.max(startPt.y, endPt.y) - box.ez = math.max(startPt.z, endPt.z) + box.x = math.min(startPt.x, endPt.x) + box.y = math.min(startPt.y, endPt.y) + box.z = math.min(startPt.z, endPt.z) + box.ex = math.max(startPt.x, endPt.x) + box.ey = math.max(startPt.y, endPt.y) + box.ez = math.max(startPt.z, endPt.z) - if not Point.inBox(firstPt, box) then - error('Starting point is not in leveling area') - end + if not Point.inBox(firstPt, box) then + error('Starting point is not in leveling area') + end - if not turtle.pathfind(firstPt) then - error('failed to reach starting point') - end + if not turtle.pathfind(firstPt) then + error('failed to reach starting point') + end - turtle.set({ - digPolicy = dig, - attackPolicy = 'attack', - movePolicy = 'moveAssured', - }) + turtle.set({ + digPolicy = dig, + attackPolicy = 'attack', + movePolicy = 'moveAssured', + }) - oldCallback = turtle.getMoveCallback() - turtle.setMoveCallback(move) + oldCallback = turtle.getMoveCallback() + turtle.setMoveCallback(move) - repeat - local key = toKey(turtle.point) + repeat + local key = toKey(turtle.point) - checkedNodes[key] = true - nodes[key] = nil + checkedNodes[key] = true + nodes[key] = nil - dig(turtle.getAction('down')) - dig(turtle.getAction('up')) - dig(turtle.getAction('forward')) + dig(turtle.getAction('down')) + dig(turtle.getAction('up')) + dig(turtle.getAction('forward')) - if verbose then - print(string.format('%d nodes remaining', Util.size(nodes))) - end + if verbose then + print(string.format('%d nodes remaining', Util.size(nodes))) + end - if not next(nodes) then - break - end + if not next(nodes) then + break + end - local node = closestPoint(turtle.point, nodes) - node = getAdjacentPoint(node) - if not turtle.go(node) then - break - end - until turtle.isAborted() + local node = closestPoint(turtle.point, nodes) + node = getAdjacentPoint(node) + if not turtle.go(node) then + break + end + until turtle.isAborted() - turtle.resetState() - turtle.setMoveCallback(oldCallback) + turtle.resetState() + turtle.setMoveCallback(oldCallback) end return true diff --git a/farms/attack.lua b/farms/attack.lua index 89e19b1..0119100 100644 --- a/farms/attack.lua +++ b/farms/attack.lua @@ -18,12 +18,12 @@ local Runners = { } Equipper.equipLeft('minecraft:diamond_sword') -local scanner = Equipper.equipRight('plethora:module:2', 'plethora:scanner') +local scanner = Equipper.equipRight('plethora:scanner') local facing = scanner.getBlockMeta(0, 0, 0).state.facing turtle.point.heading = Point.facings[facing].heading -local sensor = Equipper.equipRight('plethora:module:3', 'plethora:sensor') +local sensor = Equipper.equipRight('plethora:sensor') turtle.setMovementStrategy('goto') turtle.set({ attackPolicy = 'attack' }) @@ -32,9 +32,9 @@ local function findChests() if chest then return { chest } end - Equipper.equipRight('plethora:module:2', 'plethora:scanner') + Equipper.equipRight('plethora:scanner') local chests = scanner.scan() - Equipper.equipRight('plethora:module:3', 'plethora:sensor') + Equipper.equipRight('plethora:sensor') Util.filterInplace(chests, function(b) if b.name == 'minecraft:chest' or diff --git a/farms/farmer.lua b/farms/farmer.lua index b2feef8..86a0716 100644 --- a/farms/farmer.lua +++ b/farms/farmer.lua @@ -12,238 +12,241 @@ local STARTUP_FILE = 'usr/autorun/farmer.lua' local MIN_FUEL = 2500 local FUEL = Util.transpose { - 'minecraft:coal:0', - 'minecraft:coal:1', - 'minecraft:blaze_rod:0', + 'minecraft:coal:0', + 'minecraft:coal:1', + 'minecraft:blaze_rod:0', } -local scanner = Equipper.equipRight('plethora:module:2', 'plethora:scanner') +local scanner = Equipper.equipRight('plethora:scanner') local crops = Util.readTable(CONFIG_FILE) or { - ['minecraft:wheat'] = - { seed = 'minecraft:wheat_seeds', mature = 7, action = 'plant' }, - ['minecraft:carrots'] = - { seed = 'minecraft:carrot', mature = 7, action = 'plant' }, - ['minecraft:potatoes'] = - { 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' }, - ['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 = 'smash' }, + ['minecraft:wheat'] = + { seed = 'minecraft:wheat_seeds', mature = 7, action = 'plant' }, + ['minecraft:carrots'] = + { seed = 'minecraft:carrot', mature = 7, action = 'plant' }, + ['minecraft:potatoes'] = + { 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' }, + ['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 = 'smash' }, } if not fs.exists(CONFIG_FILE) then - Util.writeTable(CONFIG_FILE, crops) + Util.writeTable(CONFIG_FILE, crops) end if not fs.exists(STARTUP_FILE) then - Util.writeFile(STARTUP_FILE, - [[os.sleep(1) + Util.writeFile(STARTUP_FILE, + [[os.sleep(1) shell.openForegroundTab('farmer.lua')]]) - print('Autorun program created: ' .. STARTUP_FILE) + print('Autorun program created: ' .. STARTUP_FILE) end local retain = Util.transpose { - "minecraft:diamond_pickaxe", - "plethora:module:2", - "plethora:module:3", + "minecraft:diamond_pickaxe", + "plethora:module:2", + "plethora:module:3", } for _, v in pairs(crops) do - if v.seed then - retain[v.seed] = true - end + if v.seed then + retain[v.seed] = true + end end local function scan() - local blocks = scanner.scan() - local summed = turtle.getSummedInventory() - local doDropOff + local blocks = scanner.scan() + local summed = turtle.getSummedInventory() + local doDropOff - for _,v in pairs(summed) do - if v.count > 32 then - doDropOff = true - break - end - end + for _,v in pairs(summed) do + if v.count > 32 then + doDropOff = true + break + end + end - Util.filterInplace(blocks, function(b) - b.action = crops[b.name] and crops[b.name].action + Util.filterInplace(blocks, function(b) + b.action = crops[b.name] and crops[b.name].action - if b.action == 'bash' then - return b.y == 0 - end - if b.action == 'drop' then - return doDropOff and (b.y == -1 or 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 == 'bash' then + return b.y == 0 + end + if b.action == 'drop' then + return doDropOff and (b.y == -1 or 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 - if b.action == 'pick' then - return b.y == 0 and b.state.age == 2 - end - 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) + if b.action == 'smash' then + return b.y == -1 + end + if b.action == 'pick' then + return b.y == 0 and b.state.age == 2 + end + if b.action == 'bump' then + return b.y == 0 + end + if b.action == 'plant' and b.y == -1 then + if not b.metadata then -- minecraft 1.10 + b = scanner.getBlockMeta(b.x, b.y, b.z) + end + return b.metadata == crops[b.name].mature + end + end) - local harvestCount = 0 - 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 + local harvestCount = 0 + 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 - return blocks, harvestCount + return blocks, harvestCount end local function harvest(blocks) - Equipper.equipRight('minecraft:diamond_pickaxe') + Equipper.equipRight('minecraft:diamond_pickaxe') - local dropped + local dropped - Point.eachClosest(turtle.point, blocks, function(b) - turtle.select(1) + Point.eachClosest(turtle.point, blocks, function(b) + turtle.select(1) - if b.action == 'bash' then - turtle.digForwardAt(b) + if b.action == 'bash' then + turtle.digForwardAt(b) - elseif b.action == 'drop' and not dropped then - if turtle.go(b.y == 1 and Point.below(b) or Point.above(b)) then - local dropFn = b.y == 1 and turtle.dropUp or turtle.dropDown - local dropDir = b.y == 1 and 'down' or 'up' + elseif b.action == 'drop' and not dropped then + if turtle.go(b.y == 1 and Point.below(b) or Point.above(b)) then + local dropFn = b.y == 1 and turtle.dropUp or turtle.dropDown + local dropDir = b.y == 1 and 'down' or 'up' - turtle.eachFilledSlot(function(slot) - if not retain[slot.name] and not retain[slot.key] then - turtle.select(slot.index) - dropFn() - end - end) - local summed = turtle.getSummedInventory() - for k,v in pairs(summed) do - if v.count > 16 then - dropFn(k, v.count - 16) - end - end + turtle.eachFilledSlot(function(slot) + if not retain[slot.name] and not retain[slot.key] then + turtle.select(slot.index) + dropFn() + end + end) + local summed = turtle.getSummedInventory() + for k,v in pairs(summed) do + if v.count > 16 then + dropFn(k, v.count - 16) + end + end - dropped = true - turtle.condense() + dropped = true + turtle.condense() - if turtle.getFuelLevel() < MIN_FUEL then - local inv = peripheral.wrap('bottom') - if inv and inv.list then - for k, v in pairs(inv.list()) do - if FUEL[table.concat({ v.name, v.damage }, ':')] then - local count = inv.pushItems(dropDir, k, v.count) - if count > 0 then - turtle.refuel(v.name, v.count) - print('Fuel: ' .. turtle.getFuelLevel()) - break - end - end - end - end - end - end + if turtle.getFuelLevel() < MIN_FUEL then + local inv = peripheral.wrap('bottom') + if inv and inv.list then + for k, v in pairs(inv.list()) do + if FUEL[table.concat({ v.name, v.damage }, ':')] then + local count = inv.pushItems(dropDir, k, v.count) + if count > 0 then + turtle.refuel(v.name, v.count) + print('Fuel: ' .. turtle.getFuelLevel()) + break + end + end + end + end + end + end - elseif b.action == 'smash' then - if turtle.digDownAt(b) then - if crops[b.name].seed then - turtle.placeDown(crops[b.name].seed) - end - end + elseif b.action == 'smash' then + if turtle.digDownAt(b) then + if crops[b.name].seed then + turtle.placeDown(crops[b.name].seed) + end + end - elseif b.action == 'plant' then - if turtle.digDownAt(b) then - turtle.placeDown(crops[b.name].seed) - end + elseif b.action == 'plant' then + if turtle.digDownAt(b) then + turtle.placeDown(crops[b.name].seed) + end - elseif b.action == 'bump' then - if turtle.faceAgainst(b) then - Equipper.equipRight('plethora:module:3', 'plethora:sensor') - os.sleep(.5) - -- search the ground for the dropped cactus - local sensed = peripheral.call('right', 'sense') - Equipper.equipRight('minecraft:diamond_pickaxe') - Util.filterInplace(sensed, function(s) - 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 - if Point.distance(b, s) < 6 then - return true - end - end - end) - Point.eachClosest(turtle.point, sensed, function(s) - turtle.suckDownAt(s) - end) - end + elseif b.action == 'bump' then + if turtle.faceAgainst(b) then + Equipper.equipRight('plethora:sensor') + os.sleep(.5) + -- search the ground for the dropped cactus + local sensed = peripheral.call('right', 'sense') + Equipper.equipRight('minecraft:diamond_pickaxe') + Util.filterInplace(sensed, function(s) + 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 + if Point.distance(b, s) < 6 then + return true + end + end + end) + Point.eachClosest(turtle.point, sensed, function(s) + turtle.suckDownAt(s) + end) + end - elseif b.action == 'pick' then - local h = Point.facings[b.state.facing].heading - local hi = Point.headings[(h + 2) % 4] -- opposite heading + 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.go({ x = b.x + hi.xd, z = b.z + hi.zd, heading = h }) then - if turtle.dig() then - turtle.place(crops[b.name].seed) - end - end - end - end) - Equipper.equipRight('plethora:module:2', 'plethora:scanner') + -- without pathfinding, will be unable to circle log + if turtle.go({ x = b.x + hi.xd, z = b.z + hi.zd, heading = h }) then + if turtle.dig() then + turtle.place(crops[b.name].seed) + end + end + end + end) + Equipper.equipRight('plethora:scanner') end local s, m = turtle.run(function() - local facing = scanner.getBlockMeta(0, 0, 0).state.facing - turtle.point.heading = Point.facings[facing].heading + local facing = scanner.getBlockMeta(0, 0, 0).state.facing + turtle.point.heading = Point.facings[facing].heading - turtle.setStatus('farming') - print('Fuel: ' .. turtle.getFuelLevel()) + turtle.setStatus('farming') + print('Fuel: ' .. turtle.getFuelLevel()) - turtle.setMovementStrategy('goto') - repeat - local blocks, harvestCount = scan() - if harvestCount > 0 then - turtle.setStatus('Harvesting') - harvest(blocks) - turtle.setStatus('Sleeping') - end - os.sleep(10) - if turtle.getFuelLevel() < 10 then - turtle.setStatus('Out of fuel') - error('Out of fuel') - end - until turtle.isAborted() + turtle.setMovementStrategy('goto') + repeat + local blocks, harvestCount = scan() + if harvestCount > 0 then + turtle.setStatus('Harvesting') + harvest(blocks) + turtle.setStatus('Sleeping') + end + os.sleep(10) + if turtle.getFuelLevel() < 10 then + turtle.setStatus('Out of fuel') + error('Out of fuel') + end + until turtle.isAborted() end) if not s and m then - error(m) + error(m) end diff --git a/farms/help/superTreefarm b/farms/help/superTreefarm index f4f7ad6..3e2cabf 100644 --- a/farms/help/superTreefarm +++ b/farms/help/superTreefarm @@ -9,8 +9,9 @@ Requirements * Standard Modem * Block Scanner * Entity Sensor -* Crafting Table +* Furnace * Vanilla Chest +* Sapling * GPS Setup @@ -19,18 +20,20 @@ Setup > package install farms > reboot +The turtle will need some fuel initially. + The tree farm fits exactly in one chunk. It's best to have a mostly level ground around the center of the farming area as the turtle will only collect saplings that have fallen to the same level as the turtle. To align the turtle perfectly in one chunk, position the turtle 8 blocks diagonally from the bottom left corner. -Place a sapling directly in front of the turtle and place all the required items into the inventory. +Place all the required items into the inventory. To start the program, run: > superTreefarm A startup file is created automatically the first time the program is run (usr/autorun/superTreefarm.lua). -If the turtle does not get any saplings from the initial tree, place down another sapling in front of the turtle. +If the turtle does not get any saplings from the initial tree, another sapling in the turtle. Tips ==== diff --git a/farms/rancher.lua b/farms/rancher.lua index 716a597..c29b95b 100644 --- a/farms/rancher.lua +++ b/farms/rancher.lua @@ -10,111 +10,117 @@ local turtle = _G.turtle local STARTUP_FILE = 'usr/autorun/rancher.lua' local retain = Util.transpose { - 'minecraft:shears', - 'minecraft:wheat', - 'minecraft:diamond_sword', - 'plethora:module:3', + 'minecraft:shears', + 'minecraft:wheat', + 'minecraft:diamond_sword', + 'plethora:module:3', } local config = { - animal = 'Cow', - max_animals = 15, + animal = 'Cow', + max_animals = 15, } Config.load('rancher', config) local ANIMALS = { - Pig = { min = 0, food = 'minecraft:carrot' }, - Sheep = { min = .5, food = 'minecraft:wheat' }, - Cow = { min = .5, food = 'minecraft:wheat' }, + Pig = { min = 0, food = 'minecraft:carrot' }, + Sheep = { min = .5, food = 'minecraft:wheat' }, + Cow = { min = .5, food = 'minecraft:wheat' }, } local animal = ANIMALS[config.animal] Equipper.equipLeft('minecraft:diamond_sword') -local sensor = Equipper.equipRight('plethora:module:3', 'plethora:sensor') +local sensor = Equipper.equipRight('plethora:sensor') local chest = Adapter({ side = 'bottom', direction = 'up' }) or error('missing chest') if not fs.exists(STARTUP_FILE) then - Util.writeFile(STARTUP_FILE, - [[os.sleep(1) + Util.writeFile(STARTUP_FILE, + [[os.sleep(1) shell.openForegroundTab('rancher.lua')]]) - print('Autorun program created: ' .. STARTUP_FILE) + print('Autorun program created: ' .. STARTUP_FILE) end local function getAnimalCount() - local blocks = sensor.sense() + local blocks = sensor.sense() - local grown = 0 - local babies = 0 + local grown = 0 + local babies = 0 - Util.filterInplace(blocks, function(v) - 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 - end - end) + Util.filterInplace(blocks, function(v) + if v.name == config.animal then + local entity = sensor.getMetaByID(v.id) + if entity then + if entity.isChild then + babies = babies + 1 + else + grown = grown + 1 + end + return not entity.isChild + end + end + end) - Util.print('%d grown, %d babies', grown, babies) + Util.print('%d grown, %d babies', grown, babies) - return #blocks + return #blocks end local function butcher() - Equipper.equipRight('minecraft:diamond_sword') - turtle.select(1) + Equipper.equipRight('minecraft:diamond_sword') + turtle.select(1) - turtle.attack() - for _ = 1, 3 do - turtle.turnRight() - turtle.attack() - end - Equipper.equipRight('plethora:module:3', 'plethora:sensor') + turtle.attack() + for _ = 1, 3 do + turtle.turnRight() + turtle.attack() + end + Equipper.equipRight('plethora:sensor') - turtle.eachFilledSlot(function(slot) - if not retain[slot.name] then - chest:insert(slot.index, 64) - end - end) + turtle.eachFilledSlot(function(slot) + if not retain[slot.name] then + chest:insert(slot.index, 64) + end + end) end local function breed() - turtle.select(1) + 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 + 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 local s, m = turtle.run(function() - print('Configured animal: ' .. config.animal) + print('Configured animal: ' .. config.animal) - repeat - local animalCount = getAnimalCount() - if animalCount > config.max_animals then - turtle.setStatus('Butchering') - butcher() - 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') - breed() - end - os.sleep(5) - until turtle.isAborted() + repeat + local animalCount = getAnimalCount() + if animalCount > config.max_animals then + turtle.setStatus('Butchering') + butcher() + 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') + breed() + end + os.sleep(5) + until turtle.isAborted() end) if not s and m then - error(m) + error(m) end diff --git a/farms/spawner.lua b/farms/spawner.lua index 3fd3302..1180553 100644 --- a/farms/spawner.lua +++ b/farms/spawner.lua @@ -11,7 +11,7 @@ local STARTUP_FILE = 'usr/autorun/spawner.lua' local mobTypes = { } Equipper.equipLeft('minecraft:diamond_sword') -local scanner = Equipper.equipRight('plethora:module:2', 'plethora:scanner') +local scanner = Equipper.equipRight('plethora:scanner') turtle.reset() local facing = scanner.getBlockMeta(0, 0, 0).state.facing @@ -28,13 +28,13 @@ Util.filterInplace(data, function(b) end) local chest = Point.closest(spawner, data) or error('missing drop off chest') -local sensor = Equipper.equipRight('plethora:module:3', 'plethora:sensor') +local sensor = Equipper.equipRight('plethora:sensor') if not fs.exists(STARTUP_FILE) then - Util.writeFile(STARTUP_FILE, - string.format([[os.sleep(1) + Util.writeFile(STARTUP_FILE, + string.format([[os.sleep(1) shell.openForegroundTab('spawner.lua %s')]], table.concat({ ... }, ' '))) - print('Autorun program created: ' .. STARTUP_FILE) + print('Autorun program created: ' .. STARTUP_FILE) end turtle.setMovementStrategy('goto') @@ -70,8 +70,8 @@ local function normalize(b) b.z = Util.round(b.z) + turtle.point.z return b.x >= spawner.x - 4 and b.x <= spawner.x + 4 and - b.y >= spawner.y - 4 and b.y <= spawner.y + 4 and - b.z >= spawner.z - 4 and b.z <= spawner.z + 4 + b.y >= spawner.y - 4 and b.y <= spawner.y + 4 and + b.z >= spawner.z - 4 and b.z <= spawner.z + 4 end local function aboveAttack(b) diff --git a/farms/superTreefarm.lua b/farms/superTreefarm.lua index 7867f56..c08c8a2 100644 --- a/farms/superTreefarm.lua +++ b/farms/superTreefarm.lua @@ -10,777 +10,528 @@ local turtle = _G.turtle local STARTUP_FILE = 'usr/autorun/superTreefarm.lua' -local FUEL_BASE = 0 -local FUEL_DIRE = FUEL_BASE + 10 -local FUEL_GOOD = FUEL_BASE + 2000 +local FUEL_DIRE = 10 +local FUEL_GOOD = 1000 local MIN_CHARCOAL = 24 local MIN_SAPLINGS = 32 local MAX_SAPLINGS = 48 +local RADIUS_X = 2 +local RADIUS_Z = 3 + local GRID = { - TL = { x = 8, y = 0, z = -7 }, - TR = { x = 8, y = 0, z = 8 }, - BL = { x = -7, y = 0, z = -7 }, - BR = { x = -7, y = 0, z = 8 }, + TL = { x = 8, y = 0, z = -7 }, + TR = { x = 8, y = 0, z = 8 }, + BL = { x = -7, y = 0, z = -7 }, + BR = { x = -7, y = 0, z = 8 }, } local HOME_PT = { x = 0, y = 0, z = 0, heading = 0 } -local DIG_BLACKLIST = { - [ 'minecraft:furnace' ] = true, - [ 'minecraft:lit_furnace' ] = true, - [ 'minecraft:chest' ] = true, -} - -local APPLE = 'minecraft:apple:0' local CHARCOAL = 'minecraft:coal:1' local CHEST = 'minecraft:chest:0' -local COBBLESTONE = 'minecraft:cobblestone:0' -local CRAFTING_TABLE = 'minecraft:crafting_table:0' -local PICKAXE = 'minecraft:diamond_pickaxe' local DIRT = 'minecraft:dirt:0' +local PICKAXE = 'minecraft:diamond_pickaxe' local FURNACE = 'minecraft:furnace:0' -local MODEM = 'computercraft:peripheral:1' local LOG = 'minecraft:log' local LOG2 = 'minecraft:log2' -local OAK_LOG = 'minecraft:log:0' -local OAK_PLANK = 'minecraft:planks:0' local OAK_SAPLING = 'minecraft:sapling:0' local SAPLING = 'minecraft:sapling' local SCANNER = 'plethora:module:2' local SENSOR = 'plethora:module:3' -local STICK = 'minecraft:stick:0' -local STONE = 'minecraft:stone:0' -local TORCH = 'minecraft:torch:0' -local ALL_SAPLINGS = { } - -local state = Util.readTable('usr/config/superTreefarm') or { - trees = { - { x = 1, y = 0, z = 0 } - } +local retain = Util.transpose { + PICKAXE, + CHARCOAL, + SAPLING, + SCANNER, + SENSOR, } +local state = Util.readTable('usr/config/superTreefarm') or { } + local clock = os.clock() -local function equip(side, item, rawName) - -- is it already equipped on the correct side? - local equipped = peripheral.getType(side) - if equipped == item then - return true - end - - -- is it equipped on the opposite side? - -- will not work for non-peripheral items :( - local osides = { left = 'right', right = 'left' } - if peripheral.getType(osides[side]) == item then - if not turtle.selectSlotWithQuantity(0) then - error('No slots available') - end - turtle.equip(osides[side]) - - elseif not turtle.has(rawName or item) then - -- don't have the item - unequip that side to see if it's the correct item - if not turtle.selectSlotWithQuantity(0) then - error('No slots available') - end - turtle.equip(side) - end - - -- TODO: if the non-peripheral item was equipped on the other side, then this will not work - - if not turtle.has(rawName or item) then - error('Missing ' .. (rawName or item)) - end - - if not turtle.equip(side, rawName or item) then - error('Unable to equip ' .. (rawName or item)) - end - - turtle.select(1) -end - -local function inspect(fn) - local s, item = fn() - if s and item then - return item.name .. ':' .. item.metadata - end - return 'minecraft:air:0' -end - local function setState(key, value) - state[key] = value - Util.writeTable('usr/config/superTreefarm', state) + state[key] = value + Util.writeTable('usr/config/superTreefarm', state) end local function refuel() - if turtle.getFuelLevel() < FUEL_GOOD then - local charcoal = turtle.getItemCount(CHARCOAL) - if charcoal > 1 then - turtle.refuel(CHARCOAL, math.min(charcoal - 1, MIN_CHARCOAL / 2)) - print('fuel: ' .. turtle.getFuelLevel()) - end - end - return true -end - -local function safePlaceBlock(item) - - if turtle.placeUp(item) then - return true - end - - local s, m = turtle.inspectUp() - if s and not DIG_BLACKLIST[m.name] then - turtle.digUp() - return turtle.placeUp(item) - end - - turtle.forward() - return turtle.placeUp(item) -end - -local function craftItem(item, qty) - local success, msg - - if safePlaceBlock(CHEST) then - - os.sleep(.2) -- needed for minecraft 1.12 - Util.print('Crafting %d %s', (qty or 1), item) - success, msg = turtle.craftItem(item, qty or 1, { - side = 'top', - direction = 'down', - }) - repeat until not turtle.suckUp() - - if not success then - print(msg) - end - - turtle.digUp() - end - - 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 - fuelCount = fuelCount or math.ceil(count / 8) - Util.print('Making %d %s', count, result) - - turtle.dropForwardAt(state.furnace, fuel, fuelCount) - turtle.dropDownAt(state.furnace, item, count) - - count = count + turtle.getItemCount(result) - turtle.select(1) - turtle.pathfind(Point.below(state.furnace)) - - local lastSuck = os.clock() - repeat - os.sleep(1) - 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') -end - -local function makeSingleCharcoal() - - local slots = turtle.getSummedInventory() - - if not state.furnace or - slots[CHARCOAL] or - not slots[OAK_LOG] or - slots[OAK_LOG].count < 2 then - return true - end - - turtle.faceAgainst(state.furnace) - if craftItem(OAK_PLANK) then - cook(OAK_LOG, 1, CHARCOAL, OAK_PLANK, 1) - turtle.refuel(OAK_PLANK) - end - - return true + if turtle.getFuelLevel() < FUEL_GOOD then + local charcoal = turtle.getItemCount(CHARCOAL) + --if charcoal > 1 then + turtle.refuel(CHARCOAL, math.min(charcoal, MIN_CHARCOAL / 2)) + print('fuel: ' .. turtle.getFuelLevel()) + --end + end + return true end local function makeCharcoal() + local slots = turtle.getSummedInventory() - local slots = turtle.getSummedInventory() + local function getLogSlot() + local maxslot = { count = 0 } + for k,slot in pairs(slots) do + if string.match(k, 'minecraft:log') then + if slot.count > maxslot.count then + maxslot = slot + end + end + end + return maxslot + end - if not state.furnace or - not slots[CHARCOAL] or - slots[CHARCOAL].count >= MIN_CHARCOAL then - return true - end + if turtle.pathfind(Point.above(state.furnace)) then + pcall(function() + local f = peripheral.wrap('bottom') + local inv = f.list() - local function getLogSlot() - local maxslot = { count = 0 } - for k,slot in pairs(slots) do - if string.match(k, 'minecraft:log') then - if slot.count > maxslot.count then - maxslot = slot - end - end - end - return maxslot - end + if inv[3] and + (not slots[CHARCOAL] or + slots[CHARCOAL].count < MIN_CHARCOAL) then + f.pushItems('up', 3, 24) + end - repeat - slots = turtle.getSummedInventory() + if turtle.has(CHARCOAL) and turtle.getFuelLevel() > 100 then + local count = inv[2] and inv[2].count or 0 + if count < 8 then + f.pullItems('up', turtle.getSlot(CHARCOAL).index, 8-count, 2) + end + else + slots = turtle.getSummedInventory() + local slot = getLogSlot(slots) + if slot.count > 0 then + local s = turtle.getSlot(slot.key) + f.pullItems('up', s.index, 1, 2) + end + end - local charcoal = slots[CHARCOAL].count - local slot = getLogSlot(slots) + local count = inv[1] and inv[1].count or 0 + if count < 32 then + for key, slot in pairs(turtle.getSummedInventory()) do + if string.match(key, 'minecraft:log') then + if turtle.dropDown(key, 32-count) then + count = count + slot.count + if count >= 32 then + break + end + end + end + end + end + end) + end - if slot.count < 8 then - break - end - - local toCook = math.min(charcoal, math.floor(slot.count / 8)) - toCook = math.min(toCook, math.floor((MIN_CHARCOAL + 8 - charcoal) / 8)) - toCook = toCook * 8 - - cook(slot.key, toCook, CHARCOAL) - - until charcoal + toCook >= MIN_CHARCOAL - - return true -end - -local function getCobblestone(count) - - local slots = turtle.getSummedInventory() - - if not slots[COBBLESTONE] or slots[COBBLESTONE].count < count then - - print('Collecting cobblestone') - - slots[COBBLESTONE] = true - slots[DIRT] = true - - local pt = Point.copy(GRID.BR) - pt.x = GRID.BR.x + 2 - pt.z = GRID.BR.z - 2 - - turtle.pathfind(pt) - - repeat - turtle.select(1) - turtle.digDown() - turtle.down() - for _ = 1, 4 do - if inspect(turtle.inspect) == STONE then - turtle.dig() - end - turtle.turnRight() - end - - for item in pairs(turtle.getSummedInventory()) do - if not slots[item] then - turtle.drop(item) - end - end - - until turtle.getItemCount(COBBLESTONE) >= count - - turtle.go(pt) - turtle.placeDown(DIRT) - - turtle.drop(DIRT) - end + return true end local function createFurnace() - if not state.furnace then - if turtle.getFuelLevel() < FUEL_BASE + 100 then - return true -- try again later - end - print('Adding a furnace') - if not turtle.has(FURNACE) then - getCobblestone(8) - end + if not state.furnace then + if not turtle.has(FURNACE) then + error('Turtle must have a furnace') + end - if turtle.has(FURNACE) or craftItem(FURNACE) then - turtle.drop(COBBLESTONE) - local furnacePt = { x = GRID.BL.x + 1, y = 1, z = GRID.BL.z + 1 } - turtle.placeAt(furnacePt, FURNACE) - setState('furnace', furnacePt) - end - end - turtle.addWorldBlock(state.furnace) + print('Adding a furnace') + local pt = Point.below(HOME_PT) + if not turtle.placeDownAt(pt, FURNACE) then + error('Error placing furnace') + end + setState('furnace', pt) + end end local function createChests() - if state.chest then - return - end - if turtle.getFuelLevel() > FUEL_GOOD and - turtle.canCraft(CHEST, 4, turtle.getSummedInventory()) then + if not state.chest and turtle.getFuelLevel() > 1 then + if not turtle.has(CHEST) then + error('Turtle must have a chest') + end - print('Adding storage') - if turtle.has(CHEST, 2) or craftItem(CHEST, 2) then + print('Adding storage') - local pt = Point.copy(GRID.BL) - pt.x = pt.x + 1 - pt.y = pt.y - 1 + local pt = Point.below(HOME_PT) + pt.x = pt.x - 1 - pt.z = pt.z + 1 + if not turtle.placeDownAt(pt, CHEST) then + error('Error placing chest') + end + setState('chest', pt) - turtle.digDownAt(pt) - turtle.placeDown(CHEST) + turtle.dropDown(DIRT) + end + return true +end - pt.z = pt.z + 1 +local function getSaplings() + local slots = turtle.getSummedInventory() + local saplings = { } - turtle.digDownAt(pt) - turtle.placeDown(CHEST) + for _, slot in pairs(slots) do + if slot.name == SAPLING then + table.insert(saplings, slot) + end + end - setState('chest', Util.shallowCopy(pt)) + if #saplings == 0 then + table.insert(saplings, { name = OAK_SAPLING, count = 0 }) + end - turtle.drop(DIRT) - turtle.refuel(OAK_PLANK) - end - end - return true + return saplings end local function dropOffItems() + local slots = turtle.getSummedInventory() - if state.chest then - local slots = turtle.getSummedInventory() + if state.chest and + slots[CHARCOAL] and + slots[CHARCOAL].count >= MIN_CHARCOAL and + (turtle.getItemCount(LOG) > 16 or + turtle.getItemCount(LOG2) > 16) then - if state.chest and - slots[CHARCOAL] and - slots[CHARCOAL].count >= MIN_CHARCOAL and - (turtle.getItemCount(LOG) > 16 or - turtle.getItemCount(LOG2) > 16) then + print('Storing logs') + turtle.pathfind(Point.above(state.chest)) - print('Storing logs') - turtle.pathfind(Point.above(state.chest)) - turtle.dropDown(LOG) - turtle.dropDown(LOG2) + for k,v in pairs(turtle.getInventory()) do + if v.count > 0 and not retain[v.name] and not retain[v.key] then + turtle.select(k) + turtle.dropDown() + end + end + end - for _, sapling in pairs(ALL_SAPLINGS) do - if sapling.count > MAX_SAPLINGS then - turtle.dropDown(sapling.key, sapling.count - MAX_SAPLINGS) - end - end - - turtle.dropDown(APPLE) - end - end - - return true + return true end local function eatSaplings() - Util.each(ALL_SAPLINGS, function(sapling) - if sapling.count > MAX_SAPLINGS then - turtle.refuel(sapling.key, sapling.count - MAX_SAPLINGS) - end - end) - return true -end - -local function placeTorches() - if state.torches then - return - end - - local slots = turtle.getSummedInventory() - if turtle.getFuelLevel() > 100 and - slots[CHARCOAL] and - slots[CHARCOAL].count >= MIN_CHARCOAL and - turtle.canCraft(TORCH, 4, slots) then - - print('Placing torches') - - if turtle.has(TORCH, 4) or craftItem(TORCH, 4) then - local pts = { } - for x = -4, 4, 8 do - for z = -4, 4, 8 do - table.insert(pts, { x = x, y = 0, z = z }) - end - end - turtle.addWorldBlocks(pts) - Point.eachClosest(turtle.point, pts, function(pt) - turtle.placeDownAt(pt, TORCH) - end) - turtle.refuel(STICK) - turtle.refuel(OAK_PLANK) - setState('torches', pts) - end - end - - return true -end - -local function countSaplings() - local slots = turtle.getSummedInventory() - ALL_SAPLINGS = { } - - for _, slot in pairs(slots) do - if slot.name == SAPLING then - table.insert(ALL_SAPLINGS, slot) - end - end - if #ALL_SAPLINGS == 0 then - table.insert(ALL_SAPLINGS, { name = OAK_SAPLING, count = 0 }) - end - - return true + Util.each(getSaplings(), function(sapling) + if sapling.count > MAX_SAPLINGS then + turtle.refuel(sapling.key, sapling.count - MAX_SAPLINGS) + end + end) + return true end local function randomSapling() - local sapling = ALL_SAPLINGS[math.random(1, #ALL_SAPLINGS)] + local saplings = getSaplings() + local sapling = saplings[math.random(1, #saplings)] - if sapling.count > 0 then - sapling.count = sapling.count - 1 - return sapling.key - end + if sapling.count > 0 then + return sapling.key + end end local function makeKey(b) - return table.concat({ b.x, b.y, b.z }, ':') + return table.concat({ b.x, b.y, b.z }, ':') end local function findDroppedSaplings() - Equipper.equipLeft(SENSOR, 'plethora:sensor') - local raw = peripheral.call('left', 'sense') + local sensor = Equipper.equipLeft('plethora:sensor') + local raw = sensor.sense() - local sensed = Util.reduce(raw, function(acc, b) - Point.rotate(b, state.home.heading) - b.x = Util.round(b.x) + turtle.point.x - b.y = math.ceil(b.y) + turtle.point.y - b.z = Util.round(b.z) + turtle.point.z - if b.y == 0 and string.find(b.displayName, 'sapling', 1, true) then - b.sapling = true - acc[makeKey(b)] = b - end - return acc - end, { }) - - return sensed + return Util.reduce(raw, function(acc, b) + Point.rotate(b, state.home.heading) + b.x = Util.round(b.x) + turtle.point.x + b.y = math.ceil(b.y) + turtle.point.y + b.z = Util.round(b.z) + turtle.point.z + if b.y == 0 and string.find(b.displayName, 'sapling', 1, true) then + b.sapling = true + acc[makeKey(b)] = b + end + return acc + end, { }) end local function scan(pt, filter, blocks) - turtle.pathfind(pt) + turtle.pathfind(pt) - Equipper.equipLeft(SCANNER, 'plethora:scanner') - local raw = peripheral.call('left', 'scan') + local scanner = Equipper.equipLeft('plethora:scanner') + local raw = scanner.scan() - return Util.reduce(raw, function(acc, b) - if b.y >= 0 then - Point.rotate(b, state.home.heading) - b.x = b.x + turtle.point.x - b.y = b.y + turtle.point.y - b.z = b.z + turtle.point.z - if filter(b) then - acc[makeKey(b)] = b - end - end - return acc - end, blocks or { }) + return Util.reduce(raw, function(acc, b) + if b.y >= 0 then + Point.rotate(b, state.home.heading) + b.x = b.x + turtle.point.x + b.y = b.y + turtle.point.y + b.z = b.z + turtle.point.z + if filter(b) then + acc[makeKey(b)] = b + end + end + return acc + end, blocks or { }) end local function getPlantLocations(blocks) - countSaplings() + for _,sapling in pairs(state.trees) do + local key = makeKey(sapling) + local b = blocks[key] + if b then + if b.name == SAPLING then + blocks[key] = nil + else + b.plant = true + end + elseif turtle.getFuelLevel() > 100 or sapling.x == 1 and sapling.z == 0 then + b = Util.shallowCopy(sapling) + b.plant = true + blocks[key] = b + end + end +end - Util.each(state.trees, function(sapling) - local key = makeKey(sapling) - local b = blocks[key] - if b then - if b.name == SAPLING then - blocks[key] = nil - else - b.plant = randomSapling() - end - return - end - b = Util.shallowCopy(sapling) - b.plant = randomSapling() - if b.plant then - blocks[key] = b - end - end) +local function desperateRefuel() + local fuels = { CHARCOAL, LOG, LOG2 } + + if turtle.getFuelLevel() < FUEL_DIRE then + while true do + for _, fuel in pairs(fuels) do + if turtle.has(fuel) then + turtle.refuel(fuel, 1) + print('fuel: ' .. turtle.getFuelLevel()) + turtle.select(1) + break + end + end + if turtle.getFuelLevel() > 0 then + break + end + print('Out of fuel') + print('Add logs or charcoal to turtle') + os.pullEvent('turtle_inventory') + end + end end local function fellTrees(blocks) - local function desperateRefuel(min) - if turtle.getFuelLevel() < min then - local logs = turtle.getItemCount(OAK_LOG) - if logs > 0 then - if craftItem(OAK_PLANK, math.min(8, logs * 4)) then - turtle.refuel(OAK_PLANK) - print('fuel: ' .. turtle.getFuelLevel()) - end - end - end - end + turtle.select(1) - turtle.setMoveCallback(function() desperateRefuel(FUEL_DIRE) end) + for pt in Point.iterateClosest(turtle.point, blocks) do + -- initial tree + if turtle.getFuelLevel() == 0 then + if not turtle.digAt(pt) then + break + end + desperateRefuel() + end - desperateRefuel(FUEL_DIRE) + if pt.y == 0 then + if pt.sapling then + repeat until not turtle.suckDownAt(pt) + elseif pt.plant then + local s = randomSapling() - if turtle.point.y == 0 then - if #state.trees == 1 and turtle.getFuelLevel() == 0 then - turtle.dig() - end - turtle.up() - end - for pt in Point.iterateClosest(turtle.point, blocks) do - if pt.y == 0 then - if pt.sapling then - repeat until not turtle.suckDownAt(pt) - else - turtle.digDownAt(pt) - if pt.plant then - turtle.placeDown(pt.plant) - turtle.select(1) - end - end - else - turtle.digAt(pt) - end - end + if pt.name and pt.name ~= SAPLING then + turtle.digDownAt(pt) + end + if s then + turtle.placeDownAt(pt, s) + turtle.select(1) + end + end + else + turtle.digAt(pt) + end + os.queueEvent('canvas', { + type = 'canvas_remove', + data = { pt }, + }) + end - desperateRefuel(FUEL_BASE + 100) - turtle.clearMoveCallback() + turtle.pathfind(HOME_PT) - return true + return true end local function fell() - local function filter(b) - return b.y >= 0 and (b.name == LOG or b.name == LOG2 or b.name == SAPLING) - end + local function filter(b) + return b.name == LOG or b.name == LOG2 or b.name == SAPLING + end - local fuel = turtle.getFuelLevel() - local sensed = { } + local fuel = turtle.getFuelLevel() + local sensed = { } - -- determine if we need saplings - if not Util.every(ALL_SAPLINGS, function(sapling) - return sapling.count >= MIN_SAPLINGS - end) then - sensed = findDroppedSaplings() - end + -- determine if we need saplings + if not Util.every(getSaplings(), function(sapling) + return sapling.count >= MIN_SAPLINGS + end) then + sensed = findDroppedSaplings() + end - -- low scan - local blocks = scan(HOME_PT, filter) + -- low scan + local blocks = scan(HOME_PT, filter) + local pt = Util.shallowCopy(HOME_PT) + while Util.any(blocks, function(b) return b.y > pt.y + 6 end) do + -- tree might be above low scan range, do a scan higher up + Equipper.equipLeft(PICKAXE) + pt.y = pt.y + 8 + blocks = scan(pt, filter, blocks) + end - local pt = Util.shallowCopy(HOME_PT) - while Util.any(blocks, function(b) return b.y > pt.y + 6 end) do - -- tree might be above low scan range, do a scan higher up - Equipper.equipLeft(PICKAXE) - pt.y = pt.y + 8 - blocks = scan(pt, filter, blocks) - end + Util.merge(blocks, sensed) - Util.merge(blocks, sensed) + -- add any locations that need saplings + getPlantLocations(blocks) - -- add any locations that need saplings - getPlantLocations(blocks) + Equipper.equipLeft(PICKAXE) - Equipper.equipLeft(PICKAXE) - if not Util.empty(blocks) then - print('Chopping') + os.queueEvent('canvas', { + type = 'canvas_update', + data = blocks, + }) + if not Util.empty(blocks) then + print('Chopping') - fellTrees(blocks) + fellTrees(blocks) - print('Used ' .. (fuel - turtle.getFuelLevel()) .. ' fuel') - end + print('Used ' .. (fuel - turtle.getFuelLevel()) .. ' fuel') + end - return true + return true end -local function moreTrees() - if #state.trees > 1 then - return - end - - if not state.chest or turtle.getItemCount(OAK_SAPLING) < 2 then - return true - end - - print('Adding more trees') - - local singleTree = state.trees[1] - - state.trees = { } - for x = -2, 2, 1 do - for z = -2, 2, 1 do - if x ~= 0 or z ~= 0 then - local tree = { x = x, y = 0, z = z } - table.insert(state.trees, tree) - turtle.addWorldBlock(tree) - end - end - end - - turtle.digAt(singleTree) - - setState('trees', state.trees) - - countSaplings() - Point.eachClosest(turtle.point, state.trees, function(pt) - local sapling = randomSapling() - if sapling then - turtle.placeDownAt(pt, sapling) - end - end) +local function setTrees() + if not state.trees then + state.trees = { } + for x = -RADIUS_X, RADIUS_X, 1 do + for z = -RADIUS_Z, RADIUS_Z, 1 do + if z ~= 0 or x > 0 then + local tree = { x = x, y = 0, z = z } + table.insert(state.trees, tree) + end + end + end + setState('trees', state.trees) + end end local function findHome() - local pt = GPS.getPoint(2) or error('GPS not found') + local pt = GPS.getPoint(2) or error('GPS not found') - Equipper.equipLeft(SCANNER, 'plethora:scanner') + local scanner = Equipper.equipLeft('plethora:scanner') - local facing = peripheral.call('left', 'getBlockMeta', 0, 0, 0).state.facing - pt.heading = Point.facings[facing].heading + local facing = scanner.getBlockMeta(0, 0, 0).state.facing + pt.heading = Point.facings[facing].heading - Equipper.equipLeft(PICKAXE) + Equipper.equipLeft(PICKAXE) - if not state.home then - setState('home', pt) - end + if not state.home then + setState('home', pt) + end - -- convert to relative coordinates - turtle.setPoint({ - x = pt.x - state.home.x, - y = pt.y - state.home.y, - z = pt.z - state.home.z, - heading = pt.heading, - }) + -- convert to relative coordinates + turtle.set({ + point = { + x = pt.x - state.home.x, + y = pt.y - state.home.y, + z = pt.z - state.home.z, + heading = pt.heading, + }, + reference = state.home, + }) - Point.rotate(turtle.point, state.home.heading) - turtle.setHeading(state.home.heading) - turtle.point.heading = 0 + Point.rotate(turtle.point, state.home.heading) + turtle.setHeading(state.home.heading) + turtle.point.heading = 0 - turtle.setPathingBox({ - x = GRID.TL.x, - y = GRID.TL.y, - z = GRID.TL.z, - ex = GRID.BR.x, - ey = 32, - ez = GRID.BR.z, - }) - - turtle.setPersistent(true) - turtle.addWorldBlocks(state.trees) - if state.torches and type(state.torches) == 'table' then - turtle.addWorldBlocks(state.trees) - end + turtle.setPathingBox({ + x = GRID.TL.x, + y = GRID.TL.y, + z = GRID.TL.z, + ex = GRID.BR.x, + ey = 32, + ez = GRID.BR.z, + }) end local function returnHome() - turtle.pathfind(HOME_PT) - return true + turtle.pathfind(HOME_PT) + return true end local function updateClock() - local ONE_HOUR = 50 + local ONE_HOUR = 50 - if os.clock() - clock > ONE_HOUR then - clock = os.clock() - else - print('sleeping for ' .. math.floor(ONE_HOUR - (os.clock() - clock))) - os.sleep(ONE_HOUR - (os.clock() - clock)) - clock = os.clock() - end + if os.clock() - clock > ONE_HOUR then + clock = os.clock() + else + print('sleeping for ' .. math.floor(ONE_HOUR - (os.clock() - clock))) + os.sleep(ONE_HOUR - (os.clock() - clock)) + clock = os.clock() + end - return true + return true +end + +local function setWorldBlocks() + turtle.setPersistent(true) + turtle.addWorldBlocks(state.trees) + return true end local function startupCheck() - Equipper.equipRight(MODEM, 'modem') - Equipper.equipLeft(PICKAXE) + Equipper.equipModem('right') + Equipper.equipLeft(PICKAXE) - local slots = turtle.getSummedInventory() + local slots = turtle.getSummedInventory() - if not slots[CHEST] or not slots[CRAFTING_TABLE] or not slots[SCANNER] or not slots[SENSOR] then - error([[ + if not slots[SCANNER] or not slots[SENSOR] then + printError([[ Required: - * chest - * crafting table - * block scanner - * entity sensor]]) - end + * block scanner + * entity sensor]]) + error('Missing required item') + end - if not fs.exists(STARTUP_FILE) then - Util.writeFile(STARTUP_FILE, - [[os.sleep(1) + if not fs.exists(STARTUP_FILE) then + Util.writeFile(STARTUP_FILE, + [[os.sleep(1) shell.openForegroundTab('superTreefarm.lua')]]) - print('Autorun program created: ' .. STARTUP_FILE) - end + print('Autorun program created: ' .. STARTUP_FILE) + end end local tasks = { - { desc = 'Startup check', fn = startupCheck }, - { desc = 'Finding home', fn = findHome }, - { desc = 'Creating furnace', fn = createFurnace }, - { desc = 'Creating chest', fn = createChests }, - { desc = 'Counting saplings', fn = countSaplings }, - { desc = 'Adding trees', fn = moreTrees }, - { desc = 'Emptying furnace', fn = emptyFurnace }, - { desc = 'Chopping', fn = fell }, - { desc = 'Snacking', fn = eatSaplings }, - { desc = 'Making charcoal', fn = makeSingleCharcoal }, - { desc = 'Making charcoal', fn = makeCharcoal }, - --{ desc = 'Placing torches', fn = placeTorches }, - { desc = 'Refueling', fn = refuel }, - { desc = 'Dropping off items', fn = dropOffItems }, - { desc = 'Condensing', fn = turtle.condense }, - { desc = 'Returning home', fn = returnHome }, - { desc = 'Sleeping', fn = updateClock }, + { desc = 'Setting trees', fn = setTrees }, + { desc = 'Startup check', fn = startupCheck }, + { desc = 'Finding home', fn = findHome }, + { desc = 'Set world blocks', fn = setWorldBlocks }, + { desc = 'Creating furnace', fn = createFurnace }, + { desc = 'Chopping', fn = fell }, + { desc = 'Creating chest', fn = createChests }, + { desc = 'Snacking', fn = eatSaplings }, + { desc = 'Making charcoal', fn = makeCharcoal }, + { desc = 'Refueling', fn = refuel }, + { desc = 'Dropping off items', fn = dropOffItems }, + { desc = 'Condensing', fn = turtle.condense }, + { desc = 'Returning home', fn = returnHome }, + { desc = 'Sleeping', fn = updateClock }, } -local s, m = turtle.run(function() - turtle.reset() - require('farms.crafting') - --turtle.addFeatures('core.crafting') - turtle.set({ - attackPolicy = 'attack', - digPolicy = 'dig', - }) +turtle.reset() +turtle.set({ + attackPolicy = 'attack', + digPolicy = 'dig', + moveCallback = desperateRefuel, +}) - while not turtle.isAborted() do - print('fuel: ' .. turtle.getFuelLevel()) - for _,task in ipairs(Util.shallowCopy(tasks)) do - --print(task.desc) - turtle.setStatus(task.desc) - turtle.select(1) - if not task.fn() then - Util.filterInplace(tasks, function(v) return v.fn ~= task.fn end) - end - end - end +local s, m = pcall(function() + while not turtle.isAborted() do + print('fuel: ' .. turtle.getFuelLevel()) + for _,task in ipairs(Util.shallowCopy(tasks)) do + --print(task.desc) + turtle.setStatus(task.desc) + turtle.select(1) + if not task.fn() then + Util.filterInplace(tasks, function(v) return v.fn ~= task.fn end) + end + end + end end) -if not s then - error(m or 'Failed') +turtle.reset() + +if not s and m then + error(m) end diff --git a/farms/treefarm.lua b/farms/treefarm.lua index 3ce6c95..ecd1b44 100644 --- a/farms/treefarm.lua +++ b/farms/treefarm.lua @@ -1,21 +1,21 @@ --[[ - Requirements: - Place turtle against an oak tree or oak sapling - 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 LEFT side + Requirements: + Place turtle against an oak tree or oak sapling + 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 LEFT side - Optional: - Add additional sapling types that can grow with a single sapling + Optional: + Add additional sapling types that can grow with a single sapling - Notes: - If the turtle does not get any saplings from the initial tree, place - down another sapling in front of the turtle. + Notes: + If the turtle does not get any saplings from the initial tree, place + down another sapling in front of the turtle. - The program will be able to survive server restarts as long as it has - created the cobblestone line. If the program is stopped before that time, - place the turtle in the original position before restarting the program. + The program will be able to survive server restarts as long as it has + created the cobblestone line. If the program is stopped before that time, + place the turtle in the original position before restarting the program. ]]-- local Point = require('point') @@ -38,18 +38,18 @@ local MAX_SAPLINGS = 32 local GRID_WIDTH = 8 local GRID_LENGTH = 10 local GRID = { - TL = { x = 8, y = 0, z = -8 }, - TR = { x = 8, y = 0, z = 8 }, - BL = { x = -10, y = 0, z = -8 }, - BR = { x = -10, y = 0, z = 8 }, + TL = { x = 8, y = 0, z = -8 }, + TR = { x = 8, y = 0, z = 8 }, + BL = { x = -10, y = 0, z = -8 }, + BR = { x = -10, y = 0, z = 8 }, } local HOME_PT = { x = 0, y = 0, z = 0, heading = 0 } local DIG_BLACKLIST = { - [ 'minecraft:furnace' ] = true, - [ 'minecraft:lit_furnace' ] = true, - [ 'minecraft:chest' ] = true, + [ 'minecraft:furnace' ] = true, + [ 'minecraft:lit_furnace' ] = true, + [ 'minecraft:chest' ] = true, } local COBBLESTONE = 'minecraft:cobblestone:0' @@ -67,706 +67,706 @@ local STICK = 'minecraft:stick:0' local CRAFTING_TABLE = 'minecraft:crafting_table:0' local ALL_SAPLINGS = { - SAPLING + SAPLING } local state = Util.readTable('usr/config/treefarm') or { - trees = { - { x = 1, y = 0, z = 0 } - } + trees = { + { x = 1, y = 0, z = 0 } + } } if not fs.exists(STARTUP_FILE) then - Util.writeFile(STARTUP_FILE, - [[os.sleep(1) + Util.writeFile(STARTUP_FILE, + [[os.sleep(1) shell.openForegroundTab('treefarm.lua')]]) - print('Autorun program created: ' .. STARTUP_FILE) + print('Autorun program created: ' .. STARTUP_FILE) end local clock = os.clock() local function inspect(fn) - local s, item = fn() - if s and item then - return item.name .. ':' .. item.metadata - end - return 'minecraft:air:0' + local s, item = fn() + if s and item then + return item.name .. ':' .. item.metadata + end + return 'minecraft:air:0' end local function setState(key, value) - state[key] = value - Util.writeTable('usr/config/treefarm', state) + state[key] = value + Util.writeTable('usr/config/treefarm', state) end local function refuel() - if turtle.getFuelLevel() < FUEL_GOOD then - local charcoal = turtle.getItemCount(CHARCOAL) - if charcoal > 1 then - turtle.refuel(CHARCOAL, math.min(charcoal - 1, MIN_CHARCOAL / 2)) - print('fuel: ' .. turtle.getFuelLevel()) - end - end - return true + if turtle.getFuelLevel() < FUEL_GOOD then + local charcoal = turtle.getItemCount(CHARCOAL) + if charcoal > 1 then + turtle.refuel(CHARCOAL, math.min(charcoal - 1, MIN_CHARCOAL / 2)) + print('fuel: ' .. turtle.getFuelLevel()) + end + end + return true end local function safePlaceBlock(item) - if turtle.placeUp(item) then - return true - end + if turtle.placeUp(item) then + return true + end - local s, m = turtle.inspectUp() - if s and not DIG_BLACKLIST[m.name] then - turtle.digUp() - return turtle.placeUp(item) - end + local s, m = turtle.inspectUp() + if s and not DIG_BLACKLIST[m.name] then + turtle.digUp() + return turtle.placeUp(item) + end - turtle.forward() - return turtle.placeUp(item) + turtle.forward() + return turtle.placeUp(item) end local function craftItem(item, qty) - local success, msg + local success, msg - if safePlaceBlock(CHEST) then + if safePlaceBlock(CHEST) then - os.sleep(.2) -- needed for minecraft 1.12 - Util.print('Crafting %d %s', (qty or 1), item) - success, msg = turtle.craftItem(item, qty or 1, { - side = 'top', - direction = 'down', - }) - repeat until not turtle.suckUp() + os.sleep(.2) -- needed for minecraft 1.12 + Util.print('Crafting %d %s', (qty or 1), item) + success, msg = turtle.craftItem(item, qty or 1, { + side = 'top', + direction = 'down', + }) + repeat until not turtle.suckUp() - if not success then - print(msg) - end + if not success then + print(msg) + end - turtle.digUp() - end + turtle.digUp() + end - return success + return success end local function emptyFurnace() - if state.cooking then + if state.cooking then - print('Emptying furnace') + print('Emptying furnace') - turtle.suckDownAt(state.furnace) - turtle.suckForwardAt(state.furnace) - turtle.suckUpAt(state.furnace) - setState('cooking') - end + 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() + emptyFurnace() - setState('cooking', true) + setState('cooking', true) - fuel = fuel or CHARCOAL - fuelCount = fuelCount or math.ceil(count / 8) - Util.print('Making %d %s', count, result) + fuel = fuel or CHARCOAL + fuelCount = fuelCount or math.ceil(count / 8) + Util.print('Making %d %s', count, result) - turtle.dropForwardAt(state.furnace, fuel, fuelCount) - turtle.dropDownAt(state.furnace, item, count) + turtle.dropForwardAt(state.furnace, fuel, fuelCount) + turtle.dropDownAt(state.furnace, item, count) - count = count + turtle.getItemCount(result) - turtle.select(1) - turtle.pathfind(Point.below(state.furnace)) + count = count + turtle.getItemCount(result) + turtle.select(1) + turtle.pathfind(Point.below(state.furnace)) - local lastSuck = os.clock() - repeat - os.sleep(1) - if turtle.suckUp() then - lastSuck = os.clock() - end + local lastSuck = os.clock() + repeat + os.sleep(1) + 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 + if os.clock() - lastSuck > 10 then + -- sponge bug + Util.print('Timed out waiting for furnace') + return + end + until turtle.getItemCount(result) >= count - setState('cooking') + setState('cooking') end local function makeSingleCharcoal() - local slots = turtle.getSummedInventory() + local slots = turtle.getSummedInventory() - if not state.furnace or - slots[CHARCOAL] or - not slots[OAK_LOG] or - slots[OAK_LOG].count < 2 then - return true - end + if not state.furnace or + slots[CHARCOAL] or + not slots[OAK_LOG] or + slots[OAK_LOG].count < 2 then + return true + end - turtle.faceAgainst(state.furnace) - if craftItem(OAK_PLANK) then - cook(OAK_LOG, 1, CHARCOAL, OAK_PLANK, 1) - turtle.refuel(OAK_PLANK) - end + turtle.faceAgainst(state.furnace) + if craftItem(OAK_PLANK) then + cook(OAK_LOG, 1, CHARCOAL, OAK_PLANK, 1) + turtle.refuel(OAK_PLANK) + end - return true + return true end local function makeCharcoal() - local slots = turtle.getSummedInventory() + local slots = turtle.getSummedInventory() - if not state.furnace or - not slots[CHARCOAL] or - slots[CHARCOAL].count >= MIN_CHARCOAL then - return true - end + if not state.furnace or + not slots[CHARCOAL] or + slots[CHARCOAL].count >= MIN_CHARCOAL then + return true + end - local function getLogSlot() - local maxslot = { count = 0 } - for k,slot in pairs(slots) do - if string.match(k, 'minecraft:log') then - if slot.count > maxslot.count then - maxslot = slot - end - end - end - return maxslot - end + local function getLogSlot() + local maxslot = { count = 0 } + for k,slot in pairs(slots) do + if string.match(k, 'minecraft:log') then + if slot.count > maxslot.count then + maxslot = slot + end + end + end + return maxslot + end - repeat - slots = turtle.getSummedInventory() + repeat + slots = turtle.getSummedInventory() - local charcoal = slots[CHARCOAL].count - local slot = getLogSlot(slots) + local charcoal = slots[CHARCOAL].count + local slot = getLogSlot(slots) - if slot.count < 8 then - break - end + if slot.count < 8 then + break + end - local toCook = math.min(charcoal, math.floor(slot.count / 8)) - toCook = math.min(toCook, math.floor((MIN_CHARCOAL + 8 - charcoal) / 8)) - toCook = toCook * 8 + local toCook = math.min(charcoal, math.floor(slot.count / 8)) + toCook = math.min(toCook, math.floor((MIN_CHARCOAL + 8 - charcoal) / 8)) + toCook = toCook * 8 - cook(slot.key, toCook, CHARCOAL) + cook(slot.key, toCook, CHARCOAL) - until charcoal + toCook >= MIN_CHARCOAL + until charcoal + toCook >= MIN_CHARCOAL - return true + return true end local function getCobblestone(count) - local slots = turtle.getSummedInventory() + local slots = turtle.getSummedInventory() - if not slots[COBBLESTONE] or slots[COBBLESTONE].count < count then + if not slots[COBBLESTONE] or slots[COBBLESTONE].count < count then - print('Collecting cobblestone') + print('Collecting cobblestone') - slots[COBBLESTONE] = true - slots[DIRT] = true + slots[COBBLESTONE] = true + slots[DIRT] = true - local pt = Point.copy(GRID.BR) - pt.x = GRID.BR.x + 2 - pt.z = GRID.BR.z - 2 + local pt = Point.copy(GRID.BR) + pt.x = GRID.BR.x + 2 + pt.z = GRID.BR.z - 2 - turtle.pathfind(pt) + turtle.pathfind(pt) - repeat - turtle.select(1) - turtle.digDown() - turtle.down() - for _ = 1, 4 do - if inspect(turtle.inspect) == STONE then - turtle.dig() - end - turtle.turnRight() - end + repeat + turtle.select(1) + turtle.digDown() + turtle.down() + for _ = 1, 4 do + if inspect(turtle.inspect) == STONE then + turtle.dig() + end + turtle.turnRight() + end - for item in pairs(turtle.getSummedInventory()) do - if not slots[item] then - turtle.drop(item) - end - end + for item in pairs(turtle.getSummedInventory()) do + if not slots[item] then + turtle.drop(item) + end + end - until turtle.getItemCount(COBBLESTONE) >= count + until turtle.getItemCount(COBBLESTONE) >= count - turtle.go(pt) - turtle.placeDown(DIRT) + turtle.go(pt) + turtle.placeDown(DIRT) - turtle.drop(DIRT) - end + turtle.drop(DIRT) + end end local function createFurnace() - if not state.furnace then - if turtle.getFuelLevel() < FUEL_BASE + 100 then - return true -- try again later - end - print('Adding a furnace') - getCobblestone(8) + if not state.furnace then + if turtle.getFuelLevel() < FUEL_BASE + 100 then + return true -- try again later + end + print('Adding a furnace') + getCobblestone(8) - if turtle.has(FURNACE) or craftItem(FURNACE) then - -- turtle.drop(COBBLESTONE) - local furnacePt = { x = GRID.BL.x + 2, y = 1, z = GRID.BL.z + 2 } - turtle.placeAt(furnacePt, FURNACE) - setState('furnace', furnacePt) - end - end + if turtle.has(FURNACE) or craftItem(FURNACE) then + -- turtle.drop(COBBLESTONE) + local furnacePt = { x = GRID.BL.x + 2, y = 1, z = GRID.BL.z + 2 } + turtle.placeAt(furnacePt, FURNACE) + setState('furnace', furnacePt) + end + end end local function createPerimeter() - if not state.perimeter then - if not state.furnace or - turtle.getFuelLevel() < FUEL_BASE + 500 or - turtle.getItemCount(OAK_LOG) == 0 or - not craftItem(OAK_PLANK, 2) then - return true - end + if not state.perimeter then + if not state.furnace or + turtle.getFuelLevel() < FUEL_BASE + 500 or + turtle.getItemCount(OAK_LOG) == 0 or + not craftItem(OAK_PLANK, 2) then + return true + end - print('Creating a perimeter') + print('Creating a perimeter') - getCobblestone(GRID_WIDTH * 2 + 1) - if not turtle.has(STONE, 2) then - cook(COBBLESTONE, 2, STONE, OAK_PLANK, 2) - end - turtle.refuel(OAK_PLANK) + getCobblestone(GRID_WIDTH * 2 + 1) + if not turtle.has(STONE, 2) then + cook(COBBLESTONE, 2, STONE, OAK_PLANK, 2) + end + turtle.refuel(OAK_PLANK) - turtle.pathfind(GRID.BL) - turtle.digDown() - turtle.placeDown(STONE) + turtle.pathfind(GRID.BL) + turtle.digDown() + turtle.placeDown(STONE) - turtle.setMoveCallback(function() - local target = COBBLESTONE - if math.abs(turtle.point.x) == GRID_LENGTH and - math.abs(turtle.point.z) == GRID_WIDTH then - target = STONE - end + turtle.setMoveCallback(function() + local target = COBBLESTONE + if math.abs(turtle.point.x) == GRID_LENGTH and + math.abs(turtle.point.z) == GRID_WIDTH then + target = STONE + end - if inspect(turtle.inspectDown) ~= target then - turtle.digDown() - turtle.placeDown(target) - end - end) + if inspect(turtle.inspectDown) ~= target then + turtle.digDown() + turtle.placeDown(target) + end + end) - turtle.pathfind(GRID.BR) + turtle.pathfind(GRID.BR) - turtle.clearMoveCallback() - turtle.drop(COBBLESTONE) - turtle.drop(DIRT) + turtle.clearMoveCallback() + turtle.drop(COBBLESTONE) + turtle.drop(DIRT) - setState('perimeter', true) - end + setState('perimeter', true) + end end local function createChests() - if state.chest then - return - end - if state.perimeter and - turtle.getFuelLevel() > FUEL_GOOD and - turtle.canCraft(CHEST, 4, turtle.getSummedInventory()) then + if state.chest then + return + end + if state.perimeter and + turtle.getFuelLevel() > FUEL_GOOD and + turtle.canCraft(CHEST, 4, turtle.getSummedInventory()) then - print('Adding storage') - if turtle.has(CHEST, 2) or craftItem(CHEST, 2) then + print('Adding storage') + if turtle.has(CHEST, 2) or craftItem(CHEST, 2) then - local pt = Point.copy(GRID.BL) - pt.x = pt.x + 1 - pt.y = pt.y - 1 + local pt = Point.copy(GRID.BL) + pt.x = pt.x + 1 + pt.y = pt.y - 1 - pt.z = pt.z + 1 + pt.z = pt.z + 1 - turtle.digDownAt(pt) - turtle.placeDown(CHEST) + turtle.digDownAt(pt) + turtle.placeDown(CHEST) - pt.z = pt.z + 1 + pt.z = pt.z + 1 - turtle.digDownAt(pt) - turtle.placeDown(CHEST) + turtle.digDownAt(pt) + turtle.placeDown(CHEST) - setState('chest', Util.shallowCopy(pt)) + setState('chest', Util.shallowCopy(pt)) - turtle.drop(DIRT) - turtle.refuel(OAK_PLANK) - end - end - return true + turtle.drop(DIRT) + turtle.refuel(OAK_PLANK) + end + end + return true end local function dropOffItems() - if state.chest then - local slots = turtle.getSummedInventory() + if state.chest then + local slots = turtle.getSummedInventory() - if state.chest and - slots[CHARCOAL] and - slots[CHARCOAL].count >= MIN_CHARCOAL and - (turtle.getItemCount('minecraft:log') > 0 or - turtle.getItemCount('minecraft:log2') > 0) then + if state.chest and + slots[CHARCOAL] and + slots[CHARCOAL].count >= MIN_CHARCOAL and + (turtle.getItemCount('minecraft:log') > 0 or + turtle.getItemCount('minecraft:log2') > 0) then - print('Storing logs') - turtle.pathfind(Point.above(state.chest)) - turtle.dropDown('minecraft:log') - turtle.dropDown('minecraft:log2') + print('Storing logs') + turtle.pathfind(Point.above(state.chest)) + turtle.dropDown('minecraft:log') + turtle.dropDown('minecraft:log2') - for _, sapling in pairs(ALL_SAPLINGS) do - if slots[sapling] and slots[sapling].count > MAX_SAPLINGS then - turtle.dropDown(sapling, slots[sapling].count - MAX_SAPLINGS) - end - end + for _, sapling in pairs(ALL_SAPLINGS) do + if slots[sapling] and slots[sapling].count > MAX_SAPLINGS then + turtle.dropDown(sapling, slots[sapling].count - MAX_SAPLINGS) + end + end - turtle.dropDown(APPLE) - end - end + turtle.dropDown(APPLE) + end + end - return true + return true end local function eatSaplings() - local slots = turtle.getSummedInventory() + local slots = turtle.getSummedInventory() - for _, sapling in pairs(ALL_SAPLINGS) do - if slots[sapling] and slots[sapling].count > MAX_SAPLINGS then - turtle.refuel(sapling, slots[sapling].count - MAX_SAPLINGS) - end - end - return true + for _, sapling in pairs(ALL_SAPLINGS) do + if slots[sapling] and slots[sapling].count > MAX_SAPLINGS then + turtle.refuel(sapling, slots[sapling].count - MAX_SAPLINGS) + end + end + return true end local function placeTorches() - if state.torches then - return - end + if state.torches then + return + end - if turtle.getFuelLevel() > 100 and - turtle.canCraft(TORCH, 4, turtle.getSummedInventory()) then + if turtle.getFuelLevel() > 100 and + turtle.canCraft(TORCH, 4, turtle.getSummedInventory()) then - print('Placing torches') + print('Placing torches') - if turtle.has(TORCH, 4) or craftItem(TORCH, 4) then - local pts = { } - for x = -4, 4, 8 do - for z = -4, 4, 8 do - table.insert(pts, { x = x, y = 0, z = z }) - end - end - Point.eachClosest(turtle.point, pts, function(pt) - turtle.placeAt(pt, TORCH) - end) - turtle.refuel(STICK) - turtle.refuel(OAK_PLANK) - setState('torches', true) - end - end + if turtle.has(TORCH, 4) or craftItem(TORCH, 4) then + local pts = { } + for x = -4, 4, 8 do + for z = -4, 4, 8 do + table.insert(pts, { x = x, y = 0, z = z }) + end + end + Point.eachClosest(turtle.point, pts, function(pt) + turtle.placeAt(pt, TORCH) + end) + turtle.refuel(STICK) + turtle.refuel(OAK_PLANK) + setState('torches', true) + end + end - return true + return true end local function randomSapling() - local sapling = SAPLING + local sapling = SAPLING - if #state.trees > 1 then - ALL_SAPLINGS = { } + if #state.trees > 1 then + ALL_SAPLINGS = { } - local slots = turtle.getFilledSlots() - for _, slot in pairs(slots) do - if slot.name == 'minecraft:sapling' then - table.insert(ALL_SAPLINGS, slot.key) - end - end - if #ALL_SAPLINGS > 0 then - sapling = ALL_SAPLINGS[math.random(1, #ALL_SAPLINGS)] - end - end + local slots = turtle.getFilledSlots() + for _, slot in pairs(slots) do + if slot.name == 'minecraft:sapling' then + table.insert(ALL_SAPLINGS, slot.key) + end + end + if #ALL_SAPLINGS > 0 then + sapling = ALL_SAPLINGS[math.random(1, #ALL_SAPLINGS)] + end + end - return sapling + return sapling end local function fellTree(pt) - local function desperateRefuel(min) - if turtle.getFuelLevel() < min then - local logs = turtle.getItemCount(OAK_LOG) - if logs > 0 then - if craftItem(OAK_PLANK, math.min(8, logs * 4)) then - turtle.refuel(OAK_PLANK) - print('fuel: ' .. turtle.getFuelLevel()) - end - end - end - end + local function desperateRefuel(min) + if turtle.getFuelLevel() < min then + local logs = turtle.getItemCount(OAK_LOG) + if logs > 0 then + if craftItem(OAK_PLANK, math.min(8, logs * 4)) then + turtle.refuel(OAK_PLANK) + print('fuel: ' .. turtle.getFuelLevel()) + end + end + end + end - turtle.setMoveCallback(function() desperateRefuel(FUEL_DIRE) end) + turtle.setMoveCallback(function() desperateRefuel(FUEL_DIRE) end) - desperateRefuel(FUEL_DIRE) + desperateRefuel(FUEL_DIRE) - if turtle.digUpAt(Point.above(pt)) then - turtle.level( - { x = GRID_WIDTH-1, y = 1, z = GRID_WIDTH-1 }, - { x = -(GRID_WIDTH-1), y = 50, z = -(GRID_WIDTH-1) }, - Point.above(pt)) - end + if turtle.digUpAt(Point.above(pt)) then + turtle.level( + { x = GRID_WIDTH-1, y = 1, z = GRID_WIDTH-1 }, + { x = -(GRID_WIDTH-1), y = 50, z = -(GRID_WIDTH-1) }, + Point.above(pt)) + end - desperateRefuel(FUEL_BASE + 100) - turtle.clearMoveCallback() - turtle.set({ attackPolicy = "attack" }) + desperateRefuel(FUEL_BASE + 100) + turtle.clearMoveCallback() + turtle.set({ attackPolicy = "attack" }) - return true + return true end local function fell() - local pts = Util.shallowCopy(state.trees) + local pts = Util.shallowCopy(state.trees) - local rpt = table.remove(pts, math.random(1, #pts)) + local rpt = table.remove(pts, math.random(1, #pts)) - -- give the pathfinder hints about what to avoid (state.trees) - if not turtle.faceAgainst(rpt, { blocks = Util.shallowCopy(state.trees) }) or - not string.match(inspect(turtle.inspect), 'minecraft:log') then - return true - end + -- give the pathfinder hints about what to avoid (state.trees) + if not turtle.faceAgainst(rpt, { blocks = Util.shallowCopy(state.trees) }) or + not string.match(inspect(turtle.inspect), 'minecraft:log') then + return true + end - print('Chopping') + print('Chopping') - local fuel = turtle.getFuelLevel() + local fuel = turtle.getFuelLevel() - -- push this point to the start of this list - table.insert(pts, 1, rpt) + -- push this point to the start of this list + table.insert(pts, 1, rpt) - Point.eachClosest(turtle.point, pts, function(pt) - if turtle.faceAgainst(pt, { blocks = Util.shallowCopy(state.trees) }) and - string.match(inspect(turtle.inspect), 'minecraft:log') then - turtle.dig() - fellTree(pt) - end - turtle.placeAt(pt, randomSapling()) - turtle.select(1) - end) + Point.eachClosest(turtle.point, pts, function(pt) + if turtle.faceAgainst(pt, { blocks = Util.shallowCopy(state.trees) }) and + string.match(inspect(turtle.inspect), 'minecraft:log') then + turtle.dig() + fellTree(pt) + end + turtle.placeAt(pt, randomSapling()) + turtle.select(1) + end) - print('Used ' .. (fuel - turtle.getFuelLevel()) .. ' fuel') - return true + print('Used ' .. (fuel - turtle.getFuelLevel()) .. ' fuel') + return true end local function moreTrees() - if #state.trees > 1 then - return - end + if #state.trees > 1 then + return + end - if not state.chest or turtle.getItemCount('minecraft:sapling') < 15 then - return true - end + if not state.chest or turtle.getItemCount('minecraft:sapling') < 15 then + return true + end - print('Adding more trees') + print('Adding more trees') - local singleTree = state.trees[1] + local singleTree = state.trees[1] - state.trees = { } - for x = -2, 2, 1 do - for z = -2, 2, 2 do - table.insert(state.trees, { x = x, y = 0, z = z }) - end - end + state.trees = { } + for x = -2, 2, 1 do + for z = -2, 2, 2 do + table.insert(state.trees, { x = x, y = 0, z = z }) + end + end - turtle.digAt(singleTree) - fellTree(singleTree) + turtle.digAt(singleTree) + fellTree(singleTree) - setState('trees', state.trees) + setState('trees', state.trees) - Point.eachClosest(turtle.point, state.trees, function(pt) - turtle.placeDownAt(pt, randomSapling()) - end) + Point.eachClosest(turtle.point, state.trees, function(pt) + turtle.placeDownAt(pt, randomSapling()) + end) end local function getTurtleFacing(block) - local directions = { - [5] = 2, - [3] = 3, - [4] = 0, - [2] = 1, - } + local directions = { + [5] = 2, + [3] = 3, + [4] = 0, + [2] = 1, + } - if not safePlaceBlock(block) then - error('unable to place chest above') - end - local _, bi = turtle.inspectUp() - turtle.digUp() - return directions[bi.metadata] + if not safePlaceBlock(block) then + error('unable to place chest above') + end + local _, bi = turtle.inspectUp() + turtle.digUp() + return directions[bi.metadata] end local function saveTurtleFacing() - if not state.facing then - setState('facing', getTurtleFacing(CHEST)) - end + if not state.facing then + setState('facing', getTurtleFacing(CHEST)) + end end local function findGround() - print('Locating ground level') - turtle.setPoint(HOME_PT) + print('Locating ground level') + turtle.setPoint(HOME_PT) - while true do - local s, block = turtle.inspectDown() + while true do + local s, block = turtle.inspectDown() - if not s then block = { name = 'minecraft:air', metadata = 0 } end - local b = block.name .. ':' .. block.metadata + if not s then block = { name = 'minecraft:air', metadata = 0 } end + local b = block.name .. ':' .. block.metadata - if b == 'minecraft:dirt:0' or - b == 'minecraft:grass:0' or - block.name == 'minecraft:chest' then - break - end + if b == 'minecraft:dirt:0' or + b == 'minecraft:grass:0' or + block.name == 'minecraft:chest' then + break + end - 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 == 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 + if b == STONE then + error('lost') + end - if b == TORCH or DIG_BLACKLIST[block.name] then - turtle.forward() - else - turtle.digDown() - turtle.down() - end + if b == TORCH or DIG_BLACKLIST[block.name] then + turtle.forward() + else + turtle.digDown() + turtle.down() + end - if turtle.point.y < -20 then - error('lost') - end - end - turtle.setPoint(HOME_PT) + if turtle.point.y < -20 then + error('lost') + end + end + turtle.setPoint(HOME_PT) end local function findHome() - if not state.perimeter then - return - end + if not state.perimeter then + return + end - print('Determining location') - turtle.point.heading = (getTurtleFacing(CHEST) - state.facing) % 4 + print('Determining location') + turtle.point.heading = (getTurtleFacing(CHEST) - state.facing) % 4 - local pt = Point.copy(turtle.point) + local pt = Point.copy(turtle.point) - while inspect(turtle.inspectDown) ~= COBBLESTONE do - pt.x = pt.x - 1 - turtle.pathfind(pt) - if pt.x < -20 then - error('lost') - end - end - while inspect(turtle.inspectDown) == COBBLESTONE do - pt.z = pt.z - 1 - turtle.pathfind(pt) - if pt.z < -20 then - error('lost') - end - end + while inspect(turtle.inspectDown) ~= COBBLESTONE do + pt.x = pt.x - 1 + turtle.pathfind(pt) + if pt.x < -20 then + error('lost') + end + end + while inspect(turtle.inspectDown) == COBBLESTONE do + pt.z = pt.z - 1 + turtle.pathfind(pt) + if pt.z < -20 then + error('lost') + end + end - turtle.setPoint({ - x = -(GRID_LENGTH), - y = 0, - z = -GRID_WIDTH, - heading = turtle.point.heading - }) + turtle.setPoint({ + x = -(GRID_LENGTH), + y = 0, + z = -GRID_WIDTH, + heading = turtle.point.heading + }) - -- when pathfinding - don't leave this box - turtle.setPathingBox({ - x = GRID.TL.x, - y = GRID.TL.y, - z = GRID.TL.z, - ex = GRID.BR.x, - ey = 5, - ez = GRID.BR.z, - }) + -- when pathfinding - don't leave this box + turtle.setPathingBox({ + x = GRID.TL.x, + y = GRID.TL.y, + z = GRID.TL.z, + ex = GRID.BR.x, + ey = 5, + ez = GRID.BR.z, + }) end local function updateClock() - local ONE_HOUR = 50 + local ONE_HOUR = 50 - if os.clock() - clock > ONE_HOUR then - clock = os.clock() - else - print('sleeping for ' .. math.floor(ONE_HOUR - (os.clock() - clock))) - os.sleep(ONE_HOUR - (os.clock() - clock)) - clock = os.clock() - end + if os.clock() - clock > ONE_HOUR then + clock = os.clock() + else + print('sleeping for ' .. math.floor(ONE_HOUR - (os.clock() - clock))) + os.sleep(ONE_HOUR - (os.clock() - clock)) + clock = os.clock() + end - return true + return true end local function startupCheck() - local slots = turtle.getSummedInventory() + local slots = turtle.getSummedInventory() - if not slots[CHEST] or not slots[CRAFTING_TABLE] then - error('A chest and crafting table must be in inventory') - end + if not slots[CHEST] or not slots[CRAFTING_TABLE] then + error('A chest and crafting table must be in inventory') + end - if state.facing and not state.perimeter then - print('Perimeter has not been established.') - print('Enter to continue if turtle is in the original starting position.') - read() - end + if state.facing and not state.perimeter then + print('Perimeter has not been established.') + print('Enter to continue if turtle is in the original starting position.') + read() + end end local tasks = { - { desc = 'Startup check', fn = startupCheck }, - { desc = 'Finding ground', fn = findGround }, - { desc = 'Determine facing', fn = saveTurtleFacing }, - { desc = 'Finding home', fn = findHome }, - { desc = 'Emptying furnace', fn = emptyFurnace }, - { desc = 'Adding trees', fn = moreTrees }, - { desc = 'Chopping', fn = fell }, + { desc = 'Startup check', fn = startupCheck }, + { desc = 'Finding ground', fn = findGround }, + { desc = 'Determine facing', fn = saveTurtleFacing }, + { desc = 'Finding home', fn = findHome }, + { desc = 'Emptying furnace', fn = emptyFurnace }, + { desc = 'Adding trees', fn = moreTrees }, + { desc = 'Chopping', fn = fell }, -- { desc = 'Snacking', fn = eatSaplings }, - { desc = 'Creating chest', fn = createChests }, - { desc = 'Creating furnace', fn = createFurnace }, - { desc = 'Making charcoal', fn = makeSingleCharcoal }, - { desc = 'Making charcoal', fn = makeCharcoal }, - { desc = 'Creating perimeter', fn = createPerimeter }, - { desc = 'Placing torches', fn = placeTorches }, - { desc = 'Refueling', fn = refuel }, - { desc = 'Dropping off items', fn = dropOffItems }, - { desc = 'Condensing', fn = turtle.condense }, - { desc = 'Sleeping', fn = updateClock }, + { desc = 'Creating chest', fn = createChests }, + { desc = 'Creating furnace', fn = createFurnace }, + { desc = 'Making charcoal', fn = makeSingleCharcoal }, + { desc = 'Making charcoal', fn = makeCharcoal }, + { desc = 'Creating perimeter', fn = createPerimeter }, + { desc = 'Placing torches', fn = placeTorches }, + { desc = 'Refueling', fn = refuel }, + { desc = 'Dropping off items', fn = dropOffItems }, + { desc = 'Condensing', fn = turtle.condense }, + { desc = 'Sleeping', fn = updateClock }, } local s, m = turtle.run(function() - require('farms.crafting') - require('farms.level') - --turtle.addFeatures('level', 'core.crafting') - turtle.set({ attackPolicy = "attack" }) + require('farms.crafting') + require('farms.level') + --turtle.addFeatures('level', 'core.crafting') + turtle.set({ attackPolicy = "attack" }) - while not turtle.isAborted() do - print('fuel: ' .. turtle.getFuelLevel()) - for _,task in ipairs(Util.shallowCopy(tasks)) do - --print(task.desc) - turtle.setStatus(task.desc) - turtle.select(1) - if not task.fn() then - Util.filterInplace(tasks, function(v) return v.fn ~= task.fn end) - end - end - end + while not turtle.isAborted() do + print('fuel: ' .. turtle.getFuelLevel()) + for _,task in ipairs(Util.shallowCopy(tasks)) do + --print(task.desc) + turtle.setStatus(task.desc) + turtle.select(1) + if not task.fn() then + Util.filterInplace(tasks, function(v) return v.fn ~= task.fn end) + end + end + end end) if not s then - error(m or 'Failed') + error(m or 'Failed') end diff --git a/games/ccTunes.lua b/games/ccTunes.lua index 6b8551a..9561fe8 100644 --- a/games/ccTunes.lua +++ b/games/ccTunes.lua @@ -3,25 +3,25 @@ local Sound = require('sound') local os = _G.os local tunes = { - { sound = 'record.11', length = '1:11' }, - { sound = 'record.13', length = '2:58' }, - { sound = 'record.blocks', length = '5:45' }, - { sound = 'record.cat', length = '3:05' }, - { sound = 'record.chirp', length = '3:05' }, - { sound = 'record.far', length = '2:54' }, - { sound = 'record.mall', length = '3:17' }, - { sound = 'record.mellohi', length = '1:36' }, - { sound = 'record.stal', length = '2:30' }, - { sound = 'record.strad', length = '3:08' }, - { sound = 'record.wait', length = '3:58' }, - { sound = 'record.ward', length = '4:11' }, + { sound = 'record.11', length = '1:11' }, + { sound = 'record.13', length = '2:58' }, + { sound = 'record.blocks', length = '5:45' }, + { sound = 'record.cat', length = '3:05' }, + { sound = 'record.chirp', length = '3:05' }, + { sound = 'record.far', length = '2:54' }, + { sound = 'record.mall', length = '3:17' }, + { sound = 'record.mellohi', length = '1:36' }, + { sound = 'record.stal', length = '2:30' }, + { sound = 'record.strad', length = '3:08' }, + { sound = 'record.wait', length = '3:58' }, + { sound = 'record.ward', length = '4:11' }, } while true do - local song = tunes[math.random(1, #tunes)] - Sound.play(song.sound) - local min, sec = song.length:match('(%d+):(%d+)') - local length = tonumber(min)*60 + tonumber(sec) - print(string.format('Playing %s (%s)', song.sound, song.length)) - os.sleep(length + 3) + local song = tunes[math.random(1, #tunes)] + Sound.play(song.sound) + local min, sec = song.length:match('(%d+):(%d+)') + local length = tonumber(min)*60 + tonumber(sec) + print(string.format('Playing %s (%s)', song.sound, song.length)) + os.sleep(length + 3) end diff --git a/gps/gpsServer.lua b/gps/gpsServer.lua index ee4ab7e..1ee4390 100644 --- a/gps/gpsServer.lua +++ b/gps/gpsServer.lua @@ -3,6 +3,7 @@ local GPS = require('gps') local Util = require('util') local args = { ... } +local colors = _G.colors local fs = _G.fs local gps = _G.gps local os = _G.os diff --git a/milo/MiloLocal.lua b/milo/MiloLocal.lua index 8ac9445..90cc784 100644 --- a/milo/MiloLocal.lua +++ b/milo/MiloLocal.lua @@ -16,68 +16,68 @@ local turtle = _G.turtle multishell.setTitle(multishell.getCurrent(), 'Milo') local function Syntax(msg) - print([[ + print([[ Turtle must be provided with: - * Introspection module (never bound) - * Workbench + * Introspection module (never bound) + * Workbench Turtle must be connected to: - * Wired modem (activated) + * Wired modem (activated) ]]) - error(msg) + error(msg) end 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 + 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') + Syntax('Wired modem missing') end if not modem.getNameLocal() then - Syntax('Wired modem is not active') + Syntax('Wired modem is not active') end local introspection = device['plethora:introspection'] or - turtle.equip('left', 'plethora:module:0') and device['plethora:introspection'] or - Syntax('Introspection module missing') + turtle.equip('left', 'plethora:module:0') and device['plethora:introspection'] or + Syntax('Introspection module missing') if not device.workbench then - turtle.equip('right', 'minecraft:crafting_table:0') - if not device.workbench then - Syntax('Workbench missing') - end + turtle.equip('right', 'minecraft:crafting_table:0') + if not device.workbench then + Syntax('Workbench missing') + end end local localName = modem.getNameLocal() local context = { - resources = Util.readTable(Milo.RESOURCE_FILE) or { }, + resources = Util.readTable(Milo.RESOURCE_FILE) or { }, - state = { }, - craftingQueue = { }, - tasks = { }, - queue = { }, - plugins = { }, - loggers = { }, + state = { }, + craftingQueue = { }, + tasks = { }, + queue = { }, + plugins = { }, + loggers = { }, - taskTimer = 0, - taskCounter = 0, + taskTimer = 0, + taskCounter = 0, - storage = Storage(), - turtleInventory = { - name = localName, - mtype = 'hidden', - adapter = introspection.getInventory(), - } + storage = Storage(), + turtleInventory = { + name = localName, + mtype = 'hidden', + adapter = introspection.getInventory(), + } } context.storage.nodes[localName] = context.turtleInventory @@ -88,23 +88,23 @@ context.storage:initStorage() context.storage.turtleInventory = context.turtleInventory local function loadPlugin(file) - local s, plugin = Util.run(_ENV, file, context) - if not s and plugin then - _G.printError('Error loading: ' .. file) - error(plugin or 'Unknown error') - end + local s, plugin = Util.run(_ENV, file, context) + if not s and plugin then + _G.printError('Error loading: ' .. file) + error(plugin or 'Unknown error') + end - if plugin and type(plugin) == 'table' then - Milo:registerPlugin(plugin) - end + if plugin and type(plugin) == 'table' then + Milo:registerPlugin(plugin) + end end local function loadDirectory(dir) - for _, file in pairs(fs.list(dir)) do - if not fs.isDir(fs.combine(dir, file)) then - loadPlugin(fs.combine(dir, file)) - end - end + for _, file in pairs(fs.list(dir)) do + if not fs.isDir(fs.combine(dir, file)) then + loadPlugin(fs.combine(dir, file)) + end + end end local programDir = fs.getDir(shell.getRunningProgram()) @@ -113,17 +113,17 @@ loadDirectory(fs.combine(programDir, 'plugins')) loadDirectory(fs.combine(programDir, 'plugins/item')) for k in pairs(Milo:getState('plugins') or { }) do - loadPlugin(k) + loadPlugin(k) end table.sort(context.tasks, function(a, b) - return a.priority < b.priority + return a.priority < b.priority end) _G._syslog('Tasks\n-----') for _, task in ipairs(context.tasks) do - task.execTime = 0 - _G._syslog('%d: %s', task.priority, task.name) + task.execTime = 0 + _G._syslog('%d: %s', task.priority, task.name) end Milo:clearGrid() @@ -132,92 +132,92 @@ UI:setPage(UI:getPage('listing')) Sound.play('ui.toast.challenge_complete') Event.on({ 'milo_cycle', 'milo_queue' }, function(e) - if context.storage:isOnline() then - if #context.queue > 0 then - local queue = context.queue - context.queue = { } - for _, entry in pairs(queue) do - local s, m = pcall(entry.callback, entry.request) - if not s and m then - _G._syslog('callback crashed') - _G._syslog(m) - end - end - end - end + if context.storage:isOnline() then + if #context.queue > 0 then + local queue = context.queue + context.queue = { } + for _, entry in pairs(queue) do + local s, m = pcall(entry.callback, entry.request) + if not s and m then + _G._syslog('callback crashed') + _G._syslog(m) + end + end + end + end - if e == 'milo_cycle' and not Milo:isCraftingPaused() then - local taskTimer = Util.timer() - Milo:resetCraftingStatus() + if e == 'milo_cycle' and not Milo:isCraftingPaused() then + local taskTimer = Util.timer() + Milo:resetCraftingStatus() - for _, task in ipairs(context.tasks) do - local timer = Util.timer() - local s, m = pcall(function() task:cycle(context) end) - if not s and m then - _G._syslog(task.name .. ' crashed') - _G._syslog(m) - end - task.execTime = task.execTime + timer() - end - context.taskTimer = context.taskTimer + taskTimer() - context.taskCounter = context.taskCounter + 1 + for _, task in ipairs(context.tasks) do + local timer = Util.timer() + local s, m = pcall(function() task:cycle(context) end) + if not s and m then + _G._syslog(task.name .. ' crashed') + _G._syslog(m) + end + task.execTime = task.execTime + timer() + end + context.taskTimer = context.taskTimer + taskTimer() + context.taskCounter = context.taskCounter + 1 - end + end - if context.storage:isOnline() and #context.queue > 0 then - os.queueEvent('milo_cycle') - end + if context.storage:isOnline() and #context.queue > 0 then + os.queueEvent('milo_cycle') + end end) Event.on('turtle_inventory', function() - Milo:queueRequest({ }, function() - if not Milo:isCraftingPaused() then - Milo:clearGrid() - end - end) + Milo:queueRequest({ }, function() + if not Milo:isCraftingPaused() then + Milo:clearGrid() + end + end) end) local cycleHandle cycleHandle = Event.onInterval(5, function() - Event.trigger('milo_cycle') - if context.taskCounter > 0 then - --local average = context.taskTimer / context.taskCounter - --_syslog('Interval: ' .. math.max(5, 2 + average * 3)) - --cycleHandle.updateInterval(math.max(5, 2 + average * 3)) - end + Event.trigger('milo_cycle') + if context.taskCounter > 0 then + --local average = context.taskTimer / context.taskCounter + --_syslog('Interval: ' .. math.max(5, 2 + average * 3)) + --cycleHandle.updateInterval(math.max(5, 2 + average * 3)) + 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 + if context.storage:isOnline() then + Milo:resumeCrafting({ key = 'storageOnline' }) + else + Milo:pauseCrafting({ key = 'storageOnline', msg = 'Storage offline' }) + end end) Event.on('terminate', function() - for _, node in pairs(context.storage.nodes) do - if node.category == 'display' and node.adapter and node.adapter.clear then - node.adapter.setBackgroundColor(colors.black) - node.adapter.clear() - end - end + for _, node in pairs(context.storage.nodes) do + if node.category == 'display' and node.adapter and node.adapter.clear then + node.adapter.setBackgroundColor(colors.black) + node.adapter.clear() + end + end end) os.queueEvent( - context.storage:isOnline() and 'storage_online' or 'storage_offline', - context.storage:isOnline()) + context.storage:isOnline() and 'storage_online' or 'storage_offline', + context.storage:isOnline()) local oldDebug = _G._syslog _G._syslog = function(...) - for _,v in pairs(context.loggers) do - v(...) - end - oldDebug(...) + for _,v in pairs(context.loggers) do + v(...) + end + oldDebug(...) end local s, m = pcall(function() - UI:pullEvents() + UI:pullEvents() end) _G._syslog = oldDebug diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index 359bf1c..6cb3bb6 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -13,512 +13,512 @@ local peripheral = _G.peripheral local shell = _ENV.shell local context = { - state = Config.load('miloRemote', { displayMode = 0, deposit = true }), - responseHandlers = { }, + state = Config.load('miloRemote', { displayMode = 0, deposit = true }), + responseHandlers = { }, } 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 = { - [0] = { text = 'A', help = 'Showing all items' }, - [1] = { text = 'I', help = 'Showing inventory items' }, + [0] = { text = 'A', help = 'Showing all items' }, + [1] = { text = 'I', help = 'Showing inventory items' }, } local page = UI.Page { - menuBar = UI.MenuBar { - buttons = { - { - text = 'Refresh', - x = -12, - event = 'refresh' - }, - { - name = 'config', - text = '\187', - x = -3, - }, - }, - infoBar = UI.StatusBar { - x = 1, ex = -16, - backgroundColor = colors.lightGray, - }, - }, - grid = UI.Grid { - y = 2, ey = -2, - columns = { - { heading = ' Qty', key = 'count' , width = 4, align = 'right' }, - { heading = 'Name', key = 'displayName' }, - }, - values = { }, - sortColumn = context.state.sortColumn or 'count', - inverseSort = context.state.inverseSort, - help = '^(s)tack, ^(a)ll' - }, - statusBar = UI.Window { - y = -1, - filter = UI.TextEntry { - x = 1, ex = -12, - limit = 50, - shadowText = 'filter', - backgroundColor = colors.cyan, - backgroundFocusColor = colors.cyan, - accelerators = { - [ 'enter' ] = 'eject', - [ 'up' ] = 'grid_up', - [ 'down' ] = 'grid_down', - [ 'control-a' ] = 'eject_all', - }, - }, - amount = UI.TextEntry { - x = -11, ex = -7, - limit = 3, - shadowText = '1', - shadowTextColor = colors.gray, - backgroundColor = colors.black, - backgroundFocusColor = colors.black, - accelerators = { - [ 'enter' ] = 'eject_specified', - [ 'control-a' ] = 'eject_all', - }, - help = 'Request amount', - }, - depositToggle = UI.Button { - x = -6, - event = 'toggle_deposit', - text = '\215', - }, - display = UI.Button { - x = -3, - event = 'toggle_display', - text = displayModes[context.state.displayMode].text, - help = displayModes[context.state.displayMode].help, - }, - }, - notification = UI.Notification { - anchor = 'top', - }, - accelerators = { - r = 'refresh', - [ 'control-r' ] = 'refresh', - [ 'control-e' ] = 'eject', - [ 'control-s' ] = 'eject_stack', - [ 'control-a' ] = 'eject_all', + menuBar = UI.MenuBar { + buttons = { + { + text = 'Refresh', + x = -12, + event = 'refresh' + }, + { + name = 'config', + text = '\187', + x = -3, + }, + }, + infoBar = UI.StatusBar { + x = 1, ex = -16, + backgroundColor = colors.lightGray, + }, + }, + grid = UI.Grid { + y = 2, ey = -2, + columns = { + { heading = ' Qty', key = 'count' , width = 4, align = 'right' }, + { heading = 'Name', key = 'displayName' }, + }, + values = { }, + sortColumn = context.state.sortColumn or 'count', + inverseSort = context.state.inverseSort, + help = '^(s)tack, ^(a)ll' + }, + statusBar = UI.Window { + y = -1, + filter = UI.TextEntry { + x = 1, ex = -12, + limit = 50, + shadowText = 'filter', + backgroundColor = colors.cyan, + backgroundFocusColor = colors.cyan, + accelerators = { + [ 'enter' ] = 'eject', + [ 'up' ] = 'grid_up', + [ 'down' ] = 'grid_down', + [ 'control-a' ] = 'eject_all', + }, + }, + amount = UI.TextEntry { + x = -11, ex = -7, + limit = 3, + shadowText = '1', + shadowTextColor = colors.gray, + backgroundColor = colors.black, + backgroundFocusColor = colors.black, + accelerators = { + [ 'enter' ] = 'eject_specified', + [ 'control-a' ] = 'eject_all', + }, + help = 'Request amount', + }, + depositToggle = UI.Button { + x = -6, + event = 'toggle_deposit', + text = '\215', + }, + display = UI.Button { + x = -3, + event = 'toggle_display', + text = displayModes[context.state.displayMode].text, + help = displayModes[context.state.displayMode].help, + }, + }, + notification = UI.Notification { + anchor = 'top', + }, + accelerators = { + r = 'refresh', + [ 'control-r' ] = 'refresh', + [ 'control-e' ] = 'eject', + [ 'control-s' ] = 'eject_stack', + [ 'control-a' ] = 'eject_all', - q = 'quit', - }, - items = { }, + q = 'quit', + }, + items = { }, } local function getPlayerName() - local neural = peripheral.find('neuralInterface') + local neural = peripheral.find('neuralInterface') - if neural and neural.getName then - return neural.getName() - end + if neural and neural.getName then + return neural.getName() + end 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) + 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 '' - return row + row = Util.shallowCopy(row) + row.count = row.count > 0 and Util.toBytes(row.count) or '' + 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) + 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 - context.state.sortColumn = event.sortColumn - context.state.inverseSort = event.inverseSort - Config.update('miloRemote', context.state) - end - return UI.Grid.eventHandler(self, event) + if event.type == 'grid_sort' then + context.state.sortColumn = event.sortColumn + context.state.inverseSort = event.inverseSort + Config.update('miloRemote', context.state) + end + return UI.Grid.eventHandler(self, event) end function page:transfer(item, count, msg) - context:sendRequest({ request = 'transfer', item = item, count = count }, msg) + context:sendRequest({ request = 'transfer', item = item, count = count }, msg) end function page:eventHandler(event) - if event.type == 'quit' then - UI:exitPullEvents() + if event.type == 'quit' then + UI:exitPullEvents() - elseif event.type == 'setup' then - self.setup.form:setValues(context.state) - self.setup:show() + elseif event.type == 'setup' then + self.setup.form:setValues(context.state) + self.setup:show() - elseif event.type == 'toggle_deposit' then - context.state.deposit = not context.state.deposit - Util.merge(self.statusBar.depositToggle, depositMode[context.state.deposit]) - self.statusBar:draw() - context:setStatus(depositMode[context.state.deposit].help) - context:notifyInfo(depositMode[context.state.deposit].help) - Config.update('miloRemote', context.state) + elseif event.type == 'toggle_deposit' then + context.state.deposit = not context.state.deposit + Util.merge(self.statusBar.depositToggle, depositMode[context.state.deposit]) + self.statusBar:draw() + context:setStatus(depositMode[context.state.deposit].help) + context:notifyInfo(depositMode[context.state.deposit].help) + Config.update('miloRemote', context.state) - elseif event.type == 'focus_change' then - context:setStatus(event.focused.help) + elseif event.type == 'focus_change' then + context:setStatus(event.focused.help) - elseif event.type == 'eject' or event.type == 'grid_select' then - local item = self.grid:getSelected() - if item then - self:transfer(item, 1, 'requesting 1 ...') - end + elseif event.type == 'eject' or event.type == 'grid_select' then + local item = self.grid:getSelected() + if item then + self:transfer(item, 1, 'requesting 1 ...') + end - elseif event.type == 'eject_stack' then - local item = self.grid:getSelected() - if item then - self:transfer(item, 'stack', 'requesting stack ...') - end + elseif event.type == 'eject_stack' then + local item = self.grid:getSelected() + if item then + self:transfer(item, 'stack', 'requesting stack ...') + end - elseif event.type == 'eject_all' then - local item = self.grid:getSelected() - if item then - self:transfer(item, 'all', 'requesting all ...') - end + elseif event.type == 'eject_all' then + local item = self.grid:getSelected() + if item then + self:transfer(item, 'all', 'requesting all ...') + 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:transfer(item, count, 'requesting ' .. count .. ' ...') - else - Sound.play('entity.villager.no') - context:notifyError('nope ...') - 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:transfer(item, count, 'requesting ' .. count .. ' ...') + else + Sound.play('entity.villager.no') + context:notifyError('nope ...') + end - elseif event.type == 'plugin' then - event.button.callback(context) + elseif event.type == 'plugin' then + event.button.callback(context) - elseif event.type == 'rescan' then - self:setFocus(self.statusBar.filter) - self:refresh('scan') - self.grid:draw() + elseif event.type == 'rescan' then + self:setFocus(self.statusBar.filter) + self:refresh('scan') + self.grid:draw() - elseif event.type == 'grid_up' then - self.grid:emit({ type = 'scroll_up' }) + elseif event.type == 'grid_up' then + self.grid:emit({ type = 'scroll_up' }) - elseif event.type == 'grid_down' then - self.grid:emit({ type = 'scroll_down' }) + elseif event.type == 'grid_down' then + self.grid:emit({ type = 'scroll_down' }) - elseif event.type == 'refresh' then - self:setFocus(self.statusBar.filter) - self:refresh('list') - self.grid:draw() + elseif event.type == 'refresh' then + self:setFocus(self.statusBar.filter) + self:refresh('list') + self.grid:draw() - elseif event.type == 'toggle_display' then - context.state.displayMode = (context.state.displayMode + 1) % 2 - Util.merge(event.button, displayModes[context.state.displayMode]) - event.button:draw() - self:applyFilter() - context:setStatus(event.button.help) - context:notifyInfo(event.button.help) - self.grid:draw() - Config.update('miloRemote', context.state) + elseif event.type == 'toggle_display' then + context.state.displayMode = (context.state.displayMode + 1) % 2 + Util.merge(event.button, displayModes[context.state.displayMode]) + event.button:draw() + self:applyFilter() + context:setStatus(event.button.help) + context:notifyInfo(event.button.help) + self.grid:draw() + Config.update('miloRemote', context.state) - 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:setIndex(1) - self.grid:draw() + 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:setIndex(1) + self.grid:draw() - else - UI.Page.eventHandler(self, event) - end - return true + else + UI.Page.eventHandler(self, event) + end + return true end function page:enable() - self:setFocus(self.statusBar.filter) - Util.merge(self.statusBar.depositToggle, depositMode[context.state.deposit]) - UI.Page.enable(self) - if not context.state.server then - self.setup.form:setValues(context.state) - self.setup:show() - end - Event.onTimeout(.1, function() - self:refresh('list') - self.grid:draw() - self:sync() - end) + self:setFocus(self.statusBar.filter) + Util.merge(self.statusBar.depositToggle, depositMode[context.state.deposit]) + UI.Page.enable(self) + if not context.state.server then + self.setup.form:setValues(context.state) + self.setup:show() + end + Event.onTimeout(.1, function() + self:refresh('list') + self.grid:draw() + self:sync() + 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 + 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.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 + local t = { } + for k,v in pairs(list) do + local item = splitKey(k) + 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 end function page:refresh(requestType) - context:sendRequest({ request = requestType }, 'refreshing...') + context:sendRequest({ request = requestType }, 'refreshing...') end function page:applyFilter() - local function filterItems(t, filter, displayMode) - self.grid.sortColumn = context.state.sortColumn or 'count' - self.grid.inverseSort = context.state.inverseSort + local function filterItems(t, filter, displayMode) + self.grid.sortColumn = context.state.sortColumn or 'count' + self.grid.inverseSort = context.state.inverseSort - if filter then - local r = { } - filter = filter:lower() - self.grid.sortColumn = 'score' - self.grid.inverseSort = true + if filter then + local r = { } + filter = filter:lower() + self.grid.sortColumn = 'score' + self.grid.inverseSort = true - for _,v in pairs(t) do - v.score = fuzzy(v.lname, filter) - if v.score then - if v.count > 0 then - v.score = v.score + 1 - end - table.insert(r, v) - end - end - return r + for _,v in pairs(t) do + v.score = fuzzy(v.lname, filter) + if v.score then + if v.count > 0 then + v.score = v.score + 1 + end + table.insert(r, v) + end + end + return r - elseif displayMode > 0 then - local r = { } + elseif displayMode > 0 then + local r = { } - for _,v in pairs(t) do - if v.count > 0 then - table.insert(r, v) - end - end - return r - end + for _,v in pairs(t) do + if v.count > 0 then + table.insert(r, v) + end + end + return r + end - return t - end - local t = filterItems(self.items, self.filter, context.state.displayMode) - self.grid:setValues(t) + return t + end + local t = filterItems(self.items, self.filter, context.state.displayMode) + self.grid:setValues(t) end context.page = page function context:setStatus(status) - page.menuBar.infoBar.values = status - page.menuBar.infoBar:draw() - page:sync() + page.menuBar.infoBar.values = status + page.menuBar.infoBar:draw() + page:sync() end function context:notifySuccess(status) - page.notification:success(status) - page:sync() + page.notification:success(status) + page:sync() end function context:notifyInfo(status) - page.notification:info(status) - page:sync() + page.notification:info(status) + page:sync() end function context:notifyError(status) - page.notification:error(status) - page:sync() + page.notification:error(status) + page:sync() end local function processMessages(s) - Event.addRoutine(function() - s.co = coroutine.running() - repeat - local response = s:read() - if not response then - break - end - local h = context.responseHandlers[response.type] - if h then - h(response) - end - if response.msg then - context:notifyInfo(response.msg) - end - until not s.connected + Event.addRoutine(function() + s.co = coroutine.running() + repeat + local response = s:read() + if not response then + break + end + local h = context.responseHandlers[response.type] + if h then + h(response) + end + if response.msg then + context:notifyInfo(response.msg) + end + until not s.connected - s:close() - s = nil - context:notifyError('disconnected ...') - Sound.play('entity.villager.no') - end) + s:close() + s = nil + context:notifyError('disconnected ...') + Sound.play('entity.villager.no') + end) end function context:sendRequest(data, statusMsg) - if not context.state.server then - self:notifyError('Invalid configuration') - return - end + if not context.state.server then + self:notifyError('Invalid configuration') + return + end - local player = getPlayerName() - if not player then - self:notifyError('Missing neural or introspection') - return - end + local player = getPlayerName() + if not player then + self:notifyError('Missing neural or introspection') + return + end - local success - sync(page, function() - local msg - for _ = 1, 2 do - if not context.socket or not context.socket.connected then - self:notifyInfo('connecting ...') - context.socket, msg = Socket.connect(context.state.server, 4242) - if context.socket then - context.socket:write(player) - local r = context.socket:read(2) - if r and not r.msg then - self:notifySuccess('connected ...') - processMessages(context.socket) - else - msg = r and r.msg or 'Timed out' - context.socket:close() - context.socket = nil - end - end - end - if context.socket then - if statusMsg then - self:notifyInfo(statusMsg) - end - if context.socket:write(data) then - success = true - return - end - context.socket:close() - context.socket = nil - end - end - self:notifyError(msg or 'Failed to connect') - end) + local success + sync(page, function() + local msg + for _ = 1, 2 do + if not context.socket or not context.socket.connected then + self:notifyInfo('connecting ...') + context.socket, msg = Socket.connect(context.state.server, 4242) + if context.socket then + context.socket:write(player) + local r = context.socket:read(2) + if r and not r.msg then + self:notifySuccess('connected ...') + processMessages(context.socket) + else + msg = r and r.msg or 'Timed out' + context.socket:close() + context.socket = nil + end + end + end + if context.socket then + if statusMsg then + self:notifyInfo(statusMsg) + end + if context.socket:write(data) then + success = true + return + end + context.socket:close() + context.socket = nil + end + end + self:notifyError(msg or 'Failed to connect') + end) - return success + return success end function context:getState(key) - return self.state[key] + return self.state[key] end function context:setState(key, value) - self.state[key] = value - Config.update('miloRemote', self.state) + self.state[key] = value + Config.update('miloRemote', self.state) end context.responseHandlers['received'] = function(response) - Sound.play('entity.item.pickup') - local ritem = page.items[response.key] - if ritem then - ritem.count = response.count - if page.enabled then - page.grid:draw() - page:sync() - end - end + Sound.play('entity.item.pickup') + local ritem = page.items[response.key] + if ritem then + ritem.count = response.count + if page.enabled then + page.grid:draw() + page:sync() + end + end end context.responseHandlers['list'] = function(response) - page.items = page:expandList(response.list) - page:applyFilter() - if page.enabled then - page.grid:draw() - page.grid:sync() - end + page.items = page:expandList(response.list) + page:applyFilter() + if page.enabled then + page.grid:draw() + page.grid:sync() + end end context.responseHandlers['transfer'] = function(response) - if response.count > 0 then - Sound.play('entity.item.pickup') - local item = page.items[response.key] - if item then - item.count = response.current - if page.enabled then - page.grid:draw() - page:sync() - end - end - end - if response.craft then - if response.craft > 0 then - context:notifyInfo(response.craft .. ' crafting ...') - elseif response.craft + response.count < response.requested then - if response.craft + response.count == 0 then - Sound.play('entity.villager.no') - end - context:notifyInfo((response.craft + response.count) .. ' available ...') - end - end + if response.count > 0 then + Sound.play('entity.item.pickup') + local item = page.items[response.key] + if item then + item.count = response.current + if page.enabled then + page.grid:draw() + page:sync() + end + end + end + if response.craft then + if response.craft > 0 then + context:notifyInfo(response.craft .. ' crafting ...') + elseif response.craft + response.count < response.requested then + if response.craft + response.count == 0 then + Sound.play('entity.villager.no') + end + context:notifyInfo((response.craft + response.count) .. ' available ...') + end + end end local function loadDirectory(dir) - local dropdown = { - { text = 'Setup', event = 'setup' }, - { spacer = true }, - { - text = 'Rescan storage', - event = 'rescan', - help = 'Rescan all inventories' - }, - } + local dropdown = { + { text = 'Setup', event = 'setup' }, + { spacer = true }, + { + text = 'Rescan storage', + event = 'rescan', + help = 'Rescan all inventories' + }, + } - for _, file in pairs(fs.list(dir)) do - local s, m = Util.run(_ENV, fs.combine(dir, file), context) - if not s and m then - _G.printError('Error loading: ' .. file) - error(m or 'Unknown error') - elseif s and m then - table.insert(dropdown, { - text = m.menuItem, - event = 'plugin', - callback = m.callback, - }) - end - end - page.menuBar.config:add({ dropmenu = UI.DropMenu { buttons = dropdown } }) + for _, file in pairs(fs.list(dir)) do + local s, m = Util.run(_ENV, fs.combine(dir, file), context) + if not s and m then + _G.printError('Error loading: ' .. file) + error(m or 'Unknown error') + elseif s and m then + table.insert(dropdown, { + text = m.menuItem, + event = 'plugin', + callback = m.callback, + }) + end + end + page.menuBar.config:add({ dropmenu = UI.DropMenu { buttons = dropdown } }) end local programDir = fs.getDir(shell.getRunningProgram()) @@ -528,5 +528,5 @@ UI:setPage(page) UI:pullEvents() if context.socket then - context.socket:close() + context.socket:close() end diff --git a/milo/apis/craft2.lua b/milo/apis/craft2.lua index 06c2403..13891c7 100644 --- a/milo/apis/craft2.lua +++ b/milo/apis/craft2.lua @@ -35,6 +35,39 @@ local function makeRecipeKey(item) return table.concat({ item.name, item.damage or 0, item.nbtHash }, ':') end +local function convert(ingredient) + return type(ingredient) == 'table' and ingredient or { + key = ingredient, + count = 1, + } +end + +local function getCraftingTool(storage, item) + local items = storage:listItems() + + for _,v in pairs(items) do + if item.name == v.name and + (not item.damage or item.damage == v.damage) and + (not item.nbtHash or item.nbtHash == v.nbtHash) then + return v + end + end + + return item +end + +function Craft.ingedients(recipe) + local i = 0 + local keys = Util.keys(recipe.ingredients) + return function() + i = i + 1 + local a = keys[i] + if a then + return a, convert(recipe.ingredients[a]) + end + end +end + function Craft.clearGrid(storage) local success = true local tasks = Tasks() @@ -74,8 +107,9 @@ 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 + for _,entry in pairs(recipe.ingredients) do + local item = convert(entry) + t[item.key] = (t[item.key] or 0) + item.count end return t end @@ -108,12 +142,13 @@ local function machineCraft(recipe, storage, machineName, request, count, item) if count > 0 then local xferred = { } for k,v in pairs(recipe.ingredients) do - local provided = storage:export(machine, k, count, splitKey(v)) + local entry = convert(v) + local provided = storage:export(machine, k, count * entry.count, splitKey(entry.key)) xferred[k] = { - key = v, + key = entry.key, count = provided, } - if provided ~= count then + if provided ~= count * entry.count then -- take back out whatever we put in for k2,v2 in pairs(xferred) do if v2.count > 0 then @@ -143,6 +178,9 @@ local function turtleCraft(recipe, storage, request, count) for k,v in pairs(recipe.ingredients) do local item = splitKey(v) + if recipe.craftingTools and recipe.craftingTools[v] then + item = getCraftingTool(storage, item) + end tasks:add(function() if storage:export(storage.turtleInventory, k, count, item) ~= count then request.status = 'rescan needed ?' @@ -185,7 +223,8 @@ local function turtleCraft(recipe, storage, request, count) end function Craft.processPending(item, storage) - for key, count in pairs(item.pending) do + for _, key in pairs(Util.keys(item.pending)) do + local count = item.pending[key] local imported = storage.activity[key] if imported then local amount = math.min(imported, count) @@ -238,7 +277,6 @@ end function Craft.craftRecipeInternal(recipe, count, storage, origItem, path) local request = origItem.ingredients[recipe.result] - --[[ if origItem.pending[recipe.result] then request.status = 'processing' @@ -425,23 +463,24 @@ function Craft.getCraftableAmount(inRecipe, inCount, items, missing) local canCraft = 0 for _ = 1, count do - for _,item in pairs(recipe.ingredients) do - local summedItem = summedItems[item] or Craft.getItemCount(items, item) + for _,entry in pairs(recipe.ingredients) do + local item = convert(entry) + local summedItem = summedItems[item.key] or Craft.getItemCount(items, item.key) - local irecipe = findValidRecipe(item, path) + local irecipe = findValidRecipe(item.key, path) if irecipe and summedItem <= 0 then local p = Util.shallowCopy(path) p[irecipe.result] = true - summedItem = summedItem + sumItems(irecipe, summedItems, 1, p) + summedItem = summedItem + sumItems(irecipe, summedItems, item.count, p) end if summedItem <= 0 then if missing and not irecipe then - missing.name = item + missing.name = item.key end return canCraft end - if not recipe.craftingTools or not recipe.craftingTools[item] then - summedItems[item] = summedItem - 1 + if not recipe.craftingTools or not recipe.craftingTools[item.key] then + summedItems[item.key] = summedItem - item.count end end canCraft = canCraft + recipe.count diff --git a/milo/apis/fuzzyMatch.lua b/milo/apis/fuzzyMatch.lua index 5fb7bd2..7a6082d 100644 --- a/milo/apis/fuzzyMatch.lua +++ b/milo/apis/fuzzyMatch.lua @@ -11,9 +11,9 @@ local _find = string.find local _max = math.max return function(str, pattern) - local start = _find(str, pattern, 1, true) - if start then - -- All letters before the current one are considered leading, so add them to our penalty - return SCORE_WEIGHT + _max(LEADING_LETTER_PENALTY * (start - 1), LEADING_LETTER_PENALTY_MAX) - end + local start = _find(str, pattern, 1, true) + if start then + -- All letters before the current one are considered leading, so add them to our penalty + return SCORE_WEIGHT + _max(LEADING_LETTER_PENALTY * (start - 1), LEADING_LETTER_PENALTY_MAX) + end end diff --git a/milo/apis/init.lua b/milo/apis/init.lua index 810a08a..8f1504a 100644 --- a/milo/apis/init.lua +++ b/milo/apis/init.lua @@ -224,8 +224,8 @@ function Milo:eject(item, count) total = total + amount count = count - amount - --Sound.play('ui.button.click') - Sound.play('entity.illusion_illager.death', .3) + Sound.play('ui.button.click') + --Sound.play('entity.illusion_illager.death', .3) turtle.emptyInventory() end return total @@ -273,6 +273,7 @@ function Milo:learnRecipe() local tool = Util.shallowCopy(v2) if tool.maxDamage > 0 then tool.damage = '*' + v2.damage = '*' end --[[ diff --git a/milo/apis/massAdapter.lua b/milo/apis/massAdapter.lua index 62e5208..0534662 100644 --- a/milo/apis/massAdapter.lua +++ b/milo/apis/massAdapter.lua @@ -7,46 +7,46 @@ local os = _G.os local Adapter = class(Mini) function Adapter:init(args) - Mini.init(self, args) + Mini.init(self, args) - self._rawList = self.list + self._rawList = self.list - function self.list() - -- wait for up to 1 sec until any items that have been inserted - -- into interface are added to the system - for _ = 0, 20 do - if #self._rawList() == 0 then - break - end - os.sleep(0) - end + function self.list() + -- wait for up to 1 sec until any items that have been inserted + -- into interface are added to the system + for _ = 0, 20 do + if #self._rawList() == 0 then + break + end + os.sleep(0) + end - local list = { } - for _, v in pairs(self.listAvailableItems()) do - list[itemDB:makeKey(v)] = v - end - return list - end + local list = { } + for _, v in pairs(self.listAvailableItems()) do + list[itemDB:makeKey(v)] = v + end + return list + end - function self.getItemMeta(key) - local item = self.findItem(itemDB:splitKey(key)) - if item and item.getMetadata then - return item.getMetadata() - end - end + function self.getItemMeta(key) + local item = self.findItem(itemDB:splitKey(key)) + if item and item.getMetadata then + return item.getMetadata() + end + end - function self.pushItems(target, key, amount, slot) - local item = self.findItem(itemDB:splitKey(key)) - if item and item.export then - return item.export(target, amount, slot) - end - return 0 - end + function self.pushItems(target, key, amount, slot) + local item = self.findItem(itemDB:splitKey(key)) + if item and item.export then + return item.export(target, amount, slot) + end + return 0 + end - function self.pullItems(target, key, amount, slot) - _G._syslog({target, key, amount, slot }) - return 0 - end + function self.pullItems(target, key, amount, slot) + _G._syslog({target, key, amount, slot }) + return 0 + end end diff --git a/milo/apis/miniAdapter.lua b/milo/apis/miniAdapter.lua index 72b958b..34fab70 100644 --- a/milo/apis/miniAdapter.lua +++ b/milo/apis/miniAdapter.lua @@ -7,37 +7,37 @@ 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 + 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() + 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 }, ':') + 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 entry then - entry.count = entry.count + v.count - else - cache[key] = itemDB:get(v, function() return self.getItemMeta(k) end) - end - throttle() - end - end + local entry = cache[key] + if entry then + entry.count = entry.count + v.count + else + cache[key] = itemDB:get(v, function() return self.getItemMeta(k) end) + end + throttle() + end + end - -- TODO: cache number of slots, free slots, used slots - -- useful for when inserting into chests - -- ie. insert only if chest does not have item and has free slots + -- TODO: cache number of slots, free slots, used slots + -- useful for when inserting into chests + -- ie. insert only if chest does not have item and has free slots - self.cache = cache + self.cache = cache end return Adapter diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 9033977..51cf986 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -12,589 +12,589 @@ local parallel = _G.parallel local Storage = class() local function loadOld(storage) - storage.nodes = Config.load('milo', { }) + storage.nodes = Config.load('milo', { }) - -- TODO: remove - temporary - if storage.nodes.remoteDefaults then - storage.nodes.nodes = storage.nodes.remoteDefaults - storage.nodes.remoteDefaults = nil - end + -- TODO: remove - temporary + if storage.nodes.remoteDefaults then + storage.nodes.nodes = storage.nodes.remoteDefaults + storage.nodes.remoteDefaults = nil + end - -- TODO: remove - temporary - if storage.nodes.nodes then - local categories = { - input = 'custom', - trashcan = 'custom', - machine = 'machine', - brewingStand = 'machine', - activity = 'display', - jobs = 'display', - ignore = 'ignore', - hidden = 'ignore', - manipulator = 'custom', - storage = 'storage', - } - for _, node in pairs(storage.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 - storage.nodes = storage.nodes.nodes - end + -- TODO: remove - temporary + if storage.nodes.nodes then + local categories = { + input = 'custom', + trashcan = 'custom', + machine = 'machine', + brewingStand = 'machine', + activity = 'display', + jobs = 'display', + ignore = 'ignore', + hidden = 'ignore', + manipulator = 'custom', + storage = 'storage', + } + for _, node in pairs(storage.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 + storage.nodes = storage.nodes.nodes + end end function Storage:init() - local defaults = { - dirty = true, - activity = { }, - storageOnline = true, - } - Util.merge(self, defaults) - self.nodes = Config.load('storage', { }) + local defaults = { + dirty = true, + activity = { }, + storageOnline = true, + } + Util.merge(self, defaults) + self.nodes = Config.load('storage', { }) - if Util.empty(self.nodes) then -- TODO: temporary - loadOld(self) - end + if Util.empty(self.nodes) then -- TODO: temporary + loadOld(self) + end - Event.on({ 'device_attach', 'device_detach' }, function(e, dev) + Event.on({ 'device_attach', 'device_detach' }, function(e, dev) _G._syslog('%s: %s', e, tostring(dev)) - self:initStorage() - end) - Event.onInterval(60, function() - self:showStorage() - end) + self:initStorage() + end) + Event.onInterval(60, function() + self:showStorage() + end) 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 not ignores[v.mtype] then - table.insert(t, k) - end - end - if #t > 0 then - _G._syslog('Adapter:') - for _, k in pairs(t) do - _G._syslog(' offline: ' .. k) - end - _G._syslog('') - end + 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 not ignores[v.mtype] then + table.insert(t, k) + end + end + if #t > 0 then + _G._syslog('Adapter:') + for _, k in pairs(t) do + _G._syslog(' offline: ' .. k) + end + _G._syslog('') + end end function Storage:isOnline() - return self.storageOnline + return self.storageOnline end function Storage:initStorage() - local online = true + local online = true - -- unknown why this is not working below - for _,v in pairs(device) do - v.transferLocations = nil - end + -- unknown why this is not working below + for _,v in pairs(device) do + v.transferLocations = nil + end - for k,v in pairs(self.nodes) do - 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 - if v.adapterType then - v.adapter = require(v.adapterType)({ side = k }) - else - v.adapter = Adapter({ side = k }) - end - v.adapter.online = true - v.adapter.dirty = true + for k,v in pairs(self.nodes) do + 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 + if v.adapterType then + v.adapter = require(v.adapterType)({ side = k }) + else + v.adapter = Adapter({ side = k }) + end + v.adapter.online = true + v.adapter.dirty = true - if v.adapter.isOn and not v.adapter.isOn() then -- turtle - v.adapter.turnOn() - end - elseif device[k] then - v.adapter = device[k] - v.adapter.online = true - end + if v.adapter.isOn and not v.adapter.isOn() then -- turtle + v.adapter.turnOn() + end + elseif device[k] then + v.adapter = device[k] + v.adapter.online = true + end - if v.adapter then - -- force a new getTransferLocations() as the list may have changed - v.adapter.transferLocations = nil - end + if v.adapter then + -- force a new getTransferLocations() as the list may have changed + v.adapter.transferLocations = nil + end - if v.mtype == 'storage' then - online = online and not not (v.adapter and v.adapter.online) - end - end - end + if v.mtype == 'storage' then + online = online and not not (v.adapter and v.adapter.online) + end + end + end - 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._syslog('Storage: %s', self.storageOnline and 'online' or 'offline') - end + 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._syslog('Storage: %s', self.storageOnline and 'online' or 'offline') + end - self:listItems() + self:listItems() end function Storage:saveConfiguration() - local t = { } - for k,v in pairs(self.nodes) do - t[k] = v.adapter - v.adapter = nil - end + local t = { } + for k,v in pairs(self.nodes) do + t[k] = v.adapter + v.adapter = nil + end - Config.update('storage', self.nodes) + Config.update('storage', self.nodes) - for k,v in pairs(t) do - self.nodes[k].adapter = v - end - self:initStorage() + 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 - return node - end + 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.mtype == mtype then - if not filter or filter(v) then - table.insert(iter, v) - end - end - end + local iter = { } + for _, v in pairs(self.nodes) do + if 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 + local i = 0 + return function() + i = i + 1 + return iter[i] + 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) + 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() - local iter = { } - for _, v in pairs(self.nodes) do - if v.adapter and v.adapter.online and v.mtype == 'storage' then - table.insert(iter, v) - end - end + local iter = { } + for _, v in pairs(self.nodes) do + if v.adapter and v.adapter.online and v.mtype == 'storage' then + table.insert(iter, v) + end + end - 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) + 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 i = 0 - return function() - i = i + 1 - local a = iter[i] - if a then - return a, a.adapter - end - end + local i = 0 + return function() + i = i + 1 + local a = iter[i] + if a then + return a, a.adapter + end + end end function Storage:setDirty() - self.dirty = true + self.dirty = true end function Storage:refresh(throttle) - self.dirty = true + self.dirty = true _G._syslog('STORAGE: Forcing full refresh') - for _, adapter in self:onlineAdapters() do - adapter.dirty = true - end - return self:listItems(throttle) + for _, adapter in self:onlineAdapters() do + adapter.dirty = true + end + return self:listItems(throttle) end -- provide a consolidated list of items function Storage:listItems(throttle) - if not self.dirty then - return self.cache - end + if not self.dirty then + return self.cache + end - local timer = Util.timer() - local cache = { } - throttle = throttle or Util.throttle() + local timer = Util.timer() + local cache = { } + throttle = throttle or Util.throttle() - local t = { } - for _, adapter in self:onlineAdapters() do - if adapter.dirty then - table.insert(t, function() - adapter:listItems(throttle) - if not adapter.__size then - adapter.__size = adapter.size() - end - adapter.dirty = false - end) - end - end + local t = { } + for _, adapter in self:onlineAdapters() do + if adapter.dirty then + table.insert(t, function() + adapter:listItems(throttle) + if not adapter.__size then + adapter.__size = adapter.size() + end + adapter.dirty = false + end) + end + end - if #t > 0 then - parallel.waitForAll(table.unpack(t)) - end + if #t > 0 then + parallel.waitForAll(table.unpack(t)) + end - for _, adapter in self:onlineAdapters() do - 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 + for _, adapter in self:onlineAdapters() do + 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._syslog('STORAGE: refresh ' .. #t .. ' inventories in ' .. Util.round(timer(), 2)) + throttle() + end + end + itemDB:flush() + _G._syslog('STORAGE: refresh ' .. #t .. ' inventories in ' .. Util.round(timer(), 2)) - self.dirty = false - self.cache = cache + self.dirty = false + self.cache = cache - return self.cache + return self.cache end function Storage:updateCache(adapter, item, count) - if not adapter.cache then - adapter.dirty = true - self.dirty = true - return - end + 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] + 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 - _G._syslog('STORAGE: update cache - count < 0', 4) - else - entry = Util.shallowCopy(item) - 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 + if not entry then + if count < 0 then + _G._syslog('STORAGE: update cache - count < 0', 4) + else + entry = Util.shallowCopy(item) + 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 - if not entry then - _G._syslog('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 + if not entry then + _G._syslog('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) - local node = self.nodes[name] - return node and node.displayName or name + local node = self.nodes[name] + return node and node.displayName or name end local function isValidTransfer(adapter, target) - -- lazily cache transfer locations - if not adapter.transferLocations then - adapter.transferLocations = adapter.getTransferLocations() - end - for _,v in pairs(adapter.transferLocations) do - if v == target then - return true - end - end + -- lazily cache transfer locations + if not adapter.transferLocations then + adapter.transferLocations = adapter.getTransferLocations() + end + for _,v in pairs(adapter.transferLocations) do + if v == target then + return true + end + end end local function rawExport(source, target, item, qty, slot) - local total = 0 - local transfer + local total = 0 + local transfer - if isValidTransfer(source, target.name) then - transfer = function(key, amount) - return source.pushItems(target.name, key, amount, slot) - end - else --if isValidTransfer(target, source.name) then - transfer = function(key, amount) - return target.pullItems(source.name, key, amount, slot) - end - end - --[[ - -- TODO: mass storage will require a transfer chest (or something) - elseif isValidTransfer(source, 'minecraft:chest_0') then - transfer = function(key, amount) - local a = source.pushItems('minecraft:chest_0', key, amount, 1) - return target.pullItems('minecraft:chest_0', 1, amount, slot) - end - else - ... - end - ]] + if isValidTransfer(source, target.name) then + transfer = function(key, amount) + return source.pushItems(target.name, key, amount, slot) + end + else --if isValidTransfer(target, source.name) then + transfer = function(key, amount) + return target.pullItems(source.name, key, amount, slot) + end + end + --[[ + -- TODO: mass storage will require a transfer chest (or something) + elseif isValidTransfer(source, 'minecraft:chest_0') then + transfer = function(key, amount) + local a = source.pushItems('minecraft:chest_0', key, amount, 1) + return target.pullItems('minecraft:chest_0', 1, amount, slot) + end + else + ... + end + ]] - 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 - amount = transfer(key, amount, slot) - if amount > 0 then - source.lastUpdate = os.clock() - target.lastUpdate = os.clock() - end - end - qty = qty - amount - total = total + amount - if qty <= 0 then - break - end - end - end - end) + 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 + amount = transfer(key, amount, slot) + if amount > 0 then + source.lastUpdate = os.clock() + target.lastUpdate = os.clock() + end + end + qty = qty - amount + total = total + amount + if qty <= 0 then + break + end + end + end + end) - if not s and m then - _G._syslog(m) - end + if not s and m then + _G._syslog(m) + end - return total, m + return total, m end function Storage:export(target, slot, count, item) - local timer = Util.timer() - local total = 0 - local key = item.key or table.concat({ item.name, item.damage, item.nbtHash }, ':') + local timer = Util.timer() + local total = 0 + local key = item.key or table.concat({ item.name, item.damage, item.nbtHash }, ':') - local function provide(adapter, pcount) - -- update cache before export to allow for simultaneous calls - self:updateCache(adapter, item, -pcount) + local function provide(adapter, pcount) + -- update cache before export to allow for simultaneous calls + self:updateCache(adapter, item, -pcount) - local amount = rawExport(adapter, target.adapter, item, pcount, slot) + local amount = rawExport(adapter, target.adapter, item, pcount, slot) - if amount ~= pcount then - -- this *should* only happen if cache is out of sync - self:updateCache(adapter, item, pcount - amount) - end + if amount ~= pcount then + -- this *should* only happen if cache is out of sync + self:updateCache(adapter, item, pcount - amount) + end - if amount > 0 then - _G._syslog('EXT: %s(%d): %s -> %s%s in %s', - item.displayName or item.name, amount, self:_sn(adapter.name), self:_sn(target.name), - slot and string.format('[%d]', slot) or '[*]', Util.round(timer(), 2)) - end - count = count - amount - total = total + amount - end + if amount > 0 then + _G._syslog('EXT: %s(%d): %s -> %s%s in %s', + item.displayName or item.name, amount, self:_sn(adapter.name), self:_sn(target.name), + slot and string.format('[%d]', slot) or '[*]', Util.round(timer(), 2)) + end + count = count - amount + total = total + amount + end - -- request from adapters with this item - for _, adapter in self:onlineAdapters() do - local cache = adapter.cache and adapter.cache[key] - if cache then - provide(adapter, math.min(count, cache.count)) - if count <= 0 then - return total - end - end - end + -- request from adapters with this item + for _, adapter in self:onlineAdapters() do + local cache = adapter.cache and adapter.cache[key] + if cache then + provide(adapter, math.min(count, cache.count)) + if count <= 0 then + return total + end + end + end - _G._syslog('STORAGE warning: %s(%d): %s%s %s failed to export', - item.displayName or item.name, count, self:_sn(target.name), - slot and string.format('[%d]', slot) or '[*]', key) + _G._syslog('STORAGE warning: %s(%d): %s%s %s failed to export', + 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... -- 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 - return total + return total end local function rawInsert(source, target, slot, qty) - local count = 0 + local count = 0 - local s, m = pcall(function() - if isValidTransfer(source, target.name) then + local s, m = pcall(function() + if isValidTransfer(source, target.name) then --_syslog('pull %s %s %d %d', source.name, target.name, slot, qty) - count = source.pullItems(target.name, slot, qty) - else + count = source.pullItems(target.name, slot, qty) + else --_syslog('push %s %s', target.name, source.name) - count = target.pushItems(source.name, slot, qty) - end - end) - if not s and m then - _G._syslog(m) - end + count = target.pushItems(source.name, slot, qty) + end + end) + if not s and m then + _G._syslog(m) + end - if count > 0 then - source.lastUpdate = os.clock() - target.lastUpdate = os.clock() - end + if count > 0 then + source.lastUpdate = os.clock() + target.lastUpdate = os.clock() + end - return count + 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 + if not source then error('Storage:import: source is required') end + if not slot then error('Storage:import: slot is required') end - local timer = Util.timer() - local total = 0 - local key = item.key or table.concat({ item.name, item.damage, item.nbtHash }, ':') + local timer = Util.timer() + local total = 0 + local key = item.key or table.concat({ item.name, item.damage, item.nbtHash }, ':') - if not self.cache then - self:listItems() - end + if not self.cache then + 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(source.adapter.getItemMeta(slot)) - end - itemDB:flush() - end - item = entry + 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(source.adapter.getItemMeta(slot)) + end + itemDB:flush() + end + item = entry - local function insert(adapter) - local amount = rawInsert(adapter, source.adapter, slot, count) + local function insert(adapter) + local amount = rawInsert(adapter, source.adapter, slot, count) - if amount > 0 then - self:updateCache(adapter, item, amount) + if amount > 0 then + self:updateCache(adapter, item, amount) - _G._syslog('INS: %s(%d): %s[%d] -> %s in %s', - item.displayName or item.name, amount, - self:_sn(source.name), slot, self:_sn(adapter.name), Util.round(timer(), 2)) + _G._syslog('INS: %s(%d): %s[%d] -> %s in %s', + item.displayName or item.name, amount, + self:_sn(source.name), slot, self:_sn(adapter.name), Util.round(timer(), 2)) - -- 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 - end + -- 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 + end - -- find a chest locked with this item - for node in self:onlineAdapters() do - if node.lock and node.lock[key] then - insert(node.adapter, item) - if count > 0 and node.void then - return total + self:trash(source, slot, count, item) - end - end - if count <= 0 then - return total - end - end + -- find a chest locked with this item + for node in self:onlineAdapters() do + if node.lock and node.lock[key] then + insert(node.adapter, item) + if count > 0 and node.void then + return total + self:trash(source, slot, count, item) + end + end + if count <= 0 then + return total + 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 not node.lock and adapter.cache and adapter.cache[key] then - insert(adapter) - end - 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 not node.lock and adapter.cache and adapter.cache[key] then + insert(adapter) + end + end + end - -- high to low priority - for node in self:onlineAdapters() do - if count <= 0 then - break - end - if not node.lock then - insert(node.adapter) - end - end + -- high to low priority + for node in self:onlineAdapters() do + if count <= 0 then + break + end + if not node.lock then + insert(node.adapter) + end + end - if count ~= 0 then - _G._syslog('STORAGE warning: %s(%d): %s -> INSERT failed', - item.displayName or item.name, count, - self:_sn(source.name)) - end + if count ~= 0 then + _G._syslog('STORAGE warning: %s(%d): %s -> INSERT failed', + item.displayName or item.name, count, + self:_sn(source.name)) + end - return total + return total end -- When importing items into a locked chest, trash any remaining items if full -- TODO: use all available trashcans function Storage:trash(source, slot, count, item) - local timer = Util.timer() - 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() - 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 + local timer = Util.timer() + 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() + 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 - _G._syslog('TRA: %s(%d): %s%s -> %s in %s', - item.displayName or item.name, amount, self:_sn(source.name), - slot and string.format('[%d]', slot) or '[*]', self:_sn(target.name), Util.round(timer(), 2)) - end) - if not s and m then - _G._syslog(m) - end - end + _G._syslog('TRA: %s(%d): %s%s -> %s in %s', + item.displayName or item.name, amount, self:_sn(source.name), + slot and string.format('[%d]', slot) or '[*]', self:_sn(target.name), Util.round(timer(), 2)) + end) + if not s and m then + _G._syslog(m) + end + end - if amount ~= count then - _G._syslog('STORAGE warning: %s(%d): %s -> TRASH failed', - item.displayName or item.name, count - amount, - self:_sn(source.name)) - end + if amount ~= count then + _G._syslog('STORAGE warning: %s(%d): %s -> TRASH failed', + item.displayName or item.name, count - amount, + self:_sn(source.name)) + end - return amount + return amount end return Storage diff --git a/milo/apis/taskRunner.lua b/milo/apis/taskRunner.lua index 2a4dd5b..470493e 100644 --- a/milo/apis/taskRunner.lua +++ b/milo/apis/taskRunner.lua @@ -7,68 +7,68 @@ local free = { } local function createTask(fn) local task = table.remove(free) - if not task then - task = { - fn = fn, - co = coroutine.create(function() - local args = { } - while true do - pcall(task.fn, table.unpack(args)) - task.dead = true - table.insert(free, task) - args = { coroutine.yield() } - end - end) - } - else - task.dead = nil - task.fn = fn - end + if not task then + task = { + fn = fn, + co = coroutine.create(function() + local args = { } + while true do + pcall(task.fn, table.unpack(args)) + task.dead = true + table.insert(free, task) + args = { coroutine.yield() } + end + end) + } + else + task.dead = nil + task.fn = fn + end return task end function TaskRunner:init(args) - self.tasks = { } - self.errorMsg = 'Task failed: ' + self.tasks = { } + self.errorMsg = 'Task failed: ' - for k,v in pairs(args or { }) do - self[k] = v - end + for k,v in pairs(args or { }) do + self[k] = v + end end function TaskRunner:add(fn) - table.insert(self.tasks, createTask(fn)) + table.insert(self.tasks, createTask(fn)) end function TaskRunner:run() - if #self.tasks > 0 then - local event = { } + if #self.tasks > 0 then + local event = { } - while true do - for n = #self.tasks, 1, -1 do - local task = self.tasks[n] - if task.filter == nil or task.filter == event[1] or event[1] == "terminate" then - local ok, param = coroutine.resume(task.co, table.unpack(event)) - if not ok then - self:onError(param) - else - task.filter = param - end - if task.dead then - table.remove(self.tasks, n) - end - end - end - if #self.tasks == 0 then - break - end - event = { os.pullEventRaw() } - end - end + while true do + for n = #self.tasks, 1, -1 do + local task = self.tasks[n] + if task.filter == nil or task.filter == event[1] or event[1] == "terminate" then + local ok, param = coroutine.resume(task.co, table.unpack(event)) + if not ok then + self:onError(param) + else + task.filter = param + end + if task.dead then + table.remove(self.tasks, n) + end + end + end + if #self.tasks == 0 then + break + end + event = { os.pullEventRaw() } + end + end end function TaskRunner:onError(msg) - _G._syslog(msg.errorMsg .. msg) + _G._syslog(msg.errorMsg .. msg) end return TaskRunner diff --git a/milo/apps/brewArray.lua b/milo/apps/brewArray.lua index ab01aca..06b3fcc 100644 --- a/milo/apps/brewArray.lua +++ b/milo/apps/brewArray.lua @@ -8,14 +8,14 @@ reboot Use multiple brewing stands at once to brew potions. SETUP: - Place an introspection module into the turtles inventory. - Connect turtle to milo network with a wired modem. - Connect turtle to a second wired modem that is connected to brewing stands ONLY. - Add as many brewing stands as needed. + Place an introspection module into the turtles inventory. + Connect turtle to milo network with a wired modem. + Connect turtle to a second wired modem that is connected to brewing stands ONLY. + Add as many brewing stands as needed. CONFIGURATION: - Set turtle as a "Generic Inventory" - export blaze powder to slot 5 - import from slots 7-9 + Set turtle as a "Generic Inventory" + export blaze powder to slot 5 + import from slots 7-9 Use this turtle for machine crafting. --]] @@ -31,23 +31,23 @@ local turtle = _G.turtle local STARTUP_FILE = 'usr/autorun/brewArray.lua' local function equip(side, item, rawName) - local equipped = peripheral.getType(side) + local equipped = peripheral.getType(side) - if equipped == item then - return true - end + if equipped == 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 + 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) + turtle.select(1) end equip('left', 'plethora:introspection', 'plethora:module:0') @@ -55,8 +55,8 @@ local intro = device['plethora:introspection'] local inv = intro.getInventory() if not fs.exists(STARTUP_FILE) then - Util.writeFile(STARTUP_FILE, - [[os.sleep(1) + Util.writeFile(STARTUP_FILE, + [[os.sleep(1) shell.openForegroundTab('packages/milo/apps/brewArray.lua')]]) end @@ -65,27 +65,27 @@ local localName print('detecting wired modem connected to brewing stands...') for _, dev in pairs(device) do - if dev.type == 'wired_modem' then - local list = dev.getNamesRemote() - brew = { } - localName = dev.getNameLocal() - for _, name in pairs(list) do - if device[name].type ~= 'minecraft:brewing_stand' then - brew = nil - break - end - table.insert(brew, device[name]) - end - end - if brew then - print('Using wired modem: ' .. dev.name) - print('Brewing stands: ' .. #brew) - break - end + if dev.type == 'wired_modem' then + local list = dev.getNamesRemote() + brew = { } + localName = dev.getNameLocal() + for _, name in pairs(list) do + if device[name].type ~= 'minecraft:brewing_stand' then + brew = nil + break + end + table.insert(brew, device[name]) + end + end + if brew then + print('Using wired modem: ' .. dev.name) + print('Brewing stands: ' .. #brew) + break + end end if not brew then - error('Turtle must be connected to a second wired_modem connected to brewing stands only') + error('Turtle must be connected to a second wired_modem connected to brewing stands only') end _G.printError([[Program must be restarted if new brewing stands are added.]]) @@ -95,66 +95,66 @@ _G.printError([[Program must be restarted if new brewing stands are added.]]) -- slot 5: blaze powder local function process(list) - local active = false + local active = false - for _, brewing in ipairs(Util.shallowCopy(brew)) do - local s, m = pcall(function()-- block updates can cause errors - local bs = brewing.list() + for _, brewing in ipairs(Util.shallowCopy(brew)) do + local s, m = pcall(function()-- block updates can cause errors + local bs = brewing.list() - local cooking = bs[1] and bs[2] and bs[3] and bs[4] - if cooking then - active = true - end + local cooking = bs[1] and bs[2] and bs[3] and bs[4] + if cooking then + active = true + end - -- fuel - local fuel = bs[5] or { count = 0 } - if fuel.count < 1 then - print('fueling ' ..brewing.name) - brewing.pullItems(localName, 5, 1, 5) - end + -- fuel + local fuel = bs[5] or { count = 0 } + if fuel.count < 1 then + print('fueling ' ..brewing.name) + brewing.pullItems(localName, 5, 1, 5) + end - if not cooking and (bs[1] or bs[2] or bs[3] or bs[4]) then - print('pulling from : ' .. brewing.name) - for i = 1, 4 do - brewing.pushItems(localName, i, 1, 6 + i) - end - end + if not cooking and (bs[1] or bs[2] or bs[3] or bs[4]) then + print('pulling from : ' .. brewing.name) + for i = 1, 4 do + brewing.pushItems(localName, i, 1, 6 + i) + end + end - if not cooking and list[1] and list[2] and list[3] and list[4] then - print('brewing : ' .. brewing.name) - for i = 1, 4 do - brewing.pullItems(localName, i, 1, i) - list[i].count = list[i].count - 1 - if list[i].count == 0 then - list[i] = nil - end - end + if not cooking and list[1] and list[2] and list[3] and list[4] then + print('brewing : ' .. brewing.name) + for i = 1, 4 do + brewing.pullItems(localName, i, 1, i) + list[i].count = list[i].count - 1 + if list[i].count == 0 then + list[i] = nil + end + end - -- push brewing stand to end of list - Util.removeByValue(brew, brewing) - table.insert(brew, brewing) - end - end) - if not s and m then - _G.printError(m) - end - end + -- push brewing stand to end of list + Util.removeByValue(brew, brewing) + table.insert(brew, brewing) + end + end) + if not s and m then + _G.printError(m) + end + end - return active + return active end Event.on('turtle_inventory', function() - while true do - if not process(inv.list()) then - break - end - os.sleep(3) - end + while true do + if not process(inv.list()) then + break + end + os.sleep(3) + end end) Event.onInterval(5, function() - -- for some reason, it keeps stalling ... - os.queueEvent('turtle_inventory') + -- for some reason, it keeps stalling ... + os.queueEvent('turtle_inventory') end) os.queueEvent('turtle_inventory') diff --git a/milo/apps/cobblegen.lua b/milo/apps/cobblegen.lua index ba30a3c..6fe4356 100644 --- a/milo/apps/cobblegen.lua +++ b/milo/apps/cobblegen.lua @@ -7,22 +7,22 @@ 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) + 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() + 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 diff --git a/milo/apps/enderchest.lua b/milo/apps/enderchest.lua index fce6d25..308b695 100644 --- a/milo/apps/enderchest.lua +++ b/milo/apps/enderchest.lua @@ -13,48 +13,48 @@ local turtle = _G.turtle local STARTUP_FILE = 'usr/autorun/enderchest.lua' local enderChest = device.manipulator and - device.manipulator.getEnder or - error('Must be connected to a manipulator with a bound introspection module') + device.manipulator.getEnder or + error('Must be connected to a manipulator with a bound introspection module') if not fs.exists(STARTUP_FILE) then - Util.writeFile(STARTUP_FILE, - [[os.sleep(1) + Util.writeFile(STARTUP_FILE, + [[os.sleep(1) shell.openForegroundTab('packages/milo/apps/enderchest')]]) end local directions = Util.transpose { - 'north', 'south', 'east', 'west', 'up', 'down' + 'north', 'south', 'east', 'west', 'up', 'down' } Event.on('turtle_inventory', function() - local s, m = pcall(function() - local direction + local s, m = pcall(function() + local direction - for _, d in pairs(enderChest().getTransferLocations()) do - if directions[d] then - direction = d - break - end - end + for _, d in pairs(enderChest().getTransferLocations()) do + if directions[d] then + direction = d + break + end + end - if not direction then - error('Unable to determine transfer direction') - end + if not direction then + error('Unable to determine transfer direction') + end - turtle.eachFilledSlot(function(s) - print('sending') - enderChest().pullItems(direction, s.index) - end) - end) - if not s and m then - _G.printError(m) - end - print('idle') + turtle.eachFilledSlot(function(s) + print('sending') + enderChest().pullItems(direction, s.index) + end) + end) + if not s and m then + _G.printError(m) + end + print('idle') end) Event.onInterval(5, function() - -- for some reason, it keeps stalling ... - os.queueEvent('turtle_inventory') + -- for some reason, it keeps stalling ... + os.queueEvent('turtle_inventory') end) os.queueEvent('turtle_inventory') diff --git a/milo/apps/furni.lua b/milo/apps/furni.lua index a031bd4..700d02e 100644 --- a/milo/apps/furni.lua +++ b/milo/apps/furni.lua @@ -2,15 +2,15 @@ Use multiple furnaces at once to smelt items. SETUP: - Place an introspection module into the turtles inventory. - Connect turtle to milo network with a wired modem. - Connect turtle to a second wired modem that is connected to furnaces ONLY. - Add as many furnaces as needed. + Place an introspection module into the turtles inventory. + Connect turtle to milo network 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" - export coal to slot 2 - import from slot 3 + Set turtle as a "Generic Inventory" + export coal to slot 2 + import from slot 3 Use this turtle for machine crafting. --]] @@ -31,23 +31,23 @@ local FUEL_SLOT = 2 local OUTPUT_SLOT = 3 local function equip(side, item, rawName) - local equipped = peripheral.getType(side) + local equipped = peripheral.getType(side) - if equipped == item then - return true - end + if equipped == 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 + 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) + turtle.select(1) end equip('left', 'plethora:introspection', 'plethora:module:0') @@ -55,8 +55,8 @@ local intro = device['plethora:introspection'] local inv = intro.getInventory() if not fs.exists(STARTUP_FILE) then - Util.writeFile(STARTUP_FILE, - [[os.sleep(1) + Util.writeFile(STARTUP_FILE, + [[os.sleep(1) shell.openForegroundTab('packages/milo/apps/furni')]]) end @@ -65,121 +65,121 @@ local localName print('detecting wired modem connected to furnaces...') for _, dev in pairs(device) do - if dev.type == 'wired_modem' and dev.getNameLocal then - local list = dev.getNamesRemote() - furnaces = { } - localName = dev.getNameLocal() - for _, name in pairs(list) do - if device[name].type ~= 'minecraft:furnace' then - furnaces = nil - break - end - table.insert(furnaces, { - dev = device[name], - list = device[name].list(), - }) - end - end - if furnaces then - print('Using wired modem: ' .. dev.name) - print('Furnaces: ' .. #furnaces) - break - end + if dev.type == 'wired_modem' and dev.getNameLocal then + local list = dev.getNamesRemote() + furnaces = { } + localName = dev.getNameLocal() + for _, name in pairs(list) do + if device[name].type ~= 'minecraft:furnace' then + furnaces = nil + break + end + table.insert(furnaces, { + dev = device[name], + list = device[name].list(), + }) + end + end + if furnaces then + print('Using wired modem: ' .. dev.name) + print('Furnaces: ' .. #furnaces) + break + end end if not furnaces then - error('Turtle must be connected to a second wired_modem connected to furnaces only') + 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.]]) local function getSlot(furnace, slotNo) - if not furnace.list[slotNo] then - furnace.list[slotNo] = { - count = 0 - } - end - return furnace.list[slotNo] + if not furnace.list[slotNo] then + furnace.list[slotNo] = { + count = 0 + } + end + return furnace.list[slotNo] end local function process(list) - local inItem = list[INPUT_SLOT] - local inFuel = list[FUEL_SLOT] - local inReturn = list[OUTPUT_SLOT] or { count = 0 } + local inItem = list[INPUT_SLOT] + local inFuel = list[FUEL_SLOT] + local inReturn = list[OUTPUT_SLOT] or { count = 0 } - for _, furnace in ipairs(Util.shallowCopy(furnaces)) do - local s, m = pcall(function() - if furnace.list[INPUT_SLOT] and furnace.list[INPUT_SLOT].count > 0 then - furnace.list = furnace.dev.list() - print('listing ' .. furnace.dev.name) - end + for _, furnace in ipairs(Util.shallowCopy(furnaces)) do + local s, m = pcall(function() + if furnace.list[INPUT_SLOT] and furnace.list[INPUT_SLOT].count > 0 then + furnace.list = furnace.dev.list() + print('listing ' .. furnace.dev.name) + end - -- items to cook - local cooking = getSlot(furnace, INPUT_SLOT) - if cooking.count < 64 and inItem and inItem.count > 0 then - if cooking.count == 0 or cooking.name == inItem.name then - print('cooking : ' .. furnace.dev.name) - local count = furnace.dev.pullItems(localName, INPUT_SLOT, SMELT_AMOUNT, INPUT_SLOT) + -- items to cook + local cooking = getSlot(furnace, INPUT_SLOT) + if cooking.count < 64 and inItem and inItem.count > 0 then + if cooking.count == 0 or cooking.name == inItem.name then + print('cooking : ' .. furnace.dev.name) + local count = furnace.dev.pullItems(localName, INPUT_SLOT, SMELT_AMOUNT, INPUT_SLOT) - if count > 0 then - inItem.count = inItem.count - count + if count > 0 then + inItem.count = inItem.count - count - cooking.name = inItem.name - cooking.count = cooking.count + count + cooking.name = inItem.name + cooking.count = cooking.count + count - -- push to end of queue - Util.removeByValue(furnaces, furnace) - table.insert(furnaces, furnace) - end - end - end + -- push to end of queue + Util.removeByValue(furnaces, furnace) + table.insert(furnaces, furnace) + end + end + end - -- fuel - local fuel = getSlot(furnace, FUEL_SLOT) - if fuel.count < 8 and inFuel and inFuel.count > 0 then - if fuel.count == 0 or fuel.name == inFuel.name then - print('fueling ' .. furnace.dev.name) - local count = furnace.dev.pullItems(localName, FUEL_SLOT, 8 - fuel.count, FUEL_SLOT) - if count > 0 then - inFuel.count = inFuel.count - count + -- fuel + local fuel = getSlot(furnace, FUEL_SLOT) + if fuel.count < 8 and inFuel and inFuel.count > 0 then + if fuel.count == 0 or fuel.name == inFuel.name then + print('fueling ' .. furnace.dev.name) + local count = furnace.dev.pullItems(localName, FUEL_SLOT, 8 - fuel.count, FUEL_SLOT) + if count > 0 then + inFuel.count = inFuel.count - count - fuel.name = inFuel.name - fuel.count = fuel.count + count - end - end - end + fuel.name = inFuel.name + fuel.count = fuel.count + count + end + end + end - local result = getSlot(furnace, OUTPUT_SLOT) - if result.count > 0 then - if inReturn.count == 0 or result.name == inReturn.name then - print('pulling from : ' .. furnace.dev.name) - local count = furnace.dev.pushItems(localName, OUTPUT_SLOT, result.count, OUTPUT_SLOT) + local result = getSlot(furnace, OUTPUT_SLOT) + if result.count > 0 then + if inReturn.count == 0 or result.name == inReturn.name then + print('pulling from : ' .. furnace.dev.name) + local count = furnace.dev.pushItems(localName, OUTPUT_SLOT, result.count, OUTPUT_SLOT) - if count > 0 then - result.count = result.count - count - if result.count == 0 then - furnace.list[OUTPUT_SLOT] = nil - end + if count > 0 then + result.count = result.count - count + if result.count == 0 then + furnace.list[OUTPUT_SLOT] = nil + end - inReturn.name = result.name - inReturn.count = inReturn.count + count - end - end - end - end) - if not s and m then - _G.printError(m) - end - end + inReturn.name = result.name + inReturn.count = inReturn.count + count + end + end + end + end) + if not s and m then + _G.printError(m) + end + end end Event.on('turtle_inventory', function() - process(inv.list()) - print('idle') + process(inv.list()) + print('idle') end) Event.onInterval(3, function() - os.queueEvent('turtle_inventory') + os.queueEvent('turtle_inventory') end) os.queueEvent('turtle_inventory') diff --git a/milo/apps/storageGen.lua b/milo/apps/storageGen.lua index 4bbc4f2..2e0cdc2 100644 --- a/milo/apps/storageGen.lua +++ b/milo/apps/storageGen.lua @@ -1,5 +1,5 @@ --[[ - For initially setting up large amounts of storage chests. + For initially setting up large amounts of storage chests. ]] local Util = require('util') @@ -11,17 +11,17 @@ local st = args[1] or error('Specify a storage type (ie. minecraft:chest)') local config = { } peripheral.find(st, function(n) - config[n] = { - name = n, - category = 'storage', - mtype = 'storage', - } + config[n] = { + name = n, + category = 'storage', + mtype = 'storage', + } end) print('Found ' .. Util.size(config)) if Util.size(config) == 0 then - error('Invalid peripheral type') + error('Invalid peripheral type') end Util.writeTable('usr/config/storageGen', config) diff --git a/milo/core/learnWizard.lua b/milo/core/learnWizard.lua index 1643201..eb0ae16 100644 --- a/milo/core/learnWizard.lua +++ b/milo/core/learnWizard.lua @@ -4,90 +4,90 @@ 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.WizardPage { - index = 1, - grid = UI.ScrollingGrid { - x = 2, ex = -2, y = 2, ey = -2, - disableHeader = true, - columns = { - { heading = 'Name', key = 'name'}, - }, - sortColumn = 'name', - }, - accelerators = { - grid_select = 'nextView', - }, - }, - }, - }, - notification = UI.Notification { }, + titleBar = UI.TitleBar { title = 'Learn Recipe' }, + wizard = UI.Wizard { + y = 2, ey = -2, + pages = { + general = UI.WizardPage { + index = 1, + grid = UI.ScrollingGrid { + x = 2, ex = -2, y = 2, ey = -2, + disableHeader = true, + columns = { + { heading = 'Name', key = 'name'}, + }, + sortColumn = 'name', + }, + accelerators = { + grid_select = 'nextView', + }, + }, + }, + }, + notification = UI.Notification { }, } local general = learnPage.wizard.pages.general function general:validate() - Milo:setState('learnType', self.grid:getSelected().value) - return true + Milo:setState('learnType', self.grid:getSelected().value) + return true end function learnPage:enable() - local t = { } + 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 '') + 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' }) + Milo:pauseCrafting({ key = 'gridInUse', msg = 'Crafting paused' }) - self:focusFirst() - UI.Page.enable(self) + self:focusFirst() + UI.Page.enable(self) end function learnPage:disable() - Milo:resumeCrafting({ key = 'gridInUse' }) - return UI.Page.disable(self) + 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) + 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] + return pages[index] end function learnPage:eventHandler(event) - if event.type == 'cancel' then - turtle.emptyInventory() - UI:setPreviousPage() + 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) + 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 + 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 d4d56f5..898c1f2 100644 --- a/milo/core/listing.lua +++ b/milo/core/listing.lua @@ -11,390 +11,390 @@ 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' }, + [0] = { text = 'A', help = 'Showing all items' }, + [1] = { text = 'I', help = 'Showing inventory items' }, } local page = UI.Page { - menuBar = UI.MenuBar { - buttons = { - { text = 'Learn', event = 'learn' }, - { text = 'Craft', event = 'craft' }, - { text = 'Edit', event = 'details' }, - { text = 'Refresh', event = 'refresh', x = -12 }, - { - text = '\187', - x = -3, - dropdown = { - { text = 'Setup', event = 'network' }, - { spacer = true }, - { - text = 'Rescan storage', - event = 'rescan', - help = 'Rescan all inventories' - }, - }, - }, - }, - }, - grid = UI.Grid { - y = 2, ey = -2, - columns = { - { heading = ' Qty', key = 'count' , width = 4, align = 'right' }, - { heading = 'Name', key = 'displayName' }, - { heading = 'Min', key = 'low' , width = 4 }, - { heading = 'Max', key = 'limit' , width = 4 }, - }, - sortColumn = Milo:getState('sortColumn') or 'count', - inverseSort = Milo:getState('inverseSort'), - }, - 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', - [ 'up' ] = 'grid_up', - [ 'down' ] = 'grid_down', - [ 'control-a' ] = 'eject_all', - }, - }, - 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', - [ 'control-a' ] = 'eject_all', - }, - 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 { - anchor = 'top', - }, - throttle = UI.Throttle { - textColor = colors.yellow, - borderColor = colors.gray, - }, - accelerators = { - r = 'refresh', - [ 'control-r' ] = 'refresh', + menuBar = UI.MenuBar { + buttons = { + { text = 'Learn', event = 'learn' }, + { text = 'Craft', event = 'craft' }, + { text = 'Edit', event = 'details' }, + { text = 'Refresh', event = 'refresh', x = -12 }, + { + text = '\187', + x = -3, + dropdown = { + { text = 'Setup', event = 'network' }, + { spacer = true }, + { + text = 'Rescan storage', + event = 'rescan', + help = 'Rescan all inventories' + }, + }, + }, + }, + }, + grid = UI.Grid { + y = 2, ey = -2, + columns = { + { heading = ' Qty', key = 'count' , width = 4, align = 'right' }, + { heading = 'Name', key = 'displayName' }, + { heading = 'Min', key = 'low' , width = 4 }, + { heading = 'Max', key = 'limit' , width = 4 }, + }, + sortColumn = Milo:getState('sortColumn') or 'count', + inverseSort = Milo:getState('inverseSort'), + }, + 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', + [ 'up' ] = 'grid_up', + [ 'down' ] = 'grid_down', + [ 'control-a' ] = 'eject_all', + }, + }, + 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', + [ 'control-a' ] = 'eject_all', + }, + 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 { + anchor = 'top', + }, + 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-e' ] = 'eject', + [ 'control-s' ] = 'eject_stack', + [ 'control-a' ] = 'eject_all', - [ 'control-m' ] = 'network', + [ 'control-m' ] = 'network', - q = 'quit', - }, - allItems = { } + q = 'quit', + }, + allItems = { } } function page.statusBar:draw() - return UI.Window.draw(self) + 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) + 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) - if row.low then - row.low = Util.toBytes(row.low) - end - if row.limit then - row.limit = Util.toBytes(row.limit) - end - return 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 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) + 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) + 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 - if item then - if amount == 'stack' then - amount = item.maxCount or 64 - elseif amount == 'all' then - item = Milo:getItem(item) - if item then - amount = item.count - end - end + 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(item) + if item then + amount = item.count + end + end - if item and 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 item and 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.craft then - if request.craft > 0 then - self:notifyInfo(request.craft .. ' crafting ...') - elseif request.craft + request.count < request.requested then - if request.craft + request.count == 0 then - Sound.play('entity.villager.no') - end - self:notifyInfo((request.craft + request.count) .. ' available ...') - end - end + if request.craft then + if request.craft > 0 then + self:notifyInfo(request.craft .. ' crafting ...') + elseif request.craft + request.count < request.requested then + if request.craft + request.count == 0 then + Sound.play('entity.villager.no') + end + self:notifyInfo((request.craft + request.count) .. ' available ...') + end + end - if request.count + request.craft > 0 then - self.grid:draw() - return true - end - end - end - end - Sound.play('entity.villager.no') + if request.count + request.craft > 0 then + self.grid:draw() + return true + end + end + end + end + Sound.play('entity.villager.no') end function page:eventHandler(event) - if event.type == 'quit' then - UI:exitPullEvents() + if event.type == 'quit' then + UI:exitPullEvents() - elseif event.type == 'eject' or event.type == 'grid_select' then - self:eject(1) + elseif event.type == 'eject' or event.type == 'grid_select' then + self:eject(1) - elseif event.type == 'eject_stack' then - self:eject('stack') + elseif event.type == 'eject_stack' then + self:eject('stack') - elseif event.type == 'eject_all' then - self:eject('all') + elseif event.type == 'eject_all' then + self:eject('all') - elseif event.type == 'eject_specified' then - if self:eject(tonumber(self.statusBar.amount.value)) then - self.statusBar.amount:reset() - self:setFocus(self.statusBar.filter) - end + elseif event.type == 'eject_specified' then + if self:eject(tonumber(self.statusBar.amount.value)) then + self.statusBar.amount:reset() + self:setFocus(self.statusBar.filter) + end - elseif event.type == 'network' then - UI:setPage('network') + 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 == 'details' or event.type == 'grid_select_right' then + local item = self.grid:getSelected() + if item then + UI:setPage('item', item) + end - elseif event.type == 'grid_up' then - self.grid:emit({ type = 'scroll_up' }) + elseif event.type == 'grid_up' then + self.grid:emit({ type = 'scroll_up' }) - elseif event.type == 'grid_down' then - self.grid:emit({ type = 'scroll_down' }) + elseif event.type == 'grid_down' then + self.grid:emit({ type = 'scroll_down' }) - elseif event.type == 'refresh' then - self:refresh() - self.grid:draw() - self:setFocus(self.statusBar.filter) + 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 == 'rescan' then + self:refresh(true) + self.grid:draw() + self:setFocus(self.statusBar.filter) - elseif event.type == 'toggle_display' then - displayMode = (displayMode + 1) % 2 - Util.merge(event.button, displayModes[displayMode]) - event.button:draw() - self:applyFilter() - self.grid:draw() - Milo:setState('displayMode', displayMode) + elseif event.type == 'toggle_display' then + displayMode = (displayMode + 1) % 2 + 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('learnWizard') + elseif event.type == 'learn' then + UI:setPage('learnWizard') - 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 == '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:setIndex(1) - self.grid:draw() - self.statusBar.filter:focus() + 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:setIndex(1) + self.grid:draw() + self.statusBar.filter:focus() - else - UI.Page.eventHandler(self, event) - end - return true + else + UI.Page.eventHandler(self, event) + end + return true end function page:notifySuccess(status) - self.notification:success(status) + self.notification:success(status) end function page:notifyInfo(status) - self.notification:info(status) + self.notification:info(status) end function page:notifyError(status) - self.notification:error(status) + self.notification:error(status) end function page:enable(args) - 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() + 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() - self:sync() + Event.onTimeout(0, function() + self:refresh() + self:draw() + self:sync() - self.timer = Event.onInterval(3, function() - for _,v in pairs(self.grid.values) do - local c = context.storage.cache[v.key] - v.count = c and c.count or 0 - end - self.grid:draw() - self:sync() - end) + self.timer = Event.onInterval(3, function() + for _,v in pairs(self.grid.values) do + local c = context.storage.cache[v.key] + v.count = c and c.count or 0 + end + self.grid:draw() + self:sync() + end) - self.handler = Event.on({ 'storage_offline', 'storage_online' }, function() - updateStatus() - self.statusBar.storageStatus:draw() - self:sync() - end) - end) + self.handler = Event.on({ 'storage_offline', 'storage_online' }, function() + updateStatus() + self.statusBar.storageStatus:draw() + self:sync() + end) + end) - if args and args.filter then - self.filter = args.filter - self.statusBar.filter.value = args.filter - 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 + if args and args.message then + self.notification:success(args.message) + end - self:setFocus(self.statusBar.filter) - UI.Page.enable(self) + self:setFocus(self.statusBar.filter) + UI.Page.enable(self) end function page:disable() - Event.off(self.timer) - Event.off(self.handler) - UI.Page.disable(self) + Event.off(self.timer) + Event.off(self.handler) + UI.Page.disable(self) end function page:refresh(force) - local throttle = function() self.throttle:update() end + local throttle = function() self.throttle:update() end - self.throttle:enable() - self.allItems = Milo:mergeResources(Milo:listItems(force, throttle)) - self:applyFilter() - self.throttle:disable() + self.throttle:enable() + self.allItems = Milo:mergeResources(Milo:listItems(force, throttle)) + self:applyFilter() + self.throttle:disable() end function page:applyFilter() - local function filterItems(t, filter) - self.grid.sortColumn = Milo:getState('sortColumn') or 'count' - self.grid.inverseSort = Milo:getState('inverseSort') + local function filterItems(t, filter) + self.grid.sortColumn = Milo:getState('sortColumn') or 'count' + self.grid.inverseSort = Milo:getState('inverseSort') - if filter then - local r = { } - filter = filter:lower() - self.grid.sortColumn = 'score' - self.grid.inverseSort = true + if filter then + local r = { } + filter = filter:lower() + self.grid.sortColumn = 'score' + self.grid.inverseSort = true - for _,v in pairs(t) do - v.score = fuzzy(v.lname, filter) - if v.score then - if v.count > 0 then - v.score = v.score + 1 - end - table.insert(r, v) - end - end - return r + for _,v in pairs(t) do + v.score = fuzzy(v.lname, filter) + if v.score then + if v.count > 0 then + v.score = v.score + 1 + end + table.insert(r, v) + end + end + return r - elseif displayMode > 0 then - local r = { } + elseif displayMode > 0 then + local r = { } - for _,v in pairs(t) do - if v.count > 0 then - table.insert(r, v) - end - end - return r - end + for _,v in pairs(t) do + if v.count > 0 then + table.insert(r, v) + end + end + return r + end - return t - end + return t + end - local t = filterItems(self.allItems, self.filter) - self.grid:setValues(t) + local t = filterItems(self.allItems, self.filter) + self.grid:setValues(t) end UI:addPage('listing', page) diff --git a/milo/etc/apps.db b/milo/etc/apps.db index 4e2a853..9085347 100644 --- a/milo/etc/apps.db +++ b/milo/etc/apps.db @@ -1,20 +1,20 @@ { - [ "9302912a2d9794a47241faefc475335b4e07a581" ] = { - title = "Remote", - category = "Apps", - run = "MiloRemote", - requires = "neuralInterface", - iconExt = "\0304\031f\135\129\0314\128\128\031f\130\030f\128\ + [ "9302912a2d9794a47241faefc475335b4e07a581" ] = { + title = "Remote", + category = "Apps", + run = "MiloRemote", + requires = "neuralInterface", + 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 = "MiloLocal", - requires = 'advancedTurtle', - iconExt = "\0304\031f\135\129\0314\128\128\031f\130\030f\128\ + }, + [ "eea426f9baef72a8fcefd091e0cec5ab94a76698" ] = { + title = "Milo", + category = "Apps", + run = "MiloLocal", + 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", - }, + }, } diff --git a/milo/plugins/activityView.lua b/milo/plugins/activityView.lua index b14d091..85b3b97 100644 --- a/milo/plugins/activityView.lua +++ b/milo/plugins/activityView.lua @@ -15,239 +15,239 @@ local template = Right-clicking on the activity monitor will reset the totals.]] local wizardPage = UI.WizardPage { - title = 'Activity Monitor', - index = 2, - backgroundColor = colors.cyan, - [1] = UI.TextArea { - x = 2, ex = -2, y = 2, ey = 6, - marginRight = 0, - value = string.format(template, Ansi.yellow, Ansi.reset), - }, - 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 }, - { name = 'Large', value = 1 }, - }, - help = 'Adjust text scaling', - }, - }, + title = 'Activity Monitor', + index = 2, + backgroundColor = colors.cyan, + [1] = UI.TextArea { + x = 2, ex = -2, y = 2, ey = 6, + marginRight = 0, + value = string.format(template, Ansi.yellow, Ansi.reset), + }, + 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 }, + { name = 'Large', value = 1 }, + }, + help = 'Adjust text scaling', + }, + }, } function wizardPage:setNode(node) - self.form:setValues(node) + self.form:setValues(node) end function wizardPage:validate() - return self.form:save() + return self.form:save() end function wizardPage:saveNode(node) - os.queueEvent('monitor_resize', node.name) + os.queueEvent('monitor_resize', node.name) end function wizardPage:isValidType(node) - local m = device[node.name] - return m and m.type == 'monitor' and { - name = 'Activity Monitor', - value = 'activity', - category = 'display', - help = 'Display storage activity' - } + local m = device[node.name] + return m and m.type == 'monitor' and { + name = 'Activity Monitor', + value = 'activity', + category = 'display', + help = 'Display storage activity' + } end function wizardPage:isValidFor(node) - return node.mtype == 'activity' + return node.mtype == 'activity' end UI:getPage('nodeWizard').wizard:add({ activity = wizardPage }) --[[ Display ]]-- local function createPage(node) - local monitor = UI.Device { - device = node.adapter, - textScale = node.textScale or .5, - } + local monitor = UI.Device { + device = node.adapter, + textScale = node.textScale or .5, + } - function monitor:resize() - self.textScale = node.textScale or .5 - UI.Device.resize(self) - end + function monitor:resize() + self.textScale = node.textScale or .5 + UI.Device.resize(self) + end - local page = UI.Page { - parent = monitor, - backgroundColor = colors.black, - grid = UI.Grid { - ey = -3, - columns = { - { heading = 'Qty', key = 'count', width = 6, align = 'right' }, - { heading = '+/-', key = 'change', width = 6, align = 'right' }, - { heading = 'Name', key = 'displayName' }, - { heading = 'Rate', key = 'rate', width = 6, align = 'right' }, - }, - sortColumn = 'displayName', - headerBackgroundColor = colors.black, - headerTextColor = colors.cyan, - headerHeight = 2, - }, - buttons = UI.Window { - y = -1, - backgroundColor = colors.black, - prevButton = UI.Button { - x = 1, width = 5, - event = 'previous', - textColor = colors.cyan, - backgroundColor = colors.black, - text = ' < ' - }, - resetButton = UI.Button { - x = 7, ex = -7, - event = 'reset', - textColor = colors.cyan, - backgroundColor = colors.black, - text = 'Reset' - }, - nextButton = UI.Button { - x = -5, width = 5, - event = 'next', - textColor = colors.cyan, - backgroundColor = colors.black, - text = ' > ' - }, - }, - timestamp = os.clock(), - } + local page = UI.Page { + parent = monitor, + backgroundColor = colors.black, + grid = UI.Grid { + ey = -3, + columns = { + { heading = 'Qty', key = 'count', width = 6, align = 'right' }, + { heading = '+/-', key = 'change', width = 6, align = 'right' }, + { heading = 'Name', key = 'displayName' }, + { heading = 'Rate', key = 'rate', width = 6, align = 'right' }, + }, + sortColumn = 'displayName', + headerBackgroundColor = colors.black, + headerTextColor = colors.cyan, + headerHeight = 2, + }, + buttons = UI.Window { + y = -1, + backgroundColor = colors.black, + prevButton = UI.Button { + x = 1, width = 5, + event = 'previous', + textColor = colors.cyan, + backgroundColor = colors.black, + text = ' < ' + }, + resetButton = UI.Button { + x = 7, ex = -7, + event = 'reset', + textColor = colors.cyan, + backgroundColor = colors.black, + text = 'Reset' + }, + nextButton = UI.Button { + x = -5, width = 5, + event = 'next', + textColor = colors.cyan, + backgroundColor = colors.black, + text = ' > ' + }, + }, + timestamp = os.clock(), + } - 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: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) + function page.grid:getDisplayValues(row) + row = Util.shallowCopy(row) - local ind = '+' - if row.change < 0 then - ind = '' - end + 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) + row.change = ind .. Util.toBytes(row.change) + row.count = Util.toBytes(row.count) + row.rate = Util.toBytes(row.rate) - return row - end + return row + end - function page:eventHandler(event) - if event.type == 'reset' then - self:reset() + function page:eventHandler(event) + if event.type == 'reset' then + self:reset() - elseif event.type == 'next' then - self.grid:nextPage() + elseif event.type == 'next' then + self.grid:nextPage() - elseif event.type == 'previous' then - self.grid:previousPage() + elseif event.type == 'previous' then + self.grid:previousPage() - else - return UI.Page.eventHandler(self, event) - end + else + return UI.Page.eventHandler(self, event) + end - Event.onTimeout(.1, function() - self:setFocus(self.grid) - self:sync() - end) - return true - end + Event.onTimeout(.1, function() + self:setFocus(self.grid) + self:sync() + end) + return true + end - function page:reset() - self.lastItems = nil - self.grid:setValues({ }) - self.grid:draw() - end + function page:reset() + self.lastItems = nil + self.grid:setValues({ }) + self.grid:draw() + end - function page:refresh() - local t = context.storage.cache + function page:refresh() + local t = context.storage.cache - if t and not self.lastItems then - self.lastItems = { } - for k,v in pairs(t) do - self.lastItems[k] = { - displayName = v.displayName, - initialCount = v.count, - } - end - self.timestamp = os.clock() - self.grid:setValues({ }) + if t and not self.lastItems then + self.lastItems = { } + for k,v in pairs(t) do + self.lastItems[k] = { + displayName = v.displayName, + initialCount = v.count, + } + end + self.timestamp = os.clock() + self.grid:setValues({ }) - else - for _,v in pairs(self.lastItems) do - v.lastCount = v.count - v.count = nil - end + else + for _,v in pairs(self.lastItems) do + v.lastCount = v.count + v.count = nil + end - self.elapsed = os.clock() - self.timestamp + 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 + 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 + 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:setValues(changedItems) + end + self.grid:draw() + end - function page:update() - page:refresh() - page:sync() - end + function page:update() + page:refresh() + page:sync() + end - UI:setPage(page) - return page + UI:setPage(page) + return page end local pages = { } --[[ Task ]]-- local ActivityTask = { - name = 'activity', - priority = 30, + name = 'activity', + priority = 30, } function ActivityTask:cycle() - 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 + 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/backupView.lua b/milo/plugins/backupView.lua index 7c75f64..aff4b1b 100644 --- a/milo/plugins/backupView.lua +++ b/milo/plugins/backupView.lua @@ -21,97 +21,97 @@ Backup configuration files each minecraft day. ]] local wizardPage = UI.WizardPage { - title = 'Backup Drive', - index = 2, - backgroundColor = colors.cyan, - [1] = UI.TextArea { - x = 2, ex = -2, y = 2, ey = -2, - value = string.format(template, Ansi.yellow, Ansi.reset), - }, + title = 'Backup Drive', + 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 == 'drive' and { - name = 'Backup Drive', - value = 'backup', - category = 'custom', - help = 'Backup configuration files', - } + local m = device[node.name] + return m and m.type == 'drive' and { + name = 'Backup Drive', + value = 'backup', + category = 'custom', + help = 'Backup configuration files', + } end function wizardPage:isValidFor(node) - return node.mtype == 'backup' + return node.mtype == 'backup' end UI:getPage('nodeWizard').wizard:add({ backupDrive = wizardPage }) local function clearOld(dir, fname) - local files = { } + local files = { } - for _, file in pairs(fs.list(dir)) do - if file:match(fname) then - table.insert(files, file) - end - end - if #files > 1 then - table.sort(files, function(a, b) - return tonumber(a:match('.(%d+)')) > tonumber(b:match('.(%d+)')) - end) - while #files > 1 do - local old = table.remove(files, #files) - fs.delete(fs.combine(dir, old)) - end - end + for _, file in pairs(fs.list(dir)) do + if file:match(fname) then + table.insert(files, file) + end + end + if #files > 1 then + table.sort(files, function(a, b) + return tonumber(a:match('.(%d+)')) > tonumber(b:match('.(%d+)')) + end) + while #files > 1 do + local old = table.remove(files, #files) + fs.delete(fs.combine(dir, old)) + end + end end local function makeBackup(dir, fname) - clearOld(dir, fname) - local source = fs.combine('usr/config', fname) - local dest = string.format('%s/%s.%d', dir, fname, os.day()) - fs.copy(source, dest) + clearOld(dir, fname) + local source = fs.combine('usr/config', fname) + local dest = string.format('%s/%s.%d', dir, fname, os.day()) + fs.copy(source, dest) end local function backupNode(node) - local files = { - 'storage', - 'milo.state', - 'machine_crafting.db', - 'recipes.db', - 'resources.db', - } - local s, m = pcall(function() - if not node.adapter.isDiskPresent() then - _G._syslog('BACKUP error: No media present') - else - local dir = node.adapter.getMountPath() - for _, v in pairs(files) do - makeBackup(dir, v) - end - end - end) - if not s and m then - _G._syslog('BACKUP error:' .. m) - end + local files = { + 'storage', + 'milo.state', + 'machine_crafting.db', + 'recipes.db', + 'resources.db', + } + local s, m = pcall(function() + if not node.adapter.isDiskPresent() then + _G._syslog('BACKUP error: No media present') + else + local dir = node.adapter.getMountPath() + for _, v in pairs(files) do + makeBackup(dir, v) + end + end + end) + if not s and m then + _G._syslog('BACKUP error:' .. m) + end end --[[ Task ]]-- local BackupTask = { - name = 'backup', - priority = 99, + name = 'backup', + priority = 99, } function BackupTask:cycle() - for node in context.storage:filterActive('backup') do - if not drives[node.name] then - drives[node.name] = Event.onInterval(DAY, function() - _G._syslog('BACKUP: started') - if node.adapter and node.adapter.online then - backupNode(node) - end - end) - end - end + for node in context.storage:filterActive('backup') do + if not drives[node.name] then + drives[node.name] = Event.onInterval(DAY, function() + _G._syslog('BACKUP: started') + if node.adapter and node.adapter.online then + backupNode(node) + end + end) + end + end end Milo:registerTask(BackupTask) diff --git a/milo/plugins/craftTask.lua b/milo/plugins/craftTask.lua index 6042b7e..40d0e7d 100644 --- a/milo/plugins/craftTask.lua +++ b/milo/plugins/craftTask.lua @@ -6,67 +6,67 @@ local Util = require('util') local context = Milo:getContext() local craftTask = { - name = 'crafting', - priority = 70, + name = 'crafting', + priority = 70, } function craftTask:craft(recipe, item) - if Milo:isCraftingPaused() then - return - end + if Milo:isCraftingPaused() then + return + end - -- TODO: refactor into craft.lua - Craft.processPending(item, context.storage) + -- TODO: refactor into craft.lua + Craft.processPending(item, context.storage) - -- 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) + -- 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) - 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] = item - item.ingredients[recipe.result].total = item.count - item.ingredients[recipe.result].crafted = 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] = item + item.ingredients[recipe.result].total = item.count + item.ingredients[recipe.result].crafted = item.crafted - Craft.craftRecipe(recipe, item.requested - item.crafted, context.storage, item) + Craft.craftRecipe(recipe, item.requested - item.crafted, context.storage, item) end function craftTask:cycle() - for _,key in pairs(Util.keys(context.craftingQueue)) do - local item = context.craftingQueue[key] - if item.requested - item.crafted > 0 then - local recipe = Craft.findRecipe(key) - if recipe then + for _,key in pairs(Util.keys(context.craftingQueue)) do + local item = context.craftingQueue[key] + if item.requested - item.crafted > 0 then + local recipe = Craft.findRecipe(key) + if recipe then - if not item.notified then - Sound.play('block.end_portal_frame.fill') - item.notified = true - end + if not item.notified then + Sound.play('block.end_portal_frame.fill') + item.notified = true + end - self:craft(recipe, item) + self:craft(recipe, item) - if item.crafted >= item.requested then - item.status = 'crafted' - item.statusCode = Craft.STATUS_SUCCESS - if item.callback then - item.callback(item) -- invoke callback - end - end + if item.crafted >= item.requested then + item.status = 'crafted' + item.statusCode = Craft.STATUS_SUCCESS + if item.callback then + item.callback(item) -- invoke callback + end + end - else - item.status = '(no recipe)' - item.statusCode = Craft.STATUS_ERROR - item.crafted = 0 - end - end - end + else + item.status = '(no recipe)' + item.statusCode = Craft.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 6464861..b6942ac 100644 --- a/milo/plugins/demandCraft.lua +++ b/milo/plugins/demandCraft.lua @@ -7,126 +7,126 @@ local Util = require('util') local colors = _G.colors local craftPage = UI.Page { - titleBar = UI.TitleBar { }, - wizard = UI.Wizard { - y = 2, ey = -2, - pages = { - quantity = UI.WizardPage { - 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.WizardPage { - 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', - }, - }, - }, - }, + titleBar = UI.TitleBar { }, + wizard = UI.Wizard { + y = 2, ey = -2, + pages = { + quantity = UI.WizardPage { + 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.WizardPage { + 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.item = item + self:focusFirst() + self.titleBar.title = itemDB:getName(item) -- self.wizard.pages.quantity.eject.value = true - UI.Page.enable(self) + UI.Page.enable(self) end function craftPage.wizard.pages.resources.grid:getDisplayValues(row) - local function dv(v) - return v == 0 and '' or 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 + local function dv(v) + return v == 0 and '' or 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) + 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) + 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 = Milo: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.WizardPage.enable(self) + local items = Milo: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.WizardPage.enable(self) end function craftPage:eventHandler(event) - if event.type == 'cancel' then - UI:setPreviousPage() + if event.type == 'cancel' then + UI:setPreviousPage() - elseif event.type == 'accept' then - local item = Util.shallowCopy(self.item) - 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.requested) - end - end - Milo:requestCrafting(item) - UI:setPreviousPage() - else - return UI.Page.eventHandler(self, event) - end - return true + elseif event.type == 'accept' then + local item = Util.shallowCopy(self.item) + 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.requested) + end + end + Milo:requestCrafting(item) + UI:setPreviousPage() + else + return UI.Page.eventHandler(self, event) + end + return true end UI:addPage('craft', craftPage) diff --git a/milo/plugins/inputChestView.lua b/milo/plugins/inputChestView.lua index 7c1643f..9defacd 100644 --- a/milo/plugins/inputChestView.lua +++ b/milo/plugins/inputChestView.lua @@ -12,27 +12,27 @@ Any items placed in this chest will be imported into storage. ]] local inputChestWizardPage = UI.WizardPage { - 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), - }, + 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', - category = 'custom', - help = 'Sends all items to storage', - } + local m = device[node.name] + return m and m.pullItems and { + name = 'Input Chest', + value = 'input', + category = 'custom', + help = 'Sends all items to storage', + } end function inputChestWizardPage:isValidFor(node) - return node.mtype == 'input' + return node.mtype == 'input' end UI:getPage('nodeWizard').wizard:add({ inputChest = inputChestWizardPage }) diff --git a/milo/plugins/item.lua b/milo/plugins/item.lua index 59cd1b1..640bfe8 100644 --- a/milo/plugins/item.lua +++ b/milo/plugins/item.lua @@ -5,57 +5,57 @@ local Util = require('util') local context = Milo:getContext() local page = UI.Page { - titleBar = UI.TitleBar { - title = 'Item settings', - previousPage = true, - }, - statusBar = UI.StatusBar { }, - notification = UI.Notification { }, + titleBar = UI.TitleBar { + title = 'Item settings', + previousPage = true, + }, + statusBar = UI.StatusBar { }, + notification = UI.Notification { }, } function page:enable(item) - if not self.tabs then - table.sort(context.plugins.itemTab, function(a, b) return a.index < b.index end) - local t = Util.shallowCopy(context.plugins.itemTab) - t.y = 2 - t.ey = -2 + if not self.tabs then + table.sort(context.plugins.itemTab, function(a, b) return a.index < b.index end) + local t = Util.shallowCopy(context.plugins.itemTab) + t.y = 2 + t.ey = -2 - self:add({ tabs = UI.Tabs(t) }) - end + self:add({ tabs = UI.Tabs(t) }) + end - for _, v in pairs(context.plugins.itemTab) do - if v.UIElement then - v:setItem(item) - end - end - self.tabs:selectTab(context.plugins.itemTab[1]) - UI.Page.enable(self) + for _, v in pairs(context.plugins.itemTab) do + if v.UIElement then + v:setItem(item) + end + end + self.tabs:selectTab(context.plugins.itemTab[1]) + UI.Page.enable(self) end function page:eventHandler(event) - if event.type == 'tab_activate' then - event.activated:focusFirst() + if event.type == 'tab_activate' then + event.activated:focusFirst() - elseif event.type == 'form_invalid' then - self.notification:error(event.message) + 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 == 'focus_change' then + self.statusBar:setStatus(event.focused.help) + self.statusBar:draw() - elseif event.type == 'success_message' then - self.notification:success(event.message) + elseif event.type == 'success_message' then + self.notification:success(event.message) - elseif event.type == 'info_message' then - self.notification:info(event.message) + elseif event.type == 'info_message' then + self.notification:info(event.message) - elseif event.type == 'error_message' then - self.notification:error(event.message) + elseif event.type == 'error_message' then + self.notification:error(event.message) - else - return UI.Page.eventHandler(self, event) - end - return true + else + return UI.Page.eventHandler(self, event) + end + return true end UI:addPage('item', page) diff --git a/milo/plugins/item/machinesTab.lua b/milo/plugins/item/machinesTab.lua index 5bdd82d..b7ef7e3 100644 --- a/milo/plugins/item/machinesTab.lua +++ b/milo/plugins/item/machinesTab.lua @@ -7,62 +7,62 @@ local colors = _G.colors local context = Milo:getContext() local machinesTab = UI.Tab { - tabTitle = 'Machine', - index = 3, - backgroundColor = colors.cyan, - grid = UI.ScrollingGrid { - x = 2, ex = -2, y = 2, ey = -2, - disableHeader = true, - columns = { - { heading = 'Name', key = 'displayName'}, - }, - sortColumn = 'displayName', - help = 'Double-click to set machine', - }, + tabTitle = 'Machine', + index = 3, + backgroundColor = colors.cyan, + grid = UI.ScrollingGrid { + x = 2, ex = -2, y = 2, ey = -2, + disableHeader = true, + columns = { + { heading = 'Name', key = 'displayName'}, + }, + sortColumn = 'displayName', + help = 'Double-click to set machine', + }, } function machinesTab:setItem(item) - self.item = item - local machine = Craft.machineLookup[self.item.key] - local t = Util.filter(context.storage.nodes, function(node) - if node.category == 'machine' or node.category == 'custom' then -- TODO: - need a setting instead (ie. canCraft) - return node.adapter and node.adapter.online and node.adapter.pushItems - end - end) - self.grid:setValues(t) - if machine then - self.grid:setSelected('name', machine) - end - self.parent:setActive(self, item.has_recipe) + self.item = item + local machine = Craft.machineLookup[self.item.key] + local t = Util.filter(context.storage.nodes, function(node) + if node.category == 'machine' or node.category == 'custom' then -- TODO: - need a setting instead (ie. canCraft) + return node.adapter and node.adapter.online and node.adapter.pushItems + end + end) + self.grid:setValues(t) + if machine then + self.grid:setSelected('name', machine) + end + self.parent:setActive(self, item.has_recipe) end function machinesTab.grid:getDisplayValues(row) - row = Util.shallowCopy(row) - row.displayName = row.displayName or row.name - return row + row = Util.shallowCopy(row) + row.displayName = row.displayName or row.name + return row end function machinesTab.grid:getRowTextColor(row, selected) - if row.name == Craft.machineLookup[self.parent.item.key] then - return colors.yellow - end - return UI.Grid:getRowTextColor(row, selected) + if row.name == Craft.machineLookup[self.parent.item.key] then + return colors.yellow + end + return UI.Grid:getRowTextColor(row, selected) end function machinesTab:eventHandler(event) - if event.type == 'grid_select' then - if event.selected.name == Craft.machineLookup[self.item.key] then - Craft.machineLookup[self.item.key] = nil - else - Craft.machineLookup[self.item.key] = event.selected.name - end - Util.writeTable(Craft.MACHINE_LOOKUP, Craft.machineLookup) + if event.type == 'grid_select' then + if event.selected.name == Craft.machineLookup[self.item.key] then + Craft.machineLookup[self.item.key] = nil + else + Craft.machineLookup[self.item.key] = event.selected.name + end + Util.writeTable(Craft.MACHINE_LOOKUP, Craft.machineLookup) - self.grid:draw() - self:emit({ type = 'info_message', message = 'Saved' }) + self.grid:draw() + self:emit({ type = 'info_message', message = 'Saved' }) - return true - end + return true + end end return { itemTab = machinesTab } diff --git a/milo/plugins/item/manageTab.lua b/milo/plugins/item/manageTab.lua index 097c998..94bed1e 100644 --- a/milo/plugins/item/manageTab.lua +++ b/milo/plugins/item/manageTab.lua @@ -7,91 +7,91 @@ local Util = require('util') local context = Milo:getContext() local manageTab = UI.Tab { - tabTitle = 'Manage', - index = 1, - form = UI.Form { - x = 1, ex = -1, ey = -1, - --manualControls = true, - [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', - }, - [3] = UI.TextEntry { - width = 7, - formLabel = 'Max', formKey = 'limit', help = 'Send to trash if above max', - validate = 'numeric', - }, - [4] = UI.Checkbox { - formLabel = 'Ignore Dmg', formKey = 'ignoreDamage', - help = 'Ignore damage of item', - }, - [5] = UI.Checkbox { - formLabel = 'Ignore NBT', formKey = 'ignoreNbtHash', - help = 'Ignore NBT of item', - }, - }, + tabTitle = 'Manage', + index = 1, + form = UI.Form { + x = 1, ex = -1, ey = -1, + --manualControls = true, + [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', + }, + [3] = UI.TextEntry { + width = 7, + formLabel = 'Max', formKey = 'limit', help = 'Send to trash if above max', + validate = 'numeric', + }, + [4] = UI.Checkbox { + formLabel = 'Ignore Dmg', formKey = 'ignoreDamage', + help = 'Ignore damage of item', + }, + [5] = UI.Checkbox { + formLabel = 'Ignore NBT', formKey = 'ignoreNbtHash', + help = 'Ignore NBT of item', + }, + }, } function manageTab:setItem(item) - self.item = item - self.res = Util.shallowCopy(context.resources[item.key] or { }) - self.res.displayName = self.item.displayName - self.form:setValues(self.res) + self.item = item + self.res = Util.shallowCopy(context.resources[item.key] or { }) + self.res.displayName = self.item.displayName + self.form:setValues(self.res) - -- TODO: ignore damage should not be active if there is not a maxDamage value + -- TODO: ignore damage should not be active if there is not a maxDamage value end function manageTab:eventHandler(event) - if event.type == 'form_cancel' then - UI:setPreviousPage() + if event.type == 'form_cancel' then + UI:setPreviousPage() - elseif event.type == 'form_complete' then - if self.form:save() then - if self.res.displayName ~= self.item.displayName then - self.item.displayName = self.res.displayName - itemDB:add(self.item) - itemDB:flush() - if context.storage.cache[self.item.key] then - context.storage.cache[self.item.key].displayName = self.res.displayName - end - --context.storage:setDirty() - end + elseif event.type == 'form_complete' then + if self.form:save() then + if self.res.displayName ~= self.item.displayName then + self.item.displayName = self.res.displayName + itemDB:add(self.item) + itemDB:flush() + if context.storage.cache[self.item.key] then + context.storage.cache[self.item.key].displayName = self.res.displayName + end + --context.storage:setDirty() + end - self.res.displayName = nil - Map.prune(self.res, function(v) - if type(v) == 'boolean' then - return v - elseif type(v) == 'string' then - return #v > 0 - end - return true - end) + self.res.displayName = nil + Map.prune(self.res, function(v) + if type(v) == 'boolean' then + return v + elseif type(v) == 'string' then + return #v > 0 + end + return true + end) - local newKey = { - name = self.item.name, - damage = self.res.ignoreDamage and 0 or self.item.damage, - nbtHash = not self.res.ignoreNbtHash and self.item.nbtHash or nil, - } + local newKey = { + name = self.item.name, + damage = self.res.ignoreDamage and 0 or self.item.damage, + nbtHash = not self.res.ignoreNbtHash and self.item.nbtHash or nil, + } - context.resources[self.item.key] = nil - if not Util.empty(self.res) then - context.resources[itemDB:makeKey(newKey)] = self.res - end + context.resources[self.item.key] = nil + if not Util.empty(self.res) then + context.resources[itemDB:makeKey(newKey)] = self.res + end - Milo:saveResources() - UI:setPreviousPage() - end - else - return - end - return true + Milo:saveResources() + UI:setPreviousPage() + end + else + return + end + return true end return { itemTab = manageTab } diff --git a/milo/plugins/item/recipeTab.lua b/milo/plugins/item/recipeTab.lua index d2a6260..4f4edc2 100644 --- a/milo/plugins/item/recipeTab.lua +++ b/milo/plugins/item/recipeTab.lua @@ -6,85 +6,89 @@ local UI = require('ui') local colors = _G.colors local recipeTab = UI.Tab { - tabTitle = 'Recipe', - index = 2, - backgroundColor = colors.cyan, - grid = UI.ScrollingGrid { - x = 2, ex = -2, y = 2, ey = -4, - disableHeader = true, - columns = { - { heading = 'Slot', key = 'slot', width = 2 }, - { heading = 'Key', key = 'key' }, - }, - sortColumn = 'slot', - }, - ignoreResultNBT = UI.Button { - x = 2, y = -2, - text = 'Ignore Result NBT', event = 'ignore_result_nbt', - }, - ignoreNBT = UI.Button { - x = -13, y = -2, - text = 'Ignore NBT', event = 'ignore_nbt', - }, + tabTitle = 'Recipe', + index = 2, + backgroundColor = colors.cyan, + grid = UI.ScrollingGrid { + x = 2, ex = -2, y = 2, ey = -4, + disableHeader = true, + columns = { + { heading = 'Slot', key = 'slot', width = 2 }, + { heading = 'Count', key = 'count', width = 2 }, + { heading = 'Key', key = 'key' }, + }, + sortColumn = 'slot', + }, + ignoreResultNBT = UI.Button { + x = 2, y = -2, + text = 'Ignore Result NBT', event = 'ignore_result_nbt', + }, + ignoreNBT = UI.Button { + x = -13, y = -2, + text = 'Ignore NBT', event = 'ignore_nbt', + }, } function recipeTab:setItem(item) - self.item = item - self.recipe = Craft.findRecipe(self.item) + self.item = item + self.recipe = Craft.findRecipe(self.item) - self.parent:setActive(self, self.recipe) + self.parent:setActive(self, self.recipe) - local t = { } - if self.recipe then - for k, v in pairs(self.recipe.ingredients) do - table.insert(t, { - slot = k, - key = v, - }) - end - local key = itemDB:splitKey(self.recipe.result) - self.ignoreResultNBT.inactive = not key.nbtHash - end - self.grid:setValues(t) + local t = { } + if self.recipe then + for k, v in Craft.ingedients(self.recipe) do +_syslog(k) +_syslog(v) + table.insert(t, { + slot = k, + key = v.key, + count = v.count, + }) + end + local key = itemDB:splitKey(self.recipe.result) + self.ignoreResultNBT.inactive = not key.nbtHash + end + self.grid:setValues(t) end function recipeTab:eventHandler(event) - if event.type == 'ignore_result_nbt' then - -- remove old entry - Milo:updateRecipe(self.recipe.result) + if event.type == 'ignore_result_nbt' then + -- remove old entry + Milo:updateRecipe(self.recipe.result) - local item = itemDB:splitKey(self.recipe.result) - item.nbtHash = nil - self.recipe.result = itemDB:makeKey(item) + local item = itemDB:splitKey(self.recipe.result) + item.nbtHash = nil + self.recipe.result = itemDB:makeKey(item) - -- add updated entry - Milo:updateRecipe(self.recipe.result, self.recipe) + -- add updated entry + Milo:updateRecipe(self.recipe.result, self.recipe) - self.ignoreResultNBT.inactive = true - self:emit({ type = 'info_message', message = 'Recipe updated' }) + self.ignoreResultNBT.inactive = true + self:emit({ type = 'info_message', message = 'Recipe updated' }) - elseif event.type == 'grid_focus_row' then - local key = itemDB:splitKey(event.selected.key) - self.ignoreNBT.inactive = not key.nbtHash - self.ignoreNBT:draw() + elseif event.type == 'grid_focus_row' then + local key = itemDB:splitKey(event.selected.key) + self.ignoreNBT.inactive = not key.nbtHash + self.ignoreNBT:draw() - elseif event.type == 'ignore_nbt' then - local selected = self.grid:getSelected() - local item = itemDB:splitKey(selected.key) - item.nbtHash = nil - selected.key = itemDB:makeKey(item) - self.grid:draw() + elseif event.type == 'ignore_nbt' then + local selected = self.grid:getSelected() + local item = itemDB:splitKey(selected.key) + item.nbtHash = nil + selected.key = itemDB:makeKey(item) + self.grid:draw() - self.recipe.ingredients = { } - for _, v in pairs(self.grid.values) do - self.recipe.ingredients[v.slot] = v.key - end + self.recipe.ingredients = { } + for _, v in pairs(self.grid.values) do + self.recipe.ingredients[v.slot] = v.key + end - Milo:updateRecipe(self.recipe.result, self.recipe) - self:emit({ type = 'info_message', message = 'Recipe updated' }) + Milo:updateRecipe(self.recipe.result, self.recipe) + self:emit({ type = 'info_message', message = 'Recipe updated' }) - return true - end + return true + end end return { itemTab = recipeTab } diff --git a/milo/plugins/item/resetTab.lua b/milo/plugins/item/resetTab.lua index da7c8eb..d524747 100644 --- a/milo/plugins/item/resetTab.lua +++ b/milo/plugins/item/resetTab.lua @@ -7,49 +7,49 @@ local colors = _G.colors local context = Milo:getContext() local resetTab = UI.Tab { - tabTitle = 'Reset', - index = 5, - backgroundColor = colors.cyan, - textArea = UI.TextArea { - y = 2, ey = 6, - textColor = colors.yellow, - value = [[ Warning! + tabTitle = 'Reset', + index = 5, + backgroundColor = colors.cyan, + textArea = UI.TextArea { + y = 2, ey = 6, + textColor = colors.yellow, + value = [[ Warning! - This will clear all setting, - recipe, and machine for this item.]] - }, - resetButton = UI.Button { - x = 17, y = 7, - event = 'reset', - text = 'Reset', - help = 'Clear recipe and all settings', - }, + This will clear all setting, + recipe, and machine for this item.]] + }, + resetButton = UI.Button { + x = 17, y = 7, + event = 'reset', + text = 'Reset', + help = 'Clear recipe and all settings', + }, } function resetTab:setItem(item) - self.item = item + self.item = item end function resetTab:eventHandler(event) - if event.type == 'reset' then - if context.userRecipes[self.item.key] then - Milo:updateRecipe(self.item.key, nil) - end + if event.type == 'reset' then + if context.userRecipes[self.item.key] then + Milo:updateRecipe(self.item.key, nil) + end - if context.resources[self.item.key] then - context.resources[self.item.key] = nil - Milo:saveResources() - 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 + if Craft.machineLookup[self.item.key] then + Craft.machineLookup[self.item.key] = nil + Util.writeTable(Craft.MACHINE_LOOKUP, Craft.machineLookup) + end - UI:setPreviousPage() + UI:setPreviousPage() - return true - end + return true + end end return { itemTab = resetTab } diff --git a/milo/plugins/jobMonitor.lua b/milo/plugins/jobMonitor.lua index f2defa9..3fb1f36 100644 --- a/milo/plugins/jobMonitor.lua +++ b/milo/plugins/jobMonitor.lua @@ -13,224 +13,224 @@ local os = _G.os --[[ Configuration Screen ]] local wizardPage = UI.WizardPage { - title = 'Crafting Monitor', - index = 2, - backgroundColor = colors.cyan, - [1] = UI.TextArea { - 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 }, - { name = 'Large', value = 1 }, - }, - help = 'Adjust text scaling', - }, - }, + title = 'Crafting Monitor', + index = 2, + backgroundColor = colors.cyan, + [1] = UI.TextArea { + 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 }, + { name = 'Large', value = 1 }, + }, + help = 'Adjust text scaling', + }, + }, } function wizardPage:setNode(node) - self.form:setValues(node) + self.form:setValues(node) end function wizardPage:saveNode(node) - os.queueEvent('monitor_resize', node.name) + os.queueEvent('monitor_resize', node.name) end function wizardPage:validate() - return self.form:save() + return self.form:save() end function wizardPage:isValidType(node) - local m = device[node.name] - return m and m.type == 'monitor' and { - name = 'Crafting Monitor', - value = 'jobs', - category = 'display', - help = 'Display crafting progress / jobs' - } + local m = device[node.name] + return m and m.type == 'monitor' and { + name = 'Crafting Monitor', + value = 'jobs', + category = 'display', + help = 'Display crafting progress / jobs' + } end function wizardPage:isValidFor(node) - return node.mtype == 'jobs' + return node.mtype == 'jobs' end UI:getPage('nodeWizard').wizard:add({ jobs = wizardPage }) --[[ Display ]] local function createPage(node) - local monitor = UI.Device { - device = node.adapter, - textScale = node.textScale or .5, - } + local monitor = UI.Device { + device = node.adapter, + textScale = node.textScale or .5, + } - function monitor:resize() - self.textScale = node.textScale or .5 - UI.Device.resize(self) - end + function monitor:resize() + self.textScale = node.textScale or .5 + UI.Device.resize(self) + end - local page = UI.Page { - parent = monitor, - grid = UI.Grid { - --ey = -6, - sortColumn = 'index', - 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 }, - }, - headerBackgroundColor = colors.black, - headerTextColor = colors.cyan, - headerHeight = 2, - }, + local page = UI.Page { + parent = monitor, + grid = UI.Grid { + --ey = -6, + sortColumn = 'index', + 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 }, + }, + headerBackgroundColor = colors.black, + headerTextColor = colors.cyan, + headerHeight = 2, + }, --[[ - 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 = ' < ' - }, - cancelButton = UI.Button { - x = 8, y = 2, height = 3, ex = -8, - event = 'cancel_job', - backgroundColor = colors.lightGray, - text = 'Cancel Job' - }, - nextButton = UI.Button { - x = -6, y = 2, height = 3, width = 5, - event = 'next', - backgroundColor = colors.lightGray, - text = ' > ' - }, - }, + 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 = ' < ' + }, + cancelButton = UI.Button { + x = 8, y = 2, height = 3, ex = -8, + event = 'cancel_job', + backgroundColor = colors.lightGray, + text = 'Cancel Job' + }, + nextButton = UI.Button { + x = -6, y = 2, height = 3, width = 5, + event = 'next', + backgroundColor = colors.lightGray, + text = ' > ' + }, + }, ]] - } + } - 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 - end - end - self.grid:setValues(t) - self.grid:update() - self:draw() - self:sync() - end - end + 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 + end + end + self.grid:setValues(t) + self.grid:update() + self:draw() + self:sync() + end + 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: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 + 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 - -- no sorting allowed - function page:setInverseSort() end - function page:setSortColumn() end + -- no sorting allowed + function page:setInverseSort() end + function page:setSortColumn() end - function page:eventHandler(event) - if event.type == 'cancel_job' then - Sound.play('entity.villager.no', .5) + function page:eventHandler(event) + if event.type == 'cancel_job' then + Sound.play('entity.villager.no', .5) - elseif event.type == 'next' then - self.grid:nextPage() + elseif event.type == 'next' then + self.grid:nextPage() - elseif event.type == 'previous' then - self.grid:previousPage() + elseif event.type == 'previous' then + self.grid:previousPage() - else - return UI.Page.eventHandler(self, event) - end + else + return UI.Page.eventHandler(self, event) + end - Event.onTimeout(.1, function() - self:setFocus(self.grid) - self:sync() - end) - return true - end + Event.onTimeout(.1, function() + self:setFocus(self.grid) + self:sync() + end) + return true + end - UI:setPage(page) - return page + UI:setPage(page) + return page end local pages = { } Event.on({ 'milo_resume', 'milo_pause' }, function(_, reason) - 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 + 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 end) --[[ Task ]] local task = { - name = 'job status', - priority = 80, + name = 'job status', + priority = 80, } 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 + 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(task) diff --git a/milo/plugins/machineLearn.lua b/milo/plugins/machineLearn.lua index dc939b5..c2fd19c 100644 --- a/milo/plugins/machineLearn.lua +++ b/milo/plugins/machineLearn.lua @@ -111,7 +111,14 @@ function pages.confirmation:validate() } for k,v in pairs(inventory) do - recipe.ingredients[k] = itemDB:makeKey(v) + if v.count == 1 then + recipe.ingredients[k] = itemDB:makeKey(v) + else + recipe.ingredients[k] = { + key = itemDB:makeKey(v), + count = v.count, + } + end end Milo:saveMachineRecipe(recipe, result, machine.name) diff --git a/milo/plugins/machineMover.lua b/milo/plugins/machineMover.lua index 51f477e..57f5527 100644 --- a/milo/plugins/machineMover.lua +++ b/milo/plugins/machineMover.lua @@ -8,100 +8,100 @@ 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, align = '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 { }, + titleBar = UI.TitleBar { title = 'Reassign Machine' }, + grid = UI.ScrollingGrid { + y = 2, ey = -4, + values = context.storage.nodes, + columns = { + { key = 'suffix', width = 4, align = '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 + 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) + 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 == 'ignore' and device[v.name] - end) + local t = Util.filter(context.storage.nodes, function(v) + return v.mtype == 'ignore' and device[v.name] + end) - self.grid:setValues(t) + self.grid:setValues(t) end function page:enable(machine) - self.machine = machine - self:applyFilter() + self.machine = machine + self:applyFilter() - UI.Page.enable(self) + 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 + 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() + 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 + 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 + UI:setPreviousPage() + end - elseif event.type == 'cancel' then - UI:setPreviousPage() + elseif event.type == 'cancel' then + UI:setPreviousPage() - else - return UI.Page.eventHandler(self, event) - end + else + return UI.Page.eventHandler(self, event) + end end UI:addPage('machineMover', page) diff --git a/milo/plugins/massStorageView.lua b/milo/plugins/massStorageView.lua index f934ee1..ca701af 100644 --- a/milo/plugins/massStorageView.lua +++ b/milo/plugins/massStorageView.lua @@ -14,29 +14,29 @@ Add all speed upgrades possible. ]] local wizardPage = UI.WizardPage { - title = 'Mass Storage', - index = 2, - backgroundColor = colors.cyan, - [1] = UI.TextArea { - x = 2, ex = -2, y = 2, ey = -2, - value = string.format(template, Ansi.red, Ansi.reset), - }, + title = 'Mass Storage', + index = 2, + backgroundColor = colors.cyan, + [1] = UI.TextArea { + x = 2, ex = -2, y = 2, ey = -2, + value = string.format(template, Ansi.red, Ansi.reset), + }, } function wizardPage:isValidFor(node) - if node.mtype == 'storage' then - local m = device[node.name] - return m and m.listAvailableItems - end + if node.mtype == 'storage' then + local m = device[node.name] + return m and m.listAvailableItems + end end function wizardPage:setNode(node) - self.node = node + self.node = node end function wizardPage:validate() - self.node.adapterType = 'massAdapter' - return true + self.node.adapterType = 'massAdapter' + return true end -- disable until a way is found to transfer between 2 non-transferrable nodes diff --git a/milo/plugins/remote/craft.lua b/milo/plugins/remote/craft.lua index 56ce323..68e14e5 100644 --- a/milo/plugins/remote/craft.lua +++ b/milo/plugins/remote/craft.lua @@ -4,18 +4,18 @@ local args = { ... } local context = args[1] local function learn() - context:sendRequest({ - request = 'craft', - slot = 15, - }) + context:sendRequest({ + request = 'craft', + slot = 15, + }) end context.responseHandlers['craft'] = function(response) - if response.success then - Sound.play('entity.item.pickup') - else - Sound.play('entity.villager.no') - end + if response.success then + Sound.play('entity.item.pickup') + else + Sound.play('entity.villager.no') + end end return { diff --git a/milo/plugins/remote/deposit.lua b/milo/plugins/remote/deposit.lua index 934aa92..1be3e09 100644 --- a/milo/plugins/remote/deposit.lua +++ b/milo/plugins/remote/deposit.lua @@ -9,36 +9,36 @@ local context = args[1] local SHIELD_SLOT = 2 Event.addRoutine(function() - local lastTransfer - while true do - local sleepTime = 1.5 - if lastTransfer and os.clock() - lastTransfer < 2 then - sleepTime = .1 - end + local lastTransfer + while true do + local sleepTime = 1.5 + if lastTransfer and os.clock() - lastTransfer < 2 then + sleepTime = .1 + end - os.sleep(context.socket and sleepTime or 5) - if context.state.deposit and context.state.server and (context.state.useShield or context.state.slot) then - local neural = device.neuralInterface - local inv = context.state.useShield and 'getEquipment' or 'getInventory' - if neural and neural[inv] then - local s, m = pcall(function() - local method = neural[inv] - local item = method and method().list()[context.state.useShield and SHIELD_SLOT or context.state.slot] - if item then - if context:sendRequest({ - request = 'deposit', - source = context.state.useShield and 'equipment' or 'inventory', - slot = context.state.useShield and SHIELD_SLOT or context.state.slot, - count = item.count, - }) then - lastTransfer = os.clock() - end - end - end) - if not s and m then - _G._syslog(m) - end - end - end - end + os.sleep(context.socket and sleepTime or 5) + if context.state.deposit and context.state.server and (context.state.useShield or context.state.slot) then + local neural = device.neuralInterface + local inv = context.state.useShield and 'getEquipment' or 'getInventory' + if neural and neural[inv] then + local s, m = pcall(function() + local method = neural[inv] + local item = method and method().list()[context.state.useShield and SHIELD_SLOT or context.state.slot] + if item then + if context:sendRequest({ + request = 'deposit', + source = context.state.useShield and 'equipment' or 'inventory', + slot = context.state.useShield and SHIELD_SLOT or context.state.slot, + count = item.count, + }) then + lastTransfer = os.clock() + end + end + end) + if not s and m then + _G._syslog(m) + end + end + end + end end) diff --git a/milo/plugins/remote/depositAll.lua b/milo/plugins/remote/depositAll.lua index 5428477..fe76df6 100644 --- a/milo/plugins/remote/depositAll.lua +++ b/milo/plugins/remote/depositAll.lua @@ -10,70 +10,70 @@ local context = args[1] local ni = peripheral.find('neuralInterface') if not context.state.depositAll then - context.state.depositAll = { } + context.state.depositAll = { } end if not context.state.depositAll.retain then - context.state.depositAll.retain = { } + context.state.depositAll.retain = { } end local page = UI.Page { - titleBar = UI.TitleBar { - backgroundColor = colors.gray, - title = 'Deposit full inventory', - previousPage = true, - }, - items = UI.ScrollingGrid { - x = 2, ex = -2, y = 2, ey = -4, - columns = { - { heading = 'Qty', key = 'count', width = 3 }, - { heading = 'Name', key = 'displayName', }, - }, - sortColumn = 'count', - inverseSort = true - }, - form = UI.Form { - x = 2, ex = -2, y = -2, ey = -2, - margin = 1, - [1] = UI.Checkbox { - formLabel = 'Include hotbar', formKey = 'includeHotbar', - help = 'Also send the contents of the hotbar to Milo (excluding the neural connector)' - } - }, - notification = UI.Notification(), + titleBar = UI.TitleBar { + backgroundColor = colors.gray, + title = 'Deposit full inventory', + previousPage = true, + }, + items = UI.ScrollingGrid { + x = 2, ex = -2, y = 2, ey = -4, + columns = { + { heading = 'Qty', key = 'count', width = 3 }, + { heading = 'Name', key = 'displayName', }, + }, + sortColumn = 'count', + inverseSort = true + }, + form = UI.Form { + x = 2, ex = -2, y = -2, ey = -2, + margin = 1, + [1] = UI.Checkbox { + formLabel = 'Include hotbar', formKey = 'includeHotbar', + help = 'Also send the contents of the hotbar to Milo (excluding the neural connector)' + } + }, + notification = UI.Notification(), } local function makeKey(item) -- group items regardless of damage - local damage = item.maxDamage == 0 and item.damage - return itemDB:makeKey({ name = item.name, damage = damage }) + local damage = item.maxDamage == 0 and item.damage + return itemDB:makeKey({ name = item.name, damage = damage }) end function page:updateInventoryList() - local inv = ni.getInventory().list() - local list = { } + local inv = ni.getInventory().list() + local list = { } - for slot, item in pairs(inv) do - if (context.state.depositAll.includeHotbar or slot > 9) and item.name ~= 'plethora:neuralconnector' then - item = itemDB:get(item, function() return ni.getInventory().getItemMeta(slot) end) - local key = makeKey(item) - if not list[key] then - item.displayName = item.displayName:match('(.+) %(damage:.+%)') or item.displayName - list[key] = item - else - list[key].count = list[key].count + item.count - end - list[key].key = key - end - end + for slot, item in pairs(inv) do + if (context.state.depositAll.includeHotbar or slot > 9) and item.name ~= 'plethora:neuralconnector' then + item = itemDB:get(item, function() return ni.getInventory().getItemMeta(slot) end) + local key = makeKey(item) + if not list[key] then + item.displayName = item.displayName:match('(.+) %(damage:.+%)') or item.displayName + list[key] = item + else + list[key].count = list[key].count + item.count + end + list[key].key = key + end + end - self.items:setValues(list) - self.items:draw() - itemDB:flush() + self.items:setValues(list) + self.items:draw() + itemDB:flush() end function page:enable() - self.form:setValues(context.state.depositAll) - self:updateInventoryList() - UI.Page.enable(self) + self.form:setValues(context.state.depositAll) + self:updateInventoryList() + UI.Page.enable(self) end function page.items:getRowTextColor(row) @@ -84,57 +84,57 @@ function page.items:getRowTextColor(row) end function page:depositAll() - self.notification:info('Depositing all items...') + self.notification:info('Depositing all items...') - local inv = ni.getInventory().list() + local inv = ni.getInventory().list() - for slot, item in pairs(inv) do - item = itemDB:get(item, function() return ni.getInventory().getItemMeta(slot) end) - local key = makeKey(item) - if not context.state.depositAll.retain[key] then - if (context.state.depositAll.includeHotbar or slot > 9) and item.name ~= 'plethora:neuralconnector' then - context:sendRequest({ - request = 'deposit', - source = 'inventory', - slot = slot, - count = item.count, - }) - end - end - end + for slot, item in pairs(inv) do + item = itemDB:get(item, function() return ni.getInventory().getItemMeta(slot) end) + local key = makeKey(item) + if not context.state.depositAll.retain[key] then + if (context.state.depositAll.includeHotbar or slot > 9) and item.name ~= 'plethora:neuralconnector' then + context:sendRequest({ + request = 'deposit', + source = 'inventory', + slot = slot, + count = item.count, + }) + end + end + end end function page:eventHandler(event) - if event.type == 'checkbox_change' and event.element.formKey == 'includeHotbar' then - context.state.depositAll.includeHotbar = event.checked - page:updateInventoryList() + if event.type == 'checkbox_change' and event.element.formKey == 'includeHotbar' then + context.state.depositAll.includeHotbar = event.checked + page:updateInventoryList() - elseif event.type == 'grid_select' then - local key = event.selected.key - if context.state.depositAll.retain[key] then - context.state.depositAll.retain[key] = nil - else - context.state.depositAll.retain[key] = true - end - context:setState('depositAll', context.state.depositAll) - self.items:draw() + elseif event.type == 'grid_select' then + local key = event.selected.key + if context.state.depositAll.retain[key] then + context.state.depositAll.retain[key] = nil + else + context.state.depositAll.retain[key] = true + end + context:setState('depositAll', context.state.depositAll) + self.items:draw() - elseif event.type == 'form_complete' then - Config.update('miloRemote', context.state) - page:depositAll() - UI:setPreviousPage() + elseif event.type == 'form_complete' then + Config.update('miloRemote', context.state) + page:depositAll() + UI:setPreviousPage() - elseif event.type == 'form_cancel' then - UI:setPreviousPage() + elseif event.type == 'form_cancel' then + UI:setPreviousPage() - else - return UI.Page.eventHandler(self, event) - end + else + return UI.Page.eventHandler(self, event) + end end return { - menuItem = 'Deposit all', - callback = function() - UI:setPage(page) - end, + menuItem = 'Deposit all', + callback = function() + UI:setPage(page) + end, } diff --git a/milo/plugins/remote/feeder.lua b/milo/plugins/remote/feeder.lua index 7d56af5..3d5353d 100644 --- a/milo/plugins/remote/feeder.lua +++ b/milo/plugins/remote/feeder.lua @@ -16,13 +16,13 @@ local page = UI.Page { title = 'Auto-feeder', previousPage = true, }, - grid = UI.ScrollingGrid { - y = 2, ey = -2, - columns = { - { heading = 'Name', key = 'displayName' }, - }, - sortColumn = 'displayName', - }, + grid = UI.ScrollingGrid { + y = 2, ey = -2, + columns = { + { heading = 'Name', key = 'displayName' }, + }, + sortColumn = 'displayName', + }, statusBar = UI.StatusBar { values = 'Double-click to toggle' }, @@ -55,45 +55,45 @@ function page.grid:getRowTextColor(row) end local function getFood(food) - for slot,v in pairs(ni.getInventory().list()) do - local key = itemDB:makeKey(v) - if key == food then - local item = ni.getInventory().getItem(slot) - if item and item.consume then - return item - end - break - end - end + for slot,v in pairs(ni.getInventory().list()) do + local key = itemDB:makeKey(v) + if key == food then + local item = ni.getInventory().getItem(slot) + if item and item.consume then + return item + end + break + end + end end function page:eventHandler(event) - if event.type == 'grid_select' then - if context.state.food == event.selected.key then - context:setState('food') - self.grid:draw() - elseif getFood(event.selected.key) then - context:setState('food', event.selected.key) - self.grid:draw() - else - Sound.play('entity.villager.no') - end + if event.type == 'grid_select' then + if context.state.food == event.selected.key then + context:setState('food') + self.grid:draw() + elseif getFood(event.selected.key) then + context:setState('food', event.selected.key) + self.grid:draw() + else + Sound.play('entity.villager.no') + end return true end end Event.onInterval(5, function() - local s, m = pcall(function() -- prevent errors from some mod items - if context.state.food and ni.getMetaOwner().food.hungry then - local item = getFood(context.state.food) - if item then - item.consume() - end - end - end) - if not s and m then - _G._syslog(m) - end + local s, m = pcall(function() -- prevent errors from some mod items + if context.state.food and ni.getMetaOwner().food.hungry then + local item = getFood(context.state.food) + if item then + item.consume() + end + end + end) + if not s and m then + _G._syslog(m) + end end) return { diff --git a/milo/plugins/remote/setup.lua b/milo/plugins/remote/setup.lua index 1c08f9e..c8ff101 100644 --- a/milo/plugins/remote/setup.lua +++ b/milo/plugins/remote/setup.lua @@ -10,78 +10,78 @@ local STARTUP_FILE = 'usr/autorun/miloRemote.lua' local context = ({ ... })[1] local setup = UI.SlideOut { - backgroundColor = colors.cyan, - titleBar = UI.TitleBar { - title = 'Remote Setup', - }, - form = UI.Form { - x = 2, ex = -2, y = 2, ey = -1, - [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 = 'Return Slot', formKey = 'slot', - help = 'Use a slot for sending to storage', - shadowText = 'Inventory slot #', - limit = 5, - validate = 'numeric', - required = false, - }, - [3] = UI.Checkbox { - 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, - 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.]], - }, - }, - statusBar = UI.StatusBar { - backgroundColor = colors.cyan, - }, + backgroundColor = colors.cyan, + titleBar = UI.TitleBar { + title = 'Remote Setup', + }, + form = UI.Form { + x = 2, ex = -2, y = 2, ey = -1, + [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 = 'Return Slot', formKey = 'slot', + help = 'Use a slot for sending to storage', + shadowText = 'Inventory slot #', + limit = 5, + validate = 'numeric', + required = false, + }, + [3] = UI.Checkbox { + 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, + 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.]], + }, + }, + statusBar = UI.StatusBar { + backgroundColor = colors.cyan, + }, } function setup:eventHandler(event) - if event.type == 'focus_change' then - self.statusBar:setStatus(event.focused.help) + if event.type == 'focus_change' then + self.statusBar:setStatus(event.focused.help) - elseif event.type == 'form_complete' then - Config.update('miloRemote', context.state) - self:hide() - context.page:refresh('list') - context.page.grid:draw() - context.page:setFocus(context.page.statusBar.filter) + elseif event.type == 'form_complete' then + Config.update('miloRemote', context.state) + self:hide() + context.page:refresh('list') + context.page.grid:draw() + context.page:setFocus(context.page.statusBar.filter) - if context.state.runOnStartup then - if not fs.exists(STARTUP_FILE) then - Util.writeFile(STARTUP_FILE, - [[os.sleep(1) + if context.state.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 + end + elseif fs.exists(STARTUP_FILE) then + fs.delete(STARTUP_FILE) + end - elseif event.type == 'form_cancel' then - self:hide() - context.page:setFocus(context.page.statusBar.filter) - end + elseif event.type == 'form_cancel' then + self:hide() + context.page:setFocus(context.page.statusBar.filter) + end - return UI.SlideOut.eventHandler(self, event) + return UI.SlideOut.eventHandler(self, event) end context.page:add({ setup = setup }) diff --git a/milo/plugins/remoteCraft.lua b/milo/plugins/remoteCraft.lua index 7a6a7d9..4cb7e8a 100644 --- a/milo/plugins/remoteCraft.lua +++ b/milo/plugins/remoteCraft.lua @@ -5,42 +5,42 @@ local context = Milo:getContext() local device = _G.device local function craftHandler(user, message, socket) - local function craft() - local slots = { - [1] = 1, [2] = 2, [3] = 3, - [5] = 10, [6] = 11, [7] = 12, - [9] = 19, [10] = 20, [11] = 21, - } - local inventory = device[user .. ':inventory'] - if inventory then - for k, v in pairs(slots) do - inventory.pushItems(context.turtleInventory.name, v + message.slot - 1, 1, k) - end - local recipe, msg = Milo:learnRecipe() - if recipe then - socket:write({ - type = 'craft', - msg = 'Learned: ' .. itemDB:getName(recipe), - success = true, - }) - for k,v in pairs(context.turtleInventory.adapter.list()) do - inventory.pullItems(context.turtleInventory.name, k, v.count) - end - else - socket:write({ - type = 'craft', - msg = msg, - }) - for k, v in pairs(slots) do - inventory.pullItems(context.turtleInventory.name, k, 1, v + message.slot - 1) - end - end - end - end + local function craft() + local slots = { + [1] = 1, [2] = 2, [3] = 3, + [5] = 10, [6] = 11, [7] = 12, + [9] = 19, [10] = 20, [11] = 21, + } + local inventory = device[user .. ':inventory'] + if inventory then + for k, v in pairs(slots) do + inventory.pushItems(context.turtleInventory.name, v + message.slot - 1, 1, k) + end + local recipe, msg = Milo:learnRecipe() + if recipe then + socket:write({ + type = 'craft', + msg = 'Learned: ' .. itemDB:getName(recipe), + success = true, + }) + for k,v in pairs(context.turtleInventory.adapter.list()) do + inventory.pullItems(context.turtleInventory.name, k, v.count) + end + else + socket:write({ + type = 'craft', + msg = msg, + }) + for k, v in pairs(slots) do + inventory.pullItems(context.turtleInventory.name, k, 1, v + message.slot - 1) + end + end + end + end - Milo:queueRequest({ }, craft) + Milo:queueRequest({ }, craft) end return { - remoteHandler = { callback = craftHandler, messages = { craft = true } } + remoteHandler = { callback = craftHandler, messages = { craft = true } } } diff --git a/milo/plugins/replenishTask.lua b/milo/plugins/replenishTask.lua index b9c55f3..3423197 100644 --- a/milo/plugins/replenishTask.lua +++ b/milo/plugins/replenishTask.lua @@ -2,39 +2,39 @@ local itemDB = require('core.itemDB') local Milo = require('milo') local ReplenishTask = { - name = 'replenish', - priority = 60, + name = 'replenish', + priority = 60, } function ReplenishTask:cycle(context) - for k,res in pairs(context.resources) do - if res.low then - local item = itemDB:splitKey(k) - item.key = k + for k,res in pairs(context.resources) do + if res.low then + local item = itemDB:splitKey(k) + item.key = k - local _, count = Milo:getMatches(item, res) + local _, count = Milo:getMatches(item, res) - if count < res.low then - 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, - replenish = true, - }) - else - local request = context.craftingQueue[itemDB:makeKey(item)] - if request and request.replenish then - --request.count = request.crafted - end - end - end - end + if count < res.low then + 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, + replenish = true, + }) + else + local request = context.craftingQueue[itemDB:makeKey(item)] + if request and request.replenish then + --request.count = request.crafted + end + end + end + end end Milo:registerTask(ReplenishTask) diff --git a/milo/plugins/speakerView.lua b/milo/plugins/speakerView.lua index 8015d97..c790358 100644 --- a/milo/plugins/speakerView.lua +++ b/milo/plugins/speakerView.lua @@ -8,66 +8,66 @@ local context = Milo:getContext() local speakerNode = context.storage:getSingleNode('speaker') if speakerNode then - Sound.setVolume(speakerNode.volume) + Sound.setVolume(speakerNode.volume) end local wizardPage = UI.WizardPage { - 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', - }, - }, + 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.form:setValues(node) + self.form:setValues(node) end function wizardPage:saveNode(node) - Sound.setVolume(node.volume) + Sound.setVolume(node.volume) end function wizardPage:validate() - return self.form:save() + return self.form:save() 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', - } + 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' + 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 + 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 }) diff --git a/milo/plugins/statsView.lua b/milo/plugins/statsView.lua index 861d887..ff13f10 100644 --- a/milo/plugins/statsView.lua +++ b/milo/plugins/statsView.lua @@ -16,278 +16,278 @@ local template = Right-clicking on the activity monitor will reset the totals.]] local wizardPage = UI.WizardPage { - title = 'Status Monitor', - index = 2, - backgroundColor = colors.cyan, - [1] = UI.TextArea { - x = 2, ex = -2, y = 2, ey = 6, - marginRight = 0, - value = string.format(template, Ansi.yellow, Ansi.reset), - }, - 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 }, - { name = 'Large', value = 1 }, - }, - help = 'Adjust text scaling', - }, - }, + title = 'Status Monitor', + index = 2, + backgroundColor = colors.cyan, + [1] = UI.TextArea { + x = 2, ex = -2, y = 2, ey = 6, + marginRight = 0, + value = string.format(template, Ansi.yellow, Ansi.reset), + }, + 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 }, + { name = 'Large', value = 1 }, + }, + help = 'Adjust text scaling', + }, + }, } function wizardPage:setNode(node) - self.form:setValues(node) + self.form:setValues(node) end function wizardPage:validate() - return self.form:save() + return self.form:save() end function wizardPage:saveNode(node) - os.queueEvent('monitor_resize', node.name) + os.queueEvent('monitor_resize', node.name) end function wizardPage:isValidType(node) - local m = device[node.name] - return m and m.type == 'monitor' and { - name = 'Status Monitor', - value = 'status', - category = 'display', - help = 'Display storage status' - } + local m = device[node.name] + return m and m.type == 'monitor' and { + name = 'Status Monitor', + value = 'status', + category = 'display', + help = 'Display storage status' + } end function wizardPage:isValidFor(node) - return node.mtype == 'status' + return node.mtype == 'status' end UI:getPage('nodeWizard').wizard:add({ statusMonitor = wizardPage }) --[[ Display ]]-- local function createPage(node) - local monitor = UI.Device { - device = node.adapter, - textScale = node.textScale or .5, - } + local monitor = UI.Device { + device = node.adapter, + textScale = node.textScale or .5, + } - function monitor:resize() - self.textScale = node.textScale or .5 - UI.Device.resize(self) - end + function monitor:resize() + self.textScale = node.textScale or .5 + UI.Device.resize(self) + end - local page = UI.Page { - parent = monitor, - tabs = UI.Tabs { - [1] = UI.Tab { - tabTitle = 'Overview', - backgroundColor = colors.black, - onlineLabel = UI.Text { - x = 2, y = 2, - value = 'Storage Status', - }, - onlineText = UI.Text { - x = 18, ex = -2, y = 2, - }, - tpsLabel = UI.Text { - x = 2, y = 3, - value = 'Tasks/sec', - }, - tpsText = UI.Text { - x = 18, ex = -2, y = 3, - }, - tasksLabel = UI.Text { - x = -18, y = 3, - value = 'Proc time', - }, - tasksText = UI.Text { - x = -6, ex = -2, y = 3, - align = 'right', - }, - storageLabel = UI.Text { - x = 2, ex = -1, y = 6, - }, - storage = UI.ProgressBar { - x = 2, ex = -2, y = 7, height = 3, - }, - unlockedLabel = UI.Text { - x = 2, ex = -1, y = 12, - }, - unlocked = UI.ProgressBar { - x = 2, ex = -2, y = 13, height = 3, - }, - craftingLabel = UI.Text { - x = 2, ex = -1, y = 18, - value = 'Crafting Status', - }, - crafting = UI.ProgressBar { - x = 2, ex = -2, y = 19, height = 3, - value = 100, - }, - }, - [2] = UI.Tab { - tabTitle = 'Stats', - textArea = UI.TextArea { - y = 3, - }, - }, - [3] = UI.Tab { - tabTitle = 'Storage', - grid = UI.ScrollingGrid { - y = 2, - columns = { - { heading = 'Name', key = 'name' }, - { heading = 'Size', key = 'size', width = 5 }, - { heading = 'Used', key = 'used', width = 5 }, - { heading = 'Perc', key = 'perc', width = 5 }, - -- TODO: add % to each number - }, - sortColumn = 'name', - }, - }, - [4] = UI.Tab { - tabTitle = 'Offline', - grid = UI.ScrollingGrid { - y = 2, - columns = { - { heading = 'Name', key = 'name' }, - }, - sortColumn = 'name', - }, - }, - [5] = UI.Tab { - tabTitle = 'Activity', - term = UI.Embedded { - --visible = true, - }, - }, - [6] = UI.Tab { - tabTitle = 'Tasks', - grid = UI.ScrollingGrid { - y = 2, - values = context.tasks, - columns = { - { heading = 'Priority', key = 'priority', width = 5 }, - { heading = 'Name', key = 'name' }, - { heading = 'Avg', key = 'avg', width = 7, align = 'right' }, - { heading = '%', key = 'perc', width = 7, align = 'right' }, - }, - sortColumn = 'priority', - }, - }, - }, - } + local page = UI.Page { + parent = monitor, + tabs = UI.Tabs { + [1] = UI.Tab { + tabTitle = 'Overview', + backgroundColor = colors.black, + onlineLabel = UI.Text { + x = 2, y = 2, + value = 'Storage Status', + }, + onlineText = UI.Text { + x = 18, ex = -2, y = 2, + }, + tpsLabel = UI.Text { + x = 2, y = 3, + value = 'Tasks/sec', + }, + tpsText = UI.Text { + x = 18, ex = -2, y = 3, + }, + tasksLabel = UI.Text { + x = -18, y = 3, + value = 'Proc time', + }, + tasksText = UI.Text { + x = -6, ex = -2, y = 3, + align = 'right', + }, + storageLabel = UI.Text { + x = 2, ex = -1, y = 6, + }, + storage = UI.ProgressBar { + x = 2, ex = -2, y = 7, height = 3, + }, + unlockedLabel = UI.Text { + x = 2, ex = -1, y = 12, + }, + unlocked = UI.ProgressBar { + x = 2, ex = -2, y = 13, height = 3, + }, + craftingLabel = UI.Text { + x = 2, ex = -1, y = 18, + value = 'Crafting Status', + }, + crafting = UI.ProgressBar { + x = 2, ex = -2, y = 19, height = 3, + value = 100, + }, + }, + [2] = UI.Tab { + tabTitle = 'Stats', + textArea = UI.TextArea { + y = 3, + }, + }, + [3] = UI.Tab { + tabTitle = 'Storage', + grid = UI.ScrollingGrid { + y = 2, + columns = { + { heading = 'Name', key = 'name' }, + { heading = 'Size', key = 'size', width = 5 }, + { heading = 'Used', key = 'used', width = 5 }, + { heading = 'Perc', key = 'perc', width = 5 }, + -- TODO: add % to each number + }, + sortColumn = 'name', + }, + }, + [4] = UI.Tab { + tabTitle = 'Offline', + grid = UI.ScrollingGrid { + y = 2, + columns = { + { heading = 'Name', key = 'name' }, + }, + sortColumn = 'name', + }, + }, + [5] = UI.Tab { + tabTitle = 'Activity', + term = UI.Embedded { + --visible = true, + }, + }, + [6] = UI.Tab { + tabTitle = 'Tasks', + grid = UI.ScrollingGrid { + y = 2, + values = context.tasks, + columns = { + { heading = 'Priority', key = 'priority', width = 5 }, + { heading = 'Name', key = 'name' }, + { heading = 'Avg', key = 'avg', width = 7, align = 'right' }, + { heading = '%', key = 'perc', width = 7, align = 'right' }, + }, + sortColumn = 'priority', + }, + }, + }, + } - local overviewTab = page.tabs[1] - local statsTab = page.tabs[2] - local usageTab = page.tabs[3] - local stateTab = page.tabs[4] - local activityTab = page.tabs[5] - local taskTab = page.tabs[6] + local overviewTab = page.tabs[1] + local statsTab = page.tabs[2] + local usageTab = page.tabs[3] + local stateTab = page.tabs[4] + local activityTab = page.tabs[5] + local taskTab = page.tabs[6] - local function getStorageStats() - local stats = { } - local totals = { - usedSlots = 0, - totalSlots = 0, - totalChests = 0, - unlockedSlots = 0, - usedUnlockedSlots = 0, - } + local function getStorageStats() + local stats = { } + local totals = { + usedSlots = 0, + totalSlots = 0, + totalChests = 0, + unlockedSlots = 0, + usedUnlockedSlots = 0, + } - for n in context.storage:filterActive('storage') do - if n.adapter.size and n.adapter.list then - pcall(function() - local updated = n.adapter.__lastUpdate ~= n.adapter.lastUpdate - if updated then - n.adapter.__used = Util.size(n.adapter.list()) - n.adapter.__lastUpdate = n.adapter.lastUpdate - end - if not n.adapter.__used then - n.adapter.__used = Util.size(n.adapter.list()) - end - table.insert(stats, { - name = n.displayName or n.name, - size = n.adapter.__size, - used = n.adapter.__used, - perc = math.floor(n.adapter.__used / n.adapter.__size * 100), - updated = updated, - }) - totals.usedSlots = totals.usedSlots + n.adapter.__used - totals.totalSlots = totals.totalSlots + n.adapter.__size - totals.totalChests = totals.totalChests + 1 - if not n.lock then - totals.unlockedSlots = totals.unlockedSlots + n.adapter.__size - totals.usedUnlockedSlots = totals.usedUnlockedSlots + n.adapter.__used - end - end) - end - end + for n in context.storage:filterActive('storage') do + if n.adapter.size and n.adapter.list then + pcall(function() + local updated = n.adapter.__lastUpdate ~= n.adapter.lastUpdate + if updated then + n.adapter.__used = Util.size(n.adapter.list()) + n.adapter.__lastUpdate = n.adapter.lastUpdate + end + if not n.adapter.__used then + n.adapter.__used = Util.size(n.adapter.list()) + end + table.insert(stats, { + name = n.displayName or n.name, + size = n.adapter.__size, + used = n.adapter.__used, + perc = math.floor(n.adapter.__used / n.adapter.__size * 100), + updated = updated, + }) + totals.usedSlots = totals.usedSlots + n.adapter.__used + totals.totalSlots = totals.totalSlots + n.adapter.__size + totals.totalChests = totals.totalChests + 1 + if not n.lock then + totals.unlockedSlots = totals.unlockedSlots + n.adapter.__size + totals.usedUnlockedSlots = totals.usedUnlockedSlots + n.adapter.__used + end + end) + end + end - return stats, totals - end + return stats, totals + end - function stateTab:refresh() - self.grid.values = { } - for _, v in pairs(context.storage.nodes) do - if v.mtype ~= 'hidden' then - if not v.adapter or not v.adapter.online then - table.insert(self.grid.values, { - name = v.displayName or v.name - }) - end - end - end - self.grid:update() - end + function stateTab:refresh() + self.grid.values = { } + for _, v in pairs(context.storage.nodes) do + if v.mtype ~= 'hidden' then + if not v.adapter or not v.adapter.online then + table.insert(self.grid.values, { + name = v.displayName or v.name + }) + end + end + end + self.grid:update() + end - function stateTab:enable() - self:refresh() - self.handle = Event.onInterval(5, function() - self:refresh() - self.grid:draw() - self:sync() - end) - UI.Tab.enable(self) - end + function stateTab:enable() + self:refresh() + self.handle = Event.onInterval(5, function() + self:refresh() + self.grid:draw() + self:sync() + end) + UI.Tab.enable(self) + end - function stateTab:disable() - Event.off(self.handle) - UI.Tab.disable(self) - end + function stateTab:disable() + Event.off(self.handle) + UI.Tab.disable(self) + end - function usageTab:refresh() - self.grid:setValues(getStorageStats()) - end + function usageTab:refresh() + self.grid:setValues(getStorageStats()) + end - function usageTab:enable() - self:refresh() - self.handle = Event.onInterval(5, function() - self:refresh() - self.grid:draw() - self:sync() - end) - UI.Tab.enable(self) - end + function usageTab:enable() + self:refresh() + self.handle = Event.onInterval(5, function() + self:refresh() + self.grid:draw() + self:sync() + end) + UI.Tab.enable(self) + end - function usageTab:disable() - Event.off(self.handle) - UI.Tab.disable(self) - end + function usageTab:disable() + Event.off(self.handle) + UI.Tab.disable(self) + end - function usageTab.grid:getRowTextColor(row, selected) - return row.updated and colors.yellow or - UI.Grid:getRowTextColor(row, selected) - end + function usageTab.grid:getRowTextColor(row, selected) + return row.updated and colors.yellow or + UI.Grid:getRowTextColor(row, selected) + end - function statsTab.textArea:draw() - local _, stats = getStorageStats() - local totalItems, nodeCount = 0, 0 - local formatString = [[ + function statsTab.textArea:draw() + local _, stats = getStorageStats() + local totalItems, nodeCount = 0, 0 + local formatString = [[ Storage Usage : %d%% Slots : %d of %d used Unique Items : %d @@ -297,191 +297,191 @@ Nodes : %d Unlocked Slots : %d of %d (%d%%) ]] - for _,v in pairs(context.storage.nodes) do - if v.adapter and v.adapter.online then - nodeCount = nodeCount + 1 - end - end + for _,v in pairs(context.storage.nodes) do + if v.adapter and v.adapter.online then + nodeCount = nodeCount + 1 + end + end - for _,v in pairs(context.storage.cache) do - totalItems = totalItems + v.count - end + for _,v in pairs(context.storage.cache) do + totalItems = totalItems + v.count + end - self.value = string.format(formatString, - math.floor(stats.usedSlots / stats.totalSlots * 100), - stats.usedSlots, - stats.totalSlots, - Util.size(context.storage.cache), - totalItems, - nodeCount, - stats.usedUnlockedSlots, - stats.unlockedSlots, - math.floor(stats.usedUnlockedSlots / stats.unlockedSlots * 100)) - UI.TextArea.draw(self) - end + self.value = string.format(formatString, + math.floor(stats.usedSlots / stats.totalSlots * 100), + stats.usedSlots, + stats.totalSlots, + Util.size(context.storage.cache), + totalItems, + nodeCount, + stats.usedUnlockedSlots, + stats.unlockedSlots, + math.floor(stats.usedUnlockedSlots / stats.unlockedSlots * 100)) + UI.TextArea.draw(self) + end - function statsTab:enable() - self.handle = Event.onInterval(5, function() - self.textArea:draw() - self:sync() - end) - UI.Tab.enable(self) - end + function statsTab:enable() + self.handle = Event.onInterval(5, function() + self.textArea:draw() + self:sync() + end) + UI.Tab.enable(self) + end - function statsTab:disable() - Event.off(self.handle) - UI.Tab.disable(self) - end + function statsTab:disable() + Event.off(self.handle) + UI.Tab.disable(self) + end - function taskTab.grid:getDisplayValues(row) - return { - name = row.name, - priority = row.priority, - avg = Util.round(row.execTime / context.taskCounter * 1000) .. ' ms', - perc = Util.round(row.execTime / context.taskTimer * 100) .. '%', - } - end + function taskTab.grid:getDisplayValues(row) + return { + name = row.name, + priority = row.priority, + avg = Util.round(row.execTime / context.taskCounter * 1000) .. ' ms', + perc = Util.round(row.execTime / context.taskTimer * 100) .. '%', + } + end - function taskTab:refresh() - self.grid:update() - end + function taskTab:refresh() + self.grid:update() + end - function taskTab:enable() - self:refresh() - self.handle = Event.onInterval(5, function() - self:refresh() - self.grid:draw() - self:sync() - end) - UI.Tab.enable(self) - end + function taskTab:enable() + self:refresh() + self.handle = Event.onInterval(5, function() + self:refresh() + self.grid:draw() + self:sync() + end) + UI.Tab.enable(self) + end - function taskTab:disable() - Event.off(self.handle) - UI.Tab.disable(self) - end + function taskTab:disable() + Event.off(self.handle) + UI.Tab.disable(self) + end - function overviewTab:draw() - local _, stats = getStorageStats() + function overviewTab:draw() + local _, stats = getStorageStats() - self.onlineText.textColor = context.storage:isOnline() and colors.green or colors.red - self.onlineText.value = context.storage:isOnline() and 'Online' or 'Offline' + self.onlineText.textColor = context.storage:isOnline() and colors.green or colors.red + self.onlineText.value = context.storage:isOnline() and 'Online' or 'Offline' - self.tpsText.value = tostring(Util.round(self.tasks / (os.clock() - self.timer), 2)) - self.tasksText.value = tostring(Util.round(context.taskTimer / context.taskCounter, 2)) + self.tpsText.value = tostring(Util.round(self.tasks / (os.clock() - self.timer), 2)) + self.tasksText.value = tostring(Util.round(context.taskTimer / context.taskCounter, 2)) - local total, crafted = 0, 0 - for _,v in pairs(context.craftingQueue) do - total = total + v.requested - crafted = crafted + v.crafted - end - if Milo:isCraftingPaused() then - self.crafting.progressColor = colors.yellow - self.crafting.value = 100 - else - self.crafting.progressColor = colors.orange - self.crafting.value = total > 0 and math.ceil(crafted / total * 100) or 0 - end + local total, crafted = 0, 0 + for _,v in pairs(context.craftingQueue) do + total = total + v.requested + crafted = crafted + v.crafted + end + if Milo:isCraftingPaused() then + self.crafting.progressColor = colors.yellow + self.crafting.value = 100 + else + self.crafting.progressColor = colors.orange + self.crafting.value = total > 0 and math.ceil(crafted / total * 100) or 0 + end - local percent = math.floor(stats.usedSlots / stats.totalSlots * 100) - local color = colors.green - if percent > 90 then - color = colors.red - elseif percent > 75 then - color = colors.yellow - end - self.storage.progressColor = color - self.storage.value = percent + local percent = math.floor(stats.usedSlots / stats.totalSlots * 100) + local color = colors.green + if percent > 90 then + color = colors.red + elseif percent > 75 then + color = colors.yellow + end + self.storage.progressColor = color + self.storage.value = percent - self.storageLabel.value = string.format('Total Usage: %s%% (%s of %s slots)', - percent, stats.usedSlots, stats.totalSlots) + self.storageLabel.value = string.format('Total Usage: %s%% (%s of %s slots)', + percent, stats.usedSlots, stats.totalSlots) - percent = math.floor(stats.usedUnlockedSlots / stats.unlockedSlots * 100) - color = colors.green - if percent > 90 then - color = colors.red - elseif percent > 75 then - color = colors.yellow - end - self.unlocked.progressColor = color - self.unlocked.value = percent + percent = math.floor(stats.usedUnlockedSlots / stats.unlockedSlots * 100) + color = colors.green + if percent > 90 then + color = colors.red + elseif percent > 75 then + color = colors.yellow + end + self.unlocked.progressColor = color + self.unlocked.value = percent - self.unlockedLabel.value = string.format('Unlocked Usage: %s%% (%s of %s slots)', - percent, stats.usedUnlockedSlots, stats.unlockedSlots) + self.unlockedLabel.value = string.format('Unlocked Usage: %s%% (%s of %s slots)', + percent, stats.usedUnlockedSlots, stats.unlockedSlots) - UI.Tab.draw(self) - end + UI.Tab.draw(self) + end - function overviewTab:enable() - self.timer = os.clock() - self.tasks = 0 - self.handle = Event.onInterval(5, function() - self:draw() - self:sync() - end) - self.handle2 = Event.on({ 'milo_resume', 'milo_pause', 'storage_offline', 'storage_online' }, function() - self:draw() - self:sync() - end) - self.handle3 = Event.on('plethora_task', function() - self.tasks = self.tasks + 1 - end) - UI.Tab.enable(self) - end + function overviewTab:enable() + self.timer = os.clock() + self.tasks = 0 + self.handle = Event.onInterval(5, function() + self:draw() + self:sync() + end) + self.handle2 = Event.on({ 'milo_resume', 'milo_pause', 'storage_offline', 'storage_online' }, function() + self:draw() + self:sync() + end) + self.handle3 = Event.on({ 'plethora_task', 'task_complete' }, function() + self.tasks = self.tasks + 1 + end) + UI.Tab.enable(self) + end - function overviewTab:disable() - Event.off(self.handle) - Event.off(self.handle2) - Event.off(self.handle3) - UI.Tab.disable(self) - end + function overviewTab:disable() + Event.off(self.handle) + Event.off(self.handle2) + Event.off(self.handle3) + UI.Tab.disable(self) + end - function page:eventHandler(event) - if event.type == 'tab_activate' then - local state = Milo:getState('statusState') or { } - state[node.name] = event.activated.tabTitle - Milo:setState('statusState', state) - end - return UI.Page.eventHandler(self, event) - end + function page:eventHandler(event) + if event.type == 'tab_activate' then + local state = Milo:getState('statusState') or { } + state[node.name] = event.activated.tabTitle + Milo:setState('statusState', state) + end + return UI.Page.eventHandler(self, event) + end - table.insert(context.loggers, function(...) - local oterm = term.redirect(activityTab.term.win) - activityTab.term.win.scrollBottom() - Util.print(...) - term.redirect(oterm) - if activityTab.enabled then - activityTab:sync() - end - end) + table.insert(context.loggers, function(...) + local oterm = term.redirect(activityTab.term.win) + activityTab.term.win.scrollBottom() + Util.print(...) + term.redirect(oterm) + if activityTab.enabled then + activityTab:sync() + end + end) - Event.onTimeout(0, function() - UI:setPage(page) - end) + Event.onTimeout(0, function() + UI:setPage(page) + end) - -- restore active tab - local tabState = Milo:getState('statusState') or { } - if tabState[node.name] then - page.tabs:selectTab(Util.find(page.tabs, 'tabTitle', tabState[node.name])) - end + -- restore active tab + local tabState = Milo:getState('statusState') or { } + if tabState[node.name] then + page.tabs:selectTab(Util.find(page.tabs, 'tabTitle', tabState[node.name])) + end - return page + return page end local pages = { } --[[ Task ]]-- local task = { - name = 'status', - priority = 99, + name = 'status', + priority = 99, } function task:cycle() - for node in context.storage:filterActive('status') do - if not pages[node.name] then - pages[node.name] = createPage(node) - end - end + for node in context.storage:filterActive('status') do + if not pages[node.name] then + pages[node.name] = createPage(node) + end + end end Milo:registerTask(task) diff --git a/milo/plugins/trashcanView.lua b/milo/plugins/trashcanView.lua index 52929b7..34a85df 100644 --- a/milo/plugins/trashcanView.lua +++ b/milo/plugins/trashcanView.lua @@ -7,89 +7,89 @@ local device = _G.device --[[ Configuration Screen ]] local wizardPage = UI.WizardPage { - 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 = 2, ex = -2, y = 4, 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 = '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' - }, - }, + 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 = 2, ex = -2, y = 4, 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 = '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 wizardPage:validate() - return self.form:save() + return self.form:save() end function wizardPage:setNode(node) - self.form:setValues(node) + self.form:setValues(node) end function wizardPage:isValidType(node) - local m = device[node.name] - return m and m.pullItems and { - name = 'Trashcan', - value = 'trashcan', - category = 'custom', - help = 'An inventory to send unwanted items', - } + local m = device[node.name] + return m and m.pullItems and { + name = 'Trashcan', + value = 'trashcan', + category = 'custom', + help = 'An inventory to send unwanted items', + } end function wizardPage:isValidFor(node) - return node.mtype == 'trashcan' + return node.mtype == 'trashcan' end UI:getPage('nodeWizard').wizard:add({ trashcan = wizardPage }) --[[ TASK ]]-- local task = { - name = 'trashcan', - priority = 90, + name = 'trashcan', + priority = 90, } local function filter(a) - return a.drop + return a.drop end function task:cycle(context) - local tasks = Tasks() + local tasks = Tasks() - for node in context.storage:filterActive('trashcan', filter) do - pcall(function() - for k in pairs(node.adapter.list()) do - local direction = node.dropDirection or 'down' - tasks:add(function() - node.adapter.drop(k, 64, direction) - end) - end - end) - end + for node in context.storage:filterActive('trashcan', filter) do + pcall(function() + for k in pairs(node.adapter.list()) do + local direction = node.dropDirection or 'down' + tasks:add(function() + node.adapter.drop(k, 64, direction) + end) + end + end) + end - tasks:run() + tasks:run() end Milo:registerTask(task) diff --git a/miners/findSwarm.lua b/miners/findSwarm.lua index f623a4f..bb6b756 100644 --- a/miners/findSwarm.lua +++ b/miners/findSwarm.lua @@ -20,160 +20,160 @@ local paused, abort local chunkIndex = 0 local swarm = Swarm() local blocks = Util.transpose({ - 'minecraft:chest', + 'minecraft:chest', -- 'minecraft:mob_spawner', - 'quark:crystal', - 'minecraft:mossy_cobblestone' + 'quark:crystal', + 'minecraft:mossy_cobblestone' }) local locations = { } gpt.x = gpt.x + 1 local function getLocations() - local y = gpt.y - 8 - while y > 5 do - table.insert(locations, y) - y = y - 16 - end - if y > 0 then - table.insert(locations, 5) - end + local y = gpt.y - 8 + while y > 5 do + table.insert(locations, y) + y = y - 16 + end + if y > 0 then + table.insert(locations, 5) + end end for _, b in pairs(scanner.scan()) do - if b.name == 'computercraft:turtle_advanced' or - b.name == 'computercraft:turtle' then + if b.name == 'computercraft:turtle_advanced' or + b.name == 'computercraft:turtle' then - local v = scanner.getBlockMeta(b.x, b.y, b.z) - if v and v.computer then - if not v.computer.isOn then - print('Powered off: ' .. v.computer.id) - elseif v.turtle.fuel < 100 then - print('not enough fuel: ' .. v.computer.id) - else - swarm:add(v.computer.id, { - point = { - x = gpt.x + b.x, - y = gpt.y + b.y, - z = gpt.z + b.z, - heading = Point.facings[v.state.facing].heading, - }, - index = Util.size(swarm.pool), - }) - end - end - end + local v = scanner.getBlockMeta(b.x, b.y, b.z) + if v and v.computer then + if not v.computer.isOn then + print('Powered off: ' .. v.computer.id) + elseif v.turtle.fuel < 100 then + print('not enough fuel: ' .. v.computer.id) + else + swarm:add(v.computer.id, { + point = { + x = gpt.x + b.x, + y = gpt.y + b.y, + z = gpt.z + b.z, + heading = Point.facings[v.state.facing].heading, + }, + index = Util.size(swarm.pool), + }) + end + end + end end local function getNextPoint(member) - local z = math.floor(chunkIndex / COLUMNS) - local x = chunkIndex % COLUMNS + local z = math.floor(chunkIndex / COLUMNS) + local x = chunkIndex % COLUMNS - chunkIndex = chunkIndex + 1 + chunkIndex = chunkIndex + 1 - while paused do - if abort then - return - end - os.sleep(3) - end + while paused do + if abort then + return + end + os.sleep(3) + end - return { - x = gpt.x + (x * 16), - y = gpt.y + member.index, - z = gpt.z + (z * 16) - } + return { + x = gpt.x + (x * 16), + y = gpt.y + member.index, + z = gpt.z + (z * 16) + } end local function run(member) - local turtle = member.turtle + local turtle = member.turtle - if not turtle.has('plethora:module:2') then - error('missing scanner') - end - turtle.reset() - turtle.set({ - attackPolicy = 'attack', - digPolicy = 'turtleSafe', - movementStrategy = 'goto', - point = member.point, - }) - turtle.select(1) - local swapSide = turtle.isEquipped('modem') == 'right' and 'left' or 'right' + if not turtle.has('plethora:module:2') then + error('missing scanner') + end + turtle.reset() + turtle.set({ + attackPolicy = 'attack', + digPolicy = 'turtleSafe', + movementStrategy = 'goto', + point = member.point, + }) + turtle.select(1) + local swapSide = turtle.isEquipped('modem') == 'right' and 'left' or 'right' - repeat - local pt = getNextPoint(member) - if pt then - turtle.set({ status = 'Relocating' }) - turtle.go({ y = pt.y }) - local c = os.clock() - while not turtle.go(pt) do - if abort then - break - end - os.sleep(.5) - if os.clock() - c > 3 then - Sound.play('entity.villager.no') - print('stuck: ' .. member.id) - turtle.set({ status = 'Stuck' }) - end - end - turtle.set({ status = 'Boring' }) + repeat + local pt = getNextPoint(member) + if pt then + turtle.set({ status = 'Relocating' }) + turtle.go({ y = pt.y }) + local c = os.clock() + while not turtle.go(pt) do + if abort then + break + end + os.sleep(.5) + if os.clock() - c > 3 then + Sound.play('entity.villager.no') + print('stuck: ' .. member.id) + turtle.set({ status = 'Stuck' }) + end + end + turtle.set({ status = 'Boring' }) - for _, v in ipairs(locations) do - if abort then - break - end - turtle.go({ y = v }) - turtle.equip(swapSide, 'plethora:module:2') - local found = turtle.scan(blocks) - turtle.equip(swapSide, 'minecraft:diamond_pickaxe') - if Util.size(found) > 0 then - paused = true - local _, b = next(found) - print(string.format('%s:%s:%s %s', b.x, b.y, b.z, b.name)) - print('press r to continue') - for _ = 1, 3 do - Sound.play('block.note.pling') - os.sleep(.3) - end - end - end - turtle.go({ y = pt.y }) - end - until abort + for _, v in ipairs(locations) do + if abort then + break + end + turtle.go({ y = v }) + turtle.equip(swapSide, 'plethora:module:2') + local found = turtle.scan(blocks) + turtle.equip(swapSide, 'minecraft:diamond_pickaxe') + if Util.size(found) > 0 then + paused = true + local _, b = next(found) + print(string.format('%s:%s:%s %s', b.x, b.y, b.z, b.name)) + print('press r to continue') + for _ = 1, 3 do + Sound.play('block.note.pling') + os.sleep(.3) + end + end + end + turtle.go({ y = pt.y }) + end + until abort - turtle.set({ status = 'Aborting' }) - turtle.go({ y = gpt.y + member.index }) - turtle.go({ x = gpt.x, y = gpt.y + member.index, z = gpt.z }) + turtle.set({ status = 'Aborting' }) + turtle.go({ y = gpt.y + member.index }) + turtle.go({ x = gpt.x, y = gpt.y + member.index, z = gpt.z }) - repeat until turtle.go({ y = gpt.y }) - turtle.set({ status = 'idle' }) + repeat until turtle.go({ y = gpt.y }) + turtle.set({ status = 'idle' }) end function swarm:onRemove(member, success, message) - if not success then - Sound.play('entity.villager.no') - print('Removed from swarm: ' .. member.id) - _G.printError(message) - end + if not success then + Sound.play('entity.villager.no') + print('Removed from swarm: ' .. member.id) + _G.printError(message) + end - print('Turtles: ' .. Util.size(self.pool)) - if Util.size(self.pool) == 0 then - Event.exitPullEvents() - end + print('Turtles: ' .. Util.size(self.pool)) + if Util.size(self.pool) == 0 then + Event.exitPullEvents() + end end print('press a to abort, r to resume') Event.on('char', function(_, k) - if k == 'r' then - print('Resuming') - paused = false - elseif k == 'a' then - gpt = GPS.getPoint() - print('Aborting') - abort = true - end + if k == 'r' then + print('Resuming') + paused = false + elseif k == 'a' then + gpt = GPS.getPoint() + print('Aborting') + abort = true + end end) getLocations() diff --git a/miners/scanningMiner.lua b/miners/scanningMiner.lua index 26fe1aa..b588c23 100644 --- a/miners/scanningMiner.lua +++ b/miners/scanningMiner.lua @@ -1,13 +1,13 @@ --[[ - Efficient miner + Efficient miner - GPS is required. + GPS is required. - Miner Requires: - Diamond pick - Ender Modem - Plethora scanner - Bucket + Miner Requires: + Diamond pick + Ender Modem + Plethora scanner + Bucket --]] local Event = require('event') local Equipper = require('turtle.equipper') @@ -27,12 +27,12 @@ UI:configure('scanningMiner', ...) 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 = 'Add ignore items' }, - help = { arg = 'h', type = 'flag', value = false, - desc = 'Displays the options' }, + chunks = { arg = 'c', type = 'number', value = -1, + desc = 'Number of chunks to mine' }, + setTrash = { arg = 's', type = 'flag', value = false, + desc = 'Add ignore items' }, + help = { arg = 'h', type = 'flag', value = false, + desc = 'Displays the options' }, } local MIN_FUEL = 7500 @@ -47,593 +47,593 @@ local STARTUP_FILE = 'usr/autorun/scanningMiner.lua' local mining local ignores = { - ignore = true, - retain = true, + 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:mob_spawner' ] = 'ignore', - [ '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', - }, + data = Util.readTable(DICTIONARY_FILE) or { + [ 'minecraft:chest' ] = 'suck', + [ 'minecraft:lava' ] = 'liquid_fuel', + [ 'minecraft:flowing_lava' ] = 'liquid_fuel', + [ 'minecraft:mob_spawner' ] = 'ignore', + [ '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) + Util.writeTable(DICTIONARY_FILE, self.data) end function dictionary:mineable(name, damage) - self.data[name .. ':' .. damage] = nil + 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 + 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] + return self.data[name] or self.data[name .. ':' .. damage] end function dictionary:isTrash(name, damage) - return self:get(name, damage) == 'ignore' + return self:get(name, damage) == 'ignore' end local page = UI.Page { - menuBar = UI.MenuBar { - buttons = { - --{ text = 'Mine', event = 'mine' }, - { text = 'Ignore', event = 'ignore' }, - { text = 'Abort', event = 'abort', x = -8 }, - }, - }, - grid = UI.Grid { - y = 2, ey = -2, - sortColumn = 'name', - columns = { - { heading = 'Count', key = 'count', width = 5, align = 'right' }, - { heading = 'Resource', key = 'displayName' }, - }, - }, - statusBar = UI.StatusBar { - columns = { - { key = 'status' }, - { key = 'distance', width = 4 }, - { key = 'fuel', width = 5 }, - }, - }, - accelerators = { - q = 'cancel', - } + menuBar = UI.MenuBar { + buttons = { + --{ text = 'Mine', event = 'mine' }, + { text = 'Ignore', event = 'ignore' }, + { text = 'Abort', event = 'abort', x = -8 }, + }, + }, + grid = UI.Grid { + y = 2, ey = -2, + sortColumn = 'name', + columns = { + { heading = 'Count', key = 'count', width = 5, align = 'right' }, + { heading = 'Resource', key = 'displayName' }, + }, + }, + statusBar = UI.StatusBar { + columns = { + { key = 'status' }, + { key = 'distance', width = 4 }, + { key = 'fuel', width = 5 }, + }, + }, + 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() + 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) - dictionary:write() - self.grid:draw() + elseif event.type == 'ignore' then + dictionary:ignore(t.name) + dictionary:write() + self.grid:draw() - end - end + end + end - if event.type == 'abort' then - turtle.abort(true) + if event.type == 'abort' then + turtle.abort(true) - elseif event.type == 'quit' then - turtle.abort(true) - end - UI.Page.eventHandler(self, event) + elseif 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) + 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 + 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 + 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 + 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) - mining.chunkIndex = mining.chunkIndex + 1 + 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.chunkIndex >= points then + mining.diameter = mining.diameter + 2 + mining.chunkIndex = 0 + end - local nc = getChunkCoordinates(mining.diameter, mining.chunkIndex, x, z) + local nc = getChunkCoordinates(mining.diameter, mining.chunkIndex, x, z) - -- enter next chunk - mining.x = nc.x - mining.z = nc.z + -- enter next chunk + mining.x = nc.x + mining.z = nc.z - Util.writeTable(PROGRESS_FILE, mining) + Util.writeTable(PROGRESS_FILE, mining) - return not isFinished() + return not isFinished() end local function status(newStatus) - turtle.setStatus(newStatus) - page.statusBar:setValue('status', newStatus) - page.statusBar:draw() - page:sync() + 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') + 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.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) + turtle.select(1) end local function safeGoto(x, z, y, h) - local oldStatus = turtle.getStatus() + local oldStatus = turtle.getStatus() - while not turtle.go({ 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 + while not turtle.go({ 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 + 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) + 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) + 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) + 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) + 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 + 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 + for _ = 1, 50 do + checkSpace() + if not suckAction() then + break + end + end end local function scan() - local scanner = Equipper.equipLeft('plethora:module:2', 'plethora:scanner') - local blocks = scanner.scan() - Equipper.equipLeft('minecraft:diamond_pickaxe') - local throttle = Util.throttle() + local scanner = Equipper.equipLeft('plethora:scanner') + local blocks = scanner.scan() + Equipper.equipLeft('minecraft:diamond_pickaxe') + local throttle = Util.throttle() - local bedrock = -256 - local counts = { } + 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 - throttle() + 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 + throttle() - if b.name == 'minecraft:bedrock' then - if b.y > bedrock then - bedrock = b.y - end - end - end + if b.name == 'minecraft:bedrock' then + if b.y > bedrock then + bedrock = b.y + end + end + end - Util.filterInplace(blocks, function(b) - throttle() - if b.y >= bedrock then - b.action = dictionary:get(b.name, b.metadata) or 'mine' + Util.filterInplace(blocks, function(b) + throttle() + if b.y >= bedrock then + b.action = dictionary:get(b.name, b.metadata) or 'mine' - if ignores[b.action] then - return false - end + if ignores[b.action] then + return false + end - if b.action == 'liquid_fuel' and (b.y <= bedrock or b.metadata > 0) then - return false - end + if b.action == 'liquid_fuel' and (b.y <= bedrock or b.metadata > 0) 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) + 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) + turtle.select(1) - local dirty = true + local dirty = true - local function display() - if dirty then - page.grid:draw() - page:sync() - end - dirty = false - end + local function display() + if dirty then + page.grid:draw() + page:sync() + end + dirty = false + end - page.grid:setValues(counts) - page.grid:draw() - display() + page.grid:setValues(counts) + page.grid:draw() + display() - status('mining') + status('mining') - local i = #blocks - Point.eachClosest(turtle.point, blocks, function(b) - if turtle.isAborted() then - error('aborted') - end + local i = #blocks + Point.eachClosest(turtle.point, blocks, function(b) + if turtle.isAborted() then + error('aborted') + end - page.grid.nextBlock = b.name .. ':' .. b.metadata + 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' - if b.action == 'suck' or b.action == 'mine' then - status('mining: ' .. table.concat({ b.x, b.y, b.z }, ', ')) + -- 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 + status('mining: ' .. table.concat({ b.x, b.y, b.z }, ', ')) - 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', b.name .. ' ' .. m) - page.statusBar:draw() - page:sync() - else - page.statusBar:setValue('mining', m) - 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) + 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', b.name .. ' ' .. m) + page.statusBar:draw() + page:sync() + else + page.statusBar:setValue('mining', m) + 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 pts = { } + 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 = math.max(pts[i].y, 8) - pts[i].y = pts[i].y - mining.home.y -- abs to rel - end + 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 = math.max(pts[i].y, 8) + pts[i].y = pts[i].y - mining.home.y -- abs to rel + end - Point.eachClosest(turtle.point, pts, function(pt) - if turtle.isAborted() then - error('aborted') - end - local chunks = math.pow(mining.diameter-2, 2) + mining.chunkIndex - status(string.format('scanning %d %d-%d', - chunks, - pt.y + mining.home.y - 8, - pt.y + mining.home.y + 8)) + Point.eachClosest(turtle.point, pts, function(pt) + if turtle.isAborted() then + error('aborted') + end + local chunks = math.pow(mining.diameter-2, 2) + mining.chunkIndex + status(string.format('scanning %d %d-%d', + chunks, + pt.y + mining.home.y - 8, + pt.y + mining.home.y + 8)) - turtle.select(1) - safeGoto(pt.x, pt.z, pt.y) - scan() + turtle.select(1) + safeGoto(pt.x, pt.z, pt.y) + scan() - if turtle.getFuelLevel() < LOW_FUEL then - refuel() - local veryMinFuel = Point.turtleDistance(turtle.point, HOME_PT) + 512 - if turtle.getFuelLevel() < veryMinFuel then - error('Not enough fuel to continue') - end - end - end) + if turtle.getFuelLevel() < LOW_FUEL then + refuel() + local veryMinFuel = Point.turtleDistance(turtle.point, HOME_PT) + 512 + if turtle.getFuelLevel() < veryMinFuel then + error('Not enough fuel to continue') + end + end + end) end local function addTrash() - local slots = turtle.getFilledSlots() + 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 + 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() + dictionary:write() end -- Startup logic if not Util.getOptions(options, args) then - return + return end -- in plethora code, we can override initialize with a scanner version turtle.initialize = function() - Equipper.equipRight('computercraft:advanced_modem', 'modem') - Equipper.equipLeft('minecraft:diamond_pickaxe') + Equipper.equipModem('right') + Equipper.equipLeft('minecraft:diamond_pickaxe') - local function verify(item) - if not turtle.has(item) then - error('Missing: ' .. item) - end - end + 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 + 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') - local scanner = Equipper.equipLeft('plethora:module:2', 'plethora:scanner') - local facing = scanner.getBlockMeta(0, 0, 0).state.facing - pt.heading = Point.facings[facing].heading - turtle.setPoint(pt, true) - Equipper.equipLeft('minecraft:diamond_pickaxe') + --os.sleep(5) + local pt = GPS.getPoint(2) or error('GPS not found') + local scanner = Equipper.equipLeft('plethora:scanner') + local facing = scanner.getBlockMeta(0, 0, 0).state.facing + pt.heading = Point.facings[facing].heading + turtle.setPoint(pt, true) + Equipper.equipLeft('minecraft:diamond_pickaxe') end local function main() - repeat - mineChunk() - until not nextChunk() + repeat + mineChunk() + until not nextChunk() end local success, msg if not fs.exists(DICTIONARY_FILE) or options.setTrash.value then - print('Place blocks into the turtles inventory to ignore, such as cobble, stone, gravel, etc.') - print('\nPress enter when ready') - read() - addTrash() + print('Place blocks into the turtles inventory to ignore, such as cobble, stone, gravel, etc.') + print('\nPress enter when ready') + read() + addTrash() end if not fs.exists(STARTUP_FILE) then - Util.writeFile(STARTUP_FILE, - [[print('sleeping for 10 seconds') + Util.writeFile(STARTUP_FILE, + [[print('sleeping for 10 seconds') os.sleep(10) shell.openForegroundTab('scanningMiner.lua')]]) - print('Autorun program created: ' .. STARTUP_FILE) + print('Autorun program created: ' .. STARTUP_FILE) end Event.addRoutine(function() - turtle.reset() + turtle.reset() - 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:setValue('distance', math.floor(Point.distance(turtle.point, HOME_PT))) - page.statusBar:draw() - page:sync() - end) + turtle.setMoveCallback(function() + page.statusBar:setValue('fuel', Util.toBytes(turtle.getFuelLevel())) + page.statusBar:setValue('distance', math.floor(Point.distance(turtle.point, HOME_PT))) + 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 - } + 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 + 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, - }) + -- 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, + }) - local distance = Point.distance( - { x = turtle.point.x, y = 0, z = turtle.point.z }, - HOME_PT - ) - local maxDistance = Point.distance( - HOME_PT, - { x = mining.x, y = 0, z = mining.z } - ) + local distance = Point.distance( + { x = turtle.point.x, y = 0, z = turtle.point.z }, + HOME_PT + ) + local maxDistance = Point.distance( + HOME_PT, + { x = mining.x, y = 0, z = mining.z } + ) - _G._syslog({ distance = distance, maxDistance = maxDistance }) + _G._syslog({ distance = distance, maxDistance = maxDistance }) - if distance > maxDistance + 16 then - term.clear() - term.setCursorPos(1, 1) - _G.printError('WARNING\n\nTurtle is outside the mining area\n') - print('Max distance: ' .. math.floor(maxDistance)) - print('Distance to home: ' .. math.floor(distance)) - print('\nIf in a new location, delete usr/config/scanning_miner.progress') - print('\nWaiting for 30 seconds to begin') - os.sleep(30) - page:sync() - end + if distance > maxDistance + 16 then + term.clear() + term.setCursorPos(1, 1) + _G.printError('WARNING\n\nTurtle is outside the mining area\n') + print('Max distance: ' .. math.floor(maxDistance)) + print('Distance to home: ' .. math.floor(distance)) + print('\nIf in a new location, delete usr/config/scanning_miner.progress') + print('\nWaiting for 30 seconds to begin') + os.sleep(30) + page:sync() + end - if not fs.exists(PROGRESS_FILE) then - Util.writeTable(PROGRESS_FILE, mining) - end + if not fs.exists(PROGRESS_FILE) then + Util.writeTable(PROGRESS_FILE, mining) + end - turtle.set({ - attackPolicy = 'attack', - digPolicy = 'turtleSafe', - movementStrategy = 'goto', - }) - status('mining') + turtle.set({ + attackPolicy = 'attack', + digPolicy = 'turtleSafe', + movementStrategy = 'goto', + }) + status('mining') - Event.onTerminate(function() - turtle.abort(true) - end) + Event.onTerminate(function() + turtle.abort(true) + end) - if isFinished() then - success = false - msg = 'Mining complete' - else - success, msg = pcall(main) - end + 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') - turtle.gotoY(0) - if turtle.go(HOME_PT) then - unload() - end - turtle.reset() + status(success and 'finished' or turtle.isAborted() and 'aborting' or 'error') + turtle.gotoY(0) + if turtle.go(HOME_PT) then + unload() + end + turtle.reset() - Event.exitPullEvents() + Event.exitPullEvents() end) UI:setPage(page) @@ -643,5 +643,5 @@ UI.term:reset() turtle.reset() if not success and msg then - _G.printError(msg) + _G.printError(msg) end diff --git a/miners/simpleMiner.lua b/miners/simpleMiner.lua index 8fba0c4..2985b1c 100644 --- a/miners/simpleMiner.lua +++ b/miners/simpleMiner.lua @@ -9,28 +9,28 @@ local turtle = _G.turtle local args = { ... } local options = { - chunks = { arg = 'c', type = 'number', value = -1, - desc = 'Number of chunks to mine' }, - depth = { arg = 'd', type = 'number', value = 9000, - desc = 'Mining depth' }, + chunks = { arg = 'c', type = 'number', value = -1, + desc = 'Number of chunks to mine' }, + depth = { arg = 'd', type = 'number', value = 9000, + desc = 'Mining depth' }, -- enderChest = { arg = 'e', type = 'flag', value = false, -- desc = 'Use ender chest' }, - resume = { arg = 'r', type = 'flag', value = false, - desc = 'Resume mining' }, - fortunePick = { arg = 'p', type = 'string', value = nil, - desc = 'Pick to use with CCTweaks toolhost' }, - setTrash = { arg = 's', type = 'flag', value = false, - desc = 'Set trash items' }, - help = { arg = 'h', type = 'flag', value = false, - desc = 'Displays the options' }, + resume = { arg = 'r', type = 'flag', value = false, + desc = 'Resume mining' }, + fortunePick = { arg = 'p', type = 'string', value = nil, + desc = 'Pick to use with CCTweaks toolhost' }, + setTrash = { arg = 's', type = 'flag', value = false, + desc = 'Set trash items' }, + help = { arg = 'h', type = 'flag', value = false, + desc = 'Displays the options' }, } local fortuneBlocks = { - [ 'minecraft:redstone_ore' ] = true, - [ 'minecraft:lapis_ore' ] = true, - [ 'minecraft:coal_ore' ] = true, - [ 'minecraft:diamond_ore' ] = true, - [ 'minecraft:emerald_ore' ] = true, + [ 'minecraft:redstone_ore' ] = true, + [ 'minecraft:lapis_ore' ] = true, + [ 'minecraft:coal_ore' ] = true, + [ 'minecraft:diamond_ore' ] = true, + [ 'minecraft:emerald_ore' ] = true, } local MIN_FUEL = 7500 @@ -41,524 +41,524 @@ local PROGRESS_FILE = 'usr/config/mining.progress' local TRASH_FILE = 'usr/config/mining.trash' local mining = { - diameter = 1, - chunkIndex = 0, - chunks = -1, + diameter = 1, + chunkIndex = 0, + chunks = -1, } local trash, boreDirection, unload 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 + 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 getBoreLocations(x, z) - local locations = {} + local locations = {} - while true do - local a = math.abs(z) - local b = math.abs(x) + while true do + local a = math.abs(z) + local b = math.abs(x) - if x > 0 and z > 0 or - x < 0 and z < 0 then - -- rotate coords - a = math.abs(x) - b = math.abs(z) - end - if (a % 5 == 0 and b % 5 == 0) or - (a % 5 == 2 and b % 5 == 1) or - (a % 5 == 4 and b % 5 == 2) or - (a % 5 == 1 and b % 5 == 3) or - (a % 5 == 3 and b % 5 == 4) then - table.insert(locations, { x = x, z = z, y = 0 }) - end - if z % 2 == 0 then -- forward dir - if (x + 1) % 16 == 0 then - z = z + 1 - else - x = x + 1 - end - else - if (x - 1) % 16 == 15 then - if (z + 1) % 16 == 0 then - break - end - z = z + 1 - else - x = x - 1 - end - end - end - return locations + if x > 0 and z > 0 or + x < 0 and z < 0 then + -- rotate coords + a = math.abs(x) + b = math.abs(z) + end + if (a % 5 == 0 and b % 5 == 0) or + (a % 5 == 2 and b % 5 == 1) or + (a % 5 == 4 and b % 5 == 2) or + (a % 5 == 1 and b % 5 == 3) or + (a % 5 == 3 and b % 5 == 4) then + table.insert(locations, { x = x, z = z, y = 0 }) + end + if z % 2 == 0 then -- forward dir + if (x + 1) % 16 == 0 then + z = z + 1 + else + x = x + 1 + end + else + if (x - 1) % 16 == 15 then + if (z + 1) % 16 == 0 then + break + end + z = z + 1 + else + x = x - 1 + end + end + end + return locations end -- get the bore location closest to the miner local function getClosestLocation(points) - local key = 1 - local leastMoves = 9000 - for k,pt in pairs(points) do + local key = 1 + local leastMoves = 9000 + for k,pt in pairs(points) do - local moves = Point.calculateMoves(turtle.point, pt) + local moves = Point.calculateMoves(turtle.point, pt) - if moves < leastMoves then - key = k - leastMoves = moves - if leastMoves == 0 then - break - end - end - end - return table.remove(points, key) + if moves < leastMoves then + key = k + leastMoves = moves + if leastMoves == 0 then + break + end + end + end + return table.remove(points, key) end local function getCornerOf(c) - return math.floor(c.x / 16) * 16, math.floor(c.z / 16) * 16 + 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 + 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.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 + 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) - mining.locations = getBoreLocations(nc.x, nc.z) + local nc = getChunkCoordinates(mining.diameter, mining.chunkIndex, x, z) + mining.locations = getBoreLocations(nc.x, nc.z) - -- enter next chunk - mining.x = nc.x - mining.z = nc.z + -- enter next chunk + mining.x = nc.x + mining.z = nc.z - Util.writeTable(PROGRESS_FILE, mining) + Util.writeTable(PROGRESS_FILE, mining) - return true + return true end local function addTrash() - if not trash then - trash = { - [ 'minecraft:cobblestone:0' ] = true - } - end + if not trash then + trash = { + [ 'minecraft:cobblestone:0' ] = true + } + end - local slots = turtle.getFilledSlots() + local slots = turtle.getFilledSlots() - for _,slot in pairs(slots) do - trash[slot.key] = true - end + for _,slot in pairs(slots) do + trash[slot.key] = true + end - trash['minecraft:bucket:0'] = nil - Util.writeTable(TRASH_FILE, trash) + trash['minecraft:bucket:0'] = nil + Util.writeTable(TRASH_FILE, trash) end local function log(text) - print(text) + print(text) end local function status(newStatus) - turtle.setStatus(newStatus) - log(newStatus) + turtle.setStatus(newStatus) + log(newStatus) end local function refuel() - if turtle.getFuelLevel() < MIN_FUEL then - local oldStatus = turtle.getStatus() - status('refueling') + if turtle.getFuelLevel() < MIN_FUEL then + local oldStatus = turtle.getStatus() + status('refueling') - if turtle.select('minecraft:coal:0') then - local qty = turtle.getItemCount() - print('refueling ' .. qty) - turtle.refuel(qty) - end - if turtle.getFuelLevel() < MIN_FUEL then - log('desperate fueling') + if turtle.select('minecraft:coal:0') then + local qty = turtle.getItemCount() + print('refueling ' .. qty) + turtle.refuel(qty) + end + if turtle.getFuelLevel() < MIN_FUEL then + log('desperate fueling') - turtle.eachFilledSlot(function(slot) - if turtle.getFuelLevel() < MIN_FUEL then - turtle.select(slot.index) - turtle.refuel(64) - end - end) - end - log('Fuel: ' .. turtle.getFuelLevel()) - status(oldStatus) - end + turtle.eachFilledSlot(function(slot) + if turtle.getFuelLevel() < MIN_FUEL then + turtle.select(slot.index) + turtle.refuel(64) + end + end) + end + log('Fuel: ' .. turtle.getFuelLevel()) + status(oldStatus) + end - turtle.select(1) + turtle.select(1) end local function safeGoto(x, z, y, h) - local oldStatus = turtle.getStatus() + local oldStatus = turtle.getStatus() - -- only pathfind above or around other turtles (never down) - 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 + -- only pathfind above or around other turtles (never down) + 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) + 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 - return false - end - os.sleep(3) - end - turtle.setStatus(oldStatus) - return true + while not turtle.pathfind({ 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 + 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 makeWalkableTunnel(action, tpt, pt) - if action ~= 'turn' and not Point.compare(tpt, { x = 0, z = 0 }) then -- not at source - if not Point.compare(tpt, pt) then -- not at dest - local r, block = turtle.inspectUp() - if r and not turtle.isTurtleAtSide('top') then - if block.name ~= 'minecraft:cobblestone' and - block.name ~= 'minecraft:chest' then - turtle.digUp() - end - end - end - end + if action ~= 'turn' and not Point.compare(tpt, { x = 0, z = 0 }) then -- not at source + if not Point.compare(tpt, pt) then -- not at dest + local r, block = turtle.inspectUp() + if r and not turtle.isTurtleAtSide('top') then + if block.name ~= 'minecraft:cobblestone' and + block.name ~= 'minecraft:chest' then + turtle.digUp() + end + end + end + end end --[[ local function enderChestUnload() - log('unloading') - turtle.select(1) - if not Util.tryTimed(5, function() - turtle.digDown() - return turtle.placeDown() - end) then - log('placedown failed') - else - turtle.reconcileInventory(slots, turtle.dropDown) + log('unloading') + turtle.select(1) + if not Util.tryTimed(5, function() + turtle.digDown() + return turtle.placeDown() + end) then + log('placedown failed') + else + turtle.reconcileInventory(slots, turtle.dropDown) - turtle.select(1) - turtle.drop(64) - turtle.digDown() - end + turtle.select(1) + turtle.drop(64) + turtle.digDown() + end end ]] local function normalChestUnload() - local oldStatus = turtle.getStatus() - status('unloading') - local pt = Util.shallowCopy(turtle.point) - safeGotoY(0) + local oldStatus = turtle.getStatus() + status('unloading') + local pt = Util.shallowCopy(turtle.point) + safeGotoY(0) - turtle.setMoveCallback(function(action, tpt) - makeWalkableTunnel(action, tpt, { x = pt.x, z = pt.z }) - end) + turtle.setMoveCallback(function(action, tpt) + makeWalkableTunnel(action, tpt, { x = pt.x, z = pt.z }) + end) - safeGoto(0, 0, 0) - if not turtle.detectUp() then - error('Chest above starting point not found') - end - local slots = turtle.getFilledSlots() - for _,slot in pairs(slots) do - if not trash[slot.key] and - slot.key ~= 'minecraft:bucket:0' and - slot.name ~= 'minecraft:diamond_pickaxe' and - slot.name ~= 'cctweaks:toolHost' then - if slot.name ~= options.fortunePick.value then - turtle.select(slot.index) - turtle.dropUp(64) - end - end - end - turtle.condense() - turtle.select(1) - safeGoto(pt.x, pt.z, 0, pt.heading) + safeGoto(0, 0, 0) + if not turtle.detectUp() then + error('Chest above starting point not found') + end + local slots = turtle.getFilledSlots() + for _,slot in pairs(slots) do + if not trash[slot.key] and + slot.key ~= 'minecraft:bucket:0' and + slot.name ~= 'minecraft:diamond_pickaxe' and + slot.name ~= 'cctweaks:toolHost' then + if slot.name ~= options.fortunePick.value then + turtle.select(slot.index) + turtle.dropUp(64) + end + end + end + turtle.condense() + turtle.select(1) + safeGoto(pt.x, pt.z, 0, pt.heading) - turtle.clearMoveCallback() + turtle.clearMoveCallback() - safeGotoY(pt.y) - status(oldStatus) + safeGotoY(pt.y) + status(oldStatus) end local function ejectTrash() - local cobbleSlotCount = 0 + local cobbleSlotCount = 0 - turtle.eachFilledSlot(function(slot) - if slot.key == 'minecraft:cobblestone:0' then - if cobbleSlotCount == 0 and slot.count > 36 then - turtle.select(slot.index) - turtle.dropDown(slot.count - 36) - end - cobbleSlotCount = cobbleSlotCount + 1 - end + turtle.eachFilledSlot(function(slot) + if slot.key == 'minecraft:cobblestone:0' then + if cobbleSlotCount == 0 and slot.count > 36 then + turtle.select(slot.index) + turtle.dropDown(slot.count - 36) + end + cobbleSlotCount = cobbleSlotCount + 1 + end - if trash[slot.key] then - -- retain 1 slot with cobble in order to indicate active mining - if slot.key ~= 'minecraft:cobblestone:0' or cobbleSlotCount > 1 then - turtle.select(slot.index) - turtle.dropDown(64) - end - end - end) + if trash[slot.key] then + -- retain 1 slot with cobble in order to indicate active mining + if slot.key ~= 'minecraft:cobblestone:0' or cobbleSlotCount > 1 then + turtle.select(slot.index) + turtle.dropDown(64) + end + end + end) end local function checkSpace() - if turtle.getItemCount(16) > 0 then - refuel() - local oldStatus = turtle.getStatus() - status('condensing') - ejectTrash() - turtle.condense() - local lastSlot = 16 - if boreDirection == 'down' then - lastSlot = 15 - end - if turtle.getItemCount(lastSlot) > 0 then - unload() - end - status(oldStatus) - turtle.select(1) - end + if turtle.getItemCount(16) > 0 then + refuel() + local oldStatus = turtle.getStatus() + status('condensing') + ejectTrash() + turtle.condense() + local lastSlot = 16 + if boreDirection == 'down' then + lastSlot = 15 + end + if turtle.getItemCount(lastSlot) > 0 then + unload() + end + status(oldStatus) + turtle.select(1) + end end local function collectDrops(suckAction) - for _ = 1, 50 do - if not suckAction() then - break - end - checkSpace() - end + for _ = 1, 50 do + if not suckAction() then + break + end + checkSpace() + end end local function mineable(action) - local r, block = action.inspect() - if not r then - return false - end + local r, block = action.inspect() + if not r then + return false + end - if block.name == 'minecraft:chest' then - collectDrops(action.suck) - end + if block.name == 'minecraft:chest' then + collectDrops(action.suck) + end - if turtle.getFuelLevel() < (MAX_FUEL - 1000) then - if block.name == 'minecraft:lava' or block.name == 'minecraft:flowing_lava' then - if turtle.select('minecraft:bucket:0') then - if action.place() then - log('Lava! ' .. turtle.getFuelLevel()) - turtle.refuel() - log(turtle.getFuelLevel()) - end - turtle.select(1) - end - return false - end - end + if turtle.getFuelLevel() < (MAX_FUEL - 1000) then + if block.name == 'minecraft:lava' or block.name == 'minecraft:flowing_lava' then + if turtle.select('minecraft:bucket:0') then + if action.place() then + log('Lava! ' .. turtle.getFuelLevel()) + turtle.refuel() + log(turtle.getFuelLevel()) + end + turtle.select(1) + end + return false + end + end - if action.side == 'bottom' then - return block.name - end + if action.side == 'bottom' then + return block.name + end - if trash[block.name .. ':0'] then - return false - end + if trash[block.name .. ':0'] then + return false + end - return block.name + return block.name end local function fortuneDig(action, blockName) - if options.fortunePick.value and fortuneBlocks[blockName] then - turtle.select('cctweaks:toolHost') - turtle.equipRight() - turtle.select(options.fortunePick.value) - repeat until not action.dig() - turtle.select('minecraft:diamond_pickaxe') - turtle.equipRight() - turtle.select(1) - return true - end + if options.fortunePick.value and fortuneBlocks[blockName] then + turtle.select('cctweaks:toolHost') + turtle.equipRight() + turtle.select(options.fortunePick.value) + repeat until not action.dig() + turtle.select('minecraft:diamond_pickaxe') + turtle.equipRight() + turtle.select(1) + return true + end end local function mine(action) - local blockName = mineable(action) - if blockName then - checkSpace() - --collectDrops(action.suck) - if not fortuneDig(action, blockName) then - action.dig() - end - end + local blockName = mineable(action) + if blockName then + checkSpace() + --collectDrops(action.suck) + if not fortuneDig(action, blockName) then + action.dig() + end + end end local function bore() - local loc = turtle.point - local level = loc.y + local loc = turtle.point + local level = loc.y - turtle.select(1) - status('boring down') - boreDirection = 'down' + turtle.select(1) + status('boring down') + boreDirection = 'down' - while true do - if turtle.isAborted() then - status('aborting') - return false - end - if loc.y <= -mining.depth then - break - end + while true do + if turtle.isAborted() then + status('aborting') + return false + end + if loc.y <= -mining.depth then + break + end - mine(turtle.getAction('down')) - if not Util.tryTimed(3, turtle.down) then - break - end + mine(turtle.getAction('down')) + if not Util.tryTimed(3, turtle.down) then + break + end - if loc.y < level - 1 then - mine(turtle.getAction('forward')) - turtle.turnRight() - mine(turtle.getAction('forward')) - end - end + if loc.y < level - 1 then + mine(turtle.getAction('forward')) + turtle.turnRight() + mine(turtle.getAction('forward')) + end + end - boreDirection = 'up' - status('boring up') + boreDirection = 'up' + status('boring up') - turtle.turnRight() - mine(turtle.getAction('forward')) + turtle.turnRight() + mine(turtle.getAction('forward')) - turtle.turnRight() - mine(turtle.getAction('forward')) + turtle.turnRight() + mine(turtle.getAction('forward')) - turtle.turnLeft() + turtle.turnLeft() - while true do - if turtle.isAborted() then - status('aborting') - return false - end + while true do + if turtle.isAborted() then + status('aborting') + return false + end - while not Util.tryTimed(3, turtle.up) do - status('stuck') - end - if turtle.getStatus() == 'stuck' then - status('boring up') - end + while not Util.tryTimed(3, turtle.up) do + status('stuck') + end + if turtle.getStatus() == 'stuck' then + status('boring up') + end - if loc.y >= level - 1 then - break - end + if loc.y >= level - 1 then + break + end - mine(turtle.getAction('forward')) - turtle.turnLeft() - mine(turtle.getAction('forward')) - end + mine(turtle.getAction('forward')) + turtle.turnLeft() + mine(turtle.getAction('forward')) + 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 - log('Not enough fuel to continue') - return false - end - 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 + log('Not enough fuel to continue') + return false + end + end - return true + return true end function Point.compare(pta, ptb) - if pta.x == ptb.x and pta.z == ptb.z then - if pta.y and ptb.y then - return pta.y == ptb.y - end - return true - end - return false + if pta.x == ptb.x and pta.z == ptb.z then + if pta.y and ptb.y then + return pta.y == ptb.y + end + return true + end + return false end local function inspect(action, name) - local r, block = action.inspect() - if r and block.name == name then - return true - end + local r, block = action.inspect() + if r and block.name == name then + return true + end end local function boreCommand() - local pt = getClosestLocation(mining.locations) + local pt = getClosestLocation(mining.locations) - turtle.setMoveCallback(function(action, tpt) - makeWalkableTunnel(action, tpt, pt) - end) + turtle.setMoveCallback(function(action, tpt) + makeWalkableTunnel(action, tpt, pt) + end) - safeGotoY(0) - safeGoto(pt.x, pt.z, 0) + safeGotoY(0) + safeGoto(pt.x, pt.z, 0) - turtle.clearMoveCallback() + turtle.clearMoveCallback() - -- location is either mined, currently being mined or is the - -- dropoff point for a turtle - if inspect(turtle.getAction('up'), 'minecraft:cobblestone') or - inspect(turtle.getAction('up'), 'minecraft:chest') or - inspect(turtle.getAction('down'), 'minecraft:cobblestone') then - return true - end + -- location is either mined, currently being mined or is the + -- dropoff point for a turtle + if inspect(turtle.getAction('up'), 'minecraft:cobblestone') or + inspect(turtle.getAction('up'), 'minecraft:chest') or + inspect(turtle.getAction('down'), 'minecraft:cobblestone') then + return true + end - turtle.digUp() - turtle.placeUp('minecraft:cobblestone:0') + turtle.digUp() + turtle.placeUp('minecraft:cobblestone:0') - local success = bore() + local success = bore() - safeGotoY(0) -- may have aborted - turtle.digUp() + safeGotoY(0) -- may have aborted + turtle.digUp() - if success then - turtle.placeDown('minecraft:cobblestone:0') -- cap with cobblestone to indicate this spot was mined out - end + if success then + turtle.placeDown('minecraft:cobblestone:0') -- cap with cobblestone to indicate this spot was mined out + end - return success + return success end if not Util.getOptions(options, args) then - return + return end -- TODO: this won't work - need to Util.merge file into mining @@ -576,78 +576,78 @@ mining.locations = getBoreLocations(0, 0) trash = Util.readTable(TRASH_FILE) if fs.exists(PROGRESS_FILE) then - mining = Util.readTable(PROGRESS_FILE) + mining = Util.readTable(PROGRESS_FILE) end --[[ if options.resume.value then - mining = Util.readTable(PROGRESS_FILE) + mining = Util.readTable(PROGRESS_FILE) elseif fs.exists(PROGRESS_FILE) then - print('Use -r to resume') - print('Teminate or enter to continue') - read() + print('Use -r to resume') + print('Teminate or enter to continue') + read() end ]] if not trash or options.setTrash.value then - print('Place the blocks to ignore into the turtle') - print('Example: stone, dirt, gravel') - print('\nPress enter when ready') - read() - addTrash() + print('Place the blocks to ignore into the turtle') + print('Example: stone, dirt, gravel') + print('\nPress enter when ready') + read() + addTrash() end if not turtle.getSlot('minecraft:bucket:0') or - not turtle.getSlot('minecraft:cobblestone:0') then - print('Add bucket and cobblestone, press enter when ready') - read() + not turtle.getSlot('minecraft:cobblestone:0') then + print('Add bucket and cobblestone, press enter when ready') + read() end if options.fortunePick.value then - local s = turtle.getSlot(options.fortunePick.value) - if not s then - error('fortunePick not found: ' .. options.fortunePick.value) - end - if not turtle.getSlot('cctweaks:toolHost:0') then - error('CCTweaks tool host not found') - end - trash[s.key] = nil - trash['minecraft:diamond_pickaxe:0'] = nil - trash['cctweaks:toolHost:0'] = nil + local s = turtle.getSlot(options.fortunePick.value) + if not s then + error('fortunePick not found: ' .. options.fortunePick.value) + end + if not turtle.getSlot('cctweaks:toolHost:0') then + error('CCTweaks tool host not found') + end + trash[s.key] = nil + trash['minecraft:diamond_pickaxe:0'] = nil + trash['cctweaks:toolHost:0'] = nil end local function main() - repeat - while #mining.locations > 0 do - status('searching') - if not boreCommand() then - return - end - Util.writeTable(PROGRESS_FILE, mining) - end - until not nextChunk() + repeat + while #mining.locations > 0 do + status('searching') + if not boreCommand() then + return + end + Util.writeTable(PROGRESS_FILE, mining) + end + until not nextChunk() end local s, m = turtle.run(function() - turtle.reset() - turtle.set({ - attackPolicy = 'attack', - digPolicy = 'turtleSafe', - }) - unload() - status('mining') + turtle.reset() + turtle.set({ + attackPolicy = 'attack', + digPolicy = 'turtleSafe', + }) + unload() + status('mining') - local s, m = pcall(main) - if not s and m then - _G.printError(m) - end - turtle.abort(false) - safeGotoY(0) - safeGoto(0, 0, 0, 0) - unload() - turtle.reset() + local s, m = pcall(main) + if not s and m then + _G.printError(m) + end + turtle.abort(false) + safeGotoY(0) + safeGoto(0, 0, 0, 0) + unload() + turtle.reset() end) if not s and m then - error(m) + error(m) end diff --git a/monitor/etc/apps.db b/monitor/etc/apps.db index d1dbdb3..bd6f768 100644 --- a/monitor/etc/apps.db +++ b/monitor/etc/apps.db @@ -1,13 +1,13 @@ { - [ "58ec8d6e36e346d9f42eb43935652e3e58e2c829" ] = { - title = "Mwm", - category = "Apps", - icon = "\030f\031f \0304 \ + [ "58ec8d6e36e346d9f42eb43935652e3e58e2c829" ] = { + title = "Mwm", + category = "Apps", + icon = "\030f\031f \0304 \ \030f\031dshell]\0304\0314 \ \0304\031f ", - iconExt = "\030 \031f\0305\031f\155\030f\128\031d\152\140\030d\031f\151\030f\128\128\0304\0314\128\ + 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", - }, + run = "mwm.lua usr/config/mwm", + }, } diff --git a/monitor/mirrorClient.lua b/monitor/mirrorClient.lua index 25e55e0..5f5acb0 100644 --- a/monitor/mirrorClient.lua +++ b/monitor/mirrorClient.lua @@ -8,76 +8,76 @@ local os = _G.os local remoteId local args = { ... } if #args == 1 then - remoteId = tonumber(args[1]) + remoteId = tonumber(args[1]) else - print('Enter host ID') - remoteId = tonumber(_G.read()) + print('Enter host ID') + remoteId = tonumber(_G.read()) end if not remoteId then - error('Syntax: mirrorClient ') + error('Syntax: mirrorClient ') end local function wrapTerm(socket) - local methods = { 'blit', 'clear', 'clearLine', 'setCursorPos', 'write', - 'setTextColor', 'setTextColour', 'setBackgroundColor', - 'setBackgroundColour', 'scroll', 'setCursorBlink', } + local methods = { 'blit', 'clear', 'clearLine', 'setCursorPos', 'write', + 'setTextColor', 'setTextColour', 'setBackgroundColor', + 'setBackgroundColour', 'scroll', 'setCursorBlink', } - socket.term = multishell.term - socket.oldTerm = Util.shallowCopy(socket.term) + socket.term = multishell.term + socket.oldTerm = Util.shallowCopy(socket.term) - for _,k in pairs(methods) do - socket.term[k] = function(...) - if not socket.queue then - socket.queue = { } - Event.onTimeout(0, function() - if socket.queue then - socket:write(socket.queue) - socket.queue = nil - end - end) - end - table.insert(socket.queue, { - f = k, - args = { ... }, - }) - socket.oldTerm[k](...) - end - end + for _,k in pairs(methods) do + socket.term[k] = function(...) + if not socket.queue then + socket.queue = { } + Event.onTimeout(0, function() + if socket.queue then + socket:write(socket.queue) + socket.queue = nil + end + end) + end + table.insert(socket.queue, { + f = k, + args = { ... }, + }) + socket.oldTerm[k](...) + end + end end while true do - print('connecting...') - local socket + print('connecting...') + local socket - while true do - socket = Socket.connect(remoteId, 5901) - if socket then - break - end - os.sleep(3) - end + while true do + socket = Socket.connect(remoteId, 5901) + if socket then + break + end + os.sleep(3) + end - print('connected') + print('connected') - wrapTerm(socket) + wrapTerm(socket) - os.queueEvent('term_resize') + os.queueEvent('term_resize') - while true do - local e = Event.pullEvent() - if e[1] == 'terminate' then - break - end - if not socket.connected then - break - end - end + while true do + local e = Event.pullEvent() + if e[1] == 'terminate' then + break + end + if not socket.connected then + break + end + end - for k,v in pairs(socket.oldTerm) do - socket.term[k] = v - end + for k,v in pairs(socket.oldTerm) do + socket.term[k] = v + end - socket:close() + socket:close() end diff --git a/monitor/mirrorHost.lua b/monitor/mirrorHost.lua index daea661..f06dad6 100644 --- a/monitor/mirrorHost.lua +++ b/monitor/mirrorHost.lua @@ -7,41 +7,41 @@ local term = _G.term local mon = term.current() local args = { ... } if args[1] then - mon = _G.device[args[1]] + mon = _G.device[args[1]] end if not mon then - error('Invalid monitor') + error('Invalid monitor') end mon.setBackgroundColor(colors.black) mon.clear() while true do - local socket = Socket.server(5901) + local socket = Socket.server(5901) - print('mirror: connection from ' .. socket.dhost) + print('mirror: connection from ' .. socket.dhost) - Event.addRoutine(function() - while true do - local data = socket:read() - if not data then - break - end - for _,v in ipairs(data) do - mon[v.f](unpack(v.args)) - end - end - end) + Event.addRoutine(function() + while true do + local data = socket:read() + if not data then + break + end + for _,v in ipairs(data) do + mon[v.f](unpack(v.args)) + end + end + end) - while true do - Event.pullEvent() - if not socket.connected then - break - end - end + while true do + Event.pullEvent() + if not socket.connected then + break + end + end - print('connection lost') + print('connection lost') - socket:close() + socket:close() end diff --git a/monitor/mwm.lua b/monitor/mwm.lua index 108bac0..49c36f0 100644 --- a/monitor/mwm.lua +++ b/monitor/mwm.lua @@ -17,8 +17,8 @@ local term = _G.term local window = _G.window local function syntax() - printError('Syntax:') - error('mwm sessionName [monitor]') + printError('Syntax:') + error('mwm sessionName [monitor]') end local args = { ... } @@ -33,9 +33,9 @@ local parentMon local defaultEnv = Util.shallowCopy(_ENV) defaultEnv.multishell = multishell if args[2] then - parentMon = peripheral.wrap(args[2]) or syntax() + parentMon = peripheral.wrap(args[2]) or syntax() else - parentMon = peripheral.find('monitor') or syntax() + parentMon = peripheral.find('monitor') or syntax() end parentMon.setTextScale(.5) @@ -54,8 +54,8 @@ monitor.setBackgroundColor(colors.gray) monitor.clear() local function nextUID() - UID = UID + 1 - return UID + UID = UID + 1 + return UID end local function xprun(env, path, ...) @@ -68,471 +68,471 @@ local function xprun(env, path, ...) end local function write(win, x, y, text) - win.setCursorPos(x, y) - win.write(text) + win.setCursorPos(x, y) + win.write(text) end local function redraw() - --monitor.clear() - monitor.canvas:dirty() - --monitor.setBackgroundColor(colors.gray) - monitor.canvas:clear(colors.gray) - for k, process in ipairs(processes) do - process.container.canvas:dirty() - process:focus(k == #processes) - end + --monitor.clear() + monitor.canvas:dirty() + --monitor.setBackgroundColor(colors.gray) + monitor.canvas:clear(colors.gray) + for k, process in ipairs(processes) do + process.container.canvas:dirty() + process:focus(k == #processes) + end end local function getProcessAt(x, y) - for k = #processes, 1, -1 do - local process = processes[k] - if x >= process.x and - y >= process.y and - x <= process.x + process.width - 1 and - y <= process.y + process.height - 1 then - return k, process - end - end + for k = #processes, 1, -1 do + local process = processes[k] + if x >= process.x and + y >= process.y and + x <= process.x + process.width - 1 and + y <= process.y + process.height - 1 then + return k, process + end + end end --[[ A runnable process ]]-- local Process = { } function Process:new(args) - args.env = args.env or Util.shallowCopy(defaultEnv) - args.width = args.width or termDim.width - args.height = args.height or termDim.height + args.env = args.env or Util.shallowCopy(defaultEnv) + args.width = args.width or termDim.width + args.height = args.height or termDim.height - -- TODO: randomize start position - local self = setmetatable({ - uid = nextUID(), - x = args.x or 1, - y = args.y or 1, - width = args.width + 2, - height = args.height + 3, - path = args.path, - args = args.args or { }, - title = args.title or 'shell', - }, { __index = Process }) + -- TODO: randomize start position + local self = setmetatable({ + uid = nextUID(), + x = args.x or 1, + y = args.y or 1, + width = args.width + 2, + height = args.height + 3, + path = args.path, + args = args.args or { }, + title = args.title or 'shell', + }, { __index = Process }) - self:adjustDimensions() - if not args.x then - self.x = math.random(1, monDim.width - self.width + 1) - self.y = math.random(1, monDim.height - self.height + 1) - end + self:adjustDimensions() + if not args.x then + self.x = math.random(1, monDim.width - self.width + 1) + self.y = math.random(1, monDim.height - self.height + 1) + end - self.container = Terminal.window(monitor, self.x, self.y, self.width, self.height, true) - self.window = window.create(self.container, 2, 3, args.width, args.height, true) - self.terminal = self.window + self.container = Terminal.window(monitor, self.x, self.y, self.width, self.height, true) + self.window = window.create(self.container, 2, 3, args.width, args.height, true) + self.terminal = self.window - self.container.canvas.parent = monitor.canvas - table.insert(monitor.canvas.layers, 1, self.container.canvas) - self.container.canvas:setVisible(true) + self.container.canvas.parent = monitor.canvas + table.insert(monitor.canvas.layers, 1, self.container.canvas) + self.container.canvas:setVisible(true) - --self.container.getSize = self.window.getSize + --self.container.getSize = self.window.getSize - self.co = coroutine.create(function() - local result, err + self.co = coroutine.create(function() + local result, err - if args.fn then - result, err = Util.runFunction(args.env, args.fn, table.unpack(self.args)) - elseif args.path then - result, err = xprun(args.env, args.path, table.unpack(self.args)) - end + if args.fn then + result, err = Util.runFunction(args.env, args.fn, table.unpack(self.args)) + elseif args.path then + result, err = xprun(args.env, args.path, table.unpack(self.args)) + end - if not result and err and err ~= 'Terminated' then - printError('\n' .. tostring(err)) - os.pullEventRaw('terminate') - end - multishell.removeProcess(self) - end) + if not result and err and err ~= 'Terminated' then + printError('\n' .. tostring(err)) + os.pullEventRaw('terminate') + end + multishell.removeProcess(self) + end) - self:focus(false) + self:focus(false) - return self + return self end function Process:focus(focused) - if focused then - self.container.setBackgroundColor(colors.yellow) - else - self.container.setBackgroundColor(colors.gray) - end - self.container.setTextColor(colors.black) - write(self.container, 2, 2, string.rep(' ', self.width - 2)) - write(self.container, 3, 2, self.title) - write(self.container, self.width - 2, 2, '*') + if focused then + self.container.setBackgroundColor(colors.yellow) + else + self.container.setBackgroundColor(colors.gray) + end + self.container.setTextColor(colors.black) + write(self.container, 2, 2, string.rep(' ', self.width - 2)) + write(self.container, 3, 2, self.title) + write(self.container, self.width - 2, 2, '*') - if focused then - self.window.restoreCursor() - elseif self.showSizers then - self:drawSizers(false) - end + if focused then + self.window.restoreCursor() + elseif self.showSizers then + self:drawSizers(false) + end end function Process:drawSizers(showSizers) - local sizeChars = { - '\135', '\139', '\141', '\142' - } + local sizeChars = { + '\135', '\139', '\141', '\142' + } - if Util.getVersion() < 1.8 then - sizeChars = { - '#', '#', '#', '#' - } - end + if Util.getVersion() < 1.8 then + sizeChars = { + '#', '#', '#', '#' + } + end - self.showSizers = showSizers + self.showSizers = showSizers - self.container.setBackgroundColor(colors.black) - self.container.setTextColor(colors.white) + self.container.setBackgroundColor(colors.black) + self.container.setTextColor(colors.white) - if self.showSizers then - write(self.container, 1, 1, sizeChars[1]) - write(self.container, self.width, 1, sizeChars[2]) - write(self.container, 1, self.height, sizeChars[3]) - write(self.container, self.width, self.height, sizeChars[4]) + if self.showSizers then + write(self.container, 1, 1, sizeChars[1]) + write(self.container, self.width, 1, sizeChars[2]) + write(self.container, 1, self.height, sizeChars[3]) + write(self.container, self.width, self.height, sizeChars[4]) - self.container.setTextColor(colors.yellow) - write(self.container, 1, 3, '+') - write(self.container, 1, 5, '-') - write(self.container, 3, 1, '+') - write(self.container, 5, 1, '-') + self.container.setTextColor(colors.yellow) + write(self.container, 1, 3, '+') + write(self.container, 1, 5, '-') + write(self.container, 3, 1, '+') + write(self.container, 5, 1, '-') - local str = string.format('%d x %d', self.width - 2, self.height - 3) - write(self.container, (self.width - #str) / 2, 1, str) + local str = string.format('%d x %d', self.width - 2, self.height - 3) + write(self.container, (self.width - #str) / 2, 1, str) - else - write(self.container, 1, 1, string.rep(' ', self.width)) - write(self.container, self.width, 1, ' ') - write(self.container, 1, self.height, ' ') - write(self.container, self.width, self.height, ' ') - write(self.container, 1, 3, ' ') - write(self.container, 1, 5, ' ') - end + else + write(self.container, 1, 1, string.rep(' ', self.width)) + write(self.container, self.width, 1, ' ') + write(self.container, 1, self.height, ' ') + write(self.container, self.width, self.height, ' ') + write(self.container, 1, 3, ' ') + write(self.container, 1, 5, ' ') + end end function Process:adjustDimensions() - self.width = math.min(self.width, monDim.width) - self.height = math.min(self.height, monDim.height) + self.width = math.min(self.width, monDim.width) + self.height = math.min(self.height, monDim.height) - self.x = math.max(1, self.x) - self.y = math.max(1, self.y) - self.x = math.min(self.x, monDim.width - self.width + 1) - self.y = math.min(self.y, monDim.height - self.height + 1) + self.x = math.max(1, self.x) + self.y = math.max(1, self.y) + self.x = math.min(self.x, monDim.width - self.width + 1) + self.y = math.min(self.y, monDim.height - self.height + 1) end function Process:reposition() - self:adjustDimensions() - self.container.reposition(self.x, self.y, self.width, self.height) - self.container.setBackgroundColor(colors.black) - self.container.clear() - self.window.reposition(2, 3, self.width - 2, self.height - 3) - if self.window ~= self.terminal then - self.terminal.reposition(1, 1, self.width - 2, self.height - 3) - end - redraw() + self:adjustDimensions() + self.container.reposition(self.x, self.y, self.width, self.height) + self.container.setBackgroundColor(colors.black) + self.container.clear() + self.window.reposition(2, 3, self.width - 2, self.height - 3) + if self.window ~= self.terminal then + self.terminal.reposition(1, 1, self.width - 2, self.height - 3) + end + redraw() end function Process:click(x, y) - if y == 2 then -- title bar - if x == self.width - 2 then - self:resume('terminate') - else - self:drawSizers(not self.showSizers) - end + if y == 2 then -- title bar + if x == self.width - 2 then + self:resume('terminate') + else + self:drawSizers(not self.showSizers) + end - elseif x == 1 or y == 1 then -- sizers - self:resizeClick(x, y) + elseif x == 1 or y == 1 then -- sizers + self:resizeClick(x, y) - elseif x > 1 and x < self.width then - if self.showSizers then - self:drawSizers(false) - end - self:resume('mouse_click', 1, x - 1, y - 2) - self:resume('mouse_up', 1, x - 1, y - 2) - end + elseif x > 1 and x < self.width then + if self.showSizers then + self:drawSizers(false) + end + self:resume('mouse_click', 1, x - 1, y - 2) + self:resume('mouse_up', 1, x - 1, y - 2) + end end function Process:resizeClick(x, y) - if x == 1 and y == 3 then - self.height = self.height + 1 - elseif x == 1 and y == 5 then - self.height = self.height - 1 - elseif x == 3 and y == 1 then - self.width = self.width + 1 - elseif x == 5 and y == 1 then - self.width = self.width - 1 - else - return - end - self:reposition() - self:resume('term_resize') - self:drawSizers(true) - multishell.saveSession(sessionFile) + if x == 1 and y == 3 then + self.height = self.height + 1 + elseif x == 1 and y == 5 then + self.height = self.height - 1 + elseif x == 3 and y == 1 then + self.width = self.width + 1 + elseif x == 5 and y == 1 then + self.width = self.width - 1 + else + return + end + self:reposition() + self:resume('term_resize') + self:drawSizers(true) + multishell.saveSession(sessionFile) end function Process:resume(event, ...) - if coroutine.status(self.co) == 'dead' then - return - end + if coroutine.status(self.co) == 'dead' then + return + end - if not self.filter or self.filter == event or event == "terminate" then - --term.redirect(self.terminal) - local previousTerm = term.redirect(self.terminal) + if not self.filter or self.filter == event or event == "terminate" then + --term.redirect(self.terminal) + local previousTerm = term.redirect(self.terminal) - local previous = running - running = self -- stupid shell set title - local ok, result = coroutine.resume(self.co, event, ...) - running = previous + local previous = running + running = self -- stupid shell set title + local ok, result = coroutine.resume(self.co, event, ...) + running = previous - self.terminal = term.current() - term.redirect(previousTerm) + self.terminal = term.current() + term.redirect(previousTerm) - if ok then - self.filter = result - else - printError(result) - end - return ok, result - end + if ok then + self.filter = result + else + printError(result) + end + return ok, result + end end --[[ Install a multishell manager for the monitor ]]-- function multishell.getFocus() - return processes[#processes].uid + return processes[#processes].uid end function multishell.setFocus(uid) - local process = Util.find(processes, 'uid', uid) + local process = Util.find(processes, 'uid', uid) - if process then - local lastFocused = processes[#processes] - if lastFocused ~= process then + if process then + local lastFocused = processes[#processes] + if lastFocused ~= process then - if lastFocused then - lastFocused:focus(false) - end + if lastFocused then + lastFocused:focus(false) + end - Util.removeByValue(processes, process) - table.insert(processes, process) + Util.removeByValue(processes, process) + table.insert(processes, process) - process.container.canvas:raise() - process:focus(true) - process.container.canvas:dirty() - end - return true - end - return false + process.container.canvas:raise() + process:focus(true) + process.container.canvas:dirty() + end + return true + end + return false end function multishell.getTitle(uid) - local process = Util.find(processes, 'uid', uid) - if process then - return process.title - end + local process = Util.find(processes, 'uid', uid) + if process then + return process.title + end end function multishell.setTitle(uid, title) - local process = Util.find(processes, 'uid', uid) - if process then - process.title = title or '' - process:focus(process == processes[#processes]) - end + local process = Util.find(processes, 'uid', uid) + if process then + process.title = title or '' + process:focus(process == processes[#processes]) + end end function multishell.getCurrent() - if running then - return running.uid - end + if running then + return running.uid + end end function multishell.getCount() - return #processes + return #processes end function multishell.getTabs() - return processes + return processes end function multishell.launch(env, file, ...) - return multishell.openTab({ - path = file, - env = env, - title = 'shell', - args = { ... }, - }) + return multishell.openTab({ + path = file, + env = env, + title = 'shell', + args = { ... }, + }) end function multishell.openTab(tabInfo) - local process = Process:new(tabInfo) + local process = Process:new(tabInfo) - table.insert(processes, 1, process) + table.insert(processes, 1, process) - --local previousTerm = term.current() - process:resume() - --term.redirect(previousTerm) + --local previousTerm = term.current() + process:resume() + --term.redirect(previousTerm) - multishell.saveSession(sessionFile) + multishell.saveSession(sessionFile) - return process.uid + return process.uid end function multishell.removeProcess(process) - Util.removeByValue(processes, process) - process.container.canvas:removeLayer() + Util.removeByValue(processes, process) + process.container.canvas:removeLayer() - multishell.saveSession(sessionFile) - redraw() + multishell.saveSession(sessionFile) + redraw() end function multishell.saveSession(filename) - local t = { } - for _,process in ipairs(processes) do - if process.path and not process.isShell then - table.insert(t, { - x = process.x, - y = process.y, - width = process.width - 2, - height = process.height - 3, - path = process.path, - args = process.args, - }) - end - end - Util.writeTable(filename, t) + local t = { } + for _,process in ipairs(processes) do + if process.path and not process.isShell then + table.insert(t, { + x = process.x, + y = process.y, + width = process.width - 2, + height = process.height - 3, + path = process.path, + args = process.args, + }) + end + end + Util.writeTable(filename, t) end function multishell.loadSession(filename) - local config = Util.readTable(filename) - if config then - for k = #config, 1, -1 do - multishell.openTab(config[k]) - end - end + local config = Util.readTable(filename) + if config then + for k = #config, 1, -1 do + multishell.openTab(config[k]) + end + end end function multishell.stop() - multishell._stop = true + multishell._stop = true end function multishell.start() - while not multishell._stop do + while not multishell._stop do - local event = { os.pullEventRaw() } + local event = { os.pullEventRaw() } - if event[1] == 'terminate' then - local focused = processes[#processes] - if focused.isShell then - focused:resume('terminate') - else - break - end + if event[1] == 'terminate' then + local focused = processes[#processes] + if focused.isShell then + focused:resume('terminate') + else + break + end - elseif event[1] == 'monitor_touch' then - local x, y = event[3], event[4] + elseif event[1] == 'monitor_touch' then + local x, y = event[3], event[4] - local key, process = getProcessAt(x, y) - if process then - if key ~= #processes then - multishell.setFocus(process.uid) - multishell.saveSession(sessionFile) - end - process:click(x - process.x + 1, y - process.y + 1) + local key, process = getProcessAt(x, y) + if process then + if key ~= #processes then + multishell.setFocus(process.uid) + multishell.saveSession(sessionFile) + end + process:click(x - process.x + 1, y - process.y + 1) - else - process = processes[#processes] - if process and process.showSizers then - process.x = math.floor(x - (process.width) / 2) - process.y = y - process:reposition() - process:drawSizers(true) - multishell.saveSession(sessionFile) - end - end + else + process = processes[#processes] + if process and process.showSizers then + process.x = math.floor(x - (process.width) / 2) + process.y = y + process:reposition() + process:drawSizers(true) + multishell.saveSession(sessionFile) + end + end - elseif event[1] == 'mouse_click' or - event[1] == 'mouse_up' then + elseif event[1] == 'mouse_click' or + event[1] == 'mouse_up' then - local focused = processes[#processes] - if not focused.isShell then - multishell.setFocus(1) -- shell is always 1 - else - focused:resume(unpack(event)) - end + local focused = processes[#processes] + if not focused.isShell then + multishell.setFocus(1) -- shell is always 1 + else + focused:resume(unpack(event)) + end - elseif event[1] == 'char' or - event[1] == 'key' or - event[1] == 'key_up' or - event[1] == 'paste' then + elseif event[1] == 'char' or + event[1] == 'key' or + event[1] == 'key_up' or + event[1] == 'paste' then - local focused = processes[#processes] - if focused then - focused:resume(unpack(event)) - end + local focused = processes[#processes] + if focused then + focused:resume(unpack(event)) + end - else - for _,process in pairs(Util.shallowCopy(processes)) do - process:resume(unpack(event)) - end - end + else + for _,process in pairs(Util.shallowCopy(processes)) do + process:resume(unpack(event)) + end + end - monitor.canvas:render(parentMon) + monitor.canvas:render(parentMon) - local focused = processes[#processes] - if focused then - focused.window.restoreCursor() - end - end + local focused = processes[#processes] + if focused then + focused.window.restoreCursor() + end + end end --[[ Special shell process for launching programs ]]-- local function addShell() - local process = setmetatable({ - x = monDim.width, - y = monDim.height, - width = 1, - height = 1, - isShell = true, - uid = nextUID(), - title = 'Terminal', - }, { __index = Process }) + local process = setmetatable({ + x = monDim.width, + y = monDim.height, + width = 1, + height = 1, + isShell = true, + uid = nextUID(), + title = 'Terminal', + }, { __index = Process }) - function process:focus(focused) - self.window.setVisible(focused) - if focused then - self.window.restoreCursor() - else - parentTerm.clear() - parentTerm.setCursorBlink(false) - local str = 'Click screen for shell' - write(parentTerm, - math.floor((termDim.width - #str) / 2), - math.floor(termDim.height / 2), - str) - end - end + function process:focus(focused) + self.window.setVisible(focused) + if focused then + self.window.restoreCursor() + else + parentTerm.clear() + parentTerm.setCursorBlink(false) + local str = 'Click screen for shell' + write(parentTerm, + math.floor((termDim.width - #str) / 2), + math.floor(termDim.height / 2), + str) + end + end - function process:click() - end + function process:click() + end - process.container = Terminal.window(monitor, process.x, process.y+1, process.width, process.height, true) - process.window = window.create(parentTerm, 1, 1, termDim.width, termDim.height, true) - process.terminal = process.window + process.container = Terminal.window(monitor, process.x, process.y+1, process.width, process.height, true) + process.window = window.create(parentTerm, 1, 1, termDim.width, termDim.height, true) + process.terminal = process.window - process.co = coroutine.create(function() - print('To run a program on the monitor, type "fg "') - print('To quit, type "exit"') - os.run(Util.shallowCopy(defaultEnv), shell.resolveProgram('shell')) - multishell.stop() - end) + process.co = coroutine.create(function() + print('To run a program on the monitor, type "fg "') + print('To quit, type "exit"') + os.run(Util.shallowCopy(defaultEnv), shell.resolveProgram('shell')) + multishell.stop() + end) - table.insert(processes, process) - process:focus(true) + table.insert(processes, process) + process:focus(true) - local previousTerm = term.current() - process:resume() - term.redirect(previousTerm) + local previousTerm = term.current() + process:resume() + term.redirect(previousTerm) end addShell() diff --git a/neural/.package b/neural/.package index 6e03865..1a29a25 100644 --- a/neural/.package +++ b/neural/.package @@ -3,4 +3,7 @@ repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/neural', description = [[ Applications using various plethora modules ]], licence = 'MIT', + required = { + 'core', + }, } diff --git a/neural/Scanner.lua b/neural/Scanner.lua index 766988b..c9fba35 100644 --- a/neural/Scanner.lua +++ b/neural/Scanner.lua @@ -1,10 +1,11 @@ -local Event = require('event') -local itemDB = require('core.itemDB') -local UI = require('ui') -local Util = require('util') +local Event = require('event') +local itemDB = require('core.itemDB') +local UI = require('ui') +local Util = require('util') -local device = _G.device -local gps = _G.gps +local device = _G.device +local gps = _G.gps +local multishell = _ENV.multishell local glasses = device['plethora:glasses'] local scanner = device['plethora:scanner'] or @@ -14,11 +15,13 @@ local projecting = { } local function getPoint() local pt = { gps.locate() } - return { - x = pt[1], - y = pt[2], - z = pt[3], - } + if pt[1] then + return { + x = pt[1], + y = pt[2], + z = pt[3], + } + end end local offset = getPoint() @@ -60,7 +63,7 @@ local page = UI.Page { }, sortColumn = 'name', accelerators = { - grid_select = 'noop', + grid_select = 'inspect', }, }, }, @@ -75,21 +78,25 @@ function page:scan() local entry = itemDB:get(table.concat({ b.name, b.metadata }, ':')) if not entry then local meta = scanner.getBlockMeta(b.x, b.y, b.z) - entry = itemDB:add({ - name = meta.name, - displayName = meta.displayName, - damage = meta.metadata, - }) + if meta.name == b.name and meta.metadata == b.metadata then + entry = itemDB:add({ + name = meta.name, + displayName = meta.displayName, + damage = meta.metadata, + }) + end end - b.key = entry.displayName - if acc[b.key] then - acc[b.key].count = acc[b.key].count + 1 - else - entry = Util.shallowCopy(entry) - entry.lname = entry.displayName:lower() - entry.count = 1 - entry.key = b.key - acc[b.key] = entry + if entry then + b.key = entry.displayName + if acc[b.key] then + acc[b.key].count = acc[b.key].count + 1 + else + entry = Util.shallowCopy(entry) + entry.lname = entry.displayName:lower() + entry.count = 1 + entry.key = b.key + acc[b.key] = entry + end end throttle() return acc @@ -121,43 +128,45 @@ function page.detail:show(blocks, entry) local scanned = scanner.scan() local pos = getPoint() - blocks = Util.reduce(scanned, function(acc, b) - if b.name == t.name and b.metadata == t.damage then - -- track block's world position - b.id = table.concat({ - math.floor(pos.x + b.x), - math.floor(pos.y + b.y), - math.floor(pos.z + b.z) }, ':') - acc[b.id] = b - end - return acc - end, { }) - - for _, b in pairs(blocks) do - if not projecting[b.id] then - projecting[b.id] = b - pcall(function() - b.box = canvas.addItem({ - pos.x - offset.x + b.x + -(pos.x % 1) + .5, - pos.y - offset.y + b.y + -(pos.y % 1) + .5, - pos.z - offset.z + b.z + -(pos.z % 1) + .5 }, - b.name, b.damage, .5) - end) - if not b.box then - b.box = canvas.addBox( - pos.x - offset.x + b.x + -(pos.x % 1) + .25, - pos.y - offset.y + b.y + -(pos.y % 1) + .25, - pos.z - offset.z + b.z + -(pos.z % 1) + .25, - .5, .5, .5) + if pos and offset then + blocks = Util.reduce(scanned, function(acc, b) + if b.name == t.name and b.metadata == t.damage then + -- track block's world position + b.id = table.concat({ + math.floor(pos.x + b.x), + math.floor(pos.y + b.y), + math.floor(pos.z + b.z) }, ':') + acc[b.id] = b end - b.box.setDepthTested(false) - end - end + return acc + end, { }) - for _, b in pairs(projecting) do - if not blocks[b.id] then - b.box.remove() - projecting[b.id] = nil + for _, b in pairs(blocks) do + if not projecting[b.id] then + projecting[b.id] = b + pcall(function() + b.box = canvas.addItem({ + pos.x - offset.x + b.x + -(pos.x % 1) + .5, + pos.y - offset.y + b.y + -(pos.y % 1) + .5, + pos.z - offset.z + b.z + -(pos.z % 1) + .5 }, + b.name, b.damage, .5) + end) + if not b.box then + b.box = canvas.addBox( + pos.x - offset.x + b.x + -(pos.x % 1) + .25, + pos.y - offset.y + b.y + -(pos.y % 1) + .25, + pos.z - offset.z + b.z + -(pos.z % 1) + .25, + .5, .5, .5) + end + b.box.setDepthTested(false) + end + end + + for _, b in pairs(projecting) do + if not blocks[b.id] then + b.box.remove() + projecting[b.id] = nil + end end end end @@ -178,7 +187,14 @@ function page:eventHandler(event) elseif event.type == 'scan' then self:scan() - elseif event.type == 'grid_select' and event.element == page.grid then + elseif event.type == 'grid_select' and event.element == self.detail.grid then + multishell.openTab({ + path = 'sys/apps/Lua.lua', + args = { event.selected }, + focused = true, + }) + + elseif event.type == 'grid_select' and event.element == self.grid then self.detail:show(self.blocks, self.grid:getSelected()) elseif event.type == 'cancel' then diff --git a/neural/Sensor.lua b/neural/Sensor.lua index 7e0cf06..4e67671 100644 --- a/neural/Sensor.lua +++ b/neural/Sensor.lua @@ -18,22 +18,13 @@ local projecting = { } local offset local canvas = glasses and intro and glasses.canvas3d().create() -local config = Config.load('Sensor', { - ignore = { } -}) +local config = Config.load('Sensor') local page = UI.Page { tabs = UI.Tabs { listing = UI.Tab { tabTitle = 'Listing', - menuBar = UI.MenuBar { - buttons = { - { text = 'Ignore', event = 'ignore' }, - { text = 'Details', event = 'detail' }, - }, - }, grid = UI.ScrollingGrid { - y = 2, columns = { { heading = 'Name', key = 'displayName' }, { heading = 'X', key = 'x', width = 3, align = 'right' }, @@ -45,14 +36,7 @@ local page = UI.Page { }, summary = UI.Tab { tabTitle = 'Summary', - menuBar = UI.MenuBar { - buttons = { - { text = 'Projector', event = 'project' }, - { text = 'Ignore', event = 'ignore' }, - }, - }, grid = UI.ScrollingGrid { - y = 2, columns = { { heading = 'Name', key = 'displayName' }, { heading = 'Count', key = 'count', width = 5, align = 'right' }, @@ -134,13 +118,6 @@ local function project(entities) end end -local function ignoreEntity(entity) - if entity then - config.ignore[entity.name] = true - Config.update('Sensor', config) - end -end - function detail:enable(entity) local function update() local t = { } @@ -194,8 +171,6 @@ end function listing:enable() self.handler = Event.onInterval(.5, function() local entities = sensor.sense() - Util.filterInplace(entities, function(e) return not config.ignore[e.name] end) - self.grid:setValues(entities) self.grid:draw() self:sync() @@ -209,14 +184,11 @@ function listing:disable() end function listing:eventHandler(event) - if event.type == 'detail' or event.type == 'grid_select' then + if event.type == 'grid_select' then local selected = self.grid:getSelected() if selected then UI:setPage(detail, selected) end - - elseif event.type == 'ignore' then - ignoreEntity(self.grid:getSelected()) end return UI.Tab.eventHandler(self, event) @@ -225,7 +197,6 @@ end function summary:enable() self.handler = Event.onInterval(.5, function() local entities = sensor.sense() - Util.filterInplace(entities, function(e) return not config.ignore[e.name] end) local t = { } local highlight = { } @@ -265,10 +236,7 @@ function summary.grid:getRowTextColor(row, selected) end function summary:eventHandler(event) - if event.type == 'ignore' then - ignoreEntity(self.grid:getSelected()) - - elseif event.type == 'project' or event.type == 'grid_select' then + if event.type == 'grid_select' then local selected = self.grid:getSelected() if selected then self.target = selected.name diff --git a/neural/apis/angle.lua b/neural/apis/angle.lua index 20fd424..7bb84e9 100644 --- a/neural/apis/angle.lua +++ b/neural/apis/angle.lua @@ -1,11 +1,11 @@ local Angle = { } function Angle.towards(x, y, z) - return math.deg(math.atan2(-x, z)), math.deg(-math.atan2(y, math.sqrt(x * x + z * z))) + return math.deg(math.atan2(-x, z)), math.deg(-math.atan2(y, math.sqrt(x * x + z * z))) end function Angle.away(x, y, z) - return math.deg(math.atan2(x, -z)), 0 + return math.deg(math.atan2(x, -z)), 0 end return Angle diff --git a/neural/apis/mobs.lua b/neural/apis/mobs.lua index 5f98113..5e6ef79 100644 --- a/neural/apis/mobs.lua +++ b/neural/apis/mobs.lua @@ -1,27 +1,27 @@ local Mobs = { } local hostiles = { - BabySkeleton = true, - BabyZombie = true, - Bat = true, - Blaze = true, - CaveSpider = true, - Creeper = true, - Ghast = true, - Husk = true, - LavaSlime = true, - PigZombie = true, - Skeleton = true, - Slime = true, - Spider = true, - Witch = true, - WitherSkeleton = true, - Zombie = true, - ZombieVillager = true, + BabySkeleton = true, + BabyZombie = true, + Bat = true, + Blaze = true, + CaveSpider = true, + Creeper = true, + Ghast = true, + Husk = true, + LavaSlime = true, + PigZombie = true, + Skeleton = true, + Slime = true, + Spider = true, + Witch = true, + WitherSkeleton = true, + Zombie = true, + ZombieVillager = true, } function Mobs.getNames() - return hostiles + return hostiles end return Mobs diff --git a/neural/apis/statemachine.lua b/neural/apis/statemachine.lua index 1d27804..a05e2b4 100644 --- a/neural/apis/statemachine.lua +++ b/neural/apis/statemachine.lua @@ -7,149 +7,149 @@ local NONE = "none" local ASYNC = "async" local function call_handler(handler, params) - if handler then - return handler(unpack(params)) - end + if handler then + return handler(unpack(params)) + end end local function create_transition(name) - local can, to, from, params + local can, to, from, params - local function transition(self, ...) - if self.asyncState == NONE then - can, to = self:can(name) - from = self.current - params = { self, name, from, to, ...} + local function transition(self, ...) + if self.asyncState == NONE then + can, to = self:can(name) + from = self.current + params = { self, name, from, to, ...} - if not can then return false end - self.currentTransitioningEvent = name + if not can then return false end + self.currentTransitioningEvent = name - local beforeReturn = call_handler(self["onbefore" .. name], params) - local leaveReturn = call_handler(self["onleave" .. from], params) + local beforeReturn = call_handler(self["onbefore" .. name], params) + local leaveReturn = call_handler(self["onleave" .. from], params) - if beforeReturn == false or leaveReturn == false then - return false - end + if beforeReturn == false or leaveReturn == false then + return false + end - self.asyncState = name .. "WaitingOnLeave" + self.asyncState = name .. "WaitingOnLeave" - if leaveReturn ~= ASYNC then - transition(self, ...) - end + if leaveReturn ~= ASYNC then + transition(self, ...) + end - return true - elseif self.asyncState == name .. "WaitingOnLeave" then - self.current = to + return true + elseif self.asyncState == name .. "WaitingOnLeave" then + self.current = to - local enterReturn = call_handler(self["onenter" .. to] or self["on" .. to], params) + local enterReturn = call_handler(self["onenter" .. to] or self["on" .. to], params) - self.asyncState = name .. "WaitingOnEnter" + self.asyncState = name .. "WaitingOnEnter" - if enterReturn ~= ASYNC then - transition(self, ...) - end + if enterReturn ~= ASYNC then + transition(self, ...) + end - return true - elseif self.asyncState == name .. "WaitingOnEnter" then - call_handler(self["onafter" .. name] or self["on" .. name], params) - call_handler(self["onstatechange"], params) - self.asyncState = NONE - self.currentTransitioningEvent = nil - return true - else - if string.find(self.asyncState, "WaitingOnLeave") or string.find(self.asyncState, "WaitingOnEnter") then - self.asyncState = NONE - transition(self, ...) - return true - end - end + return true + elseif self.asyncState == name .. "WaitingOnEnter" then + call_handler(self["onafter" .. name] or self["on" .. name], params) + call_handler(self["onstatechange"], params) + self.asyncState = NONE + self.currentTransitioningEvent = nil + return true + else + if string.find(self.asyncState, "WaitingOnLeave") or string.find(self.asyncState, "WaitingOnEnter") then + self.asyncState = NONE + transition(self, ...) + return true + end + end - self.currentTransitioningEvent = nil - return false - end + self.currentTransitioningEvent = nil + return false + end - return transition + return transition end local function add_to_map(map, event) - if type(event.from) == 'string' then - map[event.from] = event.to - else - for _, from in ipairs(event.from) do - map[from] = event.to - end - end + if type(event.from) == 'string' then + map[event.from] = event.to + else + for _, from in ipairs(event.from) do + map[from] = event.to + end + end end function machine.create(options) - assert(options.events) + assert(options.events) - local fsm = {} - setmetatable(fsm, machine) + local fsm = {} + setmetatable(fsm, machine) - fsm.options = options - fsm.current = options.initial or 'none' - fsm.asyncState = NONE - fsm.events = {} + fsm.options = options + fsm.current = options.initial or 'none' + fsm.asyncState = NONE + fsm.events = {} - for _, event in ipairs(options.events or {}) do - local name = event.name - fsm[name] = fsm[name] or create_transition(name) - fsm.events[name] = fsm.events[name] or { map = {} } - add_to_map(fsm.events[name].map, event) - end + for _, event in ipairs(options.events or {}) do + local name = event.name + fsm[name] = fsm[name] or create_transition(name) + fsm.events[name] = fsm.events[name] or { map = {} } + add_to_map(fsm.events[name].map, event) + end - for name, callback in pairs(options.callbacks or {}) do - fsm[name] = callback - end + for name, callback in pairs(options.callbacks or {}) do + fsm[name] = callback + end - return fsm + return fsm end function machine:is(state) - return self.current == state + return self.current == state end function machine:can(e) - local event = self.events[e] - local to = event and event.map[self.current] or event.map['*'] - return to ~= nil, to + local event = self.events[e] + local to = event and event.map[self.current] or event.map['*'] + return to ~= nil, to end function machine:cannot(e) - return not self:can(e) + return not self:can(e) end function machine:todot(filename) - local dotfile = io.open(filename,'w') - dotfile:write('digraph {\n') - local transition = function(event,from,to) - dotfile:write(string.format('%s -> %s [label=%s];\n',from,to,event)) - end - for _, event in pairs(self.options.events) do - if type(event.from) == 'table' then - for _, from in ipairs(event.from) do - transition(event.name,from,event.to) - end - else - transition(event.name,event.from,event.to) - end - end - dotfile:write('}\n') - dotfile:close() + local dotfile = io.open(filename,'w') + dotfile:write('digraph {\n') + local transition = function(event,from,to) + dotfile:write(string.format('%s -> %s [label=%s];\n',from,to,event)) + end + for _, event in pairs(self.options.events) do + if type(event.from) == 'table' then + for _, from in ipairs(event.from) do + transition(event.name,from,event.to) + end + else + transition(event.name,event.from,event.to) + end + end + dotfile:write('}\n') + dotfile:close() end function machine:transition(event) - if self.currentTransitioningEvent == event then - return self[self.currentTransitioningEvent](self) - end + if self.currentTransitioningEvent == event then + return self[self.currentTransitioningEvent](self) + end end function machine:cancelTransition(event) - if self.currentTransitioningEvent == event then - self.asyncState = NONE - self.currentTransitioningEvent = nil - end + if self.currentTransitioningEvent == event then + self.asyncState = NONE + self.currentTransitioningEvent = nil + end end machine.NONE = NONE diff --git a/neural/autorun/interface.lua b/neural/autorun/interface.lua index 032d73a..0a94eb0 100644 --- a/neural/autorun/interface.lua +++ b/neural/autorun/interface.lua @@ -3,28 +3,28 @@ local GPS = require('gps') local device = _G.device if device.neuralInterface and device.wireless_modem then - local ni = require('neural.interface') - device.neuralInterface.goTo = function(x, y, z) - local pt = GPS.locate(2) - if pt then - return pcall(function() - if device.neuralInterface.walk then - 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 device.neuralInterface.walk(gpt.x, gpt.y, gpt.z, 2) - elseif ni.launch then - local y, p = ni.yap(pt, { x = x, y = y + 3, z = z }) - ni.look(y, 0) - return ni.launch(y, p, 1.5) - end - end) - end - return false, 'No GPS' - end + local ni = require('neural.interface') + device.neuralInterface.goTo = function(x, y, z) + local pt = GPS.locate(2) + if pt then + return pcall(function() + if device.neuralInterface.walk then + 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 device.neuralInterface.walk(gpt.x, gpt.y, gpt.z, 2) + elseif ni.launch then + local y, p = ni.yap(pt, { x = x, y = y + 3, z = z }) + ni.look(y, 0) + return ni.launch(y, p, 1.5) + end + end) + end + return false, 'No GPS' + end end diff --git a/neural/autorun/splash.lua b/neural/autorun/splash.lua index bd395fb..2ce8cc2 100644 --- a/neural/autorun/splash.lua +++ b/neural/autorun/splash.lua @@ -1,70 +1,70 @@ local opus = { - 'fffff00', - 'ffff07000', - 'ff00770b00 4444', - 'ff077777444444444', - 'f07777744444444444', - 'f0000777444444444', - '070000111744444', - '777770000', - '7777000000', - '70700000000', - '077000000000', + 'fffff00', + 'ffff07000', + 'ff00770b00 4444', + 'ff077777444444444', + 'f07777744444444444', + 'f0000777444444444', + '070000111744444', + '777770000', + '7777000000', + '70700000000', + '077000000000', } local hex = { - ['0'] = 0xF0F0F04F, - ['1'] = 0xF2B2334F, - ['2'] = 0xE57FD84F, - ['3'] = 0x99B2F24F, - ['4'] = 0xDEDE6C4F, - ['5'] = 0x7FCC194F, - ['6'] = 0xF2B2CC4F, - ['7'] = 0x4C4C4C4F, - ['8'] = 0x9999994F, - ['9'] = 0x4C99B24F, - ['a'] = 0xB266E54F, - ['b'] = 0x3366CC4F, - ['c'] = 0x7F664C4F, - ['d'] = 0x57A64E4F, - ['e'] = 0xCC4C4C4F, + ['0'] = 0xF0F0F04F, + ['1'] = 0xF2B2334F, + ['2'] = 0xE57FD84F, + ['3'] = 0x99B2F24F, + ['4'] = 0xDEDE6C4F, + ['5'] = 0x7FCC194F, + ['6'] = 0xF2B2CC4F, + ['7'] = 0x4C4C4C4F, + ['8'] = 0x9999994F, + ['9'] = 0x4C99B24F, + ['a'] = 0xB266E54F, + ['b'] = 0x3366CC4F, + ['c'] = 0x7F664C4F, + ['d'] = 0x57A64E4F, + ['e'] = 0xCC4C4C4F, -- ['f'] = 0x191919FF, -- transparent } local function update() - local canvas = device['plethora:glasses'] and device['plethora:glasses'].canvas() - if canvas then - local Tween = require('ui.tween') + local canvas = device['plethora:glasses'] and device['plethora:glasses'].canvas() + if canvas then + local Tween = require('ui.tween') - canvas.clear() - local w, h = canvas.getSize() - local pos = { x = w / 2, y = h / 2 - 30 } - local group = canvas.addGroup(pos) - local function drawLine(k, line) - for i = 1, #line do - local pix = hex[line:sub(i, i)] - if pix then - group.addRectangle(i*1.5, k*2.25, 1.5, 2.25, pix) - end - end - end + canvas.clear() + local w, h = canvas.getSize() + local pos = { x = w / 2, y = h / 2 - 30 } + local group = canvas.addGroup(pos) + local function drawLine(k, line) + for i = 1, #line do + local pix = hex[line:sub(i, i)] + if pix then + group.addRectangle(i*1.5, k*2.25, 1.5, 2.25, pix) + end + end + end - for k,line in ipairs(opus) do - drawLine(k, line) - end - os.sleep(.5) - local tween = Tween.new(40, pos, { x = w - 60, y = h - 30 }, 'outBounce') - repeat - local finished = tween:update(1) - os.sleep(0) - group.setPosition(pos.x, pos.y) - until finished - end + for k,line in ipairs(opus) do + drawLine(k, line) + end + os.sleep(.5) + local tween = Tween.new(40, pos, { x = w - 60, y = h - 30 }, 'outBounce') + repeat + local finished = tween:update(1) + os.sleep(0) + group.setPosition(pos.x, pos.y) + until finished + end end kernel.run({ - title = 'opus', - env = _ENV, - hidden = true, - fn = update, + title = 'opus', + env = _ENV, + hidden = true, + fn = update, }) diff --git a/neural/canvasServer.lua b/neural/canvasServer.lua new file mode 100644 index 0000000..556dffe --- /dev/null +++ b/neural/canvasServer.lua @@ -0,0 +1,189 @@ +local neural = require('neural.interface') + +local device = _G.device +local gps = _G.gps +local os = _G.os +local parallel = _G.parallel + +neural.assertModules({ + 'plethora:glasses', + 'plethora:introspection', + 'plethora:sensor', +}) + +local function getPoint() + local pt = { gps.locate() } + if pt[1] then + return { + x = pt[1], + y = pt[2], + z = pt[3], + } + end +end + +local projecting = { } +local offset = getPoint() or error('GPS not found') + +local canvas = neural.canvas3d().create({ + -(offset.x % 1), + -(offset.y % 1), + -(offset.z % 1) +}) + +local function update(scanned) + for _, b in pairs(scanned) do + if not projecting[b.id] then + local box + + local x = b.x - math.floor(offset.x) + local y = b.y - math.floor(offset.y) + local z = b.z - math.floor(offset.z) + + -- items are centered at the mid-point of the cube + -- boxes are aligned to the top corner - sigh + if b.path then + box = canvas.addBox(x + .4, y + .4, z + .4, .2, .2, .2, 0xa0902080) + elseif b.name then + pcall(function() + box = canvas.addItem({ x + .5, y + .5, z + .5 }, b.name, b.damage or b.metadata, .5) + end) + end + if not box then + box = canvas.addBox(x, y, z, 1, 1, 1, 0x8080ff30) + end + if box then + box.setDepthTested(false) + end + projecting[b.id] = box + end + end + + for id, box in pairs(projecting) do + if not scanned[id] then + box.remove() + projecting[id] = nil + end + end +end + +local scanned = { } +local dirty + +local function processMessage(msg) + if msg.type == 'canvas_clear' then + scanned = { } + projecting = { } + canvas.clear() + + elseif msg.type == 'canvas_remove' then + for _, v in pairs(msg.data) do + v.id = table.concat({ v.x, v.y, v.z }, ':') + scanned[v.id] = nil + end + dirty = true + + elseif msg.type == 'canvas_update' then + scanned = { } + for _, v in pairs(msg.data) do + v.id = table.concat({ v.x, v.y, v.z }, ':') + scanned[v.id] = v + end + dirty = true + + elseif msg.type == 'canvas_barrier' then + for _, v in pairs(msg.data) do + v.id = table.concat({ v.x, v.y, v.z }, ':') + if projecting[v.id] then + projecting[v.id].remove() + projecting[v.id] = nil + end + v.name = 'minecraft:barrier' + v.damage = 0 + scanned[v.id] = v + end + dirty = true + + elseif msg.type == 'canvas_path' then + for k, v in pairs(scanned or { }) do + if v.path then + scanned[k] = nil + end + end + + for _, v in pairs(msg.data) do + v.path = true + v.id = table.concat({ v.x, v.y, v.z }, ':') + scanned[v.id] = v + end + dirty = true + end +end + +local function recenter() + while true do + os.sleep(3) + + local pos = getPoint() + + if pos then + if math.abs(pos.x - offset.x) + + math.abs(pos.y - offset.y) + + math.abs(pos.z - offset.z) > 64 then + for _, box in pairs(projecting) do + box.remove() + end + projecting = { } + offset = pos + canvas.recenter({ + -(offset.x % 1), + -(offset.y % 1), + -(offset.z % 1) + }) + + update(scanned) + end + end + end +end + +local function queueListener() + while true do + local _, msg = os.pullEvent('canvas_message') + processMessage(msg) + end +end + +local function modemListener() + device.wireless_modem.open(3773) + while true do + local _, _, dport, _, msg = os.pullEvent('modem_message') + if dport == 3773 and type(msg) == 'table' then + processMessage(msg) + end + end +end + +local s, m = pcall(function() + parallel.waitForAny( + queueListener, + modemListener, + recenter, + function() + while true do + if dirty then + dirty = false + update(scanned) + end + os.sleep(1) + end + end + ) +end) + +canvas.clear() +device.wireless_modem.close(3773) + +if not s and m then + _G.printError(m) +end diff --git a/neural/disableAI.lua b/neural/disableAI.lua index a9c283b..a04d432 100644 --- a/neural/disableAI.lua +++ b/neural/disableAI.lua @@ -1,10 +1,10 @@ local device = _G.device local kinetic = device['plethora:kinetic'] or - error('Missing kinetic augment') + error('Missing kinetic augment') if not kinetic.disableAI then - error('Nope') + error('Nope') end kinetic.disableAI() diff --git a/neural/dropNeural.lua b/neural/dropNeural.lua index f4246e8..bcda4df 100644 --- a/neural/dropNeural.lua +++ b/neural/dropNeural.lua @@ -1,10 +1,10 @@ local device = _G.device local intro = device['plethora:introspection'] or - error('Missing Introspection module') + error('Missing Introspection module') if intro.getBaubles then - intro.getBaubles().drop(5) + intro.getBaubles().drop(5) else - intro.getEquipment().drop(6) + intro.getEquipment().drop(6) end diff --git a/neural/elytraFly.lua b/neural/elytraFly.lua index 2e1202a..253375f 100644 --- a/neural/elytraFly.lua +++ b/neural/elytraFly.lua @@ -11,154 +11,154 @@ local parallel = _G.parallel local STARTUP_FILE = 'usr/autorun/fly.lua' if not modules.launch or not modules.getMetaOwner then - print([[Required: + print([[Required: * Kinetic augment * Entity sensor * Introspection module]]) - error('missing required item') + error('missing required item') end if not fs.exists(STARTUP_FILE) then - Util.writeFile(STARTUP_FILE, [[os.sleep(1) + Util.writeFile(STARTUP_FILE, [[os.sleep(1) shell.openHiddenTab('elytraFly.lua')]]) - print('Autorun program created: ' .. STARTUP_FILE) + print('Autorun program created: ' .. STARTUP_FILE) end local canvas = modules.canvas and modules.canvas() local function dist(x, y, z) - return math.sqrt( - math.pow(x, 2) + - math.pow(y, 2) + - math.pow(z, 2)) + return math.sqrt( + math.pow(x, 2) + + math.pow(y, 2) + + math.pow(z, 2)) end local function display(meta) - if canvas then - if not canvas.group then - canvas.group = canvas.addGroup({ 4, 90 }) - canvas.group.addRectangle(0, 0, 70, 30, 0x00000033) - canvas.pitch = canvas.group.addText({ 4, 5 }, '') -- , 0x202020FF) - canvas.pitch.setShadow(true) - canvas.pitch.setScale(.75) - canvas.group.addItem({ 5, 30 }, 'minecraft:elytra') - canvas.group2 = canvas.addGroup({ 80, 10 }) - canvas.group2.addLines( - { 0, 0 }, - { 0, 180 }, - { 5, 180 }, - { 5, 0 }, - 0x202020FF, - 2) - canvas.meter = canvas.group2.addRectangle(0, 0, 5, 1) - end - local size = math.abs(meta.pitch) -- math.ceil(math.abs(meta.pitch) / 9) - local y = 0 - local color = 0x202020FF - if meta.pitch < 0 then - y = size - color = 0x808080FF - end - canvas.meter.setPosition(0, 90 - y) - canvas.meter.setSize(5, size) - canvas.meter.setColor(color) - canvas.pitch.setText(string.format('Pitch: %s\nMotion Y: %s\nSpeed: %s m/s', - math.floor(-meta.pitch), - Util.round(meta.motionY, 2), - Util.round(dist(meta.motionX, meta.motionY, meta.motionZ) * 20, 2))) - end + if canvas then + if not canvas.group then + canvas.group = canvas.addGroup({ 4, 90 }) + canvas.group.addRectangle(0, 0, 70, 30, 0x00000033) + canvas.pitch = canvas.group.addText({ 4, 5 }, '') -- , 0x202020FF) + canvas.pitch.setShadow(true) + canvas.pitch.setScale(.75) + canvas.group.addItem({ 5, 30 }, 'minecraft:elytra') + canvas.group2 = canvas.addGroup({ 80, 10 }) + canvas.group2.addLines( + { 0, 0 }, + { 0, 180 }, + { 5, 180 }, + { 5, 0 }, + 0x202020FF, + 2) + canvas.meter = canvas.group2.addRectangle(0, 0, 5, 1) + end + local size = math.abs(meta.pitch) -- math.ceil(math.abs(meta.pitch) / 9) + local y = 0 + local color = 0x202020FF + if meta.pitch < 0 then + y = size + color = 0x808080FF + end + canvas.meter.setPosition(0, 90 - y) + canvas.meter.setSize(5, size) + canvas.meter.setColor(color) + canvas.pitch.setText(string.format('Pitch: %s\nMotion Y: %s\nSpeed: %s m/s', + math.floor(-meta.pitch), + Util.round(meta.motionY, 2), + Util.round(dist(meta.motionX, meta.motionY, meta.motionZ) * 20, 2))) + end end local function clearDisplay() - if canvas and canvas.group then - canvas.group.remove() - canvas.group = nil - canvas.group2.remove() - end + if canvas and canvas.group then + canvas.group.remove() + canvas.group = nil + canvas.group2.remove() + end end local function run() - local launchCounter = 0 + local launchCounter = 0 - while true do - local meta = modules.getMetaOwner() + while true do + local meta = modules.getMetaOwner() - if meta.isOnLadder then - os.sleep(0.5) + if meta.isOnLadder then + os.sleep(0.5) - elseif not meta.isSneaking and meta.isElytraFlying then + elseif not meta.isSneaking and meta.isElytraFlying then - if meta.isInWater then - display(meta) - os.sleep(0.5) + if meta.isInWater then + display(meta) + os.sleep(0.5) - elseif meta.pitch < 0 then -- looking up - modules.launch(meta.yaw, meta.pitch, -meta.pitch / 22.5) - Sound.play('entity.bobber.throw', .6) - display(meta) - os.sleep(0.1) + elseif meta.pitch < 0 then -- looking up + modules.launch(meta.yaw, meta.pitch, -meta.pitch / 22.5) + Sound.play('entity.bobber.throw', .6) + display(meta) + os.sleep(0.1) - elseif meta.motionY < -0.5 then -- falling fast - modules.launch(0, 270, math.min(-meta.motionY + 1, 4)) - Sound.play('entity.bat.takeoff') - display(meta) - os.sleep(0) + elseif meta.motionY < -0.5 then -- falling fast + modules.launch(0, 270, math.min(-meta.motionY + 1, 4)) + Sound.play('entity.bat.takeoff') + display(meta) + os.sleep(0) - else - display(meta) - os.sleep(0.1) - end + else + display(meta) + os.sleep(0.1) + end - elseif meta.isSneaking and not meta.isElytraFlying and meta.pitch == -90 then - if launchCounter < 2 then - launchCounter = launchCounter + 1 - Sound.play('block.note.pling', .5) - os.sleep(0.5) + elseif meta.isSneaking and not meta.isElytraFlying and meta.pitch == -90 then + if launchCounter < 2 then + launchCounter = launchCounter + 1 + Sound.play('block.note.pling', .5) + os.sleep(0.5) - else - Sound.play('entity.bobber.throw', 1) - modules.launch(0, 270, 4) - os.sleep(0.2) - end + else + Sound.play('entity.bobber.throw', 1) + modules.launch(0, 270, 4) + os.sleep(0.2) + end - elseif not meta.isSneaking and meta.motionY < -0.8 then - print('falling...') - modules.launch(0, 270, -meta.motionY + 1) - Sound.play('entity.bat.takeoff') - os.sleep(0) + elseif not meta.isSneaking and meta.motionY < -0.8 then + print('falling...') + modules.launch(0, 270, -meta.motionY + 1) + Sound.play('entity.bat.takeoff') + os.sleep(0) - elseif meta and meta.isElytraFlying and meta.isSneaking then - display(meta) - os.sleep(0.1) + elseif meta and meta.isElytraFlying and meta.isSneaking then + display(meta) + os.sleep(0.1) - else - clearDisplay() - --display(meta) - launchCounter = 0 - os.sleep(0.4) - end - end + else + clearDisplay() + --display(meta) + launchCounter = 0 + os.sleep(0.4) + end + end end parallel.waitForAny( - function() - print('\nFlight control initialized') - print('\nSneak and look straight up for launch') - print('Sneak to deactivate during flight') - print('\nPress any key to exit') - os.pullEvent('char') - end, - function() - while true do - local _, m = pcall(run) - if m then - print(m) - end - print('Waiting for 5 seconds before restarting') - os.sleep(5) - modules = _G.peripheral.wrap('back') - canvas = modules and modules.canvas and modules.canvas() - end - end) + function() + print('\nFlight control initialized') + print('\nSneak and look straight up for launch') + print('Sneak to deactivate during flight') + print('\nPress any key to exit') + os.pullEvent('char') + end, + function() + while true do + local _, m = pcall(run) + if m then + print(m) + end + print('Waiting for 5 seconds before restarting') + os.sleep(5) + modules = _G.peripheral.wrap('back') + canvas = modules and modules.canvas and modules.canvas() + end + end) clearDisplay() diff --git a/neural/etc/apps.db b/neural/etc/apps.db index 7b92a8c..4ed9bce 100644 --- a/neural/etc/apps.db +++ b/neural/etc/apps.db @@ -1,36 +1,36 @@ { - [ "elytraFly" ] = { - title = "ElytraFly", - category = "Neural", - run = "elytraFly.lua", - requires = "neuralInterface", - }, - [ "fisher" ] = { - title = "Fisher", - category = "Neural", - run = "fisher.lua", - requires = "neuralInterface", - }, - [ "9101fc1744ab274aaa0b54ee22b14ca53ee6e236" ] = { - title = "Sensor", - category = "Neural", - run = "Sensor.lua", - iconExt = "\0315\156\0305\031f\131\147\0300\159\129\031e\152\031f\139\ + [ "elytraFly" ] = { + title = "ElytraFly", + category = "Neural", + run = "elytraFly.lua", + requires = "neuralInterface", + }, + [ "fisher" ] = { + title = "Fisher", + category = "Neural", + run = "fisher.lua", + requires = "neuralInterface", + }, + [ "9101fc1744ab274aaa0b54ee22b14ca53ee6e236" ] = { + title = "Sensor", + category = "Neural", + run = "Sensor.lua", + iconExt = "\0315\156\0305\031f\131\147\0300\159\129\031e\152\031f\139\ \0315\157\131\0305\031f\145\030b\157\0300\031b\157\031e\136\140\030f\0316\148\ \0305\031f\138\131\133\030f\0310\130\0300\031f\144\031e\137\030f\0310\135\0306\031f\149", - }, - [ "c7ee483aa3d72126406fe52b8ed91bb9c419d02f" ] = { - title = "Scanner", - category = "Neural", - run = "Scanner.lua", - iconExt = "\030d\031f\143\143\143\0300\159\129\031e\152\031f\139\ + }, + [ "c7ee483aa3d72126406fe52b8ed91bb9c419d02f" ] = { + title = "Scanner", + category = "Neural", + run = "Scanner.lua", + iconExt = "\030d\031f\143\143\143\0300\159\129\031e\152\031f\139\ \030c\031d\129\129\130\030b\031f\157\0300\031b\157\031e\136\140\030f\0316\148\ \031c\143\143\143\0310\130\0300\031f\144\031e\137\030f\0310\135\0306\031f\149", - }, - [ "orescanner" ] = { - title = "Ores", - category = "Neural", - run = "ores.lua", - requires = "neuralInterface", - }, + }, + [ "orescanner" ] = { + title = "Ores", + category = "Neural", + run = "ores.lua", + requires = "neuralInterface", + }, } diff --git a/neural/fisher.lua b/neural/fisher.lua index e11065f..88597af 100644 --- a/neural/fisher.lua +++ b/neural/fisher.lua @@ -4,9 +4,9 @@ local neural = require('neural.interface') local os = _G.os neural.assertModules({ - 'plethora:kinetic', - 'plethora:introspection', - 'plethora:sensor', + 'plethora:kinetic', + 'plethora:introspection', + 'plethora:sensor', }) local depth = -3 @@ -17,93 +17,93 @@ local w, h local canvas = neural.canvas and neural.canvas() if canvas then - w, h = canvas.getSize() - icon = canvas.addItem({ w - 20, h - 20 }, 'minecraft:fishing_rod' ) + w, h = canvas.getSize() + icon = canvas.addItem({ w - 20, h - 20 }, 'minecraft:fishing_rod' ) end local fsm = machine.create({ - events = { - { name = 'startup', from = 'none', to = 'wait' }, - { name = 'rod', from = 'wait', to = 'idle' }, - { name = 'norod', from = 'idle', to = 'wait' }, - { name = 'norod', from = 'fishing', to = 'wait' }, - { name = 'cast', from = 'idle', to = 'fishing' }, - { name = 'reel', from = 'fishing', to = 'idle' }, - }, + events = { + { name = 'startup', from = 'none', to = 'wait' }, + { name = 'rod', from = 'wait', to = 'idle' }, + { name = 'norod', from = 'idle', to = 'wait' }, + { name = 'norod', from = 'fishing', to = 'wait' }, + { name = 'cast', from = 'idle', to = 'fishing' }, + { name = 'reel', from = 'fishing', to = 'idle' }, + }, - callbacks = { - -- events - oncast = function() - neural.use(.2) - os.sleep(.5) - local meta = neural.getMetaByName('unknown') - depth = meta and meta.y - .5 or depth - end, + callbacks = { + -- events + oncast = function() + neural.use(.2) + os.sleep(.5) + local meta = neural.getMetaByName('unknown') + depth = meta and meta.y - .5 or depth + end, - onreel = function() - neural.use(.3) - os.sleep(.5) - end, + onreel = function() + neural.use(.3) + os.sleep(.5) + end, - -- state changes - onenterwait = function() - print('waitng for fishing rod to be selected') - if icon then - icon.remove() - icon = canvas.addItem({ w - 20, h - 20 }, 'minecraft:fishing_rod' ) - end - end, + -- state changes + onenterwait = function() + print('waitng for fishing rod to be selected') + if icon then + icon.remove() + icon = canvas.addItem({ w - 20, h - 20 }, 'minecraft:fishing_rod' ) + end + end, - onleavewait = function() - print('fishing...') - end, + onleavewait = function() + print('fishing...') + end, - onenterfishing = function() - if icon then - icon.remove() - scale = 0 - icon = canvas.addItem({ w - 20, h - 20 }, 'minecraft:fish', math.random(0, 3) ) - end - end, - } + onenterfishing = function() + if icon then + icon.remove() + scale = 0 + icon = canvas.addItem({ w - 20, h - 20 }, 'minecraft:fish', math.random(0, 3) ) + end + end, + } }) local function isHoldingRod() - local owner = neural.getMetaOwner() - local held = owner.heldItem and owner.heldItem.getMetadata() - return held and held.rawName == 'item.fishingRod' + local owner = neural.getMetaOwner() + local held = owner.heldItem and owner.heldItem.getMetadata() + return held and held.rawName == 'item.fishingRod' end local function fish() - fsm:startup() - while true do - local meta = neural.getMetaByName('unknown') - if isHoldingRod() then - fsm:rod() - if not meta then - fsm:cast() - elseif meta.y < depth then - fsm:reel() - end - os.sleep(.1) - else - fsm:norod() - os.sleep(1) - end + fsm:startup() + while true do + local meta = neural.getMetaByName('unknown') + if isHoldingRod() then + fsm:rod() + if not meta then + fsm:cast() + elseif meta.y < depth then + fsm:reel() + end + os.sleep(.1) + else + fsm:norod() + os.sleep(1) + end - if icon and fsm.current == 'fishing' then - scale = scale + 1 - icon.setScale(scales[(scale % #scales) + 1]) - end - end + if icon and fsm.current == 'fishing' then + scale = scale + 1 + icon.setScale(scales[(scale % #scales) + 1]) + end + end end local s, m = pcall(fish) if icon then - icon.remove() + icon.remove() end if not s and m then - error(m) + error(m) end diff --git a/neural/mobFollow.lua b/neural/mobFollow.lua index 84fcea2..6163c56 100644 --- a/neural/mobFollow.lua +++ b/neural/mobFollow.lua @@ -1,7 +1,7 @@ local GPS = require('gps') local Util = require('util') local Point = require('point') -local Proxy = require('proxy') +local Proxy = require('core.proxy') local os = _G.os @@ -10,38 +10,38 @@ local remoteId = args[1] or error('mobFollow ') local ni = Proxy.create(remoteId, 'device/neuralInterface') if not ni then - error('failed to connect') + error('failed to connect') end local lpt = nil while true do - local pt = GPS.locate(2) + 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 + 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) + os.sleep(.5) end diff --git a/neural/mobPickup.lua b/neural/mobPickup.lua index 75a4c6b..ca9248c 100644 --- a/neural/mobPickup.lua +++ b/neural/mobPickup.lua @@ -1,6 +1,6 @@ --[[ - Pickup items from a flat area and drop into a hopper. - Must be a 1 high mob + Pickup items from a flat area and drop into a hopper. + Must be a 1 high mob ]] local neural = require('neural.interface') @@ -11,66 +11,66 @@ local Util = require('util') local os = _G.os neural.assertModules({ - 'plethora:sensor', - 'plethora:scanner', - 'plethora:kinetic', - 'plethora:introspection', + 'plethora:sensor', + 'plethora:scanner', + 'plethora:kinetic', + 'plethora:introspection', }) local function dropOff() - print('dropping') + print('dropping') - local b = Util.find(neural.scan(), 'name', 'minecraft:hopper') - if b then - neural.walkTo({ x = b.x, y = 0, z = b.z }, 2) + local b = Util.find(neural.scan(), 'name', 'minecraft:hopper') + if b then + neural.walkTo({ x = b.x, y = 0, z = b.z }, 2) - b = Util.find(neural.scan(), 'name', 'minecraft:hopper') - if b and math.abs(b.x) < 1 and math.abs(b.z) < 1 then - print('dropped') - neural.getEquipment().drop(1) - neural.getEquipment().drop(2) - os.sleep(1) - end - end + b = Util.find(neural.scan(), 'name', 'minecraft:hopper') + if b and math.abs(b.x) < 1 and math.abs(b.z) < 1 then + print('dropped') + neural.getEquipment().drop(1) + neural.getEquipment().drop(2) + os.sleep(1) + end + end end local function pickup(id) - local b = neural.getMetaByID(id) - if b then - neural.walkTo(b, 2) + local b = neural.getMetaByID(id) + if b then + neural.walkTo(b, 2) - local amount = neural.getEquipment().suck() - print('sucked: ' .. amount) - if amount > 0 then - Sound.play('entity.item.pickup') - return true - end - end + local amount = neural.getEquipment().suck() + print('sucked: ' .. amount) + if amount > 0 then + Sound.play('entity.item.pickup') + return true + end + end end while true do - local sensed = Util.reduce(neural.sense(), function(acc, s) - if Util.round(s.y) == 0 and s.name == 'Item' then - acc[s.id] = s - end - return acc - end, { }) + local sensed = Util.reduce(neural.sense(), function(acc, s) + if Util.round(s.y) == 0 and s.name == 'Item' then + acc[s.id] = s + end + return acc + end, { }) - local pt = { x = 0, y = 0, z = 0 } - while true do - local b = Point.closest(pt, sensed) - if not b then - os.sleep(5) - break - end - sensed[b.id] = nil + local pt = { x = 0, y = 0, z = 0 } + while true do + local b = Point.closest(pt, sensed) + if not b then + os.sleep(5) + break + end + sensed[b.id] = nil - if pickup(b.id) then - pt = b - else - dropOff() - break - end - end + if pickup(b.id) then + pt = b + else + dropOff() + break + end + end end diff --git a/neural/mobRancher.lua b/neural/mobRancher.lua index 2a919a1..5ba8deb 100644 --- a/neural/mobRancher.lua +++ b/neural/mobRancher.lua @@ -1,6 +1,6 @@ --[[ - Breed either cows or sheep. - Must be run on a mob with the same height. + Breed either cows or sheep. + Must be run on a mob with the same height. ]] local neural = require('neural.interface') @@ -14,107 +14,107 @@ local WALK_SPEED = 1.5 local MAX_GROWN = 12 neural.assertModules({ - 'plethora:sensor', - 'plethora:scanner', - 'plethora:laser', - 'plethora:kinetic', - 'plethora:introspection', + 'plethora:sensor', + 'plethora:scanner', + 'plethora:laser', + 'plethora:kinetic', + 'plethora:introspection', }) local fed = { } local function resupply() - local slot = neural.getEquipment().list()[1] - if slot and slot.count > 32 then - return - end - print('resupplying') - for _ = 1, 2 do - local dispenser = Map.find(neural.scan(), 'name', 'minecraft:dispenser') - if not dispenser then - print('dispenser not found') - break - end - if math.abs(dispenser.x) <= 1 and math.abs(dispenser.z) <= 1 then - neural.lookAt(dispenser) - for _ = 1, 8 do - neural.use(0, 'off') - os.sleep(.2) - neural.getEquipment().suck(1, 64) - end - break - else - neural.walkTo({ x = dispenser.x, y = 0, z = dispenser.z }, WALK_SPEED) - end - end + local slot = neural.getEquipment().list()[1] + if slot and slot.count > 32 then + return + end + print('resupplying') + for _ = 1, 2 do + local dispenser = Map.find(neural.scan(), 'name', 'minecraft:dispenser') + if not dispenser then + print('dispenser not found') + break + end + if math.abs(dispenser.x) <= 1 and math.abs(dispenser.z) <= 1 then + neural.lookAt(dispenser) + for _ = 1, 8 do + neural.use(0, 'off') + os.sleep(.2) + neural.getEquipment().suck(1, 64) + end + break + else + neural.walkTo({ x = dispenser.x, y = 0, z = dispenser.z }, WALK_SPEED) + end + end end local function breed(entity) - print('breeding') - entity.lastFed = os.clock() - fed[entity.id] = entity + print('breeding') + entity.lastFed = os.clock() + fed[entity.id] = entity - neural.walkTo(entity, WALK_SPEED, 1) - entity = neural.getMetaByID(entity.id) - if entity then - neural.lookAt(entity) - neural.use(1) - os.sleep(.1) - end + neural.walkTo(entity, WALK_SPEED, 1) + entity = neural.getMetaByID(entity.id) + if entity then + neural.lookAt(entity) + neural.use(1) + os.sleep(.1) + end end local function kill(entity) - print('killing') - neural.walkTo(entity, WALK_SPEED, 2.5) - entity = neural.getMetaByID(entity.id) - if entity then - neural.lookAt(entity) - neural.fireAt({ x = entity.x, y = 0, z = entity.z }) - Sound.play('entity.firework.launch') - os.sleep(.2) - end + print('killing') + neural.walkTo(entity, WALK_SPEED, 2.5) + entity = neural.getMetaByID(entity.id) + if entity then + neural.lookAt(entity) + neural.fireAt({ x = entity.x, y = 0, z = entity.z }) + Sound.play('entity.firework.launch') + os.sleep(.2) + end end local function getEntities() - return Map.filter(neural.sense(), function(entity) - if entity.name == BREEDING and entity.y > -.5 then - return true - end - end) + return Map.filter(neural.sense(), function(entity) + if entity.name == BREEDING and entity.y > -.5 then + return true + end + end) end local function getHungry(entities) - for _,v in pairs(entities) do - if not fed[v.id] or os.clock() - fed[v.id].lastFed > 60 then - return v - end - end + for _,v in pairs(entities) do + if not fed[v.id] or os.clock() - fed[v.id].lastFed > 60 then + return v + end + end end local function randomEntity(entities) - local r = math.random(1, Map.size(entities)) - local i = 1 - for _, v in pairs(entities) do - i = i + 1 - if i > r then - return v - end - end + local r = math.random(1, Map.size(entities)) + local i = 1 + for _, v in pairs(entities) do + i = i + 1 + if i > r then + return v + end + end end while true do - resupply() + resupply() - local entities = getEntities() + local entities = getEntities() - if Map.size(entities) > MAX_GROWN then - kill(randomEntity(entities)) - else - local entity = getHungry(entities) - if entity then - breed(entity) - else - os.sleep(5) - end - end + if Map.size(entities) > MAX_GROWN then + kill(randomEntity(entities)) + else + local entity = getHungry(entities) + if entity then + breed(entity) + else + os.sleep(5) + end + end end diff --git a/neural/neuralLook.lua b/neural/neuralLook.lua index db5ad8a..fb27c50 100644 --- a/neural/neuralLook.lua +++ b/neural/neuralLook.lua @@ -5,8 +5,8 @@ local Point = require('point') local os = _G.os neural.assertModules({ - 'plethora:sensor', - 'plethora:introspection', + 'plethora:sensor', + 'plethora:introspection', }) local pos = { x = 0, y = 0, z = 0 } @@ -15,58 +15,58 @@ local ownerId = neural.getMetaOwner().id local targets = { } local function findTargets() - local now = os.clock() - local moved = { } + local now = os.clock() + local moved = { } - local l = Array.filter(neural.sense(), function(a) - if math.abs(a.motionY) > 0 and ownerId ~= a.id then - local loc = table.concat({ a.x, a.y, a.z }, ':') - if not targets[a.id] then - targets[a.id] = { c = now } - elseif targets[a.id].loc ~= loc then - targets[a.id].c = now - table.insert(moved, a) - end - targets[a.id].loc = loc - a.c = targets[a.id].c - return now - a.c < 5 - end - end) + local l = Array.filter(neural.sense(), function(a) + if math.abs(a.motionY) > 0 and ownerId ~= a.id then + local loc = table.concat({ a.x, a.y, a.z }, ':') + if not targets[a.id] then + targets[a.id] = { c = now } + elseif targets[a.id].loc ~= loc then + targets[a.id].c = now + table.insert(moved, a) + end + targets[a.id].loc = loc + a.c = targets[a.id].c + return now - a.c < 5 + end + end) - if #moved > 0 then - table.sort(moved, function(e1, e2) - return Point.distance(e1, pos) < Point.distance(e2, pos) - end) - return moved[1] - end + if #moved > 0 then + table.sort(moved, function(e1, e2) + return Point.distance(e1, pos) < Point.distance(e2, pos) + end) + return moved[1] + end - if #l > 1 then - table.sort(l, function(e1, e2) - return now - e1.c < now - e2.c - end) - return targets[1] - end - return l[1] + if #l > 1 then + table.sort(l, function(e1, e2) + return now - e1.c < now - e2.c + end) + return targets[1] + end + return l[1] end local count = 50 while true do - local target = findTargets() - if target then - count = 0 - neural.lookAt(target) - os.sleep(0) - elseif count > 25 then - neural.lookAt({ - x = math.random(-10, 10), - y = math.random(-10, 10), - z = math.random(-10, 10) - }) - os.sleep(3) - else - count = count + 1 - os.sleep(.1) - end + local target = findTargets() + if target then + count = 0 + neural.lookAt(target) + os.sleep(0) + elseif count > 25 then + neural.lookAt({ + x = math.random(-10, 10), + y = math.random(-10, 10), + z = math.random(-10, 10) + }) + os.sleep(3) + else + count = count + 1 + os.sleep(.1) + end end diff --git a/neural/neuralRecorder.lua b/neural/neuralRecorder.lua index f19ee2e..10f8ea3 100644 --- a/neural/neuralRecorder.lua +++ b/neural/neuralRecorder.lua @@ -8,11 +8,11 @@ local peripheral = _G.peripheral local t = { } local ni = - peripheral.find('neuralInterface') or - error('Neural Interface not found') + peripheral.find('neuralInterface') or + error('Neural Interface not found') if not ni.getID then - error('Missing Introspection Module') + error('Missing Introspection Module') end local uid = ni.getID() @@ -23,66 +23,66 @@ 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 + 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 + 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' + if v then + Util.print(v) + table.insert(t, v) + end + until event == 'char' and ch == 'q' end parallel.waitForAny(gps, record) diff --git a/neural/neuralReplay.lua b/neural/neuralReplay.lua index c90ca85..a796df4 100644 --- a/neural/neuralReplay.lua +++ b/neural/neuralReplay.lua @@ -12,39 +12,39 @@ local t = Util.readTable(shell.resolve(fileName)) or error('Unable to read ' .. local ni = peripheral.find('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 + 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) + 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 + --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() diff --git a/neural/ores.lua b/neural/ores.lua index 69bb5c3..373dd85 100644 --- a/neural/ores.lua +++ b/neural/ores.lua @@ -12,126 +12,133 @@ local parallel = _G.parallel local peripheral = _G.peripheral local function showRequirements(missing) - print([[A neural interface is required containing: + print([[A neural interface is required containing: * Overlay glasses * Scanner * Modem ]]) - error('Missing: ' .. missing) + error('Missing: ' .. missing) end local modules = peripheral.find('neuralInterface') if not modules then - showRequirements('Neural interface') + showRequirements('Neural interface') elseif not modules.canvas then - showRequirements('Overlay glasses') + showRequirements('Overlay glasses') elseif not modules.scan then - showRequirements('Scanner module') + showRequirements('Scanner module') end +-- size of displayed block +local BLOCK_SIZE = .5 + local function getPoint() - local pt = { gps.locate() } - if pt[1] then - return { - x = pt[1], - y = pt[2], - z = pt[3], - } - end + local pt = { gps.locate() } + if pt[1] then + return { + x = pt[1], + y = pt[2], + z = pt[3], + } + end end local targets = { - ["minecraft:emerald_ore"] = { "minecraft:emerald_ore", 0 }, - ["minecraft:diamond_ore"] = { "minecraft:diamond_ore", 0 }, - ["minecraft:gold_ore"] = { "minecraft:gold_ore", 0 }, - ["minecraft:redstone_ore"] = { "minecraft:redstone_ore", 0 }, - ["minecraft:lit_redstone_ore"] = { "minecraft:redstone_ore", 0 }, - ["minecraft:iron_ore"] = { "minecraft:iron_ore", 0 }, - ["minecraft:lapis_ore"] = { "minecraft:lapis_ore", 0 }, - ["minecraft:coal_ore"] = { "minecraft:coal_ore", 0 }, - ["minecraft:quartz_ore"] = { "minecraft:quartz_ore", 0 }, - ["minecraft:glowstone"] = { "minecraft:glowstone", 0 }, + ["minecraft:emerald_ore"] = { "minecraft:emerald_ore", 0 }, + ["minecraft:diamond_ore"] = { "minecraft:diamond_ore", 0 }, + ["minecraft:gold_ore"] = { "minecraft:gold_ore", 0 }, + ["minecraft:redstone_ore"] = { "minecraft:redstone_ore", 0 }, + ["minecraft:lit_redstone_ore"] = { "minecraft:redstone_ore", 0 }, + ["minecraft:iron_ore"] = { "minecraft:iron_ore", 0 }, + ["minecraft:lapis_ore"] = { "minecraft:lapis_ore", 0 }, + ["minecraft:coal_ore"] = { "minecraft:coal_ore", 0 }, + ["minecraft:quartz_ore"] = { "minecraft:quartz_ore", 0 }, + ["minecraft:glowstone"] = { "minecraft:glowstone", 0 }, } local projecting = { } local offset = getPoint() or showRequirements('GPS') -local canvas = modules.canvas3d().create() +local canvas = modules.canvas3d().create({ + -(offset.x % 1) + .5, + -(offset.y % 1) + .5, + -(offset.z % 1) + .5 }) local function update() - while true do - -- order matters - local scanned = modules.scan() - local pos = getPoint() + while true do + -- order matters + local scanned = modules.scan() + local pos = getPoint() - if pos then - if math.abs(pos.x - offset.x) + - math.abs(pos.y - offset.y) + - math.abs(pos.z - offset.z) > 64 then - for _, b in pairs(projecting) do - b.box.remove() - end - projecting = { } - offset = pos - canvas.recenter() - end + if pos then + if math.abs(pos.x - offset.x) + + math.abs(pos.y - offset.y) + + math.abs(pos.z - offset.z) > 64 then + for _, b in pairs(projecting) do + b.box.remove() + end + projecting = { } + offset = pos + canvas.recenter({ + -(offset.x % 1) + .5, + -(offset.y % 1) + .5, + -(offset.z % 1) + .5 }) + end - local blocks = { } - for _, b in pairs(scanned) do - if targets[b.name] then - -- track block's world position - b.id = table.concat({ - math.floor(pos.x + b.x), - math.floor(pos.y + b.y), - math.floor(pos.z + b.z) }, ':') - blocks[b.id] = b - end - end + local blocks = { } + for _, b in pairs(scanned) do + if targets[b.name] then + -- track block's world position + b.id = table.concat({ + math.floor(pos.x + b.x), + math.floor(pos.y + b.y), + math.floor(pos.z + b.z) }, ':') + blocks[b.id] = b + end + end - for _, b in pairs(blocks) do - if not projecting[b.id] then - projecting[b.id] = b - local target = targets[b.name] - --[[ - b.box = canvas.addFrame({ - pos.x - offset.x + b.x + -(pos.x % 1), - pos.y - offset.y + b.y + -(pos.y % 1), - pos.z - offset.z + b.z + -(pos.z % 1), - }) - b.box.setDepthTested(false) - b.box.addItem({ .25, .25 }, target[1], target[2], 2) - --]] - b.box = canvas.addItem({ - pos.x - offset.x + b.x + -(pos.x % 1) + .5, - pos.y - offset.y + b.y + -(pos.y % 1) + .5, - pos.z - offset.z + b.z + -(pos.z % 1) + .5, - }, target[1], target[2], .5) - b.box.setDepthTested(false) - end - end + for _, b in pairs(blocks) do + if not projecting[b.id] then + projecting[b.id] = b + local target = targets[b.name] - for _, b in pairs(projecting) do - if not blocks[b.id] then - b.box.remove() - projecting[b.id] = nil - end - end - end + local x = b.x - math.floor(offset.x) + math.floor(pos.x) + local y = b.y - math.floor(offset.y) + math.floor(pos.y) + local z = b.z - math.floor(offset.z) + math.floor(pos.z) - os.sleep(.5) - end + --[[ + b.box = canvas.addFrame({ x, y, z }) + b.box.setDepthTested(false) + b.box.addItem({ .25, .25 }, target[1], target[2], 2) + --]] + + b.box = canvas.addItem({ x, y, z }, target[1], target[2], BLOCK_SIZE) + b.box.setDepthTested(false) + end + end + + for _, b in pairs(projecting) do + if not blocks[b.id] then + b.box.remove() + projecting[b.id] = nil + end + end + end + + os.sleep(.5) + end end parallel.waitForAny( - function() - print('Ore visualization started') - print('Press enter to exit') - while true do - local e, key = os.pullEventRaw('key') - if key == keys.enter or e == 'terminate' then - break - end - end - end, - update + function() + print('Ore visualization started') + print('Press enter to exit') + while true do + local e, key = os.pullEventRaw('key') + if key == keys.enter or e == 'terminate' then + break + end + end + end, + update ) -canvas.clear() \ No newline at end of file +canvas.clear() diff --git a/neural/shootingGallery.lua b/neural/shootingGallery.lua index 7377fb2..1cd1115 100644 --- a/neural/shootingGallery.lua +++ b/neural/shootingGallery.lua @@ -11,46 +11,46 @@ local uid = '' local function shootAt(pt) local yaw, pitch = Angle.towards(pt.x, pt.y, pt.z) - weapon.fire(yaw, pitch, 4) + weapon.fire(yaw, pitch, 4) end if not weapon then - weapon = device['plethora:introspection'] - if not weapon or not weapon.shoot then - error('Either a laser or a skeleton with introspection module is required') - end - uid = weapon.getID() - shootAt = function(pt) - local yaw, pitch = Angle.towards(pt.x, pt.y, pt.z) - weapon.look(yaw, pitch) - weapon.shoot(1) - end + weapon = device['plethora:introspection'] + if not weapon or not weapon.shoot then + error('Either a laser or a skeleton with introspection module is required') + end + uid = weapon.getID() + shootAt = function(pt) + local yaw, pitch = Angle.towards(pt.x, pt.y, pt.z) + weapon.look(yaw, pitch) + weapon.shoot(1) + end end local function findTargets() - local pos = { x = 0, y = 0, z = 0 } - local l = sensor.sense() - table.sort(l, function(e1, e2) - return Point.distance(e1, pos) < Point.distance(e2, pos) - end) + local pos = { x = 0, y = 0, z = 0 } + local l = sensor.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 v.y >= 0 and v.y < 1 then - table.insert(targets, v) - end - end - end - return #targets > 0 and targets + local targets = { } + for _,v in ipairs(l) do + if v.id ~= uid and Mobs.getNames()[v.name] then + if v.y >= 0 and v.y < 1 then + table.insert(targets, v) + end + end + end + return #targets > 0 and targets end while true do - local targets = findTargets() - if targets then - for _, entity in ipairs(targets) do - shootAt(entity, 1) - end - end - os.sleep(.5) + local targets = findTargets() + if targets then + for _, entity in ipairs(targets) do + shootAt(entity, 1) + end + end + os.sleep(.5) end diff --git a/oc/3dprint.lua b/oc/3dprint.lua index 6631f90..d9d2b2c 100644 --- a/oc/3dprint.lua +++ b/oc/3dprint.lua @@ -2,29 +2,29 @@ local printer = peripheral.find "printer3d" local args = {...} if #args < 1 then - error("Usage: print3d FILE [count]\n") - + error("Usage: print3d FILE [count]\n") + end if args[1] == "reset" then printer.reset() end local count = 1 if #args > 1 then - count = assert(tonumber(args[2]), tostring(args[2]) .. " is not a valid count") + count = assert(tonumber(args[2]), tostring(args[2]) .. " is not a valid count") end local file = fs.open(args[1], "r") if not file then - error("Failed opening file\n") - return 1 + error("Failed opening file\n") + return 1 end local rawdata = file.readAll() file.close() local data, reason = loadstring("return " .. rawdata) if not data then - error("Failed loading model: " .. reason .. "\n") - return 2 + error("Failed loading model: " .. reason .. "\n") + return 2 end data = data() @@ -32,47 +32,47 @@ io.write("Configuring...\n") printer.reset() if data.label then - printer.setLabel(data.label) + printer.setLabel(data.label) end if data.tooltip then - printer.setTooltip(data.tooltip) + printer.setTooltip(data.tooltip) end if data.lightLevel and printer.setLightLevel then -- as of OC 1.5.7 - printer.setLightLevel(data.lightLevel) + printer.setLightLevel(data.lightLevel) end if data.emitRedstone then - printer.setRedstoneEmitter(data.emitRedstone) + printer.setRedstoneEmitter(data.emitRedstone) end if data.buttonMode then - printer.setButtonMode(data.buttonMode) + printer.setButtonMode(data.buttonMode) end if data.collidable and printer.setCollidable then - printer.setCollidable(not not data.collidable[1], not not data.collidable[2]) + printer.setCollidable(not not data.collidable[1], not not data.collidable[2]) end for i, shape in ipairs(data.shapes or {}) do - local result, reason = printer.addShape(shape[1], shape[2], shape[3], shape[4], shape[5], shape[6], shape.texture, shape.state, shape.tint) - if not result then - io.write("Failed adding shape: " .. tostring(reason) .. "\n") - end + local result, reason = printer.addShape(shape[1], shape[2], shape[3], shape[4], shape[5], shape[6], shape.texture, shape.state, shape.tint) + if not result then + io.write("Failed adding shape: " .. tostring(reason) .. "\n") + end end io.write("Label: '" .. (printer.getLabel() or "not set") .. "'\n") io.write("Tooltip: '" .. (printer.getTooltip() or "not set") .. "'\n") if printer.getLightLevel then -- as of OC 1.5.7 - io.write("Light level: " .. printer.getLightLevel() .. "\n") + io.write("Light level: " .. printer.getLightLevel() .. "\n") end io.write("Redstone level: " .. select(2, printer.isRedstoneEmitter()) .. "\n") io.write("Button mode: " .. tostring(printer.isButtonMode()) .. "\n") if printer.isCollidable then -- as of OC 1.5.something - io.write("Collidable: " .. tostring(select(1, printer.isCollidable())) .. "/" .. tostring(select(2, printer.isCollidable())) .. "\n") + io.write("Collidable: " .. tostring(select(1, printer.isCollidable())) .. "/" .. tostring(select(2, printer.isCollidable())) .. "\n") end io.write("Shapes: " .. printer.getShapeCount() .. " inactive, " .. select(2, printer.getShapeCount()) .. " active\n") local result, reason = printer.commit(count) if result then - io.write("Job successfully committed!\n") - print('Printed!') - os.sleep(3) + io.write("Job successfully committed!\n") + print('Printed!') + os.sleep(3) else - error("Failed committing job: " .. tostring(reason) .. "\n") + error("Failed committing job: " .. tostring(reason) .. "\n") end \ No newline at end of file diff --git a/recipeBook/etc/apps.db b/recipeBook/etc/apps.db index a60b5a4..32ac5de 100644 --- a/recipeBook/etc/apps.db +++ b/recipeBook/etc/apps.db @@ -1,10 +1,10 @@ { - [ "9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e" ] = { - title = "Recipes", - category = "Apps", - run = "recipeBook", - iconExt = "\030d\031f\143\135\131\129\0307\031d\155\030d\031f\130\030f\031d\144\ + [ "9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e" ] = { + title = "Recipes", + category = "Apps", + run = "recipeBook", + iconExt = "\030d\031f\143\135\131\129\0307\031d\155\030d\031f\130\030f\031d\144\ \0300\031d\130\030d\0310\144\0317\137\0300\031d\159\143\135\131\ \130\143\143\135\131\129", - }, + }, } diff --git a/recipeBook/etc/recipeBook.db b/recipeBook/etc/recipeBook.db index ed60c0a..3e1b613 100644 --- a/recipeBook/etc/recipeBook.db +++ b/recipeBook/etc/recipeBook.db @@ -1,8 +1,8 @@ { - { - url = "https://raw.githubusercontent.com/kepler155c/opus-recipes/master/switchcraft", - version = "MC 1.12", - localName = "switchcraft", - name = "Switchcraft Server", - }, + { + url = "https://raw.githubusercontent.com/kepler155c/opus-recipes/master/switchcraft", + version = "MC 1.12", + localName = "switchcraft", + name = "Switchcraft Server", + }, } \ No newline at end of file diff --git a/shellex/apis/computer.lua b/shellex/apis/computer.lua index 5e95cf8..befb907 100644 --- a/shellex/apis/computer.lua +++ b/shellex/apis/computer.lua @@ -1,3 +1,3 @@ return { - uptime = os.clock, + uptime = os.clock, } \ No newline at end of file diff --git a/shellex/apis/filesystem.lua b/shellex/apis/filesystem.lua index 9aa02c6..7389faf 100644 --- a/shellex/apis/filesystem.lua +++ b/shellex/apis/filesystem.lua @@ -1,68 +1,68 @@ local fs = _G.fs local function get(path) - while not fs.exists(path) do - path = fs.getDir(path) - end + while not fs.exists(path) do + path = fs.getDir(path) + end - if fs.exists(path) then - local proxy = { - getLabel = function() return fs.getDrive(path) end, - isReadOnly = function() return fs.isReadOnly(path) end, - spaceTotal = function() return fs.getSize(path, true) + fs.getFreeSpace(path) end, - spaceUsed = function() return fs.getSize(path, true) end, - } - return proxy, path - end + if fs.exists(path) then + local proxy = { + getLabel = function() return fs.getDrive(path) end, + isReadOnly = function() return fs.isReadOnly(path) end, + spaceTotal = function() return fs.getSize(path, true) + fs.getFreeSpace(path) end, + spaceUsed = function() return fs.getSize(path, true) end, + } + return proxy, path + end end local function mounts() - local t = { - [ fs.getDrive('/') ] = '/' - } - for _,path in pairs(fs.list('/')) do - local label = fs.getDrive(path) - if not t[label] then - t[label] = path - end - end + local t = { + [ fs.getDrive('/') ] = '/' + } + for _,path in pairs(fs.list('/')) do + local label = fs.getDrive(path) + if not t[label] then + t[label] = path + end + end - return function() - local label, path = next(t) - if label then - t[label] = nil - return get(path) - end - end + return function() + local label, path = next(t) + if label then + t[label] = nil + return get(path) + end + end end local function list(path) - local success, set = pcall(fs.list, path) - if success then - return function() - local key, value = next(set) - set[key or false] = nil - return value - end - end - return success, set + local success, set = pcall(fs.list, path) + if success then + return function() + local key, value = next(set) + set[key or false] = nil + return value + end + end + return success, set end return { - canonical = function(...) return ... end, - concat = fs.combine, - copy = function(...) fs.copy(...) return true end, - exists = fs.exists, - get = get, - isDirectory = fs.isDir, - isLink = function() return false end, - link = function(s, t) return fs.mount(t, 'linkfs', s) end, - list = list, - makeDirectory = fs.makeDir, - mounts = mounts, - name = fs.getName, - open = function(n, m) return fs.open(n, m or 'r') end, - realPath = function(...) return ... end, - remove = function(a) fs.delete(a) return true end, - size = fs.getSize, + canonical = function(...) return ... end, + concat = fs.combine, + copy = function(...) fs.copy(...) return true end, + exists = fs.exists, + get = get, + isDirectory = fs.isDir, + isLink = function() return false end, + link = function(s, t) return fs.mount(t, 'linkfs', s) end, + list = list, + makeDirectory = fs.makeDir, + mounts = mounts, + name = fs.getName, + open = function(n, m) return fs.open(n, m or 'r') end, + realPath = function(...) return ... end, + remove = function(a) fs.delete(a) return true end, + size = fs.getSize, } diff --git a/shellex/apis/globtopattern.lua b/shellex/apis/globtopattern.lua index ea91145..44aa6e1 100644 --- a/shellex/apis/globtopattern.lua +++ b/shellex/apis/globtopattern.lua @@ -2,118 +2,118 @@ local M = {_TYPE='module', _NAME='globtopattern', _VERSION='0.2.1.20120406'} function M.globtopattern(g) - -- Some useful references: - -- - apr_fnmatch in Apache APR. For example, - -- http://apr.apache.org/docs/apr/1.3/group__apr__fnmatch.html - -- which cites POSIX 1003.2-1992, section B.6. + -- Some useful references: + -- - apr_fnmatch in Apache APR. For example, + -- http://apr.apache.org/docs/apr/1.3/group__apr__fnmatch.html + -- which cites POSIX 1003.2-1992, section B.6. - local p = "^" -- pattern being built - local i = 0 -- index in g - local c -- char at index i in g. + local p = "^" -- pattern being built + local i = 0 -- index in g + local c -- char at index i in g. - -- unescape glob char - local function unescape() - if c == '\\' then - i = i + 1; c = g:sub(i,i) - if c == '' then - p = '[^]' - return false - end - end - return true - end + -- unescape glob char + local function unescape() + if c == '\\' then + i = i + 1; c = g:sub(i,i) + if c == '' then + p = '[^]' + return false + end + end + return true + end - -- escape pattern char - local function escape(c) - return c:match("^%w$") and c or '%' .. c - end + -- escape pattern char + local function escape(c) + return c:match("^%w$") and c or '%' .. c + end - -- Convert tokens at end of charset. - local function charset_end() - while 1 do - if c == '' then - p = '[^]' - return false - elseif c == ']' then - p = p .. ']' - break - else - if not unescape() then break end - local c1 = c - i = i + 1; c = g:sub(i,i) - if c == '' then - p = '[^]' - return false - elseif c == '-' then - i = i + 1; c = g:sub(i,i) - if c == '' then - p = '[^]' - return false - elseif c == ']' then - p = p .. escape(c1) .. '%-]' - break - else - if not unescape() then break end - p = p .. escape(c1) .. '-' .. escape(c) - end - elseif c == ']' then - p = p .. escape(c1) .. ']' - break - else - p = p .. escape(c1) - i = i - 1 -- put back - end - end - i = i + 1; c = g:sub(i,i) - end - return true - end + -- Convert tokens at end of charset. + local function charset_end() + while 1 do + if c == '' then + p = '[^]' + return false + elseif c == ']' then + p = p .. ']' + break + else + if not unescape() then break end + local c1 = c + i = i + 1; c = g:sub(i,i) + if c == '' then + p = '[^]' + return false + elseif c == '-' then + i = i + 1; c = g:sub(i,i) + if c == '' then + p = '[^]' + return false + elseif c == ']' then + p = p .. escape(c1) .. '%-]' + break + else + if not unescape() then break end + p = p .. escape(c1) .. '-' .. escape(c) + end + elseif c == ']' then + p = p .. escape(c1) .. ']' + break + else + p = p .. escape(c1) + i = i - 1 -- put back + end + end + i = i + 1; c = g:sub(i,i) + end + return true + end - -- Convert tokens in charset. - local function charset() - i = i + 1; c = g:sub(i,i) - if c == '' or c == ']' then - p = '[^]' - return false - elseif c == '^' or c == '!' then - i = i + 1; c = g:sub(i,i) - if c == ']' then - -- ignored - else - p = p .. '[^' - if not charset_end() then return false end - end - else - p = p .. '[' - if not charset_end() then return false end - end - return true - end + -- Convert tokens in charset. + local function charset() + i = i + 1; c = g:sub(i,i) + if c == '' or c == ']' then + p = '[^]' + return false + elseif c == '^' or c == '!' then + i = i + 1; c = g:sub(i,i) + if c == ']' then + -- ignored + else + p = p .. '[^' + if not charset_end() then return false end + end + else + p = p .. '[' + if not charset_end() then return false end + end + return true + end - -- Convert tokens. - while 1 do - i = i + 1; c = g:sub(i,i) - if c == '' then - p = p .. '$' - break - elseif c == '?' then - p = p .. '.' - elseif c == '*' then - p = p .. '.*' - elseif c == '[' then - if not charset() then break end - elseif c == '\\' then - i = i + 1; c = g:sub(i,i) - if c == '' then - p = p .. '\\$' - break - end - p = p .. escape(c) - else - p = p .. escape(c) - end - end - return p + -- Convert tokens. + while 1 do + i = i + 1; c = g:sub(i,i) + if c == '' then + p = p .. '$' + break + elseif c == '?' then + p = p .. '.' + elseif c == '*' then + p = p .. '.*' + elseif c == '[' then + if not charset() then break end + elseif c == '\\' then + i = i + 1; c = g:sub(i,i) + if c == '' then + p = p .. '\\$' + break + end + p = p .. escape(c) + else + p = p .. escape(c) + end + end + return p end return M \ No newline at end of file diff --git a/shellex/apis/keyboard.lua b/shellex/apis/keyboard.lua index 2c98c23..3dced4d 100644 --- a/shellex/apis/keyboard.lua +++ b/shellex/apis/keyboard.lua @@ -1,3 +1,3 @@ return { - keys = _G.keys, + keys = _G.keys, } \ No newline at end of file diff --git a/shellex/apis/sh.lua b/shellex/apis/sh.lua index 6a45865..0b70c1d 100644 --- a/shellex/apis/sh.lua +++ b/shellex/apis/sh.lua @@ -1,6 +1,6 @@ local shell = _ENV.shell return { - execute = function(_, ...) return shell.run(...) end, - getLastExitCode = function() return 0 end, + execute = function(_, ...) return shell.run(...) end, + getLastExitCode = function() return 0 end, } \ No newline at end of file diff --git a/shellex/apis/shell.lua b/shellex/apis/shell.lua index ee5c6d9..23a4bb2 100644 --- a/shellex/apis/shell.lua +++ b/shellex/apis/shell.lua @@ -3,8 +3,8 @@ local Util = require('util') local shell = _ENV.shell return { - getWorkingDirectory = shell.dir, - resolve = shell.resolve, - resolveProgram = shell.resolveProgram, - parse = Util.parse, + getWorkingDirectory = shell.dir, + resolve = shell.resolve, + resolveProgram = shell.resolveProgram, + parse = Util.parse, } diff --git a/shellex/apis/text.lua b/shellex/apis/text.lua index ce3f4f3..f39c49a 100644 --- a/shellex/apis/text.lua +++ b/shellex/apis/text.lua @@ -7,138 +7,138 @@ text.internal = {} text.syntax = {"^%d?>>?&%d+","^%d?>>?",">>?","<%&%d+","<",";","&&","||?"} local function checkArg(n, have, ...) - have = type(have) - local function check(want, ...) - if not want then - return false - else - return have == want or check(...) - end - end - if not check(...) then - local msg = string.format("bad argument #%d (%s expected, got %s)", - n, table.concat({...}, " or "), have) - error(msg, 3) - end + have = type(have) + local function check(want, ...) + if not want then + return false + else + return have == want or check(...) + end + end + if not check(...) then + local msg = string.format("bad argument #%d (%s expected, got %s)", + n, table.concat({...}, " or "), have) + error(msg, 3) + end end function text.trim(value) -- from http://lua-users.org/wiki/StringTrim - local from = string.match(value, "^%s*()") - return from > #value and "" or string.match(value, ".*%S", from) + local from = string.match(value, "^%s*()") + return from > #value and "" or string.match(value, ".*%S", from) end -- used by lib/sh function text.escapeMagic(txt) - return txt:gsub('[%(%)%.%%%+%-%*%?%[%^%$]', '%%%1') + return txt:gsub('[%(%)%.%%%+%-%*%?%[%^%$]', '%%%1') end function text.removeEscapes(txt) - return txt:gsub("%%([%(%)%.%%%+%-%*%?%[%^%$])","%1") + return txt:gsub("%%([%(%)%.%%%+%-%*%?%[%^%$])","%1") end function text.internal.tokenize(value, options) - checkArg(1, value, "string") - checkArg(2, options, "table", "nil") - options = options or {} - local delimiters = options.delimiters - local custom = not not options.delimiters - delimiters = delimiters or text.syntax + checkArg(1, value, "string") + checkArg(2, options, "table", "nil") + options = options or {} + local delimiters = options.delimiters + local custom = not not options.delimiters + delimiters = delimiters or text.syntax - local words, reason = text.internal.words(value, options) + local words, reason = text.internal.words(value, options) - local splitter = text.escapeMagic(custom and table.concat(delimiters) or "<>|;&") - if type(words) ~= "table" or - #splitter == 0 or - not value:find("["..splitter.."]") then - return words, reason - end + local splitter = text.escapeMagic(custom and table.concat(delimiters) or "<>|;&") + if type(words) ~= "table" or + #splitter == 0 or + not value:find("["..splitter.."]") then + return words, reason + end - return text.internal.splitWords(words, delimiters) + return text.internal.splitWords(words, delimiters) end -- tokenize input by quotes and whitespace function text.internal.words(input, options) - checkArg(1, input, "string") - checkArg(2, options, "table", "nil") - options = options or {} - local quotes = options.quotes - local show_escapes = options.show_escapes - local qr = nil - quotes = quotes or {{"'","'",true},{'"','"'},{'`','`'}} - local function append(dst, txt, _qr) - local size = #dst - if size == 0 or dst[size].qr ~= _qr then - dst[size+1] = {txt=txt, qr=_qr} - else - dst[size].txt = dst[size].txt..txt - end - end - -- token meta is {string,quote rule} - local tokens, token = {}, {} - local escaped, start = false, -1 - for i = 1, unicode.len(input) do - local char = unicode.sub(input, i, i) - if escaped then -- escaped character - escaped = false - -- include escape char if show_escapes - -- or the followwing are all true - -- 1. qr active - -- 2. the char escaped is NOT the qr closure - -- 3. qr is not literal - if show_escapes or (qr and not qr[3] and qr[2] ~= char) then - append(token, '\\', qr) - end - append(token, char, qr) - elseif char == "\\" and (not qr or not qr[3]) then - escaped = true - elseif qr and qr[2] == char then -- end of quoted string - -- if string is empty, we can still capture a quoted empty arg - if #token == 0 or #token[#token] == 0 then - append(token, '', qr) - end - qr = nil - elseif not qr and tx.first(quotes,function(Q) - qr=Q[1]==char and Q or nil return qr end) then - start = i - elseif not qr and string.find(char, "%s") then - if #token > 0 then - table.insert(tokens, token) - end - token = {} - else -- normal char - append(token, char, qr) - end - end - if qr then - return nil, "unclosed quote at index " .. start - end + checkArg(1, input, "string") + checkArg(2, options, "table", "nil") + options = options or {} + local quotes = options.quotes + local show_escapes = options.show_escapes + local qr = nil + quotes = quotes or {{"'","'",true},{'"','"'},{'`','`'}} + local function append(dst, txt, _qr) + local size = #dst + if size == 0 or dst[size].qr ~= _qr then + dst[size+1] = {txt=txt, qr=_qr} + else + dst[size].txt = dst[size].txt..txt + end + end + -- token meta is {string,quote rule} + local tokens, token = {}, {} + local escaped, start = false, -1 + for i = 1, unicode.len(input) do + local char = unicode.sub(input, i, i) + if escaped then -- escaped character + escaped = false + -- include escape char if show_escapes + -- or the followwing are all true + -- 1. qr active + -- 2. the char escaped is NOT the qr closure + -- 3. qr is not literal + if show_escapes or (qr and not qr[3] and qr[2] ~= char) then + append(token, '\\', qr) + end + append(token, char, qr) + elseif char == "\\" and (not qr or not qr[3]) then + escaped = true + elseif qr and qr[2] == char then -- end of quoted string + -- if string is empty, we can still capture a quoted empty arg + if #token == 0 or #token[#token] == 0 then + append(token, '', qr) + end + qr = nil + elseif not qr and tx.first(quotes,function(Q) + qr=Q[1]==char and Q or nil return qr end) then + start = i + elseif not qr and string.find(char, "%s") then + if #token > 0 then + table.insert(tokens, token) + end + token = {} + else -- normal char + append(token, char, qr) + end + end + if qr then + return nil, "unclosed quote at index " .. start + end - if #token > 0 then - table.insert(tokens, token) - end + if #token > 0 then + table.insert(tokens, token) + end - return tokens + return tokens end -- separate string value into an array of words delimited by whitespace -- groups by quotes -- options is a table used for internal undocumented purposes function text.tokenize(value, options) - checkArg(1, value, "string") - checkArg(2, options, "table", "nil") - options = options or {} + checkArg(1, value, "string") + checkArg(2, options, "table", "nil") + options = options or {} - local tokens, reason = text.internal.tokenize(value, options) + local tokens, reason = text.internal.tokenize(value, options) - if type(tokens) ~= "table" then - return nil, reason - end + if type(tokens) ~= "table" then + return nil, reason + end - if options.doNotNormalize then - return tokens - end + if options.doNotNormalize then + return tokens + end - return text.internal.normalize(tokens) + return text.internal.normalize(tokens) end ------------------------------------------------------------------------------- @@ -146,41 +146,41 @@ end -- splits input into an array for sub strings delimited by delimiters -- delimiters are included in the result if not dropDelims function text.split(input, delimiters, dropDelims, di) - checkArg(1, input, "string") - checkArg(2, delimiters, "table") - checkArg(3, dropDelims, "boolean", "nil") - checkArg(4, di, "number", "nil") + checkArg(1, input, "string") + checkArg(2, delimiters, "table") + checkArg(3, dropDelims, "boolean", "nil") + checkArg(4, di, "number", "nil") - if #input == 0 then return {} end - di = di or 1 - local result = {input} - if di > #delimiters then return result end + if #input == 0 then return {} end + di = di or 1 + local result = {input} + if di > #delimiters then return result end - local function add(part, index, r, s, e) - local sub = part:sub(s,e) - if #sub == 0 then return index end - local subs = r and text.split(sub,delimiters,dropDelims,r) or {sub} - for i=1,#subs do - table.insert(result, index+i-1, subs[i]) - end - return index+#subs - end + local function add(part, index, r, s, e) + local sub = part:sub(s,e) + if #sub == 0 then return index end + local subs = r and text.split(sub,delimiters,dropDelims,r) or {sub} + for i=1,#subs do + table.insert(result, index+i-1, subs[i]) + end + return index+#subs + end - local i,d=1,delimiters[di] - while true do - local next = table.remove(result,i) - if not next then break end - local si,ei = next:find(d) - if si and ei and ei~=0 then -- delim found - i=add(next, i, di+1, 1, si-1) - i=dropDelims and i or add(next, i, false, si, ei) - i=add(next, i, di, ei+1) - else - i=add(next, i, di+1, 1, #next) - end - end + local i,d=1,delimiters[di] + while true do + local next = table.remove(result,i) + if not next then break end + local si,ei = next:find(d) + if si and ei and ei~=0 then -- delim found + i=add(next, i, di+1, 1, si-1) + i=dropDelims and i or add(next, i, false, si, ei) + i=add(next, i, di, ei+1) + else + i=add(next, i, di+1, 1, #next) + end + end - return result + return result end ----------------------------------------------------------------------------- @@ -189,217 +189,217 @@ end -- delimiters are kept as their own words -- quoted word parts are not split function text.internal.splitWords(words, delimiters) - checkArg(1,words,"table") - checkArg(2,delimiters,"table") + checkArg(1,words,"table") + checkArg(2,delimiters,"table") - local split_words = {} - local next_word - local function add_part(part) - if next_word then - split_words[#split_words+1] = {} - end - table.insert(split_words[#split_words], part) - next_word = false - end - for wi=1,#words do local word = words[wi] - next_word = true - for pi=1,#word do local part = word[pi] - local qr = part.qr - if qr then - add_part(part) - else - local part_text_splits = text.split(part.txt, delimiters) - tx.foreach(part_text_splits, function(sub_txt) - local delim = #text.split(sub_txt, delimiters, true) == 0 - next_word = next_word or delim - add_part({txt=sub_txt,qr=qr}) - next_word = delim - end) - end - end - end + local split_words = {} + local next_word + local function add_part(part) + if next_word then + split_words[#split_words+1] = {} + end + table.insert(split_words[#split_words], part) + next_word = false + end + for wi=1,#words do local word = words[wi] + next_word = true + for pi=1,#word do local part = word[pi] + local qr = part.qr + if qr then + add_part(part) + else + local part_text_splits = text.split(part.txt, delimiters) + tx.foreach(part_text_splits, function(sub_txt) + local delim = #text.split(sub_txt, delimiters, true) == 0 + next_word = next_word or delim + add_part({txt=sub_txt,qr=qr}) + next_word = delim + end) + end + end + end - return split_words + return split_words end function text.internal.normalize(words, omitQuotes) - checkArg(1, words, "table") - checkArg(2, omitQuotes, "boolean", "nil") - local norms = {} - for _,word in ipairs(words) do - local norm = {} - for _,part in ipairs(word) do - norm = tx.concat(norm, not omitQuotes and part.qr and {part.qr[1], part.txt, part.qr[2]} or {part.txt}) - end - norms[#norms+1]=table.concat(norm) - end - return norms + checkArg(1, words, "table") + checkArg(2, omitQuotes, "boolean", "nil") + local norms = {} + for _,word in ipairs(words) do + local norm = {} + for _,part in ipairs(word) do + norm = tx.concat(norm, not omitQuotes and part.qr and {part.qr[1], part.txt, part.qr[2]} or {part.txt}) + end + norms[#norms+1]=table.concat(norm) + end + return norms end function text.internal.stream_base(binary) - return - { - binary = binary, - plen = binary and string.len or unicode.len, - psub = binary and string.sub or unicode.sub, - seek = function (handle, whence, to) - if not handle.txt then - return nil, "bad file descriptor" - end - to = to or 0 - local offset = handle:indexbytes() - if whence == "cur" then - offset = offset + to - elseif whence == "set" then - offset = to - elseif whence == "end" then - offset = handle.len + to - end - offset = math.max(0, math.min(offset, handle.len)) - handle:byteindex(offset) - return offset - end, - indexbytes = function (handle) - return handle.psub(handle.txt, 1, handle.index):len() - end, - byteindex = function (handle, offset) - local sub = string.sub(handle.txt, 1, offset) - handle.index = handle.plen(sub) - end, - } + return + { + binary = binary, + plen = binary and string.len or unicode.len, + psub = binary and string.sub or unicode.sub, + seek = function (handle, whence, to) + if not handle.txt then + return nil, "bad file descriptor" + end + to = to or 0 + local offset = handle:indexbytes() + if whence == "cur" then + offset = offset + to + elseif whence == "set" then + offset = to + elseif whence == "end" then + offset = handle.len + to + end + offset = math.max(0, math.min(offset, handle.len)) + handle:byteindex(offset) + return offset + end, + indexbytes = function (handle) + return handle.psub(handle.txt, 1, handle.index):len() + end, + byteindex = function (handle, offset) + local sub = string.sub(handle.txt, 1, offset) + handle.index = handle.plen(sub) + end, + } end function text.internal.reader(txt, mode) - checkArg(1, txt, "string") - local reader = setmetatable( - { - txt = txt, - len = string.len(txt), - index = 0, - read = function(_, n) - checkArg(1, n, "number") - if not _.txt then - return nil, "bad file descriptor" - end - if _.index >= _.plen(_.txt) then - return nil - end - local next = _.psub(_.txt, _.index + 1, _.index + n) - _.index = _.index + _.plen(next) - return next - end, - close = function(_) - if not _.txt then - return nil, "bad file descriptor" - end - _.txt = nil - return true - end, - }, {__index=text.internal.stream_base(mode:match("b"))}) + checkArg(1, txt, "string") + local reader = setmetatable( + { + txt = txt, + len = string.len(txt), + index = 0, + read = function(_, n) + checkArg(1, n, "number") + if not _.txt then + return nil, "bad file descriptor" + end + if _.index >= _.plen(_.txt) then + return nil + end + local next = _.psub(_.txt, _.index + 1, _.index + n) + _.index = _.index + _.plen(next) + return next + end, + close = function(_) + if not _.txt then + return nil, "bad file descriptor" + end + _.txt = nil + return true + end, + }, {__index=text.internal.stream_base(mode:match("b"))}) - return require("buffer").new("r", reader) + return require("buffer").new("r", reader) end function text.internal.writer(ostream, mode, append_txt) - if type(ostream) == "table" then - local mt = getmetatable(ostream) or {} - checkArg(1, mt.__call, "function") - end - checkArg(1, ostream, "function", "table") - checkArg(2, append_txt, "string", "nil") - local writer = setmetatable( - { - txt = "", - index = 0, -- last location of write - len = 0, - write = function(_, ...) - if not _.txt then - return nil, "bad file descriptor" - end - local pre = _.psub(_.txt, 1, _.index) - local vs = {} - local pos = _.psub(_.txt, _.index + 1) - for _,v in ipairs({...}) do - table.insert(vs, v) - end - vs = table.concat(vs) - _.index = _.index + _.plen(vs) - _.txt = pre .. vs .. pos - _.len = string.len(_.txt) - return true - end, - close = function(_) - if not _.txt then - return nil, "bad file descriptor" - end - ostream((append_txt or "") .. _.txt) - _.txt = nil - return true - end, - }, {__index=text.internal.stream_base(mode:match("b"))}) + if type(ostream) == "table" then + local mt = getmetatable(ostream) or {} + checkArg(1, mt.__call, "function") + end + checkArg(1, ostream, "function", "table") + checkArg(2, append_txt, "string", "nil") + local writer = setmetatable( + { + txt = "", + index = 0, -- last location of write + len = 0, + write = function(_, ...) + if not _.txt then + return nil, "bad file descriptor" + end + local pre = _.psub(_.txt, 1, _.index) + local vs = {} + local pos = _.psub(_.txt, _.index + 1) + for _,v in ipairs({...}) do + table.insert(vs, v) + end + vs = table.concat(vs) + _.index = _.index + _.plen(vs) + _.txt = pre .. vs .. pos + _.len = string.len(_.txt) + return true + end, + close = function(_) + if not _.txt then + return nil, "bad file descriptor" + end + ostream((append_txt or "") .. _.txt) + _.txt = nil + return true + end, + }, {__index=text.internal.stream_base(mode:match("b"))}) - return require("buffer").new("w", writer) + return require("buffer").new("w", writer) end function text.detab(value, tabWidth) - checkArg(1, value, "string") - checkArg(2, tabWidth, "number", "nil") - tabWidth = tabWidth or 8 - local function rep(match) - local spaces = tabWidth - match:len() % tabWidth - return match .. string.rep(" ", spaces) - end - local result = value:gsub("([^\n]-)\t", rep) -- truncate results - return result + checkArg(1, value, "string") + checkArg(2, tabWidth, "number", "nil") + tabWidth = tabWidth or 8 + local function rep(match) + local spaces = tabWidth - match:len() % tabWidth + return match .. string.rep(" ", spaces) + end + local result = value:gsub("([^\n]-)\t", rep) -- truncate results + return result end function text.padLeft(value, length) - checkArg(1, value, "string", "nil") - checkArg(2, length, "number") - if not value or unicode.wlen(value) == 0 then - return string.rep(" ", length) - else - return string.rep(" ", length - unicode.wlen(value)) .. value - end + checkArg(1, value, "string", "nil") + checkArg(2, length, "number") + if not value or unicode.wlen(value) == 0 then + return string.rep(" ", length) + else + return string.rep(" ", length - unicode.wlen(value)) .. value + end end function text.padRight(value, length) - checkArg(1, value, "string", "nil") - checkArg(2, length, "number") - if not value or unicode.wlen(value) == 0 then - return string.rep(" ", length) - else - return value .. string.rep(" ", length - unicode.wlen(value)) - end + checkArg(1, value, "string", "nil") + checkArg(2, length, "number") + if not value or unicode.wlen(value) == 0 then + return string.rep(" ", length) + else + return value .. string.rep(" ", length - unicode.wlen(value)) + end end function text.wrap(value, width, maxWidth) - checkArg(1, value, "string") - checkArg(2, width, "number") - checkArg(3, maxWidth, "number") - local line, nl = value:match("([^\r\n]*)(\r?\n?)") -- read until newline - if unicode.wlen(line) > width then -- do we even need to wrap? - local partial = unicode.wtrunc(line, width) - local wrapped = partial:match("(.*[^a-zA-Z0-9._()'`=])") - if wrapped or unicode.wlen(line) > maxWidth then - partial = wrapped or partial - return partial, unicode.sub(value, unicode.len(partial) + 1), true - else - return "", value, true -- write in new line. - end - end - local start = unicode.len(line) + unicode.len(nl) + 1 - return line, start <= unicode.len(value) and unicode.sub(value, start) or nil, unicode.len(nl) > 0 + checkArg(1, value, "string") + checkArg(2, width, "number") + checkArg(3, maxWidth, "number") + local line, nl = value:match("([^\r\n]*)(\r?\n?)") -- read until newline + if unicode.wlen(line) > width then -- do we even need to wrap? + local partial = unicode.wtrunc(line, width) + local wrapped = partial:match("(.*[^a-zA-Z0-9._()'`=])") + if wrapped or unicode.wlen(line) > maxWidth then + partial = wrapped or partial + return partial, unicode.sub(value, unicode.len(partial) + 1), true + else + return "", value, true -- write in new line. + end + end + local start = unicode.len(line) + unicode.len(nl) + 1 + return line, start <= unicode.len(value) and unicode.sub(value, start) or nil, unicode.len(nl) > 0 end function text.wrappedLines(value, width, maxWidth) - local line - return function() - if value then - line, value = text.wrap(value, width, maxWidth) - return line - end - end + local line + return function() + if value then + line, value = text.wrap(value, width, maxWidth) + return line + end + end end return text \ No newline at end of file diff --git a/shellex/apis/transfer.lua b/shellex/apis/transfer.lua index 8a89d30..aeb781a 100644 --- a/shellex/apis/transfer.lua +++ b/shellex/apis/transfer.lua @@ -5,269 +5,269 @@ local text = require("shellex.text") local lib = {} local function perr(ops, format, ...) - if format then - io.stderr:write(ops.cmd .. string.format(": " .. format, ...) .. "\n") - ops.exit_code = 1 - return 1 - end + if format then + io.stderr:write(ops.cmd .. string.format(": " .. format, ...) .. "\n") + ops.exit_code = 1 + return 1 + end end local function contents_check(arg, options, bMustExist) - if arg == "" then - return perr(options, "cannot create regular file '' No such file or directory") - end - local path = shell.resolve(arg) - local content_pattern = "^(%.*)(.?)" - local contents_of, of_dir = arg:reverse():match(content_pattern) - of_dir = of_dir:match("^/?$") - local dots = contents_of and contents_of:len() or 0 - contents_of = of_dir and ({true,true})[dots] + if arg == "" then + return perr(options, "cannot create regular file '' No such file or directory") + end + local path = shell.resolve(arg) + local content_pattern = "^(%.*)(.?)" + local contents_of, of_dir = arg:reverse():match(content_pattern) + of_dir = of_dir:match("^/?$") + local dots = contents_of and contents_of:len() or 0 + contents_of = of_dir and ({true,true})[dots] - if (not bMustExist or fs.exists(path)) and of_dir and not fs.isDirectory(path) then - perr(options, "'%s' is not a directory", arg) - os.exit(1) - end + if (not bMustExist or fs.exists(path)) and of_dir and not fs.isDirectory(path) then + perr(options, "'%s' is not a directory", arg) + os.exit(1) + end - return contents_of, path + return contents_of, path end local function areEqual(path1, path2) - local f1, f2 = fs.open(path1, "rb") - local result = true - if f1 then - f2 = fs.open(path2, "rb") - if f2 then - local chunkSize = 4 * 1024 - repeat - local s1, s2 = f1:read(chunkSize), f2:read(chunkSize) - if s1 ~= s2 then - result = false - break - end - until not s1 or not s2 - f2:close() - end - f1:close() - end - assert(f1 and f2, "could not open files for reading: " .. path1 .. ", " .. path2) - return result + local f1, f2 = fs.open(path1, "rb") + local result = true + if f1 then + f2 = fs.open(path2, "rb") + if f2 then + local chunkSize = 4 * 1024 + repeat + local s1, s2 = f1:read(chunkSize), f2:read(chunkSize) + if s1 ~= s2 then + result = false + break + end + until not s1 or not s2 + f2:close() + end + f1:close() + end + assert(f1 and f2, "could not open files for reading: " .. path1 .. ", " .. path2) + return result end local function status(verbose, from, to) - if verbose then - to = to and (" -> " .. to) or "" - io.write(from .. to .. "\n") - end - os.sleep(0) -- allow interrupting + if verbose then + to = to and (" -> " .. to) or "" + io.write(from .. to .. "\n") + end + os.sleep(0) -- allow interrupting end local function prompt(message) - io.write(message .. " [Y/n] ") - local result = io.read() - if not result then -- closed pipe - os.exit(1) - end - return result and (result == "" or result:sub(1, 1):lower() == "y") + io.write(message .. " [Y/n] ") + local result = io.read() + if not result then -- closed pipe + os.exit(1) + end + return result and (result == "" or result:sub(1, 1):lower() == "y") end local function stat(path, ops, P) - local real, reason = fs.realPath(path) - if not real and not P then - perr(ops, "cannot read '%s': '%s'", path, reason) - return false - end - local isLink, linkTarget = fs.isLink(path) - return true, - real, - reason, - isLink, - linkTarget, - fs.exists(path), - fs.get(path), - real and fs.isDirectory(real) + local real, reason = fs.realPath(path) + if not real and not P then + perr(ops, "cannot read '%s': '%s'", path, reason) + return false + end + local isLink, linkTarget = fs.isLink(path) + return true, + real, + reason, + isLink, + linkTarget, + fs.exists(path), + fs.get(path), + real and fs.isDirectory(real) end function lib.recurse(fromPath, toPath, options, origin, top) - fromPath = fromPath:gsub("/+", "/") - toPath = toPath:gsub("/+", "/") - local fromPathFull = shell.resolve(fromPath) - local toPathFull = shell.resolve(toPath) - local mv = options.cmd == "mv" - local verbose = options.v and (not mv or top) - if select(2, fromPathFull:find(options.skip)) == #fromPathFull then - status(verbose, string.format("skipping %s", fromPath)) - return true - end - local function release(result, reason) - if result and mv and top then - local rm_result = not fs.get(fromPathFull).isReadOnly() and fs.remove(fromPathFull) - if not rm_result then - perr(options, "cannot remove '%s': filesystem is readonly", fromPath) - result = false - end - end - return result, reason - end + fromPath = fromPath:gsub("/+", "/") + toPath = toPath:gsub("/+", "/") + local fromPathFull = shell.resolve(fromPath) + local toPathFull = shell.resolve(toPath) + local mv = options.cmd == "mv" + local verbose = options.v and (not mv or top) + if select(2, fromPathFull:find(options.skip)) == #fromPathFull then + status(verbose, string.format("skipping %s", fromPath)) + return true + end + local function release(result, reason) + if result and mv and top then + local rm_result = not fs.get(fromPathFull).isReadOnly() and fs.remove(fromPathFull) + if not rm_result then + perr(options, "cannot remove '%s': filesystem is readonly", fromPath) + result = false + end + end + return result, reason + end - local - ok, - fromReal, - _, --fromError, - fromIsLink, - fromLinkTarget, - fromExists, - fromFs, - fromIsDir = stat(fromPathFull, options, options.P) - if not ok then return nil end - local - ok, - toReal, - _,--toError, - toIsLink, - _,--toLinkTarget, - toExists, - toFs, - toIsDir = stat(toPathFull, options) - if not ok then os.exit(1) end - if toFs.isReadOnly() then - perr(options, "cannot create target '%s': filesystem is readonly", toPath) - return - end + local + ok, + fromReal, + _, --fromError, + fromIsLink, + fromLinkTarget, + fromExists, + fromFs, + fromIsDir = stat(fromPathFull, options, options.P) + if not ok then return nil end + local + ok, + toReal, + _,--toError, + toIsLink, + _,--toLinkTarget, + toExists, + toFs, + toIsDir = stat(toPathFull, options) + if not ok then os.exit(1) end + if toFs.isReadOnly() then + perr(options, "cannot create target '%s': filesystem is readonly", toPath) + return + end - local same_path = fromReal == toReal + local same_path = fromReal == toReal - local same_fs = fromFs == toFs - local is_mount = origin[fromReal] + local same_fs = fromFs == toFs + local is_mount = origin[fromReal] - if mv and is_mount then - return false, string.format("cannot move '%s', it is a mount point", fromPath) - end + if mv and is_mount then + return false, string.format("cannot move '%s', it is a mount point", fromPath) + end - if fromIsLink and options.P and not (toExists and same_path and not toIsLink) then - if toExists and options.n then - return true - end - fs.remove(toPathFull) - if toExists then - status(verbose, string.format("removed '%s'", toPath)) - end - status(verbose, fromPath, toPath) - return release(fs.link(fromLinkTarget, toPathFull)) - elseif fromIsDir then - if not options.r then - status(true, string.format("omitting directory '%s'", fromPath)) - options.exit_code = 1 - return true - end - if toExists and not toIsDir then - -- my real cp always does this, even with -f, -n or -i. - return nil, "cannot overwrite non-directory '" .. toPath .. "' with directory '" .. fromPath .. "'" - end - if options.x and not top and is_mount then - return true - end - if same_fs then - if (toReal.."/"):find(fromReal.."/",1,true) then - return nil, "cannot write a directory, '" .. fromPath .. "', into itself, '" .. toPath .. "'" - end - end - if mv then - if fs.list(toReal)() then -- to is NOT empty - return nil, "cannot move '" .. fromPath .. "' to '" .. toPath .. "': Directory not empty" - end - status(verbose, fromPath, toPath) - end - if not toExists then - status(verbose, fromPath, toPath) - fs.makeDirectory(toPathFull) - end - for file in fs.list(fromPathFull) do - local result, reason = lib.recurse(fromPath .."/".. file, toPath.."/"..file, options, origin, false) - -- false, no longer top - if not result then - return false, reason - end - end - return release(true) - elseif fromExists then - if toExists then - if same_path then - return nil, "'" .. fromPath .. "' and '" .. toPath .. "' are the same file" - end - if options.n then - return true - end - if options.u and not toIsDir and areEqual(fromReal, toReal) then - return true - end - if options.i then - if not prompt("overwrite '" .. toPath .. "'?") then - return true - end - end - if toIsDir then - return nil, "cannot overwrite directory '" .. toPath .. "' with non-directory" - end - fs.remove(toReal) - end - status(verbose, fromPath, toPath) - return release(fs.copy(fromPathFull, toPathFull)) - else - return nil, "'" .. fromPath .. "': No such file or directory" - end + if fromIsLink and options.P and not (toExists and same_path and not toIsLink) then + if toExists and options.n then + return true + end + fs.remove(toPathFull) + if toExists then + status(verbose, string.format("removed '%s'", toPath)) + end + status(verbose, fromPath, toPath) + return release(fs.link(fromLinkTarget, toPathFull)) + elseif fromIsDir then + if not options.r then + status(true, string.format("omitting directory '%s'", fromPath)) + options.exit_code = 1 + return true + end + if toExists and not toIsDir then + -- my real cp always does this, even with -f, -n or -i. + return nil, "cannot overwrite non-directory '" .. toPath .. "' with directory '" .. fromPath .. "'" + end + if options.x and not top and is_mount then + return true + end + if same_fs then + if (toReal.."/"):find(fromReal.."/",1,true) then + return nil, "cannot write a directory, '" .. fromPath .. "', into itself, '" .. toPath .. "'" + end + end + if mv then + if fs.list(toReal)() then -- to is NOT empty + return nil, "cannot move '" .. fromPath .. "' to '" .. toPath .. "': Directory not empty" + end + status(verbose, fromPath, toPath) + end + if not toExists then + status(verbose, fromPath, toPath) + fs.makeDirectory(toPathFull) + end + for file in fs.list(fromPathFull) do + local result, reason = lib.recurse(fromPath .."/".. file, toPath.."/"..file, options, origin, false) + -- false, no longer top + if not result then + return false, reason + end + end + return release(true) + elseif fromExists then + if toExists then + if same_path then + return nil, "'" .. fromPath .. "' and '" .. toPath .. "' are the same file" + end + if options.n then + return true + end + if options.u and not toIsDir and areEqual(fromReal, toReal) then + return true + end + if options.i then + if not prompt("overwrite '" .. toPath .. "'?") then + return true + end + end + if toIsDir then + return nil, "cannot overwrite directory '" .. toPath .. "' with non-directory" + end + fs.remove(toReal) + end + status(verbose, fromPath, toPath) + return release(fs.copy(fromPathFull, toPathFull)) + else + return nil, "'" .. fromPath .. "': No such file or directory" + end end function lib.batch(args, options) - options.exit_code = 0 + options.exit_code = 0 - -- standardized options - options.i = options.i and not options.f - options.P = options.P or options.r - options.skip = text.escapeMagic(options.skip or "") + -- standardized options + options.i = options.i and not options.f + options.P = options.P or options.r + options.skip = text.escapeMagic(options.skip or "") - local origin = {} - for dev,path in fs.mounts() do - origin[path] = dev - end + local origin = {} + for dev,path in fs.mounts() do + origin[path] = dev + end - local toArg = table.remove(args) - local _, ok = contents_check(toArg, options) - if not ok then - return 1 - end - local originalToIsDir = fs.isDirectory(ok) + local toArg = table.remove(args) + local _, ok = contents_check(toArg, options) + if not ok then + return 1 + end + local originalToIsDir = fs.isDirectory(ok) - for _, arg in ipairs(args) do - local files = glob.matches(shell.getWorkingDirectory(), arg) - for _, fromArg in pairs(files) do - -- a "contents of" copy is where src path ends in . or .. - -- a source path ending with . is not sufficient - could be the source filename - local contents_of - contents_of, ok = contents_check(fromArg, options, true) - if ok then - -- we do not append fromPath name to toPath in case of contents_of copy - local toPath = toArg - if contents_of and options.cmd == "mv" then - perr(options, "invalid move path '%s'", fromArg) - else - if not contents_of and originalToIsDir then - local fromName = fs.name(fromArg) - if fromName then - toPath = toPath .. "/" .. fromName - end - end + for _, arg in ipairs(args) do + local files = glob.matches(shell.getWorkingDirectory(), arg) + for _, fromArg in pairs(files) do + -- a "contents of" copy is where src path ends in . or .. + -- a source path ending with . is not sufficient - could be the source filename + local contents_of + contents_of, ok = contents_check(fromArg, options, true) + if ok then + -- we do not append fromPath name to toPath in case of contents_of copy + local toPath = toArg + if contents_of and options.cmd == "mv" then + perr(options, "invalid move path '%s'", fromArg) + else + if not contents_of and originalToIsDir then + local fromName = fs.name(fromArg) + if fromName then + toPath = toPath .. "/" .. fromName + end + end - local result, reason = lib.recurse(fromArg, toPath, options, origin, true) + local result, reason = lib.recurse(fromArg, toPath, options, origin, true) - if not result then - perr(options, reason) - end - end - end - end - end + if not result then + perr(options, reason) + end + end + end + end + end - return options.exit_code + return options.exit_code end return lib \ No newline at end of file diff --git a/shellex/apis/transforms.lua b/shellex/apis/transforms.lua index f9dc1cc..b4f7124 100644 --- a/shellex/apis/transforms.lua +++ b/shellex/apis/transforms.lua @@ -2,198 +2,198 @@ local lib={} lib.internal={} local function checkArg(n, have, ...) - have = type(have) - local function check(want, ...) - if not want then - return false - else - return have == want or check(...) - end - end - if not check(...) then - local msg = string.format("bad argument #%d (%s expected, got %s)", - n, table.concat({...}, " or "), have) - error(msg, 3) - end + have = type(have) + local function check(want, ...) + if not want then + return false + else + return have == want or check(...) + end + end + if not check(...) then + local msg = string.format("bad argument #%d (%s expected, got %s)", + n, table.concat({...}, " or "), have) + error(msg, 3) + end end function lib.internal.range_adjust(f,l,s) - checkArg(1,f,'number','nil') - checkArg(2,l,'number','nil') - checkArg(3,s,'number') - if f==nil then f=1 elseif f<0 then f=s+f+1 end - if l==nil then l=s elseif l<0 then l=s+l+1 end - return f,l + checkArg(1,f,'number','nil') + checkArg(2,l,'number','nil') + checkArg(3,s,'number') + if f==nil then f=1 elseif f<0 then f=s+f+1 end + if l==nil then l=s elseif l<0 then l=s+l+1 end + return f,l end function lib.internal.table_view(tbl,f,l) - return setmetatable({}, - { - __index = function(_, key) - return (type(key) ~= 'number' or (key >= f and key <= l)) and tbl[key] or nil - end, - __len = function(_) - return l - end, - }) + return setmetatable({}, + { + __index = function(_, key) + return (type(key) ~= 'number' or (key >= f and key <= l)) and tbl[key] or nil + end, + __len = function(_) + return l + end, + }) end local adjust=lib.internal.range_adjust local view=lib.internal.table_view -- first(p1,p2) searches for the first range in p1 that satisfies p2 function lib.first(tbl,pred,f,l) - checkArg(1,tbl,'table') - checkArg(2,pred,'function','table') - if type(pred)=='table'then - local set;set,pred=pred,function(_,fi,tbl) - for vi=1,#set do - local v=set[vi] - if lib.begins(tbl,v,fi) then return true,#v end - end - end - end - local s=#tbl - f,l=adjust(f,l,s) - tbl=view(tbl,f,l) - for i=f,l do - local si,ei=pred(tbl[i],i,tbl) - if si then - return i,i+(ei or 1)-1 - end - end + checkArg(1,tbl,'table') + checkArg(2,pred,'function','table') + if type(pred)=='table'then + local set;set,pred=pred,function(_,fi,tbl) + for vi=1,#set do + local v=set[vi] + if lib.begins(tbl,v,fi) then return true,#v end + end + end + end + local s=#tbl + f,l=adjust(f,l,s) + tbl=view(tbl,f,l) + for i=f,l do + local si,ei=pred(tbl[i],i,tbl) + if si then + return i,i+(ei or 1)-1 + end + end end -- returns true if p1 at first p3 equals element for element p2 function lib.begins(tbl,v,f,l) - checkArg(1,tbl,'table') - checkArg(2,v,'table') - local vs=#v - f,l=adjust(f,l,#tbl) - if vs>(l-f+1)then return end - for i=1,vs do - if tbl[f+i-1]~=v[i] then return end - end - return true + checkArg(1,tbl,'table') + checkArg(2,v,'table') + local vs=#v + f,l=adjust(f,l,#tbl) + if vs>(l-f+1)then return end + for i=1,vs do + if tbl[f+i-1]~=v[i] then return end + end + return true end function lib.concat(...) - local r,rn,k={},0 - for _,tbl in ipairs({...})do - if type(tbl)~='table'then - return nil,'parameter '..tostring(_)..' to concat is not a table' - end - local n=tbl.n or #tbl - k=k or tbl.n - for i=1,n do - rn=rn+1;r[rn]=tbl[i] - end - end - r.n=k and rn or nil - return r + local r,rn,k={},0 + for _,tbl in ipairs({...})do + if type(tbl)~='table'then + return nil,'parameter '..tostring(_)..' to concat is not a table' + end + local n=tbl.n or #tbl + k=k or tbl.n + for i=1,n do + rn=rn+1;r[rn]=tbl[i] + end + end + r.n=k and rn or nil + return r end -- works like string.sub but on elements of an indexed table function lib.sub(tbl,f,l) - checkArg(1,tbl,'table') - local r,s={},#tbl - f,l=adjust(f,l,s) - l=math.min(l,s) - for i=math.max(f,1),l do - r[#r+1]=tbl[i] - end - return r + checkArg(1,tbl,'table') + local r,s={},#tbl + f,l=adjust(f,l,s) + l=math.min(l,s) + for i=math.max(f,1),l do + r[#r+1]=tbl[i] + end + return r end -- Returns a list of subsets of tbl where partitioner acts as a delimiter. function lib.partition(tbl,partitioner,dropEnds,f,l) - checkArg(1,tbl,'table') - checkArg(2,partitioner,'function','table') - checkArg(3,dropEnds,'boolean','nil') - if type(partitioner)=='table'then - return lib.partition(tbl,function(_,i,tbl) - return lib.first(tbl,partitioner,i) - end,dropEnds,f,l) - end - local s=#tbl - f,l=adjust(f,l,s) - local cut=view(tbl,f,l) - local result={} - local need=true - local exp=function()if need then result[#result+1]={}need=false end end - local i=f - while i<=l do - local e=cut[i] - local ds,de=partitioner(e,i,cut) - -- true==partition here - if ds==true then ds,de=i,i - elseif ds==false then ds,de=nil,nil end - if ds~=nil then - ds,de=adjust(ds,de,l) - ds=ds>=i and ds--no more - end - if not ds then -- false or nil - exp() - table.insert(result[#result],e) - else - local sub=lib.sub(cut,i,not dropEnds and de or (ds-1)) - if #sub>0 then - exp() - result[#result+math.min(#result[#result],1)]=sub - end - -- ensure i moves forward - local ensured=math.max(math.max(de or ds,ds),i) - if de and ds and de=i and ds--no more + end + if not ds then -- false or nil + exp() + table.insert(result[#result],e) + else + local sub=lib.sub(cut,i,not dropEnds and de or (ds-1)) + if #sub>0 then + exp() + result[#result+math.min(#result[#result],1)]=sub + end + -- ensure i moves forward + local ensured=math.max(math.max(de or ds,ds),i) + if de and ds and de + io.write([[Usage: cp [OPTIONS] -i: prompt before overwrite (overrides -n option). -n: do not overwrite an existing file. -r: copy directories recursively. -u: copy only when the SOURCE file differs from the destination - file or when the destination file is missing. + file or when the destination file is missing. -P: preserve attributes, e.g. symbolic links. -v: verbose output. -x: stay on original source file system. --skip=P: skip files matching lua regex P ]]) - return not not options.h + return not not options.h end -- clean options for copy (as opposed to move) options = { - cmd = "cp", - i = options.i, - f = options.f, - n = options.n, - r = options.r, - u = options.u, - P = options.P, - v = options.v, - x = options.x, - skip = options.skip, + cmd = "cp", + i = options.i, + f = options.f, + n = options.n, + r = options.r, + u = options.u, + P = options.P, + v = options.v, + x = options.x, + skip = options.skip, } return transfer.batch(args, options) diff --git a/shellex/df.lua b/shellex/df.lua index c7b3580..a6c4b1b 100644 --- a/shellex/df.lua +++ b/shellex/df.lua @@ -5,72 +5,72 @@ local text = require("shellex.text") local args, options = shell.parse(...) local function formatSize(size) - if not options.h then - return tostring(size) - elseif type(size) == "string" then - return size - end - local sizes = {"", "K", "M", "G"} - local unit = 1 - local power = options.si and 1000 or 1024 - while size > power and unit < #sizes do - unit = unit + 1 - size = size / power - end - return math.floor(size * 10) / 10 .. sizes[unit] + if not options.h then + return tostring(size) + elseif type(size) == "string" then + return size + end + local sizes = {"", "K", "M", "G"} + local unit = 1 + local power = options.si and 1000 or 1024 + while size > power and unit < #sizes do + unit = unit + 1 + size = size / power + end + return math.floor(size * 10) / 10 .. sizes[unit] end local mounts = {} if #args == 0 then - for proxy, path in fs.mounts() do - if not mounts[proxy] or mounts[proxy]:len() > path:len() then - mounts[proxy] = path - end - end + for proxy, path in fs.mounts() do + if not mounts[proxy] or mounts[proxy]:len() > path:len() then + mounts[proxy] = path + end + end else - for i = 1, #args do - local proxy, path = fs.get(shell.resolve(args[i])) - if not proxy then - io.stderr:write(args[i], ": no such file or directory\n") - else - mounts[proxy] = path - end - end + for i = 1, #args do + local proxy, path = fs.get(shell.resolve(args[i])) + if not proxy then + io.stderr:write(args[i], ": no such file or directory\n") + else + mounts[proxy] = path + end + end end local result = {{"Filesystem", "Used", "Available", "Use%", "Mounted on"}} for proxy, path in pairs(mounts) do - local label = proxy.getLabel() or proxy.address - local used, total = proxy.spaceUsed(), proxy.spaceTotal() - local available, percent - if total == math.huge then - used = used or "N/A" - available = "unlimited" - percent = "0%" - else - available = total - used - percent = used / total - if percent ~= percent then -- NaN - available = "N/A" - percent = "N/A" - else - percent = math.ceil(percent * 100) .. "%" - end - end - table.insert(result, {label, formatSize(used), formatSize(available), tostring(percent), path}) + local label = proxy.getLabel() or proxy.address + local used, total = proxy.spaceUsed(), proxy.spaceTotal() + local available, percent + if total == math.huge then + used = used or "N/A" + available = "unlimited" + percent = "0%" + else + available = total - used + percent = used / total + if percent ~= percent then -- NaN + available = "N/A" + percent = "N/A" + else + percent = math.ceil(percent * 100) .. "%" + end + end + table.insert(result, {label, formatSize(used), formatSize(available), tostring(percent), path}) end local m = {} for _, row in ipairs(result) do - for col, value in ipairs(row) do - m[col] = math.max(m[col] or 1, value:len()) - end + for col, value in ipairs(row) do + m[col] = math.max(m[col] or 1, value:len()) + end end for _, row in ipairs(result) do - for col, value in ipairs(row) do - local padding = col == #row and 0 or 2 - io.write(text.padRight(value, m[col] + padding)) - end - print() + for col, value in ipairs(row) do + local padding = col == #row and 0 or 2 + io.write(text.padRight(value, m[col] + padding)) + end + print() end diff --git a/shellex/dmesg.lua b/shellex/dmesg.lua index 96203f2..48ebffb 100644 --- a/shellex/dmesg.lua +++ b/shellex/dmesg.lua @@ -5,28 +5,28 @@ local gpu = tty.gpu() io.write("Press 'Ctrl-C' to exit\n") local events = { } for _, e in pairs(args) do - events[e] = true + events[e] = true end --pcall(function() - repeat - local evt = table.pack(os.pullEventRaw()) - if #args == 0 or events[evt[1]] then - gpu.setForeground(0xCC2200) - io.write("[" .. math.floor(os.clock("utc")) .. "] ") - gpu.setForeground(0x44CC00) - io.write(tostring(evt[1]) .. string.rep(" ", math.max(12 - #tostring(evt[1]), 0) + 1)) - gpu.setForeground(0xB0B00F) - io.write(tostring(evt[2]) .. string.rep(" ", 37 - #tostring(evt[2]))) - gpu.setForeground(0xFFFFFF) - if evt.n > 2 then - for i = 3, evt.n do - io.write(" " .. tostring(evt[i])) - end - end + repeat + local evt = table.pack(os.pullEventRaw()) + if #args == 0 or events[evt[1]] then + gpu.setForeground(0xCC2200) + io.write("[" .. math.floor(os.clock("utc")) .. "] ") + gpu.setForeground(0x44CC00) + io.write(tostring(evt[1]) .. string.rep(" ", math.max(12 - #tostring(evt[1]), 0) + 1)) + gpu.setForeground(0xB0B00F) + io.write(tostring(evt[2]) .. string.rep(" ", 37 - #tostring(evt[2]))) + gpu.setForeground(0xFFFFFF) + if evt.n > 2 then + for i = 3, evt.n do + io.write(" " .. tostring(evt[i])) + end + end - io.write("\n") - end - until evt[1] == "terminate" + io.write("\n") + end + until evt[1] == "terminate" --end) gpu.setForeground(0xFFFFFF) diff --git a/shellex/du.lua b/shellex/du.lua index 4bdbea8..ca0ca8a 100644 --- a/shellex/du.lua +++ b/shellex/du.lua @@ -4,7 +4,7 @@ local shell = require("shellex.shell") local args, options = shell.parse(...) if #args == 0 then - args[1] = '.' + args[1] = '.' end local TRY=[[ @@ -18,116 +18,116 @@ local HELP=[[ Usage: du [OPTION]... [FILE]... Summarize disk usage of each FILE, recursively for directories. - -h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G) - -s, --summarize display only a total for each argument - --help display this help and exit - --version output version information and exit]] + -h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G) + -s, --summarize display only a total for each argument + --help display this help and exit + --version output version information and exit]] if options.help then - print(HELP) - return true + print(HELP) + return true end if options.version then - print(VERSION) - return true + print(VERSION) + return true end local function addTrailingSlash(path) - if path:sub(-1) ~= '/' then - return path .. '/' - else - return path - end + if path:sub(-1) ~= '/' then + return path .. '/' + else + return path + end end local function opCheck(shortName, longName) - local enabled = options[shortName] or options[longName] - options[shortName] = nil - options[longName] = nil - return enabled + local enabled = options[shortName] or options[longName] + options[shortName] = nil + options[longName] = nil + return enabled end local bHuman = opCheck('h', 'human-readable') local bSummary = opCheck('s', 'summarize') if next(options) then - for op in pairs(options) do - io.stderr:write(string.format("du: invalid option -- '%s'\n", op)) - end - io.stderr:write(TRY..'\n') - return 1 + for op in pairs(options) do + io.stderr:write(string.format("du: invalid option -- '%s'\n", op)) + end + io.stderr:write(TRY..'\n') + return 1 end local function formatSize(size) - if not bHuman then - return tostring(size) - end - local sizes = {"", "K", "M", "G"} - local unit = 1 - local power = options.si and 1000 or 1024 - while size > power and unit < #sizes do - unit = unit + 1 - size = size / power - end + if not bHuman then + return tostring(size) + end + local sizes = {"", "K", "M", "G"} + local unit = 1 + local power = options.si and 1000 or 1024 + while size > power and unit < #sizes do + unit = unit + 1 + size = size / power + end - return math.floor(size * 10) / 10 .. sizes[unit] + return math.floor(size * 10) / 10 .. sizes[unit] end local function printSize(size, rpath) - local displaySize = formatSize(size) - io.write(string.format("%s%s\n", string.format("%-12s", displaySize), rpath)) + local displaySize = formatSize(size) + io.write(string.format("%s%s\n", string.format("%-12s", displaySize), rpath)) end local function visitor(rpath) - local subtotal = 0 - local dirs = 0 - local spath = shell.resolve(rpath) + local subtotal = 0 + local dirs = 0 + local spath = shell.resolve(rpath) - if fs.isDirectory(spath) then - local list_result = fs.list(spath) - if list_result then - for list_item in list_result do - local vtotal, vdirs = visitor(addTrailingSlash(rpath) .. list_item) - subtotal = subtotal + vtotal - dirs = dirs + vdirs - end - end + if fs.isDirectory(spath) then + local list_result = fs.list(spath) + if list_result then + for list_item in list_result do + local vtotal, vdirs = visitor(addTrailingSlash(rpath) .. list_item) + subtotal = subtotal + vtotal + dirs = dirs + vdirs + end + end - if dirs == 0 then -- no child dirs - if not bSummary then - printSize(subtotal, rpath) - end - end + if dirs == 0 then -- no child dirs + if not bSummary then + printSize(subtotal, rpath) + end + end - elseif not fs.isLink(spath) then - subtotal = fs.size(spath) - end + elseif not fs.isLink(spath) then + subtotal = fs.size(spath) + end - return subtotal, dirs + return subtotal, dirs end for _,arg in ipairs(args) do - local files = glob.matches(shell.getWorkingDirectory(), arg) - for _, v in pairs(files) do - local path = shell.resolve(v) - if not fs.exists(path) then - io.stderr:write(string.format("du: cannot access '%s': no such file or directory\n", v)) - return 1 - else - if fs.isDirectory(path) then - local total = visitor(v) + local files = glob.matches(shell.getWorkingDirectory(), arg) + for _, v in pairs(files) do + local path = shell.resolve(v) + if not fs.exists(path) then + io.stderr:write(string.format("du: cannot access '%s': no such file or directory\n", v)) + return 1 + else + if fs.isDirectory(path) then + local total = visitor(v) - if bSummary then - printSize(total, v) - end - elseif fs.isLink(path) then - printSize(0, v) - else - printSize(fs.size(path), v) - end - end - end + if bSummary then + printSize(total, v) + end + elseif fs.isLink(path) then + printSize(0, v) + else + printSize(fs.size(path), v) + end + end + end end return true diff --git a/shellex/find.lua b/shellex/find.lua index b4695e2..ab53134 100644 --- a/shellex/find.lua +++ b/shellex/find.lua @@ -4,26 +4,26 @@ local text = require("shellex.text") local USAGE = [===[Usage: find [path] [--type=[dfs]] [--[i]name=EXPR] - --path if not specified, path is assumed to be current working directory - --type returns results of a given type, d:directory, f:file, and s:symlinks - --name specify the file name pattern. Use quote to include *. iname is - case insensitive - --help display this help and exit]===] + --path if not specified, path is assumed to be current working directory + --type returns results of a given type, d:directory, f:file, and s:symlinks + --name specify the file name pattern. Use quote to include *. iname is + case insensitive + --help display this help and exit]===] local args, options = shell.parse(...) if (not args or not options) or options.help then - print(USAGE) - if not options.help then - return 1 - else - return -- nil return, meaning no error - end + print(USAGE) + if not options.help then + return 1 + else + return -- nil return, meaning no error + end end if #args > 1 then - io.stderr:write(USAGE..'\n') - return 1 + io.stderr:write(USAGE..'\n') + return 1 end local path = #args == 1 and args[1] or "." @@ -36,97 +36,97 @@ local fileNamePattern = "" local bCaseSensitive = true if options.iname and options.name then - io.stderr:write("find cannot define both iname and name\n") - return 1 + io.stderr:write("find cannot define both iname and name\n") + return 1 end if options.type then - bDirs = false - bFiles = false - bSyms = false + bDirs = false + bFiles = false + bSyms = false - if options.type == "f" then - bFiles = true - elseif options.type == "d" then - bDirs = true - elseif options.type == "s" then - bSyms = true - else - io.stderr:write(string.format("find: Unknown argument to type: %s\n", options.type)) - io.stderr:write(USAGE..'\n') - return 1 - end + if options.type == "f" then + bFiles = true + elseif options.type == "d" then + bDirs = true + elseif options.type == "s" then + bSyms = true + else + io.stderr:write(string.format("find: Unknown argument to type: %s\n", options.type)) + io.stderr:write(USAGE..'\n') + return 1 + end end if options.iname or options.name then - bCaseSensitive = options.iname ~= nil - fileNamePattern = options.iname or options.name + bCaseSensitive = options.iname ~= nil + fileNamePattern = options.iname or options.name - if type(fileNamePattern) ~= "string" then - io.stderr:write('find: missing argument to `name\'\n') - return 1 - end + if type(fileNamePattern) ~= "string" then + io.stderr:write('find: missing argument to `name\'\n') + return 1 + end - if not bCaseSensitive then - fileNamePattern = fileNamePattern:lower() - end + if not bCaseSensitive then + fileNamePattern = fileNamePattern:lower() + end - -- prefix any * with . for gnu find glob matching - fileNamePattern = text.escapeMagic(fileNamePattern) - fileNamePattern = fileNamePattern:gsub("%%%*", ".*") + -- prefix any * with . for gnu find glob matching + fileNamePattern = text.escapeMagic(fileNamePattern) + fileNamePattern = fileNamePattern:gsub("%%%*", ".*") end local function isValidType(spath) - if not fs.exists(spath) then - return false - end + if not fs.exists(spath) then + return false + end - if fileNamePattern:len() > 0 then - local fileName = spath:gsub('.*/','') + if fileNamePattern:len() > 0 then + local fileName = spath:gsub('.*/','') - if fileName:len() == 0 then - return false - end + if fileName:len() == 0 then + return false + end - local caseFileName = fileName + local caseFileName = fileName - if not bCaseSensitive then - caseFileName = caseFileName:lower() - end + if not bCaseSensitive then + caseFileName = caseFileName:lower() + end - local s, e = caseFileName:find(fileNamePattern) - if not s or not e then - return false - end + local s, e = caseFileName:find(fileNamePattern) + if not s or not e then + return false + end - if s ~= 1 or e ~= caseFileName:len() then - return false - end - end + if s ~= 1 or e ~= caseFileName:len() then + return false + end + end - if fs.isDirectory(spath) then - return bDirs - elseif fs.isLink(spath) then - return bSyms - else - return bFiles - end + if fs.isDirectory(spath) then + return bDirs + elseif fs.isLink(spath) then + return bSyms + else + return bFiles + end end local function visit(rpath) - local spath = shell.resolve(rpath) + local spath = shell.resolve(rpath) - if isValidType(spath) then - local result = rpath:gsub('/+$','') - print(result) - end + if isValidType(spath) then + local result = rpath:gsub('/+$','') + print(result) + end - if fs.isDirectory(spath) then - local list_result = fs.list(spath) - for list_item in list_result do - visit(rpath:gsub('/+$', '') .. '/' .. list_item) - end - end + if fs.isDirectory(spath) then + local list_result = fs.list(spath) + for list_item in list_result do + visit(rpath:gsub('/+$', '') .. '/' .. list_item) + end + end end visit(path) diff --git a/shellex/grep.lua b/shellex/grep.lua index 9a8af85..e6d97ee 100644 --- a/shellex/grep.lua +++ b/shellex/grep.lua @@ -19,11 +19,11 @@ local args, options = shell.parse(...) local gpu = tty.gpu() local function printUsage(ostream, msg) - local s = ostream or io.stdout - if msg then - s:write(msg,'\n') - end - s:write([[Usage: grep [OPTION]... PATTERN [FILE]... + local s = ostream or io.stdout + if msg then + s:write(msg,'\n') + end + s:write([[Usage: grep [OPTION]... PATTERN [FILE]... Example: grep -i "hello world" menu.lua main.lua for more information, run: man grep ]]) @@ -32,10 +32,10 @@ end local PATTERNS = { table.remove(args, 1) } local FILES = { } for _, arg in pairs(args) do - local files = glob.matches(shell.getWorkingDirectory(), arg) - for _, v in pairs(files) do - table.insert(FILES, v) - end + local files = glob.matches(shell.getWorkingDirectory(), arg) + for _, v in pairs(files) do + table.insert(FILES, v) + end end local LABEL_COLOR = 0xb000b0 @@ -44,17 +44,17 @@ local MATCH_COLOR = 0xB0B00F local COLON_COLOR = 0x00FFFF local function pop(...) - local result - for _,key in ipairs({...}) do - result = options[key] or result - options[key] = nil - end - return result + local result + for _,key in ipairs({...}) do + result = options[key] or result + options[key] = nil + end + return result end -- Specify the variables for the options local plain = pop('F','fixed-strings') - plain = not pop('e','--lua-regexp') and plain + plain = not pop('e','--lua-regexp') and plain local pattern_file = pop('file') local match_whole_word = pop('w','word-regexp') local match_whole_line = pop('x','line-regexp') @@ -65,8 +65,8 @@ local invert_match = not not pop('v','invert-match') -- no version output, just help if pop('V','version','help') then - printUsage() - return 0 + printUsage() + return 0 end local max_matches = tonumber(pop('max-count')) or math.huge @@ -75,40 +75,40 @@ local search_recursively = pop('r','recursive') -- Table with patterns to check for if pattern_file then - local pattern_file_path = shell.resolve(pattern_file) - if not fs.exists(pattern_file_path) then - stderr:write('grep: ',pattern_file,': file not found') - return 2 - end - table.insert(FILES, 1, PATTERNS[1]) - PATTERNS = {} - for line in io.lines(pattern_file_path) do - PATTERNS[#PATTERNS+1] = line - end + local pattern_file_path = shell.resolve(pattern_file) + if not fs.exists(pattern_file_path) then + stderr:write('grep: ',pattern_file,': file not found') + return 2 + end + table.insert(FILES, 1, PATTERNS[1]) + PATTERNS = {} + for line in io.lines(pattern_file_path) do + PATTERNS[#PATTERNS+1] = line + end end if #PATTERNS == 0 then - printUsage(stderr) - return 2 + printUsage(stderr) + return 2 end if #FILES == 0 then - FILES = search_recursively and {'.'} or {'-'} + FILES = search_recursively and {'.'} or {'-'} end if not options.h and search_recursively then - options.H = true + options.H = true end if #FILES < 2 then - options.h = true + options.h = true end local f_only = pop('l','files-with-matches') local no_only = pop('L','files-without-match') and not f_only local include_filename = pop('H','with-filename') - include_filename = not pop('h','no-filename') or include_filename + include_filename = not pop('h','no-filename') or include_filename local m_only = pop('o','only-matching') local quiet = pop('q','quiet','silent') @@ -125,212 +125,212 @@ local trim_front = trim and function(s)return s:gsub('^%s+','')end or noop local trim_back = trim and function(s)return s:gsub('%s+$','')end or noop if next(options) then - if not quiet then - printUsage(stderr, 'unexpected option: '..next(options)) - return 2 - end - return 0 + if not quiet then + printUsage(stderr, 'unexpected option: '..next(options)) + return 2 + end + return 0 end -- Resolve the location of a file, without searching the path local function resolve(file) - if file:sub(1,1) == '/' then - return fs.canonical(file) - else - if file:sub(1,2) == './' then - file = file:sub(3, -1) - end - return fs.canonical(fs.concat(shell.getWorkingDirectory(), file)) - end + if file:sub(1,1) == '/' then + return fs.canonical(file) + else + if file:sub(1,2) == './' then + file = file:sub(3, -1) + end + return fs.canonical(fs.concat(shell.getWorkingDirectory(), file)) + end end --- Builds a case insensitive patterns, code from stackoverflow --- (questions/11401890/case-insensitive-lua-pattern-matching) if ignore_case then - for i=1,#PATTERNS do - -- find an optional '%' (group 1) followed by any character (group 2) - PATTERNS[i] = PATTERNS[i]:gsub("(%%?)(.)", function(percent, letter) - if percent ~= "" or not letter:match("%a") then - -- if the '%' matched, or `letter` is not a letter, return "as is" - return percent .. letter - else -- case-insensitive - return string.format("[%s%s]", letter:lower(), letter:upper()) - end - end) - end + for i=1,#PATTERNS do + -- find an optional '%' (group 1) followed by any character (group 2) + PATTERNS[i] = PATTERNS[i]:gsub("(%%?)(.)", function(percent, letter) + if percent ~= "" or not letter:match("%a") then + -- if the '%' matched, or `letter` is not a letter, return "as is" + return percent .. letter + else -- case-insensitive + return string.format("[%s%s]", letter:lower(), letter:upper()) + end + end) + end end local function getAllFiles(dir, file_list) - for node in fs.list(shell.resolve(dir)) do - local rel_path = dir:gsub("/+$","") .. '/' .. node - local resolved_path = shell.resolve(rel_path) - if fs.isDirectory(resolved_path) then - getAllFiles(rel_path, file_list) - else - file_list[#file_list+1] = rel_path - end - end + for node in fs.list(shell.resolve(dir)) do + local rel_path = dir:gsub("/+$","") .. '/' .. node + local resolved_path = shell.resolve(rel_path) + if fs.isDirectory(resolved_path) then + getAllFiles(rel_path, file_list) + else + file_list[#file_list+1] = rel_path + end + end end if search_recursively then - local files = {} - for _,arg in ipairs(FILES) do - if fs.isDirectory(shell.resolve(arg)) then - getAllFiles(arg, files) - else - files[#files+1]=arg - end - end - FILES=files + local files = {} + for _,arg in ipairs(FILES) do + if fs.isDirectory(shell.resolve(arg)) then + getAllFiles(arg, files) + else + files[#files+1]=arg + end + end + FILES=files end -- Prepare an iterator for reading files local function readLines() - local curHand = nil - local curFile = nil - local meta = nil - return function() - if not curFile then - local file = table.remove(FILES, 1) - if not file then - return - end - meta = {line_num=0,hits=0} - if file == "-" then - curFile = file - meta.label = stdin_label - curHand = io.input() - else - meta.label = file - local file, reason = resolve(file) - if fs.exists(file) then - if fs.isDirectory(file) then - local msg = string.format("%s: Is a directory", meta.label) - stderr:write("grep: ",msg,"\n") - return false, 2 - else - curHand, reason = io.open(file, 'r') - if not curHand then - local msg = string.format("failed to read from %s: %s", meta.label, reason) - stderr:write("grep: ",msg,"\n") - return false, 2 - else - curFile = meta.label - end - end - else - stderr:write("grep: ",file,": file not found\n") - return false, 2 - end - end - end - meta.line = nil - if not meta.close and curHand then - meta.line_num = meta.line_num + 1 - meta.line = curHand:read("*l") - end - if not meta.line then - curFile = nil - if curHand then - curHand:close() - end - return false, meta - else - return meta, curFile - end - end + local curHand = nil + local curFile = nil + local meta = nil + return function() + if not curFile then + local file = table.remove(FILES, 1) + if not file then + return + end + meta = {line_num=0,hits=0} + if file == "-" then + curFile = file + meta.label = stdin_label + curHand = io.input() + else + meta.label = file + local file, reason = resolve(file) + if fs.exists(file) then + if fs.isDirectory(file) then + local msg = string.format("%s: Is a directory", meta.label) + stderr:write("grep: ",msg,"\n") + return false, 2 + else + curHand, reason = io.open(file, 'r') + if not curHand then + local msg = string.format("failed to read from %s: %s", meta.label, reason) + stderr:write("grep: ",msg,"\n") + return false, 2 + else + curFile = meta.label + end + end + else + stderr:write("grep: ",file,": file not found\n") + return false, 2 + end + end + end + meta.line = nil + if not meta.close and curHand then + meta.line_num = meta.line_num + 1 + meta.line = curHand:read("*l") + end + if not meta.line then + curFile = nil + if curHand then + curHand:close() + end + return false, meta + else + return meta, curFile + end + end end local function write(part, color) - local prev_color = color and getc() - if color then setc(color) end - io.write(part) - if color then setc(prev_color) end + local prev_color = color and getc() + if color then setc(color) end + io.write(part) + if color then setc(prev_color) end end local flush=(f_only or no_only or print_count) and function(m) - if no_only and m.hits == 0 or f_only and m.hits ~= 0 then - write(m.label, LABEL_COLOR) - write('\n') - elseif print_count then - if include_filename then - write(m.label, LABEL_COLOR) - write(':', COLON_COLOR) - end - write(m.hits) - write('\n') - end + if no_only and m.hits == 0 or f_only and m.hits ~= 0 then + write(m.label, LABEL_COLOR) + write('\n') + elseif print_count then + if include_filename then + write(m.label, LABEL_COLOR) + write(':', COLON_COLOR) + end + write(m.hits) + write('\n') + end end local ec = nil local any_hit_ec = 1 local function test(m,p) - local empty_line = true - local last_index, slen = 1, #m.line - local needs_filename, needs_line_num = include_filename, print_line_num - local hit_value = 1 - while last_index <= slen and not m.close do - local i, j = m.line:find(p, last_index, plain) - local word_fail, line_fail = - match_whole_word and not (i and not (m.line:sub(i-1,i-1)..m.line:sub(j+1,j+1)):find("[%a_]")), - match_whole_line and not (i==1 and j==slen) - local matched = not ((m_only or last_index==1) and not i) - if (hit_value == 1 and word_fail) or line_fail then - matched,i,j = false - end - if invert_match == matched then break end - if max_matches == 0 then os.exit(1) end - any_hit_ec = 0 - m.hits, hit_value = m.hits + hit_value, 0 - if f_only or no_only then - m.close = true - end - if flush or quiet then return end - if needs_filename then - write(m.label, LABEL_COLOR) - write(':', COLON_COLOR) - needs_filename = nil - end - if needs_line_num then - write(m.line_num, LINE_NUM_COLOR) - write(':', COLON_COLOR) - needs_line_num = nil - end - local s=m_only and '' or m.line:sub(last_index,(i or 0)-1) - local g=i and m.line:sub(i,j) or '' - if i==1 then g=trim_front(g) elseif last_index==1 then s=trim_front(s) end - if j==slen then g=trim_back(g) elseif not i then s=trim_back(s) end - write(s) - write(g, MATCH_COLOR) - empty_line = false - last_index = (j or slen)+1 - if m_only or last_index>slen then - write("\n") - empty_line = true - needs_filename, needs_line_num = include_filename, print_line_num - elseif p:find("^^") and not plain then p="^$" end - end - if not empty_line then write("\n") end - if max_matches ~= math.huge and max_matches >= m.hits then - m.close = true - end + local empty_line = true + local last_index, slen = 1, #m.line + local needs_filename, needs_line_num = include_filename, print_line_num + local hit_value = 1 + while last_index <= slen and not m.close do + local i, j = m.line:find(p, last_index, plain) + local word_fail, line_fail = + match_whole_word and not (i and not (m.line:sub(i-1,i-1)..m.line:sub(j+1,j+1)):find("[%a_]")), + match_whole_line and not (i==1 and j==slen) + local matched = not ((m_only or last_index==1) and not i) + if (hit_value == 1 and word_fail) or line_fail then + matched,i,j = false + end + if invert_match == matched then break end + if max_matches == 0 then os.exit(1) end + any_hit_ec = 0 + m.hits, hit_value = m.hits + hit_value, 0 + if f_only or no_only then + m.close = true + end + if flush or quiet then return end + if needs_filename then + write(m.label, LABEL_COLOR) + write(':', COLON_COLOR) + needs_filename = nil + end + if needs_line_num then + write(m.line_num, LINE_NUM_COLOR) + write(':', COLON_COLOR) + needs_line_num = nil + end + local s=m_only and '' or m.line:sub(last_index,(i or 0)-1) + local g=i and m.line:sub(i,j) or '' + if i==1 then g=trim_front(g) elseif last_index==1 then s=trim_front(s) end + if j==slen then g=trim_back(g) elseif not i then s=trim_back(s) end + write(s) + write(g, MATCH_COLOR) + empty_line = false + last_index = (j or slen)+1 + if m_only or last_index>slen then + write("\n") + empty_line = true + needs_filename, needs_line_num = include_filename, print_line_num + elseif p:find("^^") and not plain then p="^$" end + end + if not empty_line then write("\n") end + if max_matches ~= math.huge and max_matches >= m.hits then + m.close = true + end end local uptime = computer.uptime local last_sleep = uptime() for meta,status in readLines() do - if uptime() - last_sleep > 1 then - os.sleep(0) - last_sleep = uptime() - end - if not meta then - if type(status) == 'table' then if flush then - flush(status) end -- this was the last object, closing out - elseif status then - ec = status or ec - end - else - for _,p in ipairs(PATTERNS) do - test(meta,p) - end - end + if uptime() - last_sleep > 1 then + os.sleep(0) + last_sleep = uptime() + end + if not meta then + if type(status) == 'table' then if flush then + flush(status) end -- this was the last object, closing out + elseif status then + ec = status or ec + end + else + for _,p in ipairs(PATTERNS) do + test(meta,p) + end + end end return ec or any_hit_ec \ No newline at end of file diff --git a/shellex/head.lua b/shellex/head.lua index 0cd41a2..9761a6a 100644 --- a/shellex/head.lua +++ b/shellex/head.lua @@ -4,18 +4,18 @@ local args, options = shell.parse(...) local error_code = 0 local function pop(key, convert) - local result = options[key] - options[key] = nil - if result and convert then - local c = tonumber(result) - if not c then - io.stderr:write(string.format("use --%s=n where n is a number\n", key)) - options.help = true - error_code = 1 - end - result = c - end - return result + local result = options[key] + options[key] = nil + if result and convert then + local c = tonumber(result) + if not c then + io.stderr:write(string.format("use --%s=n where n is a number\n", key)) + options.help = true + error_code = 1 + end + result = c + end + return result end local bytes = pop('bytes', true) @@ -28,109 +28,109 @@ local help = pop('help') local invalid_key = next(options) if bytes and lines then - invalid_key = 'bytes and lines both specified' + invalid_key = 'bytes and lines both specified' end if help or next(options) then - local invalid_key = next(options) - if invalid_key then - invalid_key = string.format('invalid option: %s\n', invalid_key) - error_code = 1 - else - invalid_key = '' - end - print(invalid_key .. [[Usage: head [--lines=n] file + local invalid_key = next(options) + if invalid_key then + invalid_key = string.format('invalid option: %s\n', invalid_key) + error_code = 1 + else + invalid_key = '' + end + print(invalid_key .. [[Usage: head [--lines=n] file Print the first 10 lines of each FILE to stdout. For more info run: man head]]) - os.exit(error_code) + os.exit(error_code) end if #args == 0 then - args = {'-'} + args = {'-'} end if quiet and verbose then - quiet = false + quiet = false end local function new_stream() - return - { - open=true, - capacity=math.abs(lines or bytes or 10), - bytes=bytes, - buffer=(lines and lines < 0 and {}) or (bytes and bytes < 0 and '') - } + return + { + open=true, + capacity=math.abs(lines or bytes or 10), + bytes=bytes, + buffer=(lines and lines < 0 and {}) or (bytes and bytes < 0 and '') + } end local function close(stream) - if stream.buffer then - if type(stream.buffer) == 'table' then - stream.buffer = table.concat(stream.buffer) - end - io.stdout:write(stream.buffer) - stream.buffer = nil - end - stream.open = false + if stream.buffer then + if type(stream.buffer) == 'table' then + stream.buffer = table.concat(stream.buffer) + end + io.stdout:write(stream.buffer) + stream.buffer = nil + end + stream.open = false end local function push(stream, line) - if not line then - return close(stream) - end + if not line then + return close(stream) + end - local cost = stream.bytes and line:len() or 1 - stream.capacity = stream.capacity - cost + local cost = stream.bytes and line:len() or 1 + stream.capacity = stream.capacity - cost - if not stream.buffer then - if stream.bytes and stream.capacity < 0 then - line = line:sub(1,stream.capacity-1) - end - io.write(line) - if stream.capacity <= 0 then - return close(stream) - end - else - if type(stream.buffer) == 'table' then -- line storage - stream.buffer[#stream.buffer+1] = line - if stream.capacity < 0 then - table.remove(stream.buffer, 1) - stream.capacity = 0 -- zero out - end - else -- byte storage - stream.buffer = stream.buffer .. line - if stream.capacity < 0 then - stream.buffer = stream.buffer:sub(-stream.capacity+1) - stream.capacity = 0 -- zero out - end - end - end + if not stream.buffer then + if stream.bytes and stream.capacity < 0 then + line = line:sub(1,stream.capacity-1) + end + io.write(line) + if stream.capacity <= 0 then + return close(stream) + end + else + if type(stream.buffer) == 'table' then -- line storage + stream.buffer[#stream.buffer+1] = line + if stream.capacity < 0 then + table.remove(stream.buffer, 1) + stream.capacity = 0 -- zero out + end + else -- byte storage + stream.buffer = stream.buffer .. line + if stream.capacity < 0 then + stream.buffer = stream.buffer:sub(-stream.capacity+1) + stream.capacity = 0 -- zero out + end + end + end end for i=1,#args do - local arg = args[i] - local file - if arg == '-' then - arg = 'standard input' - file = io.stdin - else - file, reason = io.open(arg, 'r') - if not file then - io.stderr:write(string.format([[head: cannot open '%s' for reading: %s]], arg, reason)) - end - end - if file then - if verbose or #args > 1 then - io.write(string.format('==> %s <==\n', arg)) - end + local arg = args[i] + local file + if arg == '-' then + arg = 'standard input' + file = io.stdin + else + file, reason = io.open(arg, 'r') + if not file then + io.stderr:write(string.format([[head: cannot open '%s' for reading: %s]], arg, reason)) + end + end + if file then + if verbose or #args > 1 then + io.write(string.format('==> %s <==\n', arg)) + end - local stream = new_stream() + local stream = new_stream() - while stream.open do - push(stream, file:read('*L')) - end + while stream.open do + push(stream, file:read('*L')) + end - file:close() - end + file:close() + end end diff --git a/shellex/hostname.lua b/shellex/hostname.lua index 8999e3b..00ef0c1 100644 --- a/shellex/hostname.lua +++ b/shellex/hostname.lua @@ -5,13 +5,13 @@ local args = shell.parse(...) local hostname = args[1] if hostname then - os.setComputerLabel(hostname) + os.setComputerLabel(hostname) else - hostname = os.getComputerLabel() - if hostname then - print(hostname) - else - io.stderr:write("Hostname not set\n") - return 1 - end + hostname = os.getComputerLabel() + if hostname then + print(hostname) + else + io.stderr:write("Hostname not set\n") + return 1 + end end diff --git a/shellex/less.lua b/shellex/less.lua index bc5d390..ef86399 100644 --- a/shellex/less.lua +++ b/shellex/less.lua @@ -5,14 +5,14 @@ local term = require("shellex.term") -- using term for negative scroll feature local args, ops = shell.parse(...) if #args > 1 then - io.write("Usage: ", os.getenv("_"):match("/([^/]+)%.lua$"), " \n") - io.write("- or no args reads stdin\n") - return 1 + io.write("Usage: ", os.getenv("_"):match("/([^/]+)%.lua$"), " \n") + io.write("- or no args reads stdin\n") + return 1 end local cat_cmd = table.concat({"cat", ...}, " ") if not io.output().tty then - return os.execute(cat_cmd) + return os.execute(cat_cmd) end local preader = io.popen(cat_cmd) @@ -23,127 +23,127 @@ local end_of_buffer = false local width, height = term.getViewport() local function split(full_line) - local index = 1 - local parts = {} - while true do - local sub = full_line:sub(index, index + width*3) - -- checking #sub < width first is faster, save a unicode call - if #sub < width or unicode.wlen(sub) <= width then - parts[#parts + 1] = sub - break - end - parts[#parts + 1] = unicode.wtrunc(sub, width + 1) - index = index + #parts[#parts] - if index > #full_line then - break - end - end - return parts + local index = 1 + local parts = {} + while true do + local sub = full_line:sub(index, index + width*3) + -- checking #sub < width first is faster, save a unicode call + if #sub < width or unicode.wlen(sub) <= width then + parts[#parts + 1] = sub + break + end + parts[#parts + 1] = unicode.wtrunc(sub, width + 1) + index = index + #parts[#parts] + if index > #full_line then + break + end + end + return parts end local function scan(num) - local result = {} - local line_count = 0 - for i=1, num do - local lines = {} - if scrollback and (bottom + i) <= #scrollback then - lines = {scrollback[bottom + i]} - else - local full_line = preader:read() - if not full_line then preader:close() break end - -- with buffering, we can buffer ahead too, and read more smoothly - local buffering = false - for _,line in ipairs(split(full_line)) do - if not buffering then - lines[#lines + 1] = line - end - if scrollback then - buffering = true - scrollback[#scrollback + 1] = line - end - end - end + local result = {} + local line_count = 0 + for i=1, num do + local lines = {} + if scrollback and (bottom + i) <= #scrollback then + lines = {scrollback[bottom + i]} + else + local full_line = preader:read() + if not full_line then preader:close() break end + -- with buffering, we can buffer ahead too, and read more smoothly + local buffering = false + for _,line in ipairs(split(full_line)) do + if not buffering then + lines[#lines + 1] = line + end + if scrollback then + buffering = true + scrollback[#scrollback + 1] = line + end + end + end - for _,line in ipairs(lines) do - result[#result + 1] = line - line_count = line_count + 1 - if #result > height then - table.remove(result, 1) - end - end + for _,line in ipairs(lines) do + result[#result + 1] = line + line_count = line_count + 1 + if #result > height then + table.remove(result, 1) + end + end - if line_count >= num then - break - end - end - return result, line_count + if line_count >= num then + break + end + end + return result, line_count end local function status() - if end_of_buffer then - if ops.noback then - os.exit() - end - io.write("(END)") - end - io.write(":") + if end_of_buffer then + if ops.noback then + os.exit() + end + io.write("(END)") + end + io.write(":") end local function goback(n) - if not scrollback then return end - local current_top = bottom - height + 1 - n = math.min(current_top, n) - if n < 1 then return end - local top = current_top - n + 1 - term.scroll(-n) - term.setCursor(1, 1) - for i=1, n do - if i >= height then - break - end - print(scrollback[top + i - 1]) - end - term.setCursor(1, height) - bottom = bottom - n - end_of_buffer = false + if not scrollback then return end + local current_top = bottom - height + 1 + n = math.min(current_top, n) + if n < 1 then return end + local top = current_top - n + 1 + term.scroll(-n) + term.setCursor(1, 1) + for i=1, n do + if i >= height then + break + end + print(scrollback[top + i - 1]) + end + term.setCursor(1, height) + bottom = bottom - n + end_of_buffer = false end local function goforward(n) - term.clearLine() - local update, line_count = scan(n) - for _,line in ipairs(update) do - print(line) - end - if line_count < n then - end_of_buffer = true - end - bottom = bottom + line_count + term.clearLine() + local update, line_count = scan(n) + for _,line in ipairs(update) do + print(line) + end + if line_count < n then + end_of_buffer = true + end + bottom = bottom + line_count end goforward(height - 1) while true do - term.clearLine() - status() - local e, _, _, code = term.pull() - if e == "interrupted" then - break - elseif e == "key_down" then - if code == keys.q then - term.clearLine() - os.exit() -- abort - elseif code == keys["end"] then - goforward(math.huge) - elseif code == keys.space or code == keys.pageDown then - goforward(height - 1) - elseif code == keys.enter or code == keys.down then - goforward(1) - elseif code == keys.up then - goback(1) - elseif code == keys.pageUp then - goback(height - 1) - elseif code == keys.home then - goback(math.huge) - end - end + term.clearLine() + status() + local e, _, _, code = term.pull() + if e == "interrupted" then + break + elseif e == "key_down" then + if code == keys.q then + term.clearLine() + os.exit() -- abort + elseif code == keys["end"] then + goforward(math.huge) + elseif code == keys.space or code == keys.pageDown then + goforward(height - 1) + elseif code == keys.enter or code == keys.down then + goforward(1) + elseif code == keys.up then + goback(1) + elseif code == keys.pageUp then + goback(height - 1) + elseif code == keys.home then + goback(math.huge) + end + end end diff --git a/shellex/ln.lua b/shellex/ln.lua index 7b5e8c3..40e2892 100644 --- a/shellex/ln.lua +++ b/shellex/ln.lua @@ -3,8 +3,8 @@ local shell = require("shellex.shell") local args = shell.parse(...) if #args == 0 then - io.write("Usage: ln []\n") - return 1 + io.write("Usage: ln []\n") + return 1 end local target_name = args[1] @@ -12,23 +12,23 @@ local target = shell.resolve(target_name) -- don't link from target if it doesn't exist, unless it is a broken link if not fs.exists(target) and not fs.isLink(target) then - io.stderr:write("ln: failed to access '" .. target_name .. "': No such file or directory\n") - return 1 + io.stderr:write("ln: failed to access '" .. target_name .. "': No such file or directory\n") + return 1 end local linkpath if #args > 1 then - linkpath = shell.resolve(args[2]) + linkpath = shell.resolve(args[2]) else - linkpath = fs.concat(shell.getWorkingDirectory(), fs.name(target)) + linkpath = fs.concat(shell.getWorkingDirectory(), fs.name(target)) end if fs.isDirectory(linkpath) then - linkpath = fs.concat(linkpath, fs.name(target)) + linkpath = fs.concat(linkpath, fs.name(target)) end local result, reason = fs.link(target, linkpath) if not result then - io.stderr:write(reason..'\n') - return 1 + io.stderr:write(reason..'\n') + return 1 end diff --git a/shellex/ls.lua b/shellex/ls.lua index 305abf5..8c86d61 100644 --- a/shellex/ls.lua +++ b/shellex/ls.lua @@ -9,383 +9,383 @@ local text = require("shellex.text") local dirsArg, ops = shell.parse(...) if ops.help then - print([[Usage: ls [OPTION]... [FILE]... - -a, --all do not ignore entries starting with . - --full-time with -l, print time in full iso format - -h, --human-readable with -l and/or -s, print human readable sizes - --si likewise, but use powers of 1000 not 1024 - -l use a long listing format - -r, --reverse reverse order while sorting - -R, --recursive list subdirectories recursively - -S sort by file size - -X sort alphabetically by entry extension - -1 list one file per line - -p append / indicator to directories - -M display Microsoft-style file and directory - count after listing - --no-color Do not colorize the output (default colorized) - --help display this help and exit + print([[Usage: ls [OPTION]... [FILE]... + -a, --all do not ignore entries starting with . + --full-time with -l, print time in full iso format + -h, --human-readable with -l and/or -s, print human readable sizes + --si likewise, but use powers of 1000 not 1024 + -l use a long listing format + -r, --reverse reverse order while sorting + -R, --recursive list subdirectories recursively + -S sort by file size + -X sort alphabetically by entry extension + -1 list one file per line + -p append / indicator to directories + -M display Microsoft-style file and directory + count after listing + --no-color Do not colorize the output (default colorized) + --help display this help and exit ]]) - return 0 + return 0 end if #dirsArg == 0 then - table.insert(dirsArg, ".") + table.insert(dirsArg, ".") end local ec = 0 local fOut = true -- tty.isAvailable() and io.output().tty local function perr(msg) io.stderr:write(msg,"\n") ec = 2 end local function stat(names, index) - local name = names[index] - if type(name) == "table" then - return name - end - local info = {} - info.key = name - info.path = name:sub(1, 1) == "/" and "" or names.path - info.full_path = fs.concat(info.path, name) - info.isDir = fs.isDirectory(info.full_path) - info.name = name:gsub("/+$", "") .. (ops.p and info.isDir and "/" or "") - info.sort_name = info.name:gsub("^%.","") - info.isLink, info.link = fs.isLink(info.full_path) - info.size = info.isLink and 0 or fs.size(info.full_path) - info.ext = info.name:match("(%.[^.]+)$") or "" - names[index] = info - return info + local name = names[index] + if type(name) == "table" then + return name + end + local info = {} + info.key = name + info.path = name:sub(1, 1) == "/" and "" or names.path + info.full_path = fs.concat(info.path, name) + info.isDir = fs.isDirectory(info.full_path) + info.name = name:gsub("/+$", "") .. (ops.p and info.isDir and "/" or "") + info.sort_name = info.name:gsub("^%.","") + info.isLink, info.link = fs.isLink(info.full_path) + info.size = info.isLink and 0 or fs.size(info.full_path) + info.ext = info.name:match("(%.[^.]+)$") or "" + names[index] = info + return info end local function toArray(i) local r={} for n in i do r[#r+1]=n end return r end local set_color = function() end local function colorize() return end if fOut and not ops["no-color"] then - local LSC = tx.foreach(text.split(os.getenv("LS_COLORS") or "", {":"}, true), function(e) - local parts = text.split(e, {"="}, true) - return parts[2], parts[1] - end) - colorize = function(info) - return - info.isLink and LSC.ln or - info.isDir and LSC.di or - LSC['*'..info.ext] or - LSC.fi - end - set_color=function(c) - local cmap = { - [ '30' ] = _G.colors.black, - [ '31' ] = _G.colors.red, - [ '32' ] = _G.colors.green, - [ '33' ] = _G.colors.yellow, - [ '34' ] = _G.colors.blue, - [ '35' ] = _G.colors.magenta, - [ '36' ] = _G.colors.cyan, - [ '37' ] = _G.colors.white, - } + local LSC = tx.foreach(text.split(os.getenv("LS_COLORS") or "", {":"}, true), function(e) + local parts = text.split(e, {"="}, true) + return parts[2], parts[1] + end) + colorize = function(info) + return + info.isLink and LSC.ln or + info.isDir and LSC.di or + LSC['*'..info.ext] or + LSC.fi + end + set_color=function(c) + local cmap = { + [ '30' ] = _G.colors.black, + [ '31' ] = _G.colors.red, + [ '32' ] = _G.colors.green, + [ '33' ] = _G.colors.yellow, + [ '34' ] = _G.colors.blue, + [ '35' ] = _G.colors.magenta, + [ '36' ] = _G.colors.cyan, + [ '37' ] = _G.colors.white, + } - if c then - c:gsub('(%d+)', function(code) - if cmap[code] then - term.setTextColor(cmap[code]) - elseif cmap[code] == '0' then - term.setTextColor(colors.white) - end - end) - else - term.setTextColor(colors.white) - end + if c then + c:gsub('(%d+)', function(code) + if cmap[code] then + term.setTextColor(cmap[code]) + elseif cmap[code] == '0' then + term.setTextColor(colors.white) + end + end) + else + term.setTextColor(colors.white) + end -- io.write(string.char(0x1b), "[", c or "", "m") - end + end end local msft={reports=0,proxies={}} function msft.report(files, dirs, used, proxy) - local free = proxy.spaceTotal() - proxy.spaceUsed() - set_color() - local pattern = "%5i File(s) %s bytes\n%5i Dir(s) %11s bytes free\n" - io.write(string.format(pattern, files, tostring(used), dirs, tostring(free))) + local free = proxy.spaceTotal() - proxy.spaceUsed() + set_color() + local pattern = "%5i File(s) %s bytes\n%5i Dir(s) %11s bytes free\n" + io.write(string.format(pattern, files, tostring(used), dirs, tostring(free))) end function msft.tail(names) - local fsproxy = fs.get(names.path) - if not fsproxy then - return - end - local totalSize, totalFiles, totalDirs = 0, 0, 0 - for i=1,#names do - local info = stat(names, i) - if info.isDir then - totalDirs = totalDirs + 1 - else - totalFiles = totalFiles + 1 - end - totalSize = totalSize + info.size - end - msft.report(totalFiles, totalDirs, totalSize, fsproxy) - local ps = msft.proxies - ps[fsproxy] = ps[fsproxy] or {files=0,dirs=0,used=0} - local p = ps[fsproxy] - p.files = p.files + totalFiles - p.dirs = p.dirs + totalDirs - p.used = p.used + totalSize - msft.reports = msft.reports + 1 + local fsproxy = fs.get(names.path) + if not fsproxy then + return + end + local totalSize, totalFiles, totalDirs = 0, 0, 0 + for i=1,#names do + local info = stat(names, i) + if info.isDir then + totalDirs = totalDirs + 1 + else + totalFiles = totalFiles + 1 + end + totalSize = totalSize + info.size + end + msft.report(totalFiles, totalDirs, totalSize, fsproxy) + local ps = msft.proxies + ps[fsproxy] = ps[fsproxy] or {files=0,dirs=0,used=0} + local p = ps[fsproxy] + p.files = p.files + totalFiles + p.dirs = p.dirs + totalDirs + p.used = p.used + totalSize + msft.reports = msft.reports + 1 end function msft.final() - if msft.reports < 2 then return end - local groups = {} - for proxy,report in pairs(msft.proxies) do - table.insert(groups, {proxy=proxy,report=report}) - end - set_color() - print("Total Files Listed:") - for _,pair in ipairs(groups) do - local proxy, report = pair.proxy, pair.report - if #groups>1 then - print("As pertaining to: "..proxy.address) - end - msft.report(report.files, report.dirs, report.used, proxy) - end + if msft.reports < 2 then return end + local groups = {} + for proxy,report in pairs(msft.proxies) do + table.insert(groups, {proxy=proxy,report=report}) + end + set_color() + print("Total Files Listed:") + for _,pair in ipairs(groups) do + local proxy, report = pair.proxy, pair.report + if #groups>1 then + print("As pertaining to: "..proxy.address) + end + msft.report(report.files, report.dirs, report.used, proxy) + end end if not ops.M then - msft.tail=function()end - msft.final=function()end + msft.tail=function()end + msft.final=function()end end local function nod(n) - return n and (tostring(n):gsub("(%.[0-9]+)0+$","%1")) or "0" + return n and (tostring(n):gsub("(%.[0-9]+)0+$","%1")) or "0" end local function formatSize(size) - if not ops.h and not ops['human-readable'] and not ops.si then - return tostring(size) - end - local sizes = {"", "K", "M", "G"} - local unit = 1 - local power = ops.si and 1000 or 1024 - while size > power and unit < #sizes do - unit = unit + 1 - size = size / power - end - return nod(math.floor(size*10)/10)..sizes[unit] + if not ops.h and not ops['human-readable'] and not ops.si then + return tostring(size) + end + local sizes = {"", "K", "M", "G"} + local unit = 1 + local power = ops.si and 1000 or 1024 + while size > power and unit < #sizes do + unit = unit + 1 + size = size / power + end + return nod(math.floor(size*10)/10)..sizes[unit] end local function filter(names) - if ops.a then - return names - end - local set = {} - for key, value in pairs(names) do - if type(key) == "number" then - local info = stat(names, key) - if fs.name(info.name):sub(1, 1) ~= "." then - table.insert(set, names[key]) - end - else - set[key] = value - end - end - return set + if ops.a then + return names + end + local set = {} + for key, value in pairs(names) do + if type(key) == "number" then + local info = stat(names, key) + if fs.name(info.name):sub(1, 1) ~= "." then + table.insert(set, names[key]) + end + else + set[key] = value + end + end + return set end local function sort(names) - local once = false - local function ni(v) - local vname = type(v) == "string" and v or v.key - for i=1,#names do - local info = stat(names, i) - if info.key == vname then - return i - end - end - end - local function sorter(key) - once = true - table.sort(names, function(a, b) - local ast = stat(names, ni(a)) - local bst = stat(names, ni(b)) - return ast[key] > bst[key] - end) - end - if ops.t then sorter("time") end - if ops.X then sorter("ext") end - if ops.S then sorter("size") end - local rev = ops.r or ops.reverse - if not once then sorter("sort_name") rev=not rev end - if rev then - for i=1,#names/2 do - names[i], names[#names - i + 1] = names[#names - i + 1], names[i] - end - end - return names + local once = false + local function ni(v) + local vname = type(v) == "string" and v or v.key + for i=1,#names do + local info = stat(names, i) + if info.key == vname then + return i + end + end + end + local function sorter(key) + once = true + table.sort(names, function(a, b) + local ast = stat(names, ni(a)) + local bst = stat(names, ni(b)) + return ast[key] > bst[key] + end) + end + if ops.t then sorter("time") end + if ops.X then sorter("ext") end + if ops.S then sorter("size") end + local rev = ops.r or ops.reverse + if not once then sorter("sort_name") rev=not rev end + if rev then + for i=1,#names/2 do + names[i], names[#names - i + 1] = names[#names - i + 1], names[i] + end + end + return names end local function dig(names, dirs, dir) - if ops.R then - local di = 1 - for i=1,#names do - local info = stat(names, i) - if info.isDir then - local path = dir..(dir:sub(-1) == "/" and "" or "/") - table.insert(dirs, di, path..info.name) - di = di + 1 - end - end - end - return names + if ops.R then + local di = 1 + for i=1,#names do + local info = stat(names, i) + if info.isDir then + local path = dir..(dir:sub(-1) == "/" and "" or "/") + table.insert(dirs, di, path..info.name) + di = di + 1 + end + end + end + return names end local first_display = true local function display(names) - local mt={} - local lines = setmetatable({}, mt) - local columns - if ops.l then - lines.n = #names - local max_size_width = 1 - for i=1,lines.n do - local info = stat(names, i) - max_size_width = math.max(max_size_width, formatSize(info.size):len()) - end - mt.__index = function(_, index) - local info = stat(names, index) - local file_type = info.isLink and 'l' or info.isDir and 'd' or 'f' - local link_target = info.isLink and string.format(" -> %s", info.link:gsub("/+$", "") .. (info.isDir and "/" or "")) or "" - pcall(function() - info.fs = fs.get(info.full_path) - end) - local write_mode = info.fs and info.fs.isReadOnly() and '-' or 'w' - local size = formatSize(info.size) - local format = "%s-r%s %+"..tostring(max_size_width)..'s ' - local meta = string.format(format, file_type, write_mode, size) - local item = info.name..link_target + local mt={} + local lines = setmetatable({}, mt) + local columns + if ops.l then + lines.n = #names + local max_size_width = 1 + for i=1,lines.n do + local info = stat(names, i) + max_size_width = math.max(max_size_width, formatSize(info.size):len()) + end + mt.__index = function(_, index) + local info = stat(names, index) + local file_type = info.isLink and 'l' or info.isDir and 'd' or 'f' + local link_target = info.isLink and string.format(" -> %s", info.link:gsub("/+$", "") .. (info.isDir and "/" or "")) or "" + pcall(function() + info.fs = fs.get(info.full_path) + end) + local write_mode = info.fs and info.fs.isReadOnly() and '-' or 'w' + local size = formatSize(info.size) + local format = "%s-r%s %+"..tostring(max_size_width)..'s ' + local meta = string.format(format, file_type, write_mode, size) + local item = info.name..link_target - return {{name = meta}, {color = colorize(info), name = item}} - end - elseif ops["1"] or not fOut then - lines.n = #names - mt.__index = function(_, index) - local info = stat(names, index) - return {{color = colorize(info), name = info.name}} - end - else -- columns - local num_columns, items_per_column, width = 0, 0, tty.getViewport() - 1 - local function real(x, y) - local index = y + ((x-1) * items_per_column) - return index <= #names and index or nil - end - local function max_name(column_index) - local max = 0 -- return the width of the max element in column_index - for r=1,items_per_column do - local ri = real(column_index, r) - if not ri then break end - local info = stat(names, ri) - max = math.max(max, unicode.wlen(info.name)) - end - return max - end - local function measure() - local total = 0 - for column_index=1,num_columns do - total = total + max_name(column_index) + (column_index > 1 and 2 or 0) - end - return total - end - while items_per_column<#names do - items_per_column = items_per_column + 1 - num_columns = math.ceil(#names/items_per_column) - if measure() < width then - break - end - end - lines.n = items_per_column - columns = num_columns - mt.__index=function(_, line_index) - return setmetatable({},{ - __len=function()return num_columns end, -- no can do in 5.1 - __index=function(_, column_index) - local ri = real(column_index, line_index) - if not ri then return end - local info = stat(names, ri) - local name = info.name - return {color = colorize(info), name = name .. string.rep(' ', max_name(column_index) - unicode.wlen(name) + (column_index < num_columns and 2 or 0))} - end, - }) - end - end + return {{name = meta}, {color = colorize(info), name = item}} + end + elseif ops["1"] or not fOut then + lines.n = #names + mt.__index = function(_, index) + local info = stat(names, index) + return {{color = colorize(info), name = info.name}} + end + else -- columns + local num_columns, items_per_column, width = 0, 0, tty.getViewport() - 1 + local function real(x, y) + local index = y + ((x-1) * items_per_column) + return index <= #names and index or nil + end + local function max_name(column_index) + local max = 0 -- return the width of the max element in column_index + for r=1,items_per_column do + local ri = real(column_index, r) + if not ri then break end + local info = stat(names, ri) + max = math.max(max, unicode.wlen(info.name)) + end + return max + end + local function measure() + local total = 0 + for column_index=1,num_columns do + total = total + max_name(column_index) + (column_index > 1 and 2 or 0) + end + return total + end + while items_per_column<#names do + items_per_column = items_per_column + 1 + num_columns = math.ceil(#names/items_per_column) + if measure() < width then + break + end + end + lines.n = items_per_column + columns = num_columns + mt.__index=function(_, line_index) + return setmetatable({},{ + __len=function()return num_columns end, -- no can do in 5.1 + __index=function(_, column_index) + local ri = real(column_index, line_index) + if not ri then return end + local info = stat(names, ri) + local name = info.name + return {color = colorize(info), name = name .. string.rep(' ', max_name(column_index) - unicode.wlen(name) + (column_index < num_columns and 2 or 0))} + end, + }) + end + end - for line_index=1,lines.n do - local line = lines[line_index] - for element_index=1,columns or #line do - local e = line[element_index] - if not e then break end - first_display = false - set_color(e.color) - io.write(e.name) - end - print() - end - msft.tail(names) + for line_index=1,lines.n do + local line = lines[line_index] + for element_index=1,columns or #line do + local e = line[element_index] + if not e then break end + first_display = false + set_color(e.color) + io.write(e.name) + end + print() + end + msft.tail(names) end local header = function() end if #dirsArg > 1 or ops.R then - header = function(path) - if not first_display then print() end - set_color() - io.write(path,":\n") - end + header = function(path) + if not first_display then print() end + set_color() + io.write(path,":\n") + end end local function displayDirList(dirs) - while #dirs > 0 do - local dir = table.remove(dirs, 1) - header(dir) - local path = shell.resolve(dir) - local list, reason = fs.list(path) - if not list then - perr(reason) - else - local names = toArray(list) - names.path = path - display(dig(sort(filter(names)), dirs, dir)) - end - end + while #dirs > 0 do + local dir = table.remove(dirs, 1) + header(dir) + local path = shell.resolve(dir) + local list, reason = fs.list(path) + if not list then + perr(reason) + else + local names = toArray(list) + names.path = path + display(dig(sort(filter(names)), dirs, dir)) + end + end end --[[ local dir_set, file_set = {}, {path=shell.getWorkingDirectory()} for _,dir in ipairs(dirsArg) do - local path = shell.resolve(dir) - local real, why = fs.realPath(path) - local access_msg = "cannot access " .. tostring(path) .. ": " - if not real then - perr(access_msg .. why) - elseif not fs.exists(path) then - perr(access_msg .. "No such file or directory") - elseif fs.isDirectory(path) then - table.insert(dir_set, dir) - else -- file or link - table.insert(file_set, dir) - end + local path = shell.resolve(dir) + local real, why = fs.realPath(path) + local access_msg = "cannot access " .. tostring(path) .. ": " + if not real then + perr(access_msg .. why) + elseif not fs.exists(path) then + perr(access_msg .. "No such file or directory") + elseif fs.isDirectory(path) then + table.insert(dir_set, dir) + else -- file or link + table.insert(file_set, dir) + end end ]] local dir_set, file_set = {}, {path=shell.getWorkingDirectory()} for _,dir in ipairs(dirsArg) do - local path = shell.resolve(dir) - if fs.isDirectory(path) then - table.insert(dir_set, dir) - else - local files = glob.matches(shell.getWorkingDirectory(), dir) - for _, v in pairs(files) do - table.insert(file_set, v) - end - if #files == 0 then - local access_msg = "cannot access " .. tostring(dir) .. ": " - perr(access_msg .. "No such file or directory") - end - end + local path = shell.resolve(dir) + if fs.isDirectory(path) then + table.insert(dir_set, dir) + else + local files = glob.matches(shell.getWorkingDirectory(), dir) + for _, v in pairs(files) do + table.insert(file_set, v) + end + if #files == 0 then + local access_msg = "cannot access " .. tostring(dir) .. ": " + perr(access_msg .. "No such file or directory") + end + end end io.output():setvbuf("line") local ok, msg = pcall(function() - if #file_set > 0 then display(sort(file_set)) end - displayDirList(dir_set) - msft.final() + if #file_set > 0 then display(sort(file_set)) end + displayDirList(dir_set) + msft.final() end) io.output():flush() @@ -393,7 +393,7 @@ io.output():setvbuf("no") set_color() if not ok then - error(msg, 2) + error(msg, 2) end assert(ok, msg) diff --git a/shellex/mv.lua b/shellex/mv.lua index b70a417..117fb35 100644 --- a/shellex/mv.lua +++ b/shellex/mv.lua @@ -4,30 +4,30 @@ local transfer = require("shellex.transfer") local args, options = shell.parse(...) options.h = options.h or options.help if #args < 2 or options.h then - io.write([[Usage: mv [OPTIONS] - -f overwrite without prompt - -i prompt before overwriting - unless -f - -v verbose - -n do not overwrite an existing file - --skip=P ignore paths matching lua regex P - -h, --help show this help + io.write([[Usage: mv [OPTIONS] + -f overwrite without prompt + -i prompt before overwriting + unless -f + -v verbose + -n do not overwrite an existing file + --skip=P ignore paths matching lua regex P + -h, --help show this help ]]) - return not not options.h + return not not options.h end -- clean options for move (as opposed to copy) options = { - cmd = "mv", - f = options.f, - i = options.i, - v = options.v, - n = options.n, -- no clobber - skip = options.skip, - P = true, -- move operations always preserve - r = true, -- move is allowed to move entire dirs - x = true, -- cannot move mount points + cmd = "mv", + f = options.f, + i = options.i, + v = options.v, + n = options.n, -- no clobber + skip = options.skip, + P = true, -- move operations always preserve + r = true, -- move is allowed to move entire dirs + x = true, -- cannot move mount points } return transfer.batch(args, options) diff --git a/shellex/rm.lua b/shellex/rm.lua index b019c1e..e04a26e 100644 --- a/shellex/rm.lua +++ b/shellex/rm.lua @@ -3,20 +3,20 @@ local glob = require('shellex.glob') local shell = require("shellex.shell") local function usage() - print("Usage: rm [options] [ [...]]"..[[ + print("Usage: rm [options] [ [...]]"..[[ - -f ignore nonexistent files and arguments, never prompt - -r remove directories and their contents recursively - -v explain what is being done - --help display this help and exit + -f ignore nonexistent files and arguments, never prompt + -r remove directories and their contents recursively + -v explain what is being done + --help display this help and exit For complete documentation and more options, run: man rm]]) end local args, options = shell.parse(...) if #args == 0 or options.help then - usage() - return 1 + usage() + return 1 end local bRec = options.r or options.R or options.recursive @@ -29,15 +29,15 @@ bVerbose = bVerbose and not bForce promptLevel = bForce and 0 or promptLevel local function perr(...) - if not bForce then - io.stderr:write(...) - end + if not bForce then + io.stderr:write(...) + end end local function pout(...) - if not bForce then - io.stdout:write(...) - end + if not bForce then + io.stdout:write(...) + end end local metas = {} @@ -53,111 +53,111 @@ local function _readonly(m) return not _exists(m) or fs.get(_path(m)).isReadOnly local function _empty(m) return _exists(m) and _dir(m) and (fs.list(_path(m))==nil) end local function createMeta(origin, rel) - local m = {origin=origin,rel=rel:gsub("/+$", "")} - if _dir(m) then - m.rel = m.rel .. '/' - end - return m + local m = {origin=origin,rel=rel:gsub("/+$", "")} + if _dir(m) then + m.rel = m.rel .. '/' + end + return m end local function unlink(path) - fs.remove(path) - return true + fs.remove(path) + return true end local function confirm() - if bForce then - return true - end - local r = io.read() - return r == 'y' or r == 'yes' + if bForce then + return true + end + local r = io.read() + return r == 'y' or r == 'yes' end local function remove_all(parent) - if parent == nil or not _dir(parent) or _empty(parent) then - return true - end + if parent == nil or not _dir(parent) or _empty(parent) then + return true + end - local all_ok = true - if bRec and promptLevel == 1 then - pout(string.format("rm: descend into directory `%s'? ", parent.rel)) - if not confirm() then - return false - end + local all_ok = true + if bRec and promptLevel == 1 then + pout(string.format("rm: descend into directory `%s'? ", parent.rel)) + if not confirm() then + return false + end - for file in fs.list(_path(parent)) do - local child = createMeta(parent.origin, parent.rel .. file) - all_ok = remove(child) and all_ok - -- uh ? - end - end + for file in fs.list(_path(parent)) do + local child = createMeta(parent.origin, parent.rel .. file) + all_ok = remove(child) and all_ok + -- uh ? + end + end - return all_ok + return all_ok end remove = function(meta) - if not remove_all(meta) then - return false - end + if not remove_all(meta) then + return false + end - if not _exists(meta) then - perr(string.format("rm: cannot remove `%s': No such file or directory\n", meta.rel)) - return false - elseif _dir(meta) and not bRec and not (_empty(meta) and bEmptyDirs) then - if not bEmptyDirs then - perr(string.format("rm: cannot remove `%s': Is a directory\n", meta.rel)) - else - perr(string.format("rm: cannot remove `%s': Directory not empty\n", meta.rel)) - end - return false - end + if not _exists(meta) then + perr(string.format("rm: cannot remove `%s': No such file or directory\n", meta.rel)) + return false + elseif _dir(meta) and not bRec and not (_empty(meta) and bEmptyDirs) then + if not bEmptyDirs then + perr(string.format("rm: cannot remove `%s': Is a directory\n", meta.rel)) + else + perr(string.format("rm: cannot remove `%s': Directory not empty\n", meta.rel)) + end + return false + end - local ok = true - if promptLevel == 1 then - if _dir(meta) then - pout(string.format("rm: remove directory `%s'? ", meta.rel)) - elseif meta.link then - pout(string.format("rm: remove symbolic link `%s'? ", meta.rel)) - else -- file - pout(string.format("rm: remove regular file `%s'? ", meta.rel)) - end + local ok = true + if promptLevel == 1 then + if _dir(meta) then + pout(string.format("rm: remove directory `%s'? ", meta.rel)) + elseif meta.link then + pout(string.format("rm: remove symbolic link `%s'? ", meta.rel)) + else -- file + pout(string.format("rm: remove regular file `%s'? ", meta.rel)) + end - ok = confirm() - end + ok = confirm() + end - if ok then - if _readonly(meta) then - perr(string.format("rm: cannot remove `%s': Is read only\n", meta.rel)) - return false - elseif not unlink(_path(meta)) then - perr(meta.rel .. ": failed to be removed\n") - ok = false - elseif bVerbose then - pout("removed '" .. meta.rel .. "'\n"); - end - end + if ok then + if _readonly(meta) then + perr(string.format("rm: cannot remove `%s': Is read only\n", meta.rel)) + return false + elseif not unlink(_path(meta)) then + perr(meta.rel .. ": failed to be removed\n") + ok = false + elseif bVerbose then + pout("removed '" .. meta.rel .. "'\n"); + end + end - return ok + return ok end for _,arg in ipairs(args) do - local files = glob.matches(shell.getWorkingDirectory(), arg) - for _, v in pairs(files) do - metas[#metas+1] = createMeta(v, v) - end + local files = glob.matches(shell.getWorkingDirectory(), arg) + for _, v in pairs(files) do + metas[#metas+1] = createMeta(v, v) + end end if promptLevel == 3 and #metas > 3 then - pout(string.format("rm: remove %i arguments? ", #metas)) - if not confirm() then - return - end + pout(string.format("rm: remove %i arguments? ", #metas)) + if not confirm() then + return + end end local ok = true for _,meta in ipairs(metas) do - local result = remove(meta) - ok = ok and result + local result = remove(meta) + ok = ok and result end return bForce or ok diff --git a/shellex/rmdir.lua b/shellex/rmdir.lua index 21d3130..0922198 100644 --- a/shellex/rmdir.lua +++ b/shellex/rmdir.lua @@ -5,26 +5,26 @@ local text = require("shellex.text") local args, options = shell.parse(...) local function usage() - print( + print( [[Usage: rmdir [OPTION]... DIRECTORY... Removes the DIRECTORY(ies), if they are empty. - -q, --ignore-fail-on-non-empty - ignore failures due solely to non-empty directories - -p, --parents remove DIRECTORY and its empty ancestors - e.g. 'rmdir -p a/b/c' is similar to 'rmdir a/b/c a/b a' - -v, --verbose output a diagnostic for every directory processed - --help display this help and exit]]) + -q, --ignore-fail-on-non-empty + ignore failures due solely to non-empty directories + -p, --parents remove DIRECTORY and its empty ancestors + e.g. 'rmdir -p a/b/c' is similar to 'rmdir a/b/c a/b a' + -v, --verbose output a diagnostic for every directory processed + --help display this help and exit]]) end if options.help then - usage() - return 0 + usage() + return 0 end if #args == 0 then - io.stderr:write("rmdir: missing operand\n") - return 1 + io.stderr:write("rmdir: missing operand\n") + return 1 end options.p = options.p or options.parents @@ -33,72 +33,72 @@ options.q = options.q or options['ignore-fail-on-non-empty'] local ec = 0 local function ec_bump() - ec = 1 - return 1 + ec = 1 + return 1 end local function remove(path, ...) - -- check to end recursion - if path == nil then - return true - end + -- check to end recursion + if path == nil then + return true + end - if options.v then - print(string.format('rmdir: removing directory, %s', path)) - end + if options.v then + print(string.format('rmdir: removing directory, %s', path)) + end - local rpath = shell.resolve(path) - if path == '.' then - io.stderr:write('rmdir: failed to remove directory \'.\': Invalid argument\n') - return ec_bump() - elseif not fs.exists(rpath) then - io.stderr:write("rmdir: cannot remove " .. path .. ": path does not exist\n") - return ec_bump() - elseif fs.isLink(rpath) or not fs.isDirectory(rpath) then - io.stderr:write("rmdir: cannot remove " .. path .. ": not a directory\n") - return ec_bump() - else - local list, reason = fs.list(rpath) + local rpath = shell.resolve(path) + if path == '.' then + io.stderr:write('rmdir: failed to remove directory \'.\': Invalid argument\n') + return ec_bump() + elseif not fs.exists(rpath) then + io.stderr:write("rmdir: cannot remove " .. path .. ": path does not exist\n") + return ec_bump() + elseif fs.isLink(rpath) or not fs.isDirectory(rpath) then + io.stderr:write("rmdir: cannot remove " .. path .. ": not a directory\n") + return ec_bump() + else + local list, reason = fs.list(rpath) - if not list then - io.stderr:write(tostring(reason)..'\n') - return ec_bump() - else - if list() then - if not options.q then - io.stderr:write("rmdir: failed to remove " .. path .. ": Directory not empty\n") - end - return ec_bump() - else - -- path exists and is empty? - local ok, reason = fs.remove(rpath) - if not ok then - io.stderr:write(tostring(reason)..'\n') - return ec_bump(), reason - end - return remove(...) -- the final return of all else - end - end - end + if not list then + io.stderr:write(tostring(reason)..'\n') + return ec_bump() + else + if list() then + if not options.q then + io.stderr:write("rmdir: failed to remove " .. path .. ": Directory not empty\n") + end + return ec_bump() + else + -- path exists and is empty? + local ok, reason = fs.remove(rpath) + if not ok then + io.stderr:write(tostring(reason)..'\n') + return ec_bump(), reason + end + return remove(...) -- the final return of all else + end + end + end end for _,path in ipairs(args) do - -- clean up the input - path = path:gsub('/+', '/') + -- clean up the input + path = path:gsub('/+', '/') - local segments = {} - if options.p and path:len() > 1 and path:find('/') then - local chain = text.split(path, {'/'}, true) - local prefix = '' - for _,e in ipairs(chain) do - table.insert(segments, 1, prefix .. e) - prefix = prefix .. e .. '/' - end - else - segments = {path} - end + local segments = {} + if options.p and path:len() > 1 and path:find('/') then + local chain = text.split(path, {'/'}, true) + local prefix = '' + for _,e in ipairs(chain) do + table.insert(segments, 1, prefix .. e) + prefix = prefix .. e .. '/' + end + else + segments = {path} + end - remove(table.unpack(segments)) + remove(table.unpack(segments)) end return ec diff --git a/shellex/time.lua b/shellex/time.lua index 7abfb2b..ff198df 100644 --- a/shellex/time.lua +++ b/shellex/time.lua @@ -4,8 +4,8 @@ local sh = require('shellex.sh') local real_before, cpu_before = computer.uptime(), os.clock() local cmd_result = 0 if ... then - sh.execute(nil, ...) - cmd_result = sh.getLastExitCode() + sh.execute(nil, ...) + cmd_result = sh.getLastExitCode() end local real_after, cpu_after = computer.uptime(), os.clock() diff --git a/shellex/touch.lua b/shellex/touch.lua index ff5a84f..3e5e4e4 100644 --- a/shellex/touch.lua +++ b/shellex/touch.lua @@ -5,50 +5,50 @@ local fs = require("shellex.filesystem") local args, options = shell.parse(...) local function usage() - print( + print( [[Usage: touch [OPTION]... FILE... Update the modification times of each FILE to the current time. A FILE argument that does not exist is created empty, unless -c is supplied. - -c, --no-create do not create any files - --help display this help and exit]]) + -c, --no-create do not create any files + --help display this help and exit]]) end if options.help then - usage() - return 0 + usage() + return 0 elseif #args == 0 then - io.stderr:write("touch: missing operand\n") - return 1 + io.stderr:write("touch: missing operand\n") + return 1 end options.c = options.c or options["no-create"] local errors = 0 for _,arg in ipairs(args) do - local path = shell.resolve(arg) + local path = shell.resolve(arg) - if fs.isDirectory(path) then - io.stderr:write(string.format("`%s' ignored: directories not supported\n", arg)) - else - local real, reason = fs.realPath(path) - if real then - local file - if fs.exists(real) or not options.c then - file = io.open(real, "a") - end - if not file then - real = options.c - reason = "permission denied" - else - file:close() - end - end - if not real then - io.stderr:write(string.format("touch: cannot touch `%s': %s\n", arg, reason)) - errors = 1 - end - end + if fs.isDirectory(path) then + io.stderr:write(string.format("`%s' ignored: directories not supported\n", arg)) + else + local real, reason = fs.realPath(path) + if real then + local file + if fs.exists(real) or not options.c then + file = io.open(real, "a") + end + if not file then + real = options.c + reason = "permission denied" + else + file:close() + end + end + if not real then + io.stderr:write(string.format("touch: cannot touch `%s': %s\n", arg, reason)) + errors = 1 + end + end end return errors diff --git a/shellex/which.lua b/shellex/which.lua index 828ddc2..15aa0d5 100644 --- a/shellex/which.lua +++ b/shellex/which.lua @@ -2,24 +2,24 @@ local shell = require("shellex.shell") local args = shell.parse(...) if #args == 0 then - io.write("Usage: which \n") - return 255 + io.write("Usage: which \n") + return 255 end for i = 1, #args do - local result, reason = shell.resolveProgram(args[i]) + local result, reason = shell.resolveProgram(args[i]) - if not result then - result = shell.getAlias(args[i]) - if result then - result = args[i] .. ": aliased to " .. result - end - end + if not result then + result = shell.getAlias(args[i]) + if result then + result = args[i] .. ": aliased to " .. result + end + end - if result then - print(result) - else - io.stderr:write(args[i] .. ": " .. reason .. "\n") - return 1 - end + if result then + print(result) + else + io.stderr:write(args[i] .. ": " .. reason .. "\n") + return 1 + end end diff --git a/swshop/installPlugin.lua b/swshop/installPlugin.lua index 14024b6..4725205 100644 --- a/swshop/installPlugin.lua +++ b/swshop/installPlugin.lua @@ -8,7 +8,7 @@ local CONFIG_FILE = '/usr/config/milo.state' local config = Util.readTable(CONFIG_FILE) or { } if not config.plugins then - config.plugins = { } + config.plugins = { } end local dir = fs.getDir(shell.getRunningProgram()) diff --git a/swshop/jua.lua b/swshop/jua.lua index 993c965..273b9ec 100644 --- a/swshop/jua.lua +++ b/swshop/jua.lua @@ -5,118 +5,118 @@ eventRegistry = {} timedRegistry = {} local function registerEvent(event, callback) - if eventRegistry[event] == nil then - eventRegistry[event] = {} - end + if eventRegistry[event] == nil then + eventRegistry[event] = {} + end - table.insert(eventRegistry[event], callback) + table.insert(eventRegistry[event], callback) end local function registerTimed(time, repeating, callback) - if repeating then - callback(true) - end + if repeating then + callback(true) + end - table.insert(timedRegistry, { - time = time, - repeating = repeating, - callback = callback, - timer = os.startTimer(time) - }) + table.insert(timedRegistry, { + time = time, + repeating = repeating, + callback = callback, + timer = os.startTimer(time) + }) end local function discoverEvents(event) - local evs = {} - for k,v in pairs(eventRegistry) do - if k == event or string.match(k, event) or event == "*" then - for i,v2 in ipairs(v) do - table.insert(evs, v2) - end - end - end + local evs = {} + for k,v in pairs(eventRegistry) do + if k == event or string.match(k, event) or event == "*" then + for i,v2 in ipairs(v) do + table.insert(evs, v2) + end + end + end - return evs + return evs end function on(event, callback) - registerEvent(event, callback) + registerEvent(event, callback) end function setInterval(callback, time) - registerTimed(time, true, callback) + registerTimed(time, true, callback) end function setTimeout(callback, time) - registerTimed(time, false, callback) + registerTimed(time, false, callback) end function tick() - local eargs = {os.pullEventRaw()} - local event = eargs[1] + local eargs = {os.pullEventRaw()} + local event = eargs[1] - if eventRegistry[event] == nil then - eventRegistry[event] = {} - else - local evs = discoverEvents(event) - for i, v in ipairs(evs) do - v(unpack(eargs)) - end - end + if eventRegistry[event] == nil then + eventRegistry[event] = {} + else + local evs = discoverEvents(event) + for i, v in ipairs(evs) do + v(unpack(eargs)) + end + end - if event == "timer" then - local timer = eargs[2] + if event == "timer" then + local timer = eargs[2] - for i = #timedRegistry, 1, -1 do - local v = timedRegistry[i] - if v.timer == timer then - v.callback(not v.repeating or nil) + for i = #timedRegistry, 1, -1 do + local v = timedRegistry[i] + if v.timer == timer then + v.callback(not v.repeating or nil) - if v.repeating then - v.timer = os.startTimer(v.time) - else - table.remove(timedRegistry, i) - end - end - end - end + if v.repeating then + v.timer = os.startTimer(v.time) + else + table.remove(timedRegistry, i) + end + end + end + end end function run() - os.queueEvent("init") - juaRunning = true - while juaRunning do - tick() - end + os.queueEvent("init") + juaRunning = true + while juaRunning do + tick() + end end function go(func) - on("init", func) - run() + on("init", func) + run() end function stop() - juaRunning = false + juaRunning = false end function await(func, ...) - local args = {...} - local out - local finished - func(function(...) - out = {...} - finished = true - end, unpack(args)) - while not finished do tick() end - return unpack(out) + local args = {...} + local out + local finished + func(function(...) + out = {...} + finished = true + end, unpack(args)) + while not finished do tick() end + return unpack(out) end return { - on = on, - setInterval = setInterval, - setTimeout = setTimeout, - tick = tick, - run = run, - go = go, - stop = stop, - await = await + on = on, + setInterval = setInterval, + setTimeout = setTimeout, + tick = tick, + run = run, + go = go, + stop = stop, + await = await } \ No newline at end of file diff --git a/swshop/k.lua b/swshop/k.lua index a52d0fd..7d0c61c 100644 --- a/swshop/k.lua +++ b/swshop/k.lua @@ -9,163 +9,163 @@ local wsEndpoint = "ws://"..endpoint local httpEndpoint = "http://"..endpoint local function asserttype(var, name, vartype, optional) - if not (type(var) == vartype or optional and type(var) == "nil") then - error(name..": expected "..vartype.." got "..type(var), 3) - end + if not (type(var) == vartype or optional and type(var) == "nil") then + error(name..": expected "..vartype.." got "..type(var), 3) + end end function init(juai, jsoni, wi, ri) - asserttype(juai, "jua", "table") - asserttype(jsoni, "json", "table") - asserttype(wi, "w", "table", true) - asserttype(ri, "r", "table") + asserttype(juai, "jua", "table") + asserttype(jsoni, "json", "table") + asserttype(wi, "w", "table", true) + asserttype(ri, "r", "table") - jua = juai - await = juai.await - json = jsoni - w = wi - r = ri + jua = juai + await = juai.await + json = jsoni + w = wi + r = ri end local function prints(...) - local objs = {...} - for i, obj in ipairs(objs) do - print(textutils.serialize(obj)) - end + local objs = {...} + for i, obj in ipairs(objs) do + print(textutils.serialize(obj)) + end end local function url(call) - return httpEndpoint..call + return httpEndpoint..call end local function api_request(cb, api, data) - local success, url, handle = await(r.request, url(api) .. (api:find("%%?") and "?cc" or "&cc"), {["Content-Type"]="application/json"}, data and json.encode(data)) - if success then - cb(success, json.decode(handle.readAll())) - handle.close() - else - cb(success) - end + local success, url, handle = await(r.request, url(api) .. (api:find("%%?") and "?cc" or "&cc"), {["Content-Type"]="application/json"}, data and json.encode(data)) + if success then + cb(success, json.decode(handle.readAll())) + handle.close() + else + cb(success) + end end local function authorize_websocket(cb, privatekey) - asserttype(cb, "callback", "function") - asserttype(privatekey, "privatekey", "string", true) + asserttype(cb, "callback", "function") + asserttype(privatekey, "privatekey", "string", true) - api_request(function(success, data) - cb(success and data and data.ok, data.url and data.url:gsub("wss:", "ws:") or data) - end, "/ws/start", { - privatekey = privatekey - }) + api_request(function(success, data) + cb(success and data and data.ok, data.url and data.url:gsub("wss:", "ws:") or data) + end, "/ws/start", { + privatekey = privatekey + }) end function address(cb, address) - asserttype(cb, "callback", "function") - asserttype(address, "address", "string") + asserttype(cb, "callback", "function") + asserttype(address, "address", "string") - api_request(function(success, data) - if data.address then - data.address.address = address - end - cb(success and data and data.ok, data.address or data) - end, "/addresses/"..address) + api_request(function(success, data) + if data.address then + data.address.address = address + end + cb(success and data and data.ok, data.address or data) + end, "/addresses/"..address) end function addressTransactions(cb, address, limit, offset) - asserttype(cb, "callback", "function") - asserttype(address, "address", "string") - asserttype(limit, "limit", "number", true) - asserttype(offset, "offset", "number", true) + asserttype(cb, "callback", "function") + asserttype(address, "address", "string") + asserttype(limit, "limit", "number", true) + asserttype(offset, "offset", "number", true) - api_request(function(success, data) - cb(success and data and data.ok, data.transactions or data) - end, "/addresses/"..address.."/transactions?limit="..(limit or 50).."&offset="..(offset or 0)) + api_request(function(success, data) + cb(success and data and data.ok, data.transactions or data) + end, "/addresses/"..address.."/transactions?limit="..(limit or 50).."&offset="..(offset or 0)) end function addressNames(cb, address) - asserttype(cb, "callback", "function") - asserttype(address, "address", "string") + asserttype(cb, "callback", "function") + asserttype(address, "address", "string") - api_request(function(success, data) - cb(success and data and data.ok, data.names or data) - end, "/addresses/"..address.."/names") + api_request(function(success, data) + cb(success and data and data.ok, data.names or data) + end, "/addresses/"..address.."/names") end function addresses(cb, limit, offset) - asserttype(cb, "callback", "function") - asserttype(limit, "limit", "number", true) - asserttype(offset, "offset", "number", true) + asserttype(cb, "callback", "function") + asserttype(limit, "limit", "number", true) + asserttype(offset, "offset", "number", true) - api_request(function(success, data) - cb(success and data and data.ok, data.addresses or data) - end, "/addresses?limit="..(limit or 50).."&offset="..(offset or 0)) + api_request(function(success, data) + cb(success and data and data.ok, data.addresses or data) + end, "/addresses?limit="..(limit or 50).."&offset="..(offset or 0)) end function rich(cb, limit, offset) - asserttype(cb, "callback", "function") - asserttype(limit, "limit", "number", true) - asserttype(offset, "offset", "number", true) + asserttype(cb, "callback", "function") + asserttype(limit, "limit", "number", true) + asserttype(offset, "offset", "number", true) - api_request(function(success, data) - cb(success and data and data.ok, data.addresses or data) - end, "/addresses/rich?limit="..(limit or 50).."&offset="..(offset or 0)) + api_request(function(success, data) + cb(success and data and data.ok, data.addresses or data) + end, "/addresses/rich?limit="..(limit or 50).."&offset="..(offset or 0)) end function transactions(cb, limit, offset) - asserttype(cb, "callback", "function") - asserttype(limit, "limit", "number", true) - asserttype(offset, "offset", "number", true) + asserttype(cb, "callback", "function") + asserttype(limit, "limit", "number", true) + asserttype(offset, "offset", "number", true) - api_request(function(success, data) - cb(success and data and data.ok, data.transactions or data) - end, "/transactions?limit="..(limit or 50).."&offset="..(offset or 0)) + api_request(function(success, data) + cb(success and data and data.ok, data.transactions or data) + end, "/transactions?limit="..(limit or 50).."&offset="..(offset or 0)) end function latestTransactions(cb, limit, offset) - asserttype(cb, "callback", "function") - asserttype(limit, "limit", "number", true) - asserttype(offset, "offset", "number", true) + asserttype(cb, "callback", "function") + asserttype(limit, "limit", "number", true) + asserttype(offset, "offset", "number", true) - api_request(function(success, data) - cb(success and data and data.ok, data.transactions or data) - end, "/transactions/latest?limit="..(limit or 50).."&offset="..(offset or 0)) + api_request(function(success, data) + cb(success and data and data.ok, data.transactions or data) + end, "/transactions/latest?limit="..(limit or 50).."&offset="..(offset or 0)) end function transaction(cb, txid) - asserttype(cb, "callback", "function") - asserttype(txid, "txid", "number") + asserttype(cb, "callback", "function") + asserttype(txid, "txid", "number") - api_request(function(success, data) - cb(success and data and data.ok, data.transaction or data) - end, "/transactions/"..txid) + api_request(function(success, data) + cb(success and data and data.ok, data.transaction or data) + end, "/transactions/"..txid) end function makeTransaction(cb, privatekey, to, amount, metadata) - asserttype(cb, "callback", "function") - asserttype(privatekey, "privatekey", "string") - asserttype(to, "to", "string") - asserttype(amount, "amount", "number") - asserttype(metadata, "metadata", "string", true) + asserttype(cb, "callback", "function") + asserttype(privatekey, "privatekey", "string") + asserttype(to, "to", "string") + asserttype(amount, "amount", "number") + asserttype(metadata, "metadata", "string", true) - api_request(function(success, data) - cb(success and data and data.ok, data.transaction or data) - end, "/transactions", { - privatekey = privatekey, - to = to, - amount = amount, - metadata = metadata - }) + api_request(function(success, data) + cb(success and data and data.ok, data.transaction or data) + end, "/transactions", { + privatekey = privatekey, + to = to, + amount = amount, + metadata = metadata + }) end local wsEventNameLookup = { - blocks = "block", - ownBlocks = "block", - transactions = "transaction", - ownTransactions = "transaction", - names = "name", - ownNames = "name", - ownWebhooks = "webhook", - motd = "motd" + blocks = "block", + ownBlocks = "block", + transactions = "transaction", + ownTransactions = "transaction", + names = "name", + ownNames = "name", + ownWebhooks = "webhook", + motd = "motd" } local wsEvents = {} @@ -176,238 +176,238 @@ local wsEvtRegistry = {} local wsHandleRegistry = {} local function newWsID() - local id = wsReqID - wsReqID = wsReqID + 1 - return id + local id = wsReqID + wsReqID = wsReqID + 1 + return id end local function registerEvent(id, event, callback) - if wsEvtRegistry[id] == nil then - wsEvtRegistry[id] = {} - end + if wsEvtRegistry[id] == nil then + wsEvtRegistry[id] = {} + end - if wsEvtRegistry[id][event] == nil then - wsEvtRegistry[id][event] = {} - end + if wsEvtRegistry[id][event] == nil then + wsEvtRegistry[id][event] = {} + end - table.insert(wsEvtRegistry[id][event], callback) + table.insert(wsEvtRegistry[id][event], callback) end local function registerRequest(id, reqid, callback) - if wsReqRegistry[id] == nil then - wsReqRegistry[id] = {} - end + if wsReqRegistry[id] == nil then + wsReqRegistry[id] = {} + end - wsReqRegistry[id][reqid] = callback + wsReqRegistry[id][reqid] = callback end local function discoverEvents(id, event) - local evs = {} - for k,v in pairs(wsEvtRegistry[id]) do - if k == event or string.match(k, event) or event == "*" then - for i,v2 in ipairs(v) do - table.insert(evs, v2) - end - end - end + local evs = {} + for k,v in pairs(wsEvtRegistry[id]) do + if k == event or string.match(k, event) or event == "*" then + for i,v2 in ipairs(v) do + table.insert(evs, v2) + end + end + end - return evs + return evs end wsEvents.success = function(id, handle) - -- fire success event - wsHandleRegistry[id] = handle - if wsEvtRegistry[id] then - local evs = discoverEvents(id, "success") - for i, v in ipairs(evs) do - v(id, handle) - end - end + -- fire success event + wsHandleRegistry[id] = handle + if wsEvtRegistry[id] then + local evs = discoverEvents(id, "success") + for i, v in ipairs(evs) do + v(id, handle) + end + end end wsEvents.failure = function(id) - -- fire failure event - if wsEvtRegistry[id] then - local evs = discoverEvents(id, "failure") - for i, v in ipairs(evs) do - v(id) - end - end + -- fire failure event + if wsEvtRegistry[id] then + local evs = discoverEvents(id, "failure") + for i, v in ipairs(evs) do + v(id) + end + end end wsEvents.message = function(id, data) - local data = json.decode(data) - --print("msg:"..tostring(data.ok)..":"..tostring(data.type)..":"..tostring(data.id)) - --prints(data) - -- handle events and responses - if wsReqRegistry[id] and wsReqRegistry[id][tonumber(data.id)] then - wsReqRegistry[id][tonumber(data.id)](data) - elseif wsEvtRegistry[id] then - local evs = discoverEvents(id, data.type) - for i, v in ipairs(evs) do - v(data) - end + local data = json.decode(data) + --print("msg:"..tostring(data.ok)..":"..tostring(data.type)..":"..tostring(data.id)) + --prints(data) + -- handle events and responses + if wsReqRegistry[id] and wsReqRegistry[id][tonumber(data.id)] then + wsReqRegistry[id][tonumber(data.id)](data) + elseif wsEvtRegistry[id] then + local evs = discoverEvents(id, data.type) + for i, v in ipairs(evs) do + v(data) + end - if data.event then - local evs = discoverEvents(id, data.event) - for i, v in ipairs(evs) do - v(data) - end - end + if data.event then + local evs = discoverEvents(id, data.event) + for i, v in ipairs(evs) do + v(data) + end + end - local evs2 = discoverEvents(id, "message") - for i, v in ipairs(evs2) do - v(id, data) - end - end + local evs2 = discoverEvents(id, "message") + for i, v in ipairs(evs2) do + v(id, data) + end + end end wsEvents.closed = function(id) - -- fire closed event - if wsEvtRegistry[id] then - local evs = discoverEvents(id, "closed") - for i, v in ipairs(evs) do - v(id) - end - end + -- fire closed event + if wsEvtRegistry[id] then + local evs = discoverEvents(id, "closed") + for i, v in ipairs(evs) do + v(id) + end + end end local function wsRequest(cb, id, type, data) - local reqID = newWsID() - registerRequest(id, reqID, function(data) - cb(data) - end) - data.id = tostring(reqID) - data.type = type - wsHandleRegistry[id].send(json.encode(data)) + local reqID = newWsID() + registerRequest(id, reqID, function(data) + cb(data) + end) + data.id = tostring(reqID) + data.type = type + wsHandleRegistry[id].send(json.encode(data)) end local function barebonesMixinHandle(id, handle) - handle.on = function(event, cb) - registerEvent(id, event, cb) - end + handle.on = function(event, cb) + registerEvent(id, event, cb) + end - return handle + return handle end local function mixinHandle(id, handle) - handle.subscribe = function(cb, event, eventcb) - local data = await(wsRequest, id, "subscribe", { - event = event - }) - registerEvent(id, wsEventNameLookup[event], eventcb) - cb(data.ok, data) - end + handle.subscribe = function(cb, event, eventcb) + local data = await(wsRequest, id, "subscribe", { + event = event + }) + registerEvent(id, wsEventNameLookup[event], eventcb) + cb(data.ok, data) + end - return barebonesMixinHandle(id, handle) + return barebonesMixinHandle(id, handle) end function connect(cb, privatekey, preconnect) - asserttype(cb, "callback", "function") - asserttype(privatekey, "privatekey", "string", true) - asserttype(preconnect, "preconnect", "function", true) - local url - if privatekey then - local success, auth = await(authorize_websocket, privatekey) - url = success and auth or wsEndpoint - end - local id = w.open(wsEvents, url) - if preconnect then - preconnect(id, barebonesMixinHandle(id, {})) - end - registerEvent(id, "success", function(id, handle) - cb(true, mixinHandle(id, handle)) - end) - registerEvent(id, "failure", function(id) - cb(false) - end) + asserttype(cb, "callback", "function") + asserttype(privatekey, "privatekey", "string", true) + asserttype(preconnect, "preconnect", "function", true) + local url + if privatekey then + local success, auth = await(authorize_websocket, privatekey) + url = success and auth or wsEndpoint + end + local id = w.open(wsEvents, url) + if preconnect then + preconnect(id, barebonesMixinHandle(id, {})) + end + registerEvent(id, "success", function(id, handle) + cb(true, mixinHandle(id, handle)) + end) + registerEvent(id, "failure", function(id) + cb(false) + end) end local domainMatch = "^([%l%d-_]*)@?([%l%d-]+).kst$" local commonMetaMatch = "^(.+)=(.+)$" function parseMeta(meta) - asserttype(meta, "meta", "string") - local tbl = {meta={}} + asserttype(meta, "meta", "string") + local tbl = {meta={}} - for m in meta:gmatch("[^;]+") do - if m:match(domainMatch) then - -- print("Matched domain") + for m in meta:gmatch("[^;]+") do + if m:match(domainMatch) then + -- print("Matched domain") - local p1, p2 = m:match("([%l%d-_]*)@"), m:match("@?([%l%d-]+).kst") - tbl.name = p1 - tbl.domain = p2 + local p1, p2 = m:match("([%l%d-_]*)@"), m:match("@?([%l%d-]+).kst") + tbl.name = p1 + tbl.domain = p2 - elseif m:match(commonMetaMatch) then - -- print("Matched common meta") + elseif m:match(commonMetaMatch) then + -- print("Matched common meta") - local p1, p2 = m:match(commonMetaMatch) + local p1, p2 = m:match(commonMetaMatch) - tbl.meta[p1] = p2 + tbl.meta[p1] = p2 - else - -- print("Unmatched standard meta") + else + -- print("Unmatched standard meta") - table.insert(tbl.meta, m) - end - -- print(m) - end - -- print(textutils.serialize(tbl)) - return tbl + table.insert(tbl.meta, m) + end + -- print(m) + end + -- print(textutils.serialize(tbl)) + return tbl end local g = string.gsub sha256 = loadstring(g(g(g(g(g(g(g(g('Sa=XbandSb=XbxWSc=XlshiftSd=unpackSe=2^32SYf(g,h)Si=g/2^hSj=i%1Ui-j+j*eVSYk(l,m)Sn=l/2^mUn-n%1VSo={0x6a09e667Tbb67ae85T3c6ef372Ta54ff53aT510e527fT9b05688cT1f83d9abT5be0cd19}Sp={0x428a2f98T71374491Tb5c0fbcfTe9b5dba5T3956c25bT59f111f1T923f82a4Tab1c5ed5Td807aa98T12835b01T243185beT550c7dc3T72be5d74T80deb1feT9bdc06a7Tc19bf174Te49b69c1Tefbe4786T0fc19dc6T240ca1ccT2de92c6fT4a7484aaT5cb0a9dcT76f988daT983e5152Ta831c66dTb00327c8Tbf597fc7Tc6e00bf3Td5a79147T06ca6351T14292967T27b70a85T2e1b2138T4d2c6dfcT53380d13T650a7354T766a0abbT81c2c92eT92722c85Ta2bfe8a1Ta81a664bTc24b8b70Tc76c51a3Td192e819Td6990624Tf40e3585T106aa070T19a4c116T1e376c08T2748774cT34b0bcb5T391c0cb3T4ed8aa4aT5b9cca4fT682e6ff3T748f82eeT78a5636fT84c87814T8cc70208T90befffaTa4506cebTbef9a3f7Tc67178f2}SYq(r,q)if e-1-r[1] 122 and 101 or byte > 57 and byte + 39 or byte) + local byte = 48 + math.floor(byte / 7) + return string.char(byte + 39 > 122 and 101 or byte > 57 and byte + 39 or byte) end function makev2address(key) - local protein = {} - local stick = sha256(sha256(key)) - local n = 0 - local link = 0 - local v2 = "k" - repeat - if n < 9 then protein[n] = string.sub(stick,0,2) - stick = sha256(sha256(stick)) end - n = n + 1 - until n == 9 - n = 0 - repeat - link = tonumber(string.sub(stick,1+(2*n),2+(2*n)),16) % 9 - if string.len(protein[link]) ~= 0 then - v2 = v2 .. makeaddressbyte(tonumber(protein[link],16)) - protein[link] = '' - n = n + 1 - else - stick = sha256(stick) - end - until n == 9 - return v2 + local protein = {} + local stick = sha256(sha256(key)) + local n = 0 + local link = 0 + local v2 = "k" + repeat + if n < 9 then protein[n] = string.sub(stick,0,2) + stick = sha256(sha256(stick)) end + n = n + 1 + until n == 9 + n = 0 + repeat + link = tonumber(string.sub(stick,1+(2*n),2+(2*n)),16) % 9 + if string.len(protein[link]) ~= 0 then + v2 = v2 .. makeaddressbyte(tonumber(protein[link],16)) + protein[link] = '' + n = n + 1 + else + stick = sha256(stick) + end + until n == 9 + return v2 end function toKristWalletFormat(passphrase) - return sha256("KRISTWALLET"..passphrase).."-000" + return sha256("KRISTWALLET"..passphrase).."-000" end return { - init = init, - address = address, - addressTransactions = addressTransactions, - addressNames = addressNames, - addresses = addresses, - rich = rich, - transactions = transactions, - latestTransactions = latestTransactions, - transaction = transaction, - makeTransaction = makeTransaction, - connect = connect, - parseMeta = parseMeta, - sha256 = sha256, - makeaddressbyte = makeaddressbyte, - makev2address = makev2address, - toKristWalletFormat = toKristWalletFormat + init = init, + address = address, + addressTransactions = addressTransactions, + addressNames = addressNames, + addresses = addresses, + rich = rich, + transactions = transactions, + latestTransactions = latestTransactions, + transaction = transaction, + makeTransaction = makeTransaction, + connect = connect, + parseMeta = parseMeta, + sha256 = sha256, + makeaddressbyte = makeaddressbyte, + makev2address = makev2address, + toKristWalletFormat = toKristWalletFormat } \ No newline at end of file diff --git a/swshop/r.lua b/swshop/r.lua index 5718be4..3982634 100644 --- a/swshop/r.lua +++ b/swshop/r.lua @@ -4,62 +4,62 @@ local idPatt = "#R%d+" callbackRegistry = {} local function gfind(str, patt) - local t = {} - for found in str:gmatch(patt) do - table.insert(t, found) - end + local t = {} + for found in str:gmatch(patt) do + table.insert(t, found) + end - if #t > 0 then - return t - else - return nil - end + if #t > 0 then + return t + else + return nil + end end local function findID(url) - local found = gfind(url, idPatt) - if found then - return tonumber(found[#found]:sub(found[#found]:find("%d+"))) - end + local found = gfind(url, idPatt) + if found then + return tonumber(found[#found]:sub(found[#found]:find("%d+"))) + end end local function newID() - return #callbackRegistry + 1 + return #callbackRegistry + 1 end local function trimID(url) - local found = gfind(url, idPatt) - local s, e = url:find(found[#found]) - return url:sub(1, s-1) + local found = gfind(url, idPatt) + local s, e = url:find(found[#found]) + return url:sub(1, s-1) end function request(callback, url, headers, postData) - local id = newID() - local newUrl = url .. "#R" .. id - http.request(newUrl, postData, headers) - callbackRegistry[id] = callback + local id = newID() + local newUrl = url .. "#R" .. id + http.request(newUrl, postData, headers) + callbackRegistry[id] = callback end function init(jua) - jua = jua - jua.on("http_success", function(event, url, handle) - local id = findID(url) - if id and callbackRegistry[id] then - callbackRegistry[id](true, trimID(url), handle) - table.remove(callbackRegistry, id) - end - end) + jua = jua + jua.on("http_success", function(event, url, handle) + local id = findID(url) + if id and callbackRegistry[id] then + callbackRegistry[id](true, trimID(url), handle) + table.remove(callbackRegistry, id) + end + end) - jua.on("http_failure", function(event, url, handle) - local id = findID(url) - if id and callbackRegistry[id] then - callbackRegistry[id](false, trimID(url), handle) - table.remove(callbackRegistry, id) - end - end) + jua.on("http_failure", function(event, url, handle) + local id = findID(url) + if id and callbackRegistry[id] then + callbackRegistry[id](false, trimID(url), handle) + table.remove(callbackRegistry, id) + end + end) end return { - request = request, - init = init + request = request, + init = init } \ No newline at end of file diff --git a/swshop/shopConfig.lua b/swshop/shopConfig.lua index 671fcc2..b71c566 100644 --- a/swshop/shopConfig.lua +++ b/swshop/shopConfig.lua @@ -6,75 +6,75 @@ local os = _G.os --[[ Configuration Page ]]-- local wizardPage = UI.WizardPage { - title = 'Store Front', - index = 2, - backgroundColor = colors.cyan, - form = UI.Form { - x = 2, ex = -2, y = 1, ey = -2, - manualControls = true, + title = 'Store Front', + index = 2, + backgroundColor = colors.cyan, + form = UI.Form { + x = 2, ex = -2, y = 1, ey = -2, + manualControls = true, [1] = UI.TextEntry { formLabel = 'Domain', formKey = 'domain', help = 'Krist wallet domain (minus .kst)', limit = 64, - shadowText = 'example', - required = true, + shadowText = 'example', + required = true, }, [2] = UI.TextEntry { formLabel = 'Password', formKey = 'password', shadowText = 'password', limit = 256, - required = true, + required = true, help = 'Krist wallet password', }, [3] = UI.TextEntry { formLabel = 'Header', formKey = 'header', help = 'Text to show in header', limit = 64, - shadowText = "xxxx's shop", - required = false, + shadowText = "xxxx's shop", + required = false, }, [4] = UI.Checkbox { formLabel = 'Is private key', formKey = 'isPrivateKey', help = 'Password is in private key format', limit = 64, }, - [5] = UI.Chooser { - width = 9, - formLabel = 'Font Size', formKey = 'textScale', - nochoice = 'Small', - choices = { - { name = 'Small', value = .5 }, - { name = 'Large', value = 1 }, - }, - help = 'Adjust text scaling', - }, - }, + [5] = UI.Chooser { + width = 9, + formLabel = 'Font Size', formKey = 'textScale', + nochoice = 'Small', + choices = { + { name = 'Small', value = .5 }, + { name = 'Large', value = 1 }, + }, + help = 'Adjust text scaling', + }, + }, } function wizardPage:setNode(node) - self.form:setValues(node) + self.form:setValues(node) end function wizardPage:validate() - return self.form:save() + return self.form:save() end function wizardPage:saveNode(node) - os.queueEvent('shop_restart', node) + os.queueEvent('shop_restart', node) end function wizardPage:isValidType(node) - local m = device[node.name] - return m and m.type == 'monitor' and { - name = 'Store Front', - value = 'shop', - category = 'display', - help = 'Add a store front display' - } + local m = device[node.name] + return m and m.type == 'monitor' and { + name = 'Store Front', + value = 'shop', + category = 'display', + help = 'Add a store front display' + } end function wizardPage:isValidFor(node) - return node.mtype == 'shop' + return node.mtype == 'shop' end UI:getPage('nodeWizard').wizard:add({ storeFront = wizardPage }) diff --git a/swshop/shopTab.lua b/swshop/shopTab.lua index ad5d14e..9bec226 100644 --- a/swshop/shopTab.lua +++ b/swshop/shopTab.lua @@ -7,69 +7,69 @@ local os = _G.os local config = Config.load('shop') local shopTab = UI.Tab { - tabTitle = 'Store', - index = 2, - form = UI.Form { - x = 2, ex = -2, y = 1, ey = -2, - manualControls = true, - [1] = UI.TextEntry { - formLabel = 'Name', formKey = 'name', - help = 'Unique name used when paying for an item', - required = true, - width = 12, - limit = 64, - }, - [2] = UI.TextEntry { - width = 6, - formLabel = 'Price', formKey = 'price', - help = 'Per item cost', - required = true, - validate = 'numeric', - }, - [3] = UI.TextEntry { - limit = 64, - formLabel = 'Extra Info', formKey = 'info', - help = 'Additional info to display for item', - }, - clearButton = UI.Button { - x = 2, y = -2, - event = 'clear', - text = 'Remove', - }, - updateButton = UI.Button { - x = -12, y = -2, - event = 'update', - text = 'Update', - }, - }, + tabTitle = 'Store', + index = 2, + form = UI.Form { + x = 2, ex = -2, y = 1, ey = -2, + manualControls = true, + [1] = UI.TextEntry { + formLabel = 'Name', formKey = 'name', + help = 'Unique name used when paying for an item', + required = true, + width = 12, + limit = 64, + }, + [2] = UI.TextEntry { + width = 6, + formLabel = 'Price', formKey = 'price', + help = 'Per item cost', + required = true, + validate = 'numeric', + }, + [3] = UI.TextEntry { + limit = 64, + formLabel = 'Extra Info', formKey = 'info', + help = 'Additional info to display for item', + }, + clearButton = UI.Button { + x = 2, y = -2, + event = 'clear', + text = 'Remove', + }, + updateButton = UI.Button { + x = -12, y = -2, + event = 'update', + text = 'Update', + }, + }, } function shopTab:setItem(item) - self.item = item - self.form:setValues(config[item.key] or { }) + self.item = item + self.form:setValues(config[item.key] or { }) end function shopTab:eventHandler(event) - if event.type == 'clear' then - self.form:setValues({ }) - config[self.item.key] = nil - Config.update('shop', config) - os.queueEvent('shop_refresh') - self.form:draw() + if event.type == 'clear' then + self.form:setValues({ }) + config[self.item.key] = nil + Config.update('shop', config) + os.queueEvent('shop_refresh') + self.form:draw() - elseif event.type == 'update' then - if self.form:save() then - Map.removeMatches(config, { name = self.form.values.name }) - config[self.item.key] = self.form.values - Config.update('shop', config) - os.queueEvent('shop_refresh') - self:emit({ type = 'success_message', message = 'Updated' }) - end + elseif event.type == 'update' then + if self.form:save() then + Map.removeMatches(config, { name = self.form.values.name }) + config[self.item.key] = self.form.values + Config.update('shop', config) + os.queueEvent('shop_refresh') + self:emit({ type = 'success_message', message = 'Updated' }) + end - else - return - end - return true + else + return + end + return true end return { itemTab = shopTab } diff --git a/swshop/shopView-example.lua b/swshop/shopView-example.lua index 5971284..c97205c 100644 --- a/swshop/shopView-example.lua +++ b/swshop/shopView-example.lua @@ -15,75 +15,75 @@ local shopTab --[[ Display ]]-- local function showListing(node) - local mon = node.adapter - local list = Milo:listItems() + local mon = node.adapter + local list = Milo:listItems() - mon.clear() - local i = 1 + mon.clear() + local i = 1 - for k,v in pairs(config) do - local item = list[k] - if item and item.count > 0 then - mon.setCursorPos(1, i) - mon.write(string.format('%d %s: %d kst', v.count, v.displayName, v.price)) - mon.setCursorPos(1, i + 1) - mon.write(v.info) - i = i + 2 - end - end + for k,v in pairs(config) do + local item = list[k] + if item and item.count > 0 then + mon.setCursorPos(1, i) + mon.write(string.format('%d %s: %d kst', v.count, v.displayName, v.price)) + mon.setCursorPos(1, i + 1) + mon.write(v.info) + i = i + 2 + end + end end -- everything below is important to keep local function startShop(node) - if shopTab then - multishell.terminate(shopTab) - end - shopTab = shell.openTab('/packages/swshop/swshop.lua', node.domain, node.password) + if shopTab then + multishell.terminate(shopTab) + end + shopTab = shell.openTab('/packages/swshop/swshop.lua', node.domain, node.password) end -- node has been reconfigured Event.on('shop_restart', function(_, node) - startShop(node) + startShop(node) end) -- milo is being terminated Event.on('terminate', function() - if shopTab then - multishell.terminate(shopTab) - shopTab = nil - end + if shopTab then + multishell.terminate(shopTab) + shopTab = nil + end end) -- called when an item to sell has been changed Event.on('shop_refresh', function() - config = Config.load('shop') + config = Config.load('shop') end) -- called from the shop when an item has been purchased Event.on('shop_provide', function(_, item, quantity, uid) - Milo:queueRequest({ }, function() - local count = Milo:eject(itemDB:splitKey(item), quantity) - os.queueEvent('shop_provided', uid, count) - end) + Milo:queueRequest({ }, function() + local count = Milo:eject(itemDB:splitKey(item), quantity) + os.queueEvent('shop_provided', uid, count) + end) end) --[[ Task ]]-- local StoreTask = { - name = 'shop', - priority = 30, + name = 'shop', + priority = 30, } function StoreTask:cycle(context) - local node = context.storage:filterActive('shop')() - if node then - -- a monitor has been configured - if not shopTab then - -- first time running - startShop(node) - end + local node = context.storage:filterActive('shop')() + if node then + -- a monitor has been configured + if not shopTab then + -- first time running + startShop(node) + end - showListing(node) - end + showListing(node) + end end Milo:registerTask(StoreTask) diff --git a/swshop/shopView.lua b/swshop/shopView.lua index 2b306ab..41b23db 100644 --- a/swshop/shopView.lua +++ b/swshop/shopView.lua @@ -15,205 +15,205 @@ local config = Config.load('shop') local shopTab local function startShop(node) - if shopTab then - multishell.terminate(shopTab) - end - shopTab = shell.openTab('/packages/swshop/swshop.lua', node.domain, node.password, node.isPrivateKey and 'true') + if shopTab then + multishell.terminate(shopTab) + end + shopTab = shell.openTab('/packages/swshop/swshop.lua', node.domain, node.password, node.isPrivateKey and 'true') end -- node has been reconfigured Event.on('shop_restart', function(_, node) - startShop(node) + startShop(node) end) -- milo is being terminated Event.on('terminate', function() - if shopTab then - multishell.terminate(shopTab) - shopTab = nil - end + if shopTab then + multishell.terminate(shopTab) + shopTab = nil + end end) --[[ Display ]]-- local function createPage(node) - local monitor = UI.Device { - device = node.adapter, - textScale = node.textScale or .5, - } + local monitor = UI.Device { + device = node.adapter, + textScale = node.textScale or .5, + } - function monitor:resize() - self.textScale = node.textScale or .5 - UI.Device.resize(self) - end + function monitor:resize() + self.textScale = node.textScale or .5 + UI.Device.resize(self) + end - local page = UI.Page { - parent = monitor, - header = UI.Window { - backgroundColor = colors.cyan, - ey = 3, - }, - grid = UI.Grid { - y = 4, ey = -7, - headerHeight = 3, - headerBackgroundColor = colors.gray, - backgroundSelectedColor = colors.black, - unfocusedBackgroundSelectedColor = colors.gray, - columns = { - { heading = 'Stock', key = 'count', width = 6, align = 'right' }, - { heading = 'Name', key = 'displayName' }, - { heading = ' Price', key = 'price', width = 9, align = 'right' }, - { heading = 'Address', key = 'address', width = 12 }, - }, - sortColumn = 'displayName', - }, - footer = UI.Window { - y = -6, - backgroundColor = colors.gray, - prevButton = UI.Button { - x = 2, y = 3, height = 3, width = 5, - event = 'previous', - backgroundColor = colors.lightGray, - text = ' \017 ', - }, - nextButton = UI.Button { - x = -6, y = 3, height = 3, width = 5, - event = 'next', - backgroundColor = colors.lightGray, - text = ' \016 ', - }, - info = UI.Window { - x = 9, ex = -9, - textColor = colors.white, - } - }, - timestamp = os.clock(), - } + local page = UI.Page { + parent = monitor, + header = UI.Window { + backgroundColor = colors.cyan, + ey = 3, + }, + grid = UI.Grid { + y = 4, ey = -7, + headerHeight = 3, + headerBackgroundColor = colors.gray, + backgroundSelectedColor = colors.black, + unfocusedBackgroundSelectedColor = colors.gray, + columns = { + { heading = 'Stock', key = 'count', width = 6, align = 'right' }, + { heading = 'Name', key = 'displayName' }, + { heading = ' Price', key = 'price', width = 9, align = 'right' }, + { heading = 'Address', key = 'address', width = 12 }, + }, + sortColumn = 'displayName', + }, + footer = UI.Window { + y = -6, + backgroundColor = colors.gray, + prevButton = UI.Button { + x = 2, y = 3, height = 3, width = 5, + event = 'previous', + backgroundColor = colors.lightGray, + text = ' \017 ', + }, + nextButton = UI.Button { + x = -6, y = 3, height = 3, width = 5, + event = 'next', + backgroundColor = colors.lightGray, + text = ' \016 ', + }, + info = UI.Window { + x = 9, ex = -9, + textColor = colors.white, + } + }, + timestamp = os.clock(), + } - function page.header:draw() - self:clear() - if node.header then - self:centeredWrite(2, node.header, nil, colors.white) - end - self:write(self.width - 15, 3, 'powered by Milo', nil, colors.gray) - end + function page.header:draw() + self:clear() + if node.header then + self:centeredWrite(2, node.header, nil, colors.white) + end + self:write(self.width - 15, 3, 'powered by Milo', nil, colors.gray) + end - function page.footer.info:draw() - self:clear() - local selected = page.grid:getSelected() + function page.footer.info:draw() + self:clear() + local selected = page.grid:getSelected() - if selected then - if selected.info then - self:centeredWrite(2, selected.info) - end + if selected then + if selected.info then + self:centeredWrite(2, selected.info) + end - self:centeredWrite(4, 'To purchase:') - self:centeredWrite(5, string.format('/pay %s@%s.kst ', selected.name, node.domain)) - end - end + self:centeredWrite(4, 'To purchase:') + self:centeredWrite(5, string.format('/pay %s@%s.kst ', selected.name, node.domain)) + end + end - function page.grid:getRowTextColor(row, selected) - if selected then - return colors.yellow - end - return UI.Grid:getRowTextColor(row, selected) - end + function page.grid:getRowTextColor(row, selected) + if selected then + return colors.yellow + end + return UI.Grid:getRowTextColor(row, selected) + end - function page.grid:getDisplayValues(row) - row = Util.shallowCopy(row) - row.count = Util.toBytes(row.count) .. ' ' - row.price = string.format('%s kst ', row.price) - row.address = row.name - return row - end + function page.grid:getDisplayValues(row) + row = Util.shallowCopy(row) + row.count = Util.toBytes(row.count) .. ' ' + row.price = string.format('%s kst ', row.price) + row.address = row.name + return row + end - function page:eventHandler(event) - if event.type == 'next' then - self.grid:emit({ type = 'scroll_down' }) + function page:eventHandler(event) + if event.type == 'next' then + self.grid:emit({ type = 'scroll_down' }) - elseif event.type == 'previous' then - self.grid:emit({ type = 'scroll_up' }) + elseif event.type == 'previous' then + self.grid:emit({ type = 'scroll_up' }) - elseif event.type == 'grid_focus_row' then - self.footer:draw() + elseif event.type == 'grid_focus_row' then + self.footer:draw() - else - return UI.Page.eventHandler(self, event) - end + else + return UI.Page.eventHandler(self, event) + end - Event.onTimeout(.1, function() - self:setFocus(self.grid) - self:sync() - end) - return true - end + Event.onTimeout(.1, function() + self:setFocus(self.grid) + self:sync() + end) + return true + end - function page:refresh() - local list = Milo:listItems() - self.grid.values = { } - for k,v in pairs(config) do - local item = list[k] - if item and item.count > 0 then - table.insert(self.grid.values, { - displayName = item.displayName, - count = item.count, - name = v.name, - price = v.price, - info = v.info, - }) - end - end - self.grid:update() - self.grid:draw() - end + function page:refresh() + local list = Milo:listItems() + self.grid.values = { } + for k,v in pairs(config) do + local item = list[k] + if item and item.count > 0 then + table.insert(self.grid.values, { + displayName = item.displayName, + count = item.count, + name = v.name, + price = v.price, + info = v.info, + }) + end + end + self.grid:update() + self.grid:draw() + end - function page:update() - page:refresh() - page:sync() - end + function page:update() + page:refresh() + page:sync() + end - local chars = { '\183', '\7', '\186', '\7' } - Event.onInterval(1, function() - local ch = chars[math.floor(os.clock() % #chars) + 1] - page.header:write(2, 2, ch) - page.header:write(page.header.width - 1, 2, ch) - page:sync() - end) + local chars = { '\183', '\7', '\186', '\7' } + Event.onInterval(1, function() + local ch = chars[math.floor(os.clock() % #chars) + 1] + page.header:write(2, 2, ch) + page.header:write(page.header.width - 1, 2, ch) + page:sync() + end) - UI:setPage(page) - return page + UI:setPage(page) + return page end local pages = { } -- called when an item to sell has been changed Event.on('shop_refresh', function() - config = Config.load('shop') + config = Config.load('shop') end) -- called from the shop when an item has been purchased Event.on('shop_provide', function(_, item, quantity, uid) - Milo:queueRequest({ }, function() - local count = Milo:eject(itemDB:splitKey(item), quantity) - os.queueEvent('shop_provided', uid, count) - Sound.play('entity.player.levelup') - end) + Milo:queueRequest({ }, function() + local count = Milo:eject(itemDB:splitKey(item), quantity) + os.queueEvent('shop_provided', uid, count) + Sound.play('entity.player.levelup') + end) end) --[[ Task ]]-- local StoreTask = { - name = 'shop', - priority = 30, + name = 'shop', + priority = 30, } function StoreTask:cycle(context) - for node in context.storage:filterActive('shop') do - if not pages[node.name] then - startShop(node) - pages[node.name] = createPage(node) - end - -- update the display - pages[node.name]:update() - end + for node in context.storage:filterActive('shop') do + if not pages[node.name] then + startShop(node) + pages[node.name] = createPage(node) + end + -- update the display + pages[node.name]:update() + end end Milo:registerTask(StoreTask) diff --git a/swshop/swshop.lua b/swshop/swshop.lua index 3eaddbc..5b17427 100644 --- a/swshop/swshop.lua +++ b/swshop/swshop.lua @@ -27,7 +27,7 @@ w.init(jua) k.init(jua, json, w, r) local function Syntax() - error('Syntax: swshop [domain] [password | privateKey] [isPrivateKey]') + error('Syntax: swshop [domain] [password | privateKey] [isPrivateKey]') end local args = { ... } @@ -38,125 +38,125 @@ local address = k.makev2address(privatekey) local transactions = Util.readTable('/usr/swshop.log') or { } jua.on("terminate", function() - rs.setOutput('top', false) - jua.stop() - _G.printError("Terminated") + rs.setOutput('top', false) + jua.stop() + _G.printError("Terminated") end) local function getItemDetails(item) - local f = fs.open('usr/config/shop', "r") - if f then - local t = f.readAll() - f.close() - t = textutils.unserialize(t) - for key, v in pairs(t) do - if v.name == item then - return key, v.price - end - end - end + local f = fs.open('usr/config/shop', "r") + if f then + local t = f.readAll() + f.close() + t = textutils.unserialize(t) + for key, v in pairs(t) do + if v.name == item then + return key, v.price + end + end + end end local function logTransaction(transaction, details) - transaction = Util.shallowCopy(transaction) - Util.merge(transaction, details) - table.insert(transactions, transaction) - Util.writeTable('/usr/swshop.log', transactions) + transaction = Util.shallowCopy(transaction) + Util.merge(transaction, details) + table.insert(transactions, transaction) + Util.writeTable('/usr/swshop.log', transactions) end local function handleTransaction(transaction) - local from = transaction.from - local to = transaction.to - local value = transaction.value - if to ~= address or not transaction.metadata then return end + local from = transaction.from + local to = transaction.to + local value = transaction.value + if to ~= address or not transaction.metadata then return end - local metadata = k.parseMeta(transaction.metadata) - if not metadata.domain or metadata.domain ~= domain then return end + local metadata = k.parseMeta(transaction.metadata) + if not metadata.domain or metadata.domain ~= domain then return end - local recipient = metadata.meta and (metadata.meta["return"] or from) or from - print("Handling transaction from ", recipient) - print('purchase: ' .. tostring(metadata.name)) - print('value: ' .. value) + local recipient = metadata.meta and (metadata.meta["return"] or from) or from + print("Handling transaction from ", recipient) + print('purchase: ' .. tostring(metadata.name)) + print('value: ' .. value) - local t = { - to = transaction.to, - from = transaction.from, - value = transaction.value, - id = metadata.name, - } + local t = { + to = transaction.to, + from = transaction.from, + value = transaction.value, + id = metadata.name, + } - local function refundTransaction(amount, reason) - print("Refunding to ", recipient) - await(k.makeTransaction, privatekey, recipient, amount, reason) - logTransaction(t, { refund = amount, reason = reason }) - end + local function refundTransaction(amount, reason) + print("Refunding to ", recipient) + await(k.makeTransaction, privatekey, recipient, amount, reason) + logTransaction(t, { refund = amount, reason = reason }) + end - t.itemId, t.price = getItemDetails(metadata.name) - if not t.itemId or not t.price then - print('invalid item') - logTransaction(t, { reason = 'invalid item' }) - --return refundTransaction(value, "error=Item specified is not valid") - return -- there could be multiple stores... - end + t.itemId, t.price = getItemDetails(metadata.name) + if not t.itemId or not t.price then + print('invalid item') + logTransaction(t, { reason = 'invalid item' }) + --return refundTransaction(value, "error=Item specified is not valid") + return -- there could be multiple stores... + end - if value < t.price then - print('value too low') - return refundTransaction(value, "error=Please pay the price listed on-screen.") - end + if value < t.price then + print('value too low') + return refundTransaction(value, "error=Please pay the price listed on-screen.") + end - local count = math.floor(value / t.price) - local uid = math.random() - print(string.format('requesting %d of %s', count, t.itemId)) - os.queueEvent('shop_provide', t.itemId, count, uid) - local timerId = os.startTimer(60) - while true do - local e, p1, p2 = os.pullEvent() - if e == 'timer' and p1 == timerId then - print('timed out waiting for provide') - refundTransaction(value, "error=Timed out attempting to provide items") - break + local count = math.floor(value / t.price) + local uid = math.random() + print(string.format('requesting %d of %s', count, t.itemId)) + os.queueEvent('shop_provide', t.itemId, count, uid) + local timerId = os.startTimer(60) + while true do + local e, p1, p2 = os.pullEvent() + if e == 'timer' and p1 == timerId then + print('timed out waiting for provide') + refundTransaction(value, "error=Timed out attempting to provide items") + break - elseif e == 'shop_provided' and p1 == uid then - local extra = value - (t.price * p2) - logTransaction(t, { purchased = p2 }) - if chat and chat.tell then - local msg = string.format('PURCHASE: %s bought %d %s for %s', - recipient, p2, t.itemId, t.price * p2) - pcall(chat.tell, msg) - end - if extra > 0 then - print('extra: ' .. extra) - refundTransaction(extra, "message=Here's your change!") - end - break - end - end + elseif e == 'shop_provided' and p1 == uid then + local extra = value - (t.price * p2) + logTransaction(t, { purchased = p2 }) + if chat and chat.tell then + local msg = string.format('PURCHASE: %s bought %d %s for %s', + recipient, p2, t.itemId, t.price * p2) + pcall(chat.tell, msg) + end + if extra > 0 then + print('extra: ' .. extra) + refundTransaction(extra, "message=Here's your change!") + end + break + end + end end local function connect() - print('opening store for: ' .. domain) + print('opening store for: ' .. domain) - local success, ws = await(k.connect, privatekey) - assert(success, "Failed to get websocket URL") + local success, ws = await(k.connect, privatekey) + assert(success, "Failed to get websocket URL") - print("Connected to websocket.") - rs.setOutput('top', true) + print("Connected to websocket.") + rs.setOutput('top', true) - success = await(ws.subscribe, "ownTransactions", function(data) - local transaction = data.transaction - handleTransaction(transaction) - end) - assert(success, "Failed to subscribe to event") + success = await(ws.subscribe, "ownTransactions", function(data) + local transaction = data.transaction + handleTransaction(transaction) + end) + assert(success, "Failed to subscribe to event") end local s, m = pcall(function() - jua.go(function() - print("Ready") - connect() - end) + jua.go(function() + print("Ready") + connect() + end) end) rs.setOutput('top', false) if not s then - error(m, 2) + error(m, 2) end diff --git a/swshop/w.lua b/swshop/w.lua index e6532b5..d157992 100644 --- a/swshop/w.lua +++ b/swshop/w.lua @@ -2,134 +2,134 @@ local jua = nil local idPatt = "#R%d+" if not ((socket and socket.websocket) or http.websocketAsync) then - error("You do not have CC:Tweaked/CCTweaks installed or you are not on the latest version.") + error("You do not have CC:Tweaked/CCTweaks installed or you are not on the latest version.") end local newws = socket and socket.websocket or http.websocketAsync local async if socket and socket.websocket then - async = false + async = false else - async = true + async = true end callbackRegistry = {} wsRegistry = {} local function gfind(str, patt) - local t = {} - for found in str:gmatch(patt) do - table.insert(t, found) - end + local t = {} + for found in str:gmatch(patt) do + table.insert(t, found) + end - if #t > 0 then - return t - else - return nil - end + if #t > 0 then + return t + else + return nil + end end local function findID(url) - local found = gfind(url, idPatt) - local id = tonumber(found[#found]:sub(found[#found]:find("%d+"))) - return id + local found = gfind(url, idPatt) + local id = tonumber(found[#found]:sub(found[#found]:find("%d+"))) + return id end local function newID() - return #callbackRegistry + 1 + return #callbackRegistry + 1 end local function trimID(url) - local found = gfind(url, idPatt) - local s, e = url:find(found[#found]) - return url:sub(1, s-1) + local found = gfind(url, idPatt) + local s, e = url:find(found[#found]) + return url:sub(1, s-1) end function open(callback, url, headers) - local id - if async then - id = newID() - end - local newUrl - if async then - newUrl = url .. "#R" .. id - newws(newUrl, headers) - else - if headers then - error("Websocket headers not supported under CCTweaks") - end - local ws = newws(url) - ws.send = ws.write - id = ws.id() - wsRegistry[id] = ws - end - callbackRegistry[id] = callback - return id + local id + if async then + id = newID() + end + local newUrl + if async then + newUrl = url .. "#R" .. id + newws(newUrl, headers) + else + if headers then + error("Websocket headers not supported under CCTweaks") + end + local ws = newws(url) + ws.send = ws.write + id = ws.id() + wsRegistry[id] = ws + end + callbackRegistry[id] = callback + return id end function init(jua) - jua = jua - if async then - jua.on("websocket_success", function(event, url, handle) - local id = findID(url) - if id and callbackRegistry[id] and callbackRegistry[id].success then - callbackRegistry[id].success(id, handle) - end - end) + jua = jua + if async then + jua.on("websocket_success", function(event, url, handle) + local id = findID(url) + if id and callbackRegistry[id] and callbackRegistry[id].success then + callbackRegistry[id].success(id, handle) + end + end) - jua.on("websocket_failure", function(event, url) - local id = findID(url) - if id and callbackRegistry[id] and callbackRegistry[id].failure then - callbackRegistry[id].failure(id) - end - table.remove(callbackRegistry, id) - end) + jua.on("websocket_failure", function(event, url) + local id = findID(url) + if id and callbackRegistry[id] and callbackRegistry[id].failure then + callbackRegistry[id].failure(id) + end + table.remove(callbackRegistry, id) + end) - jua.on("websocket_message", function(event, url, data) - local id = findID(url) - if id and callbackRegistry[id] and callbackRegistry[id].message then - callbackRegistry[id].message(id, data) - end - end) + jua.on("websocket_message", function(event, url, data) + local id = findID(url) + if id and callbackRegistry[id] and callbackRegistry[id].message then + callbackRegistry[id].message(id, data) + end + end) - jua.on("websocket_closed", function(event, url) - local id = findID(url) - if id and callbackRegistry[id] and callbackRegistry[id].closed then - callbackRegistry[id].closed(id) - end - table.remove(callbackRegistry, id) - end) - else - jua.on("socket_connect", function(event, id) - if id and callbackRegistry[id] and callbackRegistry[id].success then - callbackRegistry[id].success(id, wsRegistry[id]) - end - end) + jua.on("websocket_closed", function(event, url) + local id = findID(url) + if id and callbackRegistry[id] and callbackRegistry[id].closed then + callbackRegistry[id].closed(id) + end + table.remove(callbackRegistry, id) + end) + else + jua.on("socket_connect", function(event, id) + if id and callbackRegistry[id] and callbackRegistry[id].success then + callbackRegistry[id].success(id, wsRegistry[id]) + end + end) - jua.on("socket_error", function(event, id, msg) - if id and callbackRegistry[id] and callbackRegistry[id].failure then - callbackRegistry[id].failure(id, msg) - end - table.remove(callbackRegistry, id) - end) + jua.on("socket_error", function(event, id, msg) + if id and callbackRegistry[id] and callbackRegistry[id].failure then + callbackRegistry[id].failure(id, msg) + end + table.remove(callbackRegistry, id) + end) - jua.on("socket_message", function(event, id) - if id and callbackRegistry[id] and callbackRegistry[id].message then - local data = wsRegistry[id].read() - callbackRegistry[id].message(id, data) - end - end) + jua.on("socket_message", function(event, id) + if id and callbackRegistry[id] and callbackRegistry[id].message then + local data = wsRegistry[id].read() + callbackRegistry[id].message(id, data) + end + end) - jua.on("socket_closed", function(event, id) - if id and callbackRegistry[id] and callbackRegistry[id].closed then - callbackRegistry[id].closed(id) - end - table.remove(callbackRegistry, id) - end) - end + jua.on("socket_closed", function(event, id) + if id and callbackRegistry[id] and callbackRegistry[id].closed then + callbackRegistry[id].closed(id) + end + table.remove(callbackRegistry, id) + end) + end end return { - open = open, - init = init + open = open, + init = init } \ No newline at end of file diff --git a/turtle/apis/equipper.lua b/turtle/apis/equipper.lua index d8c13c6..7a43965 100644 --- a/turtle/apis/equipper.lua +++ b/turtle/apis/equipper.lua @@ -1,117 +1,147 @@ +local Config = require('config') + local peripheral = _G.peripheral local turtle = _G.turtle local Equipper = { } +local equipmentList = Config.load('equipment', { + [ 'plethora:scanner' ] = 'plethora:module:2', + [ 'plethora:sensor' ] = 'plethora:module:3', + [ 'plethora:laser' ] = 'plethora:module:1', + [ 'plethora:introspection' ] = 'plethora:module:0', + [ 'plethora:kinetic' ] = 'plethora:module:4', + [ 'advanced_modem' ] = 'computercraft:advanced_modem:0', + [ 'standard_modem' ] = 'computercraft:peripheral:1', +}) + local SCANNER_EQUIPPED = 'plethora:scanner' -local SCANNER_INV = 'plethora:module:2' +local SCANNER_INV = equipmentList[SCANNER_EQUIPPED] or 'unknown' local reversed = { - left = 'right', - right = 'left' + left = 'right', + right = 'left' } local function getEquipped() - Equipper.equipped = { } - Equipper.equipped.left = peripheral.getType('left') - Equipper.equipped.right = peripheral.getType('right') + Equipper.equipped = { } + Equipper.equipped.left = peripheral.getType('left') + Equipper.equipped.right = peripheral.getType('right') - if not Equipper.equipped.left or not Equipper.equipped.right then - -- try to detect non-peripheral type items - such as minecraft:diamond_pickaxe - local side = Equipper.isEquipped(SCANNER_EQUIPPED) - local meta - if side then - meta = peripheral.call(side, 'getBlockMeta', 0, 0, 0) + if not Equipper.equipped.left or not Equipper.equipped.right then + -- try to detect non-peripheral type items - such as minecraft:diamond_pickaxe + local side = Equipper.isEquipped(SCANNER_EQUIPPED) + local meta + if side then + meta = peripheral.call(side, 'getBlockMeta', 0, 0, 0) - elseif turtle.has(SCANNER_INV) then - local swapSide = peripheral.getType('right') == 'modem' and 'left' or 'right' - turtle.equip(swapSide, SCANNER_INV) - Equipper.equipped[swapSide] = 'plethora:scanner' - meta = peripheral.call(swapSide, 'getBlockMeta', 0, 0, 0) - end + elseif turtle.has(SCANNER_INV) then + local swapSide = peripheral.getType('right') == 'modem' and 'left' or 'right' + turtle.equip(swapSide, SCANNER_INV) + Equipper.equipped[swapSide] = 'plethora:scanner' + meta = peripheral.call(swapSide, 'getBlockMeta', 0, 0, 0) + end - if meta then - if not Equipper.equipped.left then - Equipper.equipped.left = meta.turtle.left and meta.turtle.left.id - end - if not Equipper.equipped.right then - Equipper.equipped.right = meta.turtle.right and meta.turtle.right.id - end + if meta then + if not Equipper.equipped.left then + Equipper.equipped.left = meta.turtle.left and meta.turtle.left.id + end + if not Equipper.equipped.right then + Equipper.equipped.right = meta.turtle.right and meta.turtle.right.id + end - elseif not Equipper.equipped.left then - local slot = Equipper.unequip('left') - if slot then - turtle.equip('left', slot.name .. ':' .. slot.damage) - Equipper.equipped.left = slot.name .. ':' .. slot.damage - end + elseif not Equipper.equipped.left then + local slot = Equipper.unequip('left') + if slot then + turtle.equip('left', slot.name .. ':' .. slot.damage) + Equipper.equipped.left = slot.name .. ':' .. slot.damage + end - elseif not Equipper.equipped.right then - local slot = Equipper.unequip('right') - if slot then - turtle.equip('right', slot.name .. ':' .. slot.damage) - Equipper.equipped.right = slot.name .. ':' .. slot.damage - end - end - end + elseif not Equipper.equipped.right then + local slot = Equipper.unequip('right') + if slot then + turtle.equip('right', slot.name .. ':' .. slot.damage) + Equipper.equipped.right = slot.name .. ':' .. slot.damage + end + end + end end local function matches(left, right) - -- return a match for 'minecraft:diamond_sword:0' with 'minecraft:diamond_sword' - if left and right then - return left:match(right) - end + -- return a match for 'minecraft:diamond_sword:0' with 'minecraft:diamond_sword' + if left and right then + return left:match(right) + end end function Equipper.unequip(side) - local slot = turtle.selectOpenSlot() - if not slot then - error('No slots available') - end - turtle.equip(side) - Equipper.equipped[side] = nil - return turtle.getItemDetail(slot) + local slot = turtle.selectOpenSlot() + if not slot then + error('No slots available') + end + turtle.equip(side) + if Equipper.equipped then + Equipper.equipped[side] = nil + end + return turtle.getItemDetail(slot) end function Equipper.isEquipped(name) - if not Equipper.equipped then - getEquipped() - end + if not Equipper.equipped then + getEquipped() + end - return Equipper.equipped.left == name and 'left' or - Equipper.equipped.right == name and 'right' + return Equipper.equipped.left == name and 'left' or + Equipper.equipped.right == name and 'right' end -function Equipper.equip(side, invName, equippedName) - if not Equipper.equipped then - getEquipped() - end - - -- is it already equipped ? - if matches(Equipper.equipped[side], equippedName or invName) then - return peripheral.getType(side) and peripheral.wrap(side) - end - - -- is it equipped on other side ? - if matches(Equipper.equipped[reversed[side]], equippedName or invName) then - Equipper.unequip(reversed[side]) - end - - local s, m = turtle.equip(side, invName) - if not s then - error(string.format('Unable to equip %s\n%s', (equippedName or invName), m)) - end - - Equipper.equipped[side] = peripheral.getType(side) or invName - - return peripheral.getType(side) and peripheral.wrap(side) +-- so convoluted - needs it's own function +function Equipper.equipModem(side) + if peripheral.getType(side) ~= 'modem' then + if peripheral.getType(reversed[side]) then + Equipper.unequip(reversed[side]) + end + if turtle.has(equipmentList['advanced_modem']) then + return Equipper.equip(side, equipmentList['advanced_modem']) + end + if turtle.has(equipmentList['standard_modem']) then + return Equipper.equip(side, equipmentList['standard_modem']) + end + error('Missing modem') + end end -function Equipper.equipLeft(invName, equippedName) - return Equipper.equip('left', invName, equippedName) +function Equipper.equip(side, item) + if not Equipper.equipped then + getEquipped() + end + + -- is it already equipped ? + if matches(Equipper.equipped[side], item) then + return peripheral.getType(side) and peripheral.wrap(side) + end + + -- is it equipped on other side ? + if matches(Equipper.equipped[reversed[side]], item) then + Equipper.unequip(reversed[side]) + end + + local s, m = turtle.equip(side, equipmentList[item] or item) + if not s then + error(string.format('Unable to equip %s\n%s', item, m)) + end + + Equipper.equipped[side] = peripheral.getType(side) or item + + return peripheral.getType(side) and peripheral.wrap(side) end -function Equipper.equipRight(invName, equippedName) - return Equipper.equip('right', invName, equippedName) +function Equipper.equipLeft(item) + return Equipper.equip('left', item) +end + +function Equipper.equipRight(item) + return Equipper.equip('right', item) end return Equipper diff --git a/turtle/autorun/startup.lua b/turtle/autorun/startup.lua index 99c7c90..cc64e0c 100644 --- a/turtle/autorun/startup.lua +++ b/turtle/autorun/startup.lua @@ -1,5 +1,5 @@ if not _G.turtle then - return + return end -- update @@ -20,37 +20,37 @@ fs.mount('sys/apps/system/turtle.lua', 'linkfs', 'packages/turtle/system/turtle. -- provide a turtle function for scanning function turtle.scan(whitelist, blacklist) - local pt = turtle.point + local pt = turtle.point - local scanner = device['plethora:scanner'] or error('Scanner not equipped') + local scanner = device['plethora:scanner'] or error('Scanner not equipped') - if not whitelist and not blacklist then - return Util.each(scanner.scan(), function(b) - b.x = pt.x + b.x - b.y = pt.y + b.y - b.z = pt.z + b.z - end) - end + if not whitelist and not blacklist then + return Util.each(scanner.scan(), function(b) + b.x = pt.x + b.x + b.y = pt.y + b.y + b.z = pt.z + b.z + end) + end - if whitelist then - return Util.filter(scanner.scan(), function(b) - if whitelist[b.name] then - b.x = pt.x + b.x - b.y = pt.y + b.y - b.z = pt.z + b.z - return true - end - end) - end + if whitelist then + return Util.filter(scanner.scan(), function(b) + if whitelist[b.name] then + b.x = pt.x + b.x + b.y = pt.y + b.y + b.z = pt.z + b.z + return true + end + end) + end - return Util.filter(scanner.scan(), function(b) - if not blacklist[b.name] then - b.x = pt.x + b.x - b.y = pt.y + b.y - b.z = pt.z + b.z - return true - end - end) + return Util.filter(scanner.scan(), function(b) + if not blacklist[b.name] then + b.x = pt.x + b.x + b.y = pt.y + b.y + b.z = pt.z + b.z + return true + end + end) end local function getHeading(apt) @@ -62,34 +62,34 @@ local function getHeading(apt) local bpt repeat - if not turtle.inspect() and turtle.forward() then - bpt = GPS.locate() + if not turtle.inspect() and turtle.forward() then + bpt = GPS.locate() break end turtle.turnRight() - until turtle.getHeading() == heading + until turtle.getHeading() == heading - if not bpt then - repeat - if not peripheral.getType('front') then - turtle.dig() - if turtle.forward() then - bpt = GPS.locate() - break - end - end - turtle.turnRight() - until turtle.point.heading == heading - end + if not bpt then + repeat + if not peripheral.getType('front') then + turtle.dig() + if turtle.forward() then + bpt = GPS.locate() + break + end + end + turtle.turnRight() + until turtle.point.heading == heading + end - if not bpt then - return false - end + if not bpt then + return false + end - local turns = (turtle.point.heading - heading) % 4 + local turns = (turtle.point.heading - heading) % 4 - turtle.back() - turtle.setHeading(heading) + turtle.back() + turtle.setHeading(heading) if apt.x < bpt.x then return (0 - turns) % 4 @@ -135,19 +135,19 @@ end -- return to home location if configured to do so if _G.device.wireless_modem then - local config = Config.load('gps') + local config = Config.load('gps') - if config.home then - if not turtle.enableGPS(2) then - error('Unable to get GPS position') - end + if config.home then + if not turtle.enableGPS(2) then + error('Unable to get GPS position') + end - if config.destructive then - turtle.set({ attackPolicy = 'attack', digPolicy = 'turtleSafe' }) - end + if config.destructive then + turtle.set({ attackPolicy = 'attack', digPolicy = 'turtleSafe' }) + end - if not turtle.pathfind(config.home) then - error('Failed to return home') - end - end + if not turtle.pathfind(config.home) then + error('Failed to return home') + end + end end diff --git a/turtle/lavaRefuel.lua b/turtle/lavaRefuel.lua index adfb72c..0b2c249 100644 --- a/turtle/lavaRefuel.lua +++ b/turtle/lavaRefuel.lua @@ -15,7 +15,7 @@ if not turtle.has('minecraft:bucket') then end local swapSide = peripheral.getType('right') == 'modem' and 'left' or 'right' -local scanner = Equipper.equip(swapSide, 'plethora:module:2', 'plethora:scanner') +local scanner = Equipper.equip(swapSide, 'plethora:scanner') if not turtle.select('minecraft:bucket') then error('bucket required') diff --git a/turtle/obsidian.lua b/turtle/obsidian.lua index 3be6e9b..4296c22 100644 --- a/turtle/obsidian.lua +++ b/turtle/obsidian.lua @@ -8,99 +8,99 @@ local checkedNodes, nodes local function addNode(node) - for i = 0, 3 do - local hi = turtle.getHeadingInfo(i) - local testNode = { x = node.x + hi.xd, z = node.z + hi.zd } + for i = 0, 3 do + local hi = turtle.getHeadingInfo(i) + local testNode = { x = node.x + hi.xd, z = node.z + hi.zd } - local key = table.concat({ testNode.x, testNode.z }, ':') - if not checkedNodes[key] then - nodes[key] = testNode - end - end + local key = table.concat({ testNode.x, testNode.z }, ':') + if not checkedNodes[key] then + nodes[key] = testNode + end + end end local function findObsidian() - repeat - local node = { x = turtle.point.x, z = turtle.point.z } - local key = table.concat({ node.x, node.z }, ':') + repeat + local node = { x = turtle.point.x, z = turtle.point.z } + local key = table.concat({ node.x, node.z }, ':') - checkedNodes[key] = true - nodes[key] = nil + checkedNodes[key] = true + nodes[key] = nil - local _,b = turtle.inspectDown() - if b and (b.name == 'minecraft:lava' or b.name == 'minecraft:flowing_lava') then - if turtle.select('minecraft:water_bucket') then - while true do - if turtle.up() then - break - end - print('stuck') - end - turtle.placeDown() - os.sleep(2) - turtle.placeDown() - turtle.down() - turtle.select(1) - _, b = turtle.inspectDown() - end - end + local _,b = turtle.inspectDown() + if b and (b.name == 'minecraft:lava' or b.name == 'minecraft:flowing_lava') then + if turtle.select('minecraft:water_bucket') then + while true do + if turtle.up() then + break + end + print('stuck') + end + turtle.placeDown() + os.sleep(2) + turtle.placeDown() + turtle.down() + turtle.select(1) + _, b = turtle.inspectDown() + end + end - if turtle.getItemCount(16) > 0 then - print('Inventory full') - print('Enter to continue...') - _G.read() - end + if turtle.getItemCount(16) > 0 then + print('Inventory full') + print('Enter to continue...') + _G.read() + end - if b and b.name == 'minecraft:obsidian' then - turtle.digDown() - addNode(node) - else - turtle.digDown() - end + if b and b.name == 'minecraft:obsidian' then + turtle.digDown() + addNode(node) + else + turtle.digDown() + end - print(string.format('%d nodes remaining', Util.size(nodes))) + print(string.format('%d nodes remaining', Util.size(nodes))) - if Util.size(nodes) == 0 then - break - end + if Util.size(nodes) == 0 then + break + end - node = Point.closest(turtle.point, nodes) - if not turtle.go(node) then - break - end - until turtle.isAborted() + node = Point.closest(turtle.point, nodes) + if not turtle.go(node) then + break + end + until turtle.isAborted() end turtle.run(function() - turtle.reset() - turtle.set({ digPolicy = 'dig' }) + turtle.reset() + turtle.set({ digPolicy = 'dig' }) - local s, m = pcall(function() - repeat - checkedNodes = { } - nodes = { } + local s, m = pcall(function() + repeat + checkedNodes = { } + nodes = { } - local _,b = turtle.inspectDown() - if not b or b.name ~= 'minecraft:obsidian' then - break - end + local _,b = turtle.inspectDown() + if not b or b.name ~= 'minecraft:obsidian' then + break + end - findObsidian() - if not turtle.select('minecraft:water_bucket') then - break - end - turtle.go({ x = 0, z = 0 }) - turtle.placeDown() - os.sleep(2) - turtle.placeDown() - turtle.down() - turtle.select(1) - until turtle.isAborted() - end) + findObsidian() + if not turtle.select('minecraft:water_bucket') then + break + end + turtle.go({ x = 0, z = 0 }) + turtle.placeDown() + os.sleep(2) + turtle.placeDown() + turtle.down() + turtle.select(1) + until turtle.isAborted() + end) - if not s and m then - error(m) - end + if not s and m then + error(m) + end - turtle.go({ x = 0, y = 0, z = 0, heading = 0 }) + turtle.go({ x = 0, y = 0, z = 0, heading = 0 }) end) diff --git a/turtle/t.lua b/turtle/t.lua index aef9b51..5a11d57 100644 --- a/turtle/t.lua +++ b/turtle/t.lua @@ -2,92 +2,92 @@ local turtle = _G.turtle local function doCommand(command, moves) - local function format(value) - if type(value) == 'boolean' then - if value then return 'true' end - return 'false' - end - if type(value) ~= 'table' then - return value - end - local str - for k,v in pairs(value) do - if not str then - str = '{ ' - else - str = str .. ', ' - end - str = str .. k .. '=' .. tostring(v) - end - if str then - str = str .. ' }' - else - str = '{ }' - end + local function format(value) + if type(value) == 'boolean' then + if value then return 'true' end + return 'false' + end + if type(value) ~= 'table' then + return value + end + local str + for k,v in pairs(value) do + if not str then + str = '{ ' + else + str = str .. ', ' + end + str = str .. k .. '=' .. tostring(v) + end + if str then + str = str .. ' }' + else + str = '{ }' + end - return str - end + return str + end - local function runCommand(fn, arg) - local r = { fn(arg) } - if r[2] then - print(format(r[1]) .. ': ' .. format(r[2])) - elseif r[1] then - print(format(r[1])) - end - return r[1] - end + local function runCommand(fn, arg) + local r = { fn(arg) } + if r[2] then + print(format(r[1]) .. ': ' .. format(r[2])) + elseif r[1] then + print(format(r[1])) + end + return r[1] + end - local cmds = { - [ 's' ] = turtle.select, - [ 'rf' ] = turtle.refuel, - [ 'gh' ] = function() turtle.pathfind({ x = 0, y = 0, z = 0, heading = 0}) end, - } + local cmds = { + [ 's' ] = turtle.select, + [ 'rf' ] = turtle.refuel, + [ 'gh' ] = function() turtle.pathfind({ x = 0, y = 0, z = 0, heading = 0}) end, + } - local repCmds = { - [ 'u' ] = turtle.up, - [ 'd' ] = turtle.down, - [ 'f' ] = turtle.forward, - [ 'r' ] = turtle.turnRight, - [ 'l' ] = turtle.turnLeft, - [ 'ta' ] = turtle.turnAround, - [ 'el' ] = turtle.equipLeft, - [ 'er' ] = turtle.equipRight, - [ 'DD' ] = turtle.digDown, - [ 'DU' ] = turtle.digUp, - [ 'D' ] = turtle.dig, - [ 'p' ] = turtle.place, - [ 'pu' ] = turtle.placeUp, - [ 'pd' ] = turtle.placeDown, - [ 'b' ] = turtle.back, - [ 'gfl' ] = turtle.getFuelLevel, - [ 'gp' ] = turtle.getPoint, - [ 'R' ] = function() turtle.setPoint({x = 0, y = 0, z = 0, heading = 0}) return turtle.point end - } + local repCmds = { + [ 'u' ] = turtle.up, + [ 'd' ] = turtle.down, + [ 'f' ] = turtle.forward, + [ 'r' ] = turtle.turnRight, + [ 'l' ] = turtle.turnLeft, + [ 'ta' ] = turtle.turnAround, + [ 'el' ] = turtle.equipLeft, + [ 'er' ] = turtle.equipRight, + [ 'DD' ] = turtle.digDown, + [ 'DU' ] = turtle.digUp, + [ 'D' ] = turtle.dig, + [ 'p' ] = turtle.place, + [ 'pu' ] = turtle.placeUp, + [ 'pd' ] = turtle.placeDown, + [ 'b' ] = turtle.back, + [ 'gfl' ] = turtle.getFuelLevel, + [ 'gp' ] = turtle.getPoint, + [ 'R' ] = function() turtle.setPoint({x = 0, y = 0, z = 0, heading = 0}) return turtle.point end + } - if cmds[command] then - runCommand(cmds[command], moves) - elseif repCmds[command] then - for _ = 1, moves do - if not runCommand(repCmds[command]) then - break - end - end - end + if cmds[command] then + runCommand(cmds[command], moves) + elseif repCmds[command] then + for _ = 1, moves do + if not runCommand(repCmds[command]) then + break + end + end + end end local args = {...} if #args > 0 then - doCommand(args[1], args[2] or 1) + doCommand(args[1], args[2] or 1) else - print('Enter command (q to quit):') - while true do - local cmd = _G.read() - if cmd == 'q' then break - end - args = { } - cmd:gsub('%w+', function(w) table.insert(args, w) end) - doCommand(args[1], args[2] or 1) - end + print('Enter command (q to quit):') + while true do + local cmd = _G.read() + if cmd == 'q' then break + end + args = { } + cmd:gsub('%w+', function(w) table.insert(args, w) end) + doCommand(args[1], args[2] or 1) + end end