1
0
mirror of https://github.com/kepler155c/opus synced 2024-12-24 23:50:26 +00:00

reorganization

This commit is contained in:
kepler155c@gmail.com 2017-09-15 01:08:04 -04:00
parent b5ee5db16b
commit 64c68f2662
54 changed files with 175 additions and 15800 deletions

View File

@ -1,135 +0,0 @@
-- Base64 Encoder / Decoder
-- By KillaVanilla
-- see: http://www.computercraft.info/forums2/index.php?/topic/12450-killavanillas-various-apis/
Base64 = { }
local alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
local function sixBitToBase64(input)
return string.sub(alphabet, input+1, input+1)
end
local function base64ToSixBit(input)
for i=1, 64 do
if input == string.sub(alphabet, i, i) then
return i-1
end
end
end
local function octetToBase64(o1, o2, o3)
local i1 = sixBitToBase64(bit.brshift(bit.band(o1, 0xFC), 2))
local i2 = "A"
local i3 = "="
local i4 = "="
if o2 then
i2 = sixBitToBase64(bit.bor( bit.blshift(bit.band(o1, 3), 4), bit.brshift(bit.band(o2, 0xF0), 4) ))
if not o3 then
i3 = sixBitToBase64(bit.blshift(bit.band(o2, 0x0F), 2))
else
i3 = sixBitToBase64(bit.bor( bit.blshift(bit.band(o2, 0x0F), 2), bit.brshift(bit.band(o3, 0xC0), 6) ))
end
else
i2 = sixBitToBase64(bit.blshift(bit.band(o1, 3), 4))
end
if o3 then
i4 = sixBitToBase64(bit.band(o3, 0x3F))
end
return i1..i2..i3..i4
end
-- octet 1 needs characters 1/2
-- octet 2 needs characters 2/3
-- octet 3 needs characters 3/4
local function base64ToThreeOctet(s1)
local c1 = base64ToSixBit(string.sub(s1, 1, 1))
local c2 = base64ToSixBit(string.sub(s1, 2, 2))
local c3 = 0
local c4 = 0
local o1 = 0
local o2 = 0
local o3 = 0
if string.sub(s1, 3, 3) == "=" then
c3 = nil
c4 = nil
elseif string.sub(s1, 4, 4) == "=" then
c3 = base64ToSixBit(string.sub(s1, 3, 3))
c4 = nil
else
c3 = base64ToSixBit(string.sub(s1, 3, 3))
c4 = base64ToSixBit(string.sub(s1, 4, 4))
end
o1 = bit.bor( bit.blshift(c1, 2), bit.brshift(bit.band( c2, 0x30 ), 4) )
if c3 then
o2 = bit.bor( bit.blshift(bit.band(c2, 0x0F), 4), bit.brshift(bit.band( c3, 0x3C ), 2) )
else
o2 = nil
end
if c4 then
o3 = bit.bor( bit.blshift(bit.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
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
end
function Base64.decode(str)
local bytes = {}
local blocks = {}
local blockNum = 1
for i=1, #str, 4 do
blocks[blockNum] = string.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)
if (i % 1000) == 0 then
os.sleep(0)
end
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

View File

@ -1,612 +0,0 @@
local class = require('class')
local Util = require('util')
local TableDB = require('tableDB')
local JSON = require('json')
-- see https://github.com/Khroki/MCEdit-Unified/blob/master/pymclevel/minecraft.yaml
-- see https://github.com/Khroki/MCEdit-Unified/blob/master/Items/minecraft/blocks.json
--[[-- nameDB --]]--
local nameDB = TableDB({
fileName = 'blocknames.db'
})
function nameDB:load(dir, blockDB)
self.fileName = fs.combine(dir, self.fileName)
if fs.exists(self.fileName) then
TableDB.load(self)
end
self.blockDB = blockDB
end
function nameDB:getName(id, dmg)
return self:lookupName(id, dmg) or id .. ':' .. dmg
end
function nameDB:lookupName(id, dmg)
-- is it in the name db ?
local name = self:get({ id, dmg })
if name then
return name
end
-- is it in the block db ?
for _,v in pairs(self.blockDB.data) do
if v.strId == id and v.dmg == dmg then
return v.name
end
end
end
--[[-- blockDB --]]--
local blockDB = TableDB()
function blockDB:load()
local blocks = JSON.decodeFromFile(fs.combine('sys/etc', 'blocks.json'))
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
end
function blockDB:lookup(id, dmg)
if not id then
return
end
return self.data[id .. ':' .. dmg]
end
function blockDB:add(id, dmg, name, strId, place)
local key = id .. ':' .. dmg
TableDB.add(self, key, {
id = id,
dmg = dmg,
name = name,
strId = strId,
place = place,
})
end
--[[-- placementDB --]]--
-- in memory table that expands the standardBlock and blockType tables for each item/dmg/placement combination
local placementDB = TableDB()
function placementDB:load(sbDB, btDB)
for k,blockType in pairs(sbDB.data) do
local bt = btDB.data[blockType]
if not bt then
error('missing block type: ' .. blockType)
end
local id, dmg = string.match(k, '(%d+):*(%d+)')
self:addSubsForBlockType(tonumber(id), tonumber(dmg), bt)
end
end
function placementDB:load2(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
-- 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
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
end
function placementDB:add(id, dmg, sid, sdmg, direction, extra)
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
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
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-block' },
{ 9, nil, 0, 'north-block' },
{ 10, nil, 0, 'east-block' },
{ 11, nil, 0, 'west-block' },
{ 12, nil, 0, 'south-block' },
{ 13, nil, 0, 'north-block' },
{ 14, nil, 0, 'east-block' },
{ 15, nil, 0, 'west-block' },
})
blockTypeDB:addTemp('piston', { -- piston placement is broken in 1.7 -- need to add work around
{ 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
self.nameDB = nameDB
blockDB:load()
-- standardBlockDB:load()
blockTypeDB:load()
nameDB:load(self.dir, blockDB)
-- placementDB:load(standardBlockDB, blockTypeDB)
placementDB:load2(blockDB, blockTypeDB)
-- _G._b = blockDB
-- _G._s = standardBlockDB
-- _G._bt = blockTypeDB
-- _G._p = placementDB
-- Util.writeTable('pb1.lua', placementDB.data)
-- placementDB.data = { }
-- Util.writeTable('pb2.lua', placementDB.data)
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 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
return { id = id, dmg = dmg }
end
return Blocks

View File

@ -1,111 +0,0 @@
local class = require('class')
local Logger = require('logger')
local ChestProvider = class()
function ChestProvider:init(args)
args = args or { }
self.stacks = {}
self.name = 'chest'
self.direction = args.direction or 'up'
self.wrapSide = args.wrapSide or 'bottom'
self.p = peripheral.wrap(self.wrapSide)
end
function ChestProvider:isValid()
return self.p and self.p.getAllStacks
end
function ChestProvider:refresh()
if self.p then
self.p.condenseItems()
self.stacks = self.p.getAllStacks(false)
local t = { }
for _,s in ipairs(self.stacks) do
local key = s.id .. ':' .. s.dmg
if t[key] and t[key].qty < 64 then
t[key].max_size = t[key].qty
else
t[key] = {
qty = s.qty
}
end
end
for _,s in ipairs(self.stacks) do
local key = s.id .. ':' .. s.dmg
if t[key].max_size then
s.max_size = t[key].qty
else
s.max_size = 64
end
end
end
return self.stacks
end
function ChestProvider:getItemInfo(id, dmg)
local item = { id = id, dmg = dmg, qty = 0, max_size = 64 }
for _,stack in pairs(self.stacks) do
if stack.id == id and stack.dmg == dmg then
item.name = stack.display_name
item.qty = item.qty + stack.qty
item.max_size = stack.max_size
end
end
if item.name then
return item
end
end
function ChestProvider:craft(id, dmg, qty)
return false
end
function ChestProvider:craftItems(items)
end
function ChestProvider:provide(item, qty, slot)
if self.p then
self.stacks = self.p.getAllStacks(false)
for key,stack in pairs(self.stacks) do
if stack.id == item.id and stack.dmg == item.dmg then
local amount = math.min(qty, stack.qty)
self.p.pushItemIntoSlot(self.direction, key, amount, slot)
qty = qty - amount
if qty <= 0 then
break
end
end
end
end
end
function ChestProvider:extract(slot, qty)
if self.p then
self.p.pushItem(self.direction, slot, qty)
end
end
function ChestProvider:insert(slot, qty)
if self.p then
local s, m = pcall(function() self.p.pullItem(self.direction, slot, qty) end)
if not s and m then
print('chestProvider:pullItem')
print(m)
Logger.log('chestProvider', 'Insert failed, trying again')
sleep(1)
s, m = pcall(function() self.p.pullItem(self.direction, slot, qty) end)
if not s and m then
print('chestProvider:pullItem')
print(m)
Logger.log('chestProvider', 'Insert failed again')
else
Logger.log('chestProvider', 'Insert successful')
end
end
end
end
return ChestProvider

View File

@ -1,140 +0,0 @@
local class = require('class')
local Util = require('util')
local itemDB = require('itemDB')
local Peripheral = require('peripheral')
local ChestAdapter = class()
local keys = Util.transpose({
'damage',
'displayName',
'maxCount',
'maxDamage',
'name',
'nbtHash',
})
function ChestAdapter:init(args)
local defaults = {
items = { },
name = 'chest',
direction = 'up',
wrapSide = 'bottom',
}
Util.merge(self, defaults)
Util.merge(self, args)
local chest = Peripheral.getBySide(self.wrapSide)
if not chest then
chest = Peripheral.getByMethod('list')
end
if chest then
Util.merge(self, chest)
end
end
function ChestAdapter:isValid()
return not not self.list
end
function ChestAdapter:getCachedItemDetails(item, k)
local key = { item.name, item.damage, item.nbtHash }
local detail = itemDB:get(key)
if not detail then
pcall(function() detail = self.getItemMeta(k) end)
if not detail then
return
end
-- NOT SUFFICIENT
if detail.name ~= item.name then
return
end
for _,k in ipairs(Util.keys(detail)) do
if not keys[k] then
detail[k] = nil
end
end
itemDB:add(key, detail)
end
if detail then
return Util.shallowCopy(detail)
end
end
function ChestAdapter:refresh(throttle)
return self:listItems(throttle)
end
-- provide a consolidated list of items
function ChestAdapter:listItems(throttle)
self.cache = { }
local items = { }
throttle = throttle or Util.throttle()
for k,v in pairs(self.list()) do
local key = table.concat({ v.name, v.damage, v.nbtHash }, ':')
local entry = self.cache[key]
if not entry then
entry = self:getCachedItemDetails(v, k)
if entry then
entry.count = 0
self.cache[key] = entry
table.insert(items, entry)
end
end
if entry then
entry.count = entry.count + v.count
end
throttle()
end
itemDB:flush()
return items
end
function ChestAdapter:getItemInfo(name, damage, nbtHash)
if not self.cache then
self:listItems()
end
local key = table.concat({ name, damage, nbtHash }, ':')
return self.cache[key]
end
function ChestAdapter:craft(name, damage, qty)
end
function ChestAdapter:craftItems(items)
end
function ChestAdapter:provide(item, qty, slot, direction)
local stacks = self.list()
for key,stack in pairs(stacks) do
if stack.name == item.name and stack.damage == item.damage then
local amount = math.min(qty, stack.count)
if amount > 0 then
self.pushItems(direction or self.direction, key, amount, slot)
end
qty = qty - amount
if qty <= 0 then
break
end
end
end
end
function ChestAdapter:extract(slot, qty, toSlot)
self.pushItems(self.direction, slot, qty, toSlot)
end
function ChestAdapter:insert(slot, qty)
self.pullItems(self.direction, slot, qty)
end
return ChestAdapter

View File

@ -1,870 +0,0 @@
--[[
LUA MODULE
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.
DESCRIPTION
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.
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.
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 .
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 .
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 .
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 .
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
gunziplua -h
DEPENDENCIES
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.
'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.
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
LICENSE
(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:
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)
--]]
local M = {_TYPE='module', _NAME='compress.deflatelua', _VERSION='0.3.20111128'}
local assert = assert
local error = error
local ipairs = ipairs
local pairs = pairs
local print = print
local require = require
local tostring = tostring
local type = type
local setmetatable = setmetatable
local io = io
local math = math
local table_sort = table.sort
local math_max = math.max
local string_char = string.char
--[[
Requires the first module listed that exists, else raises like `require`.
If a non-string is encountered, it is returned.
Second return value is module name loaded (or '').
--]]
local function requireany(...)
local errs = {}
for i = 1, select('#', ...) do local name = select(i, ...)
if type(name) ~= 'string' then return name, '' end
local ok, mod = pcall(require, name)
if ok then return mod, name end
errs[#errs+1] = mod
end
error(table.concat(errs, '\n'), 2)
end
--local crc32 = require "digest.crc32lua" . crc32_byte
--local bit, name_ = requireany('bit', 'bit32', 'bit.numberlua', nil)
local bit
local crc32
local DEBUG = false
-- Whether to use `bit` library functions in current module.
-- Unlike the crc32 library, it doesn't make much difference in this module.
local NATIVE_BITOPS = (bit ~= nil)
local band, lshift, rshift
if NATIVE_BITOPS then
band = bit.band
lshift = bit.lshift
rshift = bit.rshift
end
local function warn(s)
io.stderr:write(s, '\n')
end
local function debug(...)
print('DEBUG', ...)
end
local function runtime_error(s, level)
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
end
local function output(outstate, byte)
-- debug('OUTPUT:', s)
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')
end
local function hasbit(bits, 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
end
-- small optimization (lookup table for powers of 2)
local pow2 = memoize(function(n) return 2^n end)
--local tbits = memoize(
-- function(bits)
-- return memoize( function(bit) return getbit(bits, bit) end )
-- end )
-- weak metatable marking objects as bitstream type
local is_bitstream = setmetatable({}, {__mode='k'})
-- DEBUG
-- prints LSB first
--[[
local function bits_tostring(bits, nbits)
local s = ''
local tmp = bits
local function f()
local b = tmp % 2 == 1 and 1 or 0
s = s .. b
tmp = (tmp - b) / 2
end
if nbits then
for i=1,nbits do f() end
else
while tmp ~= 0 do f() end
end
return s
end
--]]
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
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
end
local function bytestream_from_function(f)
local i = 0
local buffer = ''
local o = {}
function o:read()
return f()
-- i = i + 1
-- if i > #buffer then
-- buffer = f()
-- if not buffer then return end
-- i = 1
-- end
-- return buffer:byte(i,i)
end
return o
end
local function bitstream_from_bytestream(bys)
local buf_byte = 0
local buf_nbit = 0
local o = {}
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
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
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
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}
--debug('*',val,nbits)
end
end
else
for i=1,#init-2,2 do
local firstval, nbits, nextval = init[i], init[i+1], init[i+2]
--debug(val, nextval, nbits)
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
--debug('huffman code:', i, s.nbits, s.val, code, bits_tostring(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
--for _,o in ipairs(t) do
-- debug(':', o.nbits, o.val)
--end
-- function t:lookup(bits) return look[bits] 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 i=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)
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
--[[NATIVE_BITOPS
code = lshift(code, 1) + b -- MSB first
--]]
end
--debug('code?', code, bits_tostring(code))
local val = look[code]
if val then
--debug('FOUND', val)
return val
end
end
end
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 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 DEBUG then
debug("CM=", cm)
debug("FLG=", flg)
debug("MTIME=", mtime)
-- debug("MTIME_str=",os.date("%Y-%m-%d %H:%M:%S",mtime)) -- non-portable
debug("XFL=", xfl)
debug("OS=", os)
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
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_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?
if DEBUG then
debug("CRC16=", crc16)
end
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
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 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
--debug('w', nbits)
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 littable = decode(nlit_codes)
local disttable = decode(ndist_codes)
return littable, disttable
end
local tdecode_len_base
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)
--debug(val, val < 256 and string_char(val))
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
--for i=257,285 do debug('T1',i,t[i]) end
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
--for i=257,285 do debug('T2',i,t[i]) end
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
--for i=0,29 do debug('T3',i,t[i]) end
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
--for i=0,29 do debug('T4',i,t[i]) end
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
--debug('BACK', len, dist)
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 BTYPE_NO_COMPRESSION = 0
local BTYPE_FIXED_HUFFMAN = 1
local BTYPE_DYNAMIC_HUFFMAN = 2
local BTYPE_RESERVED_ = 3
if DEBUG then
debug('bfinal=', bfinal)
debug('btype=', btype)
end
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 i=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
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)
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
parse_gzip_header(bs)
local data_crc32 = 0
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())
local expected_crc32 = bs:read(32)
local isize = bs:read(32) -- ignored
if DEBUG then
debug('crc32=', expected_crc32)
debug('isize=', isize)
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
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 window_size_ = parse_zlib_header(bs)
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
}
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
if DEBUG then
debug('alder32=', expected_adler32)
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
return M

View File

@ -9,8 +9,10 @@ function gitfs.mount(dir, user, repo, branch)
local list = git.list(user, repo, branch)
for path, entry in pairs(list) do
local node = fs.mount(fs.combine(dir, path), 'urlfs', entry.url)
node.size = entry.size
if not fs.exists(fs.combine(dir, path)) then
local node = fs.mount(fs.combine(dir, path), 'urlfs', entry.url)
node.size = entry.size
end
end
end

View File

@ -38,6 +38,11 @@ end
function urlfs.open(node, fn, fl)
if fl == 'w' or fl == 'wb' then
fs.delete(fn)
return fs.open(fn, fl)
end
if fl ~= 'r' and fl ~= 'rb' then
error('Unsupported mode')
end

View File

@ -1,112 +0,0 @@
local input = { }
function input:translate(event, code)
if event == 'key' then
local ch = keys.getName(code)
if ch then
if code == keys.leftCtrl or code == keys.rightCtrl then
self.control = true
self.combo = false
return
end
if code == keys.leftShift or code == keys.rightShift then
self.shift = true
self.combo = false
return
end
if self.shift then
if #ch > 1 then
ch = 'shift-' .. ch
elseif self.control then
-- will create control-X
-- better than shift-control-x
ch = ch:upper()
end
self.combo = true
end
if self.control then
ch = 'control-' .. ch
self.combo = true
-- even return numbers such as
-- control-seven
return ch
end
-- filter out characters that will be processed in
-- the subsequent char event
if ch and #ch > 1 and (code < 2 or code > 11) then
return ch
end
end
elseif event == 'key_up' then
if code == keys.leftCtrl or code == keys.rightCtrl then
self.control = false
elseif code == keys.leftShift or code == keys.rightShift then
self.shift = false
else
return
end
-- only send through the shift / control event if it wasn't
-- used in combination with another event
if not self.combo then
return keys.getName(code)
end
elseif event == 'char' then
if not self.control then
self.combo = true
return event
end
elseif event == 'mouse_click' then
local buttons = { 'mouse_click', 'mouse_rightclick', 'mouse_doubleclick' }
self.combo = true
if self.shift then
return 'shift-' .. buttons[code]
end
return buttons[code]
elseif event == "mouse_scroll" then
local directions = {
[ -1 ] = 'scrollUp',
[ 1 ] = 'scrollDown'
}
return directions[code]
elseif event == 'paste' then
self.combo = true
return event
elseif event == 'mouse_drag' then
return event
end
end
-- can be useful for testing what keys are generated
function input:test()
print('press a key...')
while true do
local e, code = os.pullEvent()
if e == 'char' and code == 'q' then
break
end
local ch = input:translate(e, code)
if ch then
print(e .. ' ' .. code .. ' ' .. ch)
end
end
end
return input

View File

@ -1,39 +0,0 @@
local Util = require('util')
local TableDB = require('tableDB')
local itemDB = TableDB({ fileName = 'usr/etc/items.db' })
function itemDB:get(key)
local item = TableDB.get(self, key)
if item then
return item
end
if key[2] ~= 0 then
item = TableDB.get(self, { key[1], 0, key[3] })
if item and item.maxDamage > 0 then
item = Util.shallowCopy(item)
item.damage = key[2]
item.displayName = string.format('%s (damage: %d)', item.displayName, item.damage)
return item
end
end
end
function itemDB:add(key, item)
if item.maxDamage > 0 then
key = { key[1], 0, key[3] }
end
TableDB.add(self, key, item)
end
function itemDB:makeKey(item)
return { item.name, item.damage, item.nbtHash }
end
itemDB:load()
return itemDB

View File

@ -1,167 +0,0 @@
local Util = require('util')
local ME = {
jobList = { }
}
function ME.setDevice(device)
ME.p = device
--Util.merge(ME, ME.p)
if not device then
error('ME device not attached')
end
for k,v in pairs(ME.p) do
if not ME[k] then
ME[k] = v
end
end
end
function ME.isAvailable()
return not Util.empty(ME.getAvailableItems())
end
-- Strip off color prefix
local function safeString(text)
local val = text:byte(1)
if val < 32 or val > 128 then
local newText = {}
for i = 4, #text do
local 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
end
function ME.getAvailableItems()
local items
pcall(function()
items = ME.p.getAvailableItems('all')
for k,v in pairs(items) do
v.id = v.item.id
v.name = safeString(v.item.display_name)
v.qty = v.item.qty
v.dmg = v.item.dmg
v.max_dmg = v.item.max_dmg
v.nbt_hash = v.item.nbt_hash
end
end)
return items or { }
end
function ME.getItemCount(id, dmg, nbt_hash, ignore_dmg)
local fingerprint = {
id = id,
nbt_hash = nbt_hash,
}
if not ignore_dmg or ignore_dmg ~= 'yes' then
fingerprint.dmg = dmg or 0
end
local item = ME.getItemDetail(fingerprint, false)
if item then
return item.qty
end
return 0
end
function ME.extract(id, dmg, nbt_hash, qty, direction, slot)
dmg = dmg or 0
qty = qty or 1
direction = direction or 'up'
return pcall(function()
local fingerprint = {
dmg = dmg,
id = id,
nbt_hash = nbt_hash
}
return ME.exportItem(fingerprint, direction, qty, slot)
end)
end
function ME.insert(slot, qty, direction)
direction = direction or 'up'
return ME.pullItem(direction, slot, qty)
end
function ME.isCrafting()
local cpus = ME.p.getCraftingCPUs() or { }
for k,v in pairs(cpus) do
if v.busy then
return true
end
end
end
function ME.isCPUAvailable()
local cpus = ME.p.getCraftingCPUs() or { }
local available = false
for cpu,v in pairs(cpus) do
if not v.busy then
available = true
elseif not ME.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 ME.getJobList()
local cpus = ME.p.getCraftingCPUs() or { }
for cpu,v in pairs(cpus) do
if not v.busy then
ME.jobList[cpu] = nil
end
end
return ME.jobList
end
function ME.craft(id, dmg, nbt_hash, qty)
local cpus = ME.p.getCraftingCPUs() or { }
for cpu,v in pairs(cpus) do
if not v.busy then
ME.p.requestCrafting({
id = id,
dmg = dmg or 0,
nbt_hash = nbt_hash,
},
qty or 1,
cpu
)
os.sleep(0) -- tell it to craft, yet it doesn't show busy - try waiting a cycle...
cpus = ME.p.getCraftingCPUs() or { }
if not cpus[cpu].busy then
-- print('sleeping again')
os.sleep(.1) -- sigh
cpus = ME.p.getCraftingCPUs() or { }
end
-- not working :(
if cpus[cpu].busy then
ME.jobList[cpu] = { id = id, dmg = dmg, qty = qty, nbt_hash = nbt_hash }
return true
end
break -- only need to try the first available cpu
end
end
return false
end
return ME

View File

@ -1,157 +0,0 @@
local class = require('class')
local Util = require('util')
local Peripheral = require('peripheral')
local MEProvider = class()
function MEProvider:init(args)
local defaults = {
items = { },
name = 'ME',
}
Util.merge(self, defaults)
Util.merge(self, args)
if self.side then
local mep = peripheral.wrap('bottom')
if mep then
Util.merge(self, mep)
end
else
local mep = Peripheral.getByMethod('getAvailableItems')
if mep then
Util.merge(self, mep)
end
end
local sides = {
top = 'down',
bottom = 'up',
east = 'west',
west = 'east',
north = 'south',
south = 'north',
}
self.oside = sides[self.direction or self.side]
end
function MEProvider:isValid()
return self.getAvailableItems and self.getAvailableItems()
end
-- Strip off color prefix
local function safeString(text)
local val = text:byte(1)
if val < 32 or val > 128 then
local newText = {}
for i = 4, #text do
local 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
end
local convertNames = {
name = 'id',
damage = 'dmg',
maxCount = 'max_size',
count = 'qty',
displayName = 'display_name',
maxDamage = 'max_dmg',
}
local function convertItem(item)
for k,v in pairs(convertNames) do
item[k] = item[v]
item[v] = nil
end
item.displayName = safeString(item.displayName)
end
function MEProvider:refresh()
self.items = self.getAvailableItems('all')
for _,v in pairs(self.items) do
Util.merge(v, v.item)
convertItem(v)
end
return self.items
end
function MEProvider:listItems()
self:refresh()
return self.items
end
function MEProvider:getItemInfo(name, damage)
for key,item in pairs(self.items) do
if item.name == name and item.damage == damage then
return item
end
end
end
function MEProvider:craft(name, damage, count)
self:refresh()
local item = self:getItemInfo(name, damage)
if item and item.is_craftable then
self.requestCrafting({ id = name, dmg = damage }, count)
return true
end
end
function MEProvider:craftItems(items)
local cpus = self.getCraftingCPUs() or { }
local count = 0
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 self:craft(item.name, item.damage, item.count) then
count = count + 1
end
end
end
function MEProvider:provide(item, count, slot)
return pcall(function()
self.exportItem({
id = item.name,
dmg = item.damage
}, self.oside, count, slot)
end)
end
function MEProvider:insert(slot, count)
local s, m = pcall(function() self.pullItem(self.oside, slot, count) end)
if not s and m then
print('MEProvider:pullItem')
print(m)
sleep(1)
s, m = pcall(function() self.pullItem(self.oside, slot, count) end)
if not s and m then
print('MEProvider:pullItem')
print(m)
read()
end
end
end
return MEProvider

View File

@ -1,106 +0,0 @@
local Event = require('event')
local Logger = require('logger')
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
end
if device and device.wireless_modem then
Message.enable()
end
Event.on('device_attach', function(event, deviceName)
if deviceName == 'wireless_modem' then
Message.enable()
end
end)
function Message.addHandler(type, f)
table.insert(messageHandlers, {
type = type,
f = f,
enabled = true
})
end
function Message.removeHandler(h)
for k,v in pairs(messageHandlers) do
if v == h then
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
Logger.log('modem_receive', { id, msg.type })
--Logger.log('modem_receive', msg.contents)
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
)
function Message.send(id, msgType, contents)
if not device.wireless_modem then
error('No modem attached', 2)
end
if id then
Logger.log('modem_send', { tostring(id), msgType })
device.wireless_modem.transmit(id, os.getComputerID(), {
type = msgType, contents = contents
})
else
Logger.log('modem_send', { 'broadcast', msgType })
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
Message.send(nil, t, contents)
-- Logger.log('rednet_send', { 'broadcast', t })
-- rednet.broadcast({ type = t, contents = 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
end
function Message.enableWirelessLogging()
Logger.setWirelessLogging()
end
return Message

View File

@ -172,6 +172,7 @@ function Point.inBox(pt, box)
pt.y >= box.y and
pt.z >= box.z and
pt.x <= box.ex and
pt.y <= box.ey and
pt.z <= box.ez
end

View File

@ -1,51 +0,0 @@
local Util = require('util')
local Logger = require('logger')
local Profile = {
start = function() end,
stop = function() end,
display = function() end,
methods = { },
}
local function Profile_display()
Logger.log('profile', 'Profiling results')
for k,v in pairs(Profile.methods) do
Logger.log('profile', '%s: %f %d %f',
k, Util.round(v.elapsed, 2), v.count, Util.round(v.elapsed/v.count, 2))
end
Profile.methods = { }
end
local function Profile_start(name)
local p = Profile.methods[name]
if not p then
p = { }
p.elapsed = 0
p.count = 0
Profile.methods[name] = p
end
p.clock = os.clock()
return p
end
local function Profile_stop(name)
local p = Profile.methods[name]
p.elapsed = p.elapsed + (os.clock() - p.clock)
p.count = p.count + 1
end
function Profile.enable()
Logger.log('profile', 'Profiling enabled')
Profile.start = Profile_start
Profile.stop = Profile_stop
Profile.display = Profile_display
end
function Profile.disable()
Profile.start = function() end
Profile.stop = function() end
Profile.display = function() end
end
return Profile

View File

@ -1,143 +0,0 @@
local class = require('class')
local Util = require('util')
local Peripheral = require('peripheral')
local itemDB = require('itemDB')
local RefinedAdapter = class()
local keys = {
'damage',
'displayName',
'maxCount',
'maxDamage',
'name',
'nbtHash',
}
function RefinedAdapter:init(args)
local defaults = {
items = { },
name = 'refinedStorage',
}
Util.merge(self, defaults)
Util.merge(self, args)
local controller = Peripheral.getByType('refinedstorage:controller')
if controller then
Util.merge(self, controller)
end
end
function RefinedAdapter:isValid()
return not not self.listAvailableItems
end
function RefinedAdapter:isOnline()
return self.getNetworkEnergyStored() > 0
end
function RefinedAdapter:getCachedItemDetails(item)
local key = { item.name, item.damage, item.nbtHash }
local detail = itemDB:get(key)
if not detail then
detail = self.findItem(item)
if detail then
local meta
pcall(function() meta = detail.getMetadata() end)
if not meta then
return
end
Util.merge(detail, meta)
local t = { }
for _,k in pairs(keys) do
t[k] = detail[k]
end
detail = t
itemDB:add(key, detail)
end
end
if detail then
return Util.shallowCopy(detail)
end
end
function RefinedAdapter:listItems()
local items = { }
local list
pcall(function()
list = self.listAvailableItems()
end)
if list then
local throttle = Util.throttle()
for _,v in pairs(list) do
local item = self:getCachedItemDetails(v)
if item then
item.count = v.count
table.insert(items, item)
end
throttle()
end
itemDB:flush()
end
return items
end
function RefinedAdapter:getItemInfo(fingerprint)
local key = { fingerprint.name, fingerprint.damage, fingerprint.nbtHash }
local item = itemDB:get(key)
if not item then
return self:getCachedItemDetails(fingerprint)
end
local detail = self.findItem(item)
if detail then
item.count = detail.count
return item
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
end
function RefinedAdapter:craft(item, qty)
local detail = self.findItem(item)
if detail then
return detail.craft(qty)
end
end
function RefinedAdapter:craftItems(items)
return false
end
function RefinedAdapter:provide(item, qty, slot)
end
function RefinedAdapter:extract(slot, qty)
-- self.pushItems(self.direction, slot, qty)
end
function RefinedAdapter:insert(slot, qty)
-- self.pullItems(self.direction, slot, qty)
end
return RefinedAdapter

File diff suppressed because it is too large Load Diff

View File

@ -1,54 +0,0 @@
local class = require('class')
local Util = require('util')
local TableDB = class()
function TableDB:init(args)
local defaults = {
fileName = '',
dirty = false,
data = { },
tabledef = { },
}
Util.merge(defaults, args)
Util.merge(self, defaults)
end
function TableDB:load()
local table = Util.readTable(self.fileName)
if table then
self.data = table.data
self.tabledef = table.tabledef
end
end
function TableDB:add(key, entry)
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]
end
function TableDB:remove(key)
self.data[key] = nil
self.dirty = true
end
function TableDB:flush()
if self.dirty then
Util.writeTable(self.fileName, {
-- tabledef = self.tabledef,
data = self.data,
})
self.dirty = false
end
end
return TableDB

View File

@ -1,166 +0,0 @@
local itemDB = require('itemDB')
local Util = require('util')
local Craft = { }
local function clearGrid(chestAdapter)
for i = 1, 16 do
local count = turtle.getItemCount(i)
if count > 0 then
chestAdapter:insert(i, count)
if turtle.getItemCount(i) ~= 0 then
return false
end
end
end
return true
end
local function splitKey(key)
local t = Util.split(key, '(.-):')
local item = { }
if #t[#t] > 2 then
item.nbtHash = table.remove(t)
end
item.damage = tonumber(table.remove(t))
item.name = table.concat(t, ':')
return item
end
local function getItemCount(items, key)
local item = splitKey(key)
for _,v in pairs(items) do
if v.name == item.name and
v.damage == item.damage and
v.nbtHash == item.nbtHash then
return v.count
end
end
return 0
end
local function turtleCraft(recipe, qty, chestAdapter)
clearGrid(chestAdapter)
for k,v in pairs(recipe.ingredients) do
local item = splitKey(v)
chestAdapter:provide(item, qty, k)
if turtle.getItemCount(k) == 0 then -- ~= qty then
-- FIX: ingredients cannot be stacked
return false
end
end
return turtle.craft()
end
function Craft.craftRecipe(recipe, count, chestAdapter)
local items = chestAdapter:listItems()
local function sumItems(items)
-- produces { ['minecraft:planks:0'] = 8 }
local t = {}
for _,item in pairs(items) do
t[item] = (t[item] or 0) + 1
end
return t
end
count = math.ceil(count / recipe.count)
local maxCount = recipe.maxCount or math.floor(64 / recipe.count)
local summedItems = sumItems(recipe.ingredients)
for key,icount in pairs(summedItems) do
local itemCount = getItemCount(items, key)
if itemCount < icount * count then
local irecipe = Craft.recipes[key]
if irecipe then
Util.print('Crafting %d %s', icount * count - itemCount, key)
if not Craft.craftRecipe(irecipe,
icount * count - itemCount,
chestAdapter) then
turtle.select(1)
return
end
end
end
end
repeat
if not turtleCraft(recipe, math.min(count, maxCount), chestAdapter) then
turtle.select(1)
return false
end
count = count - maxCount
until count <= 0
turtle.select(1)
return true
end
-- given a certain quantity, return how many of those can be crafted
function Craft.getCraftableAmount(recipe, count, items)
local function sumItems(recipe, items, summedItems, count)
local canCraft = 0
for i = 1, count do
for _,item in pairs(recipe.ingredients) do
local summedItem = summedItems[item] or getItemCount(items, item)
local irecipe = Craft.recipes[item]
if irecipe and summedItem <= 0 then
summedItem = summedItem + sumItems(irecipe, items, summedItems, 1)
end
if summedItem <= 0 then
return canCraft
end
summedItems[item] = summedItem - 1
end
canCraft = canCraft + recipe.count
end
return canCraft
end
return sumItems(recipe, items, { }, math.ceil(count / recipe.count))
end
function Craft.canCraft(item, count, items)
return Craft.getCraftableAmount(Craft.recipes[item], count, items) == count
end
function Craft.setRecipes(recipes)
Craft.recipes = recipes
end
function Craft.getCraftableAmountTest()
local results = { }
Craft.setRecipes(Util.readTable('sys/etc/recipes.db'))
local items = {
{ name = 'minecraft:planks', damage = 0, count = 5 },
{ name = 'minecraft:log', damage = 0, count = 2 },
}
results[1] = { item = 'chest', expected = 1, got = Craft.getCraftableAmount(Craft.recipes['minecraft:chest:0'], 2, items) }
items = {
{ name = 'minecraft:log', damage = 0, count = 1 },
{ name = 'minecraft:coal', damage = 1, count = 1 },
}
results[2] = { item = 'torch', expected = 4, got = Craft.getCraftableAmount(Craft.recipes['minecraft:torch:0'], 4, items) }
return results
end
function Craft.craftRecipeTest(name, count)
local ChestAdapter = require('chestAdapter18')
local chestAdapter = ChestAdapter({ wrapSide = 'top', direction = 'down' })
Craft.setRecipes(Util.readTable('usr/etc/recipes.db'))
return { Craft.craftRecipe(Craft.recipes[name], count, chestAdapter) }
end
return Craft

View File

@ -1,165 +0,0 @@
local Point = require('point')
local Util = require('util')
local checkedNodes = { }
local nodes = { }
local box = { }
local oldCallback
local function toKey(pt)
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 }
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',
}
-- 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 }
if Point.inBox(node, box) then
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
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 oldCallback then
oldCallback(action)
end
end
-- find the closest block
-- * favor same plane
-- * going backwards only if the dest is above or below
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
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
return closestPoint(turtle.getPoint(), t)
end
return function(startPt, endPt, firstPt, verbose)
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)
if not turtle.pathfind(firstPt) then
error('failed to reach starting point')
end
turtle.setPolicy("attack", { dig = dig }, "assuredMove")
oldCallback = turtle.getMoveCallback()
turtle.setMoveCallback(move)
repeat
local key = toKey(turtle.point)
checkedNodes[key] = true
nodes[key] = nil
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 Util.size(nodes) == 0 then
break
end
local node = closestPoint(turtle.point, nodes)
node = getAdjacentPoint(node)
if not turtle.gotoPoint(node) then
break
end
until turtle.abort
turtle.resetState()
turtle.setMoveCallback(oldCallback)
end

View File

@ -88,10 +88,6 @@ local function mapDimensions(dest, blocks, boundingBox)
}
end
local function nodeToString(n)
return string.format('%d:%d:%d:%d', n._x, n._y, n._z, n.__heading or 9)
end
-- shifting and coordinate flipping
local function pointToMap(dim, pt)
return { x = pt.x + dim.ox, z = pt.y + dim.oy, y = pt.z + dim.oz }
@ -144,15 +140,34 @@ local function addSensorBlocks(blocks, sblocks)
end
end
local function selectDestination(pts, box, map, dim)
while #pts > 0 do
local pt = Point.closest(turtle.point, pts)
if (box and not Point.inBox(pt, box)) or
map[pt.z + dim.oz][pt.x + dim.ox][pt.y + dim.oy] == 1 then
Util.removeByValue(pts, pt)
else
return pt
end
end
end
local function pathTo(dest, options)
local blocks = options.blocks or { }
local allDests = options.dest or { } -- support alternative destinations
local blocks = options.blocks or turtle.getState().blocks or { }
local dests = options.dest or { dest } -- support alternative destinations
local box = options.box or turtle.getState().box
local lastDim = nil
local map = nil
local grid = nil
if box then
box = Point.normalizeBox(box)
end
-- Creates a pathfinder object
local myFinder = Pathfinder(grid, 'ASTAR', walkable)
@ -162,7 +177,7 @@ local function pathTo(dest, options)
while turtle.point.x ~= dest.x or turtle.point.z ~= dest.z or turtle.point.y ~= dest.y do
-- map expands as we encounter obstacles
local dim = mapDimensions(dest, blocks, options.box)
local dim = mapDimensions(dest, blocks, box)
-- reuse map if possible
if not lastDim or not dimsAreEqual(dim, lastDim) then
@ -179,6 +194,15 @@ local function pathTo(dest, options)
addBlock(map, dim, b)
end
dest = selectDestination(dests, box, map, dim)
if not dest then
error('failed to reach destination')
-- return false, 'failed to reach destination'
end
if turtle.point.x == dest.x and turtle.point.z == dest.z and turtle.point.y == dest.y then
break
end
-- Define start and goal locations coordinates
local startPt = pointToMap(dim, turtle.point)
local endPt = pointToMap(dim, dest)
@ -187,14 +211,8 @@ local function pathTo(dest, options)
local path = myFinder:getPath(startPt.x, startPt.y, startPt.z, turtle.point.heading, endPt.x, endPt.y, endPt.z, dest.heading)
if not path then
Util.removeByValue(allDests, dest)
dest = Point.closest(turtle.point, allDests)
if not dest then
return false, 'failed to recalculate'
end
Util.removeByValue(dests, dest)
else
for node, count in path:nodes() do
local pt = nodeToPoint(dim, node)
@ -206,9 +224,6 @@ local function pathTo(dest, options)
-- when encountering obstacles -- IS THIS RIGHT ??
if not turtle.gotoSingleTurn(pt.x, pt.z, pt.y, node.heading) then
table.insert(blocks, pt)
if #allDests > 0 then
dest = Point.closest(turtle.point, allDests)
end
--if device.turtlesensorenvironment then
-- addSensorBlocks(blocks, device.turtlesensorenvironment.sonicScan())
--end
@ -224,10 +239,27 @@ local function pathTo(dest, options)
return dest
end
return function(dest, options)
options = options or { }
if not options.blocks and turtle.gotoPoint(dest) then
return dest
end
return pathTo(dest, options)
end
return {
pathfind = function(dest, options)
options = options or { }
--if not options.blocks and turtle.gotoPoint(dest) then
-- return dest
--end
return pathTo(dest, options)
end,
-- set a global bounding box
-- box can be overridden by passing box in pathfind options
setBox = function(box)
turtle.getState().box = box
end,
setBlocks = function(blocks)
turtle.getState().blocks = blocks
end,
reset = function()
turtle.getState().box = nil
turtle.getState().blocks = nil
end,
}

View File

@ -1,8 +1,8 @@
local Util = require('util')
local class = require('class')
local Event = require('event')
local Tween = require('tween')
local Region = require('region')
local Tween = require('ui.tween')
local Region = require('ui.region')
local mapColorToGray = {
[ colors.white ] = colors.white,

View File

@ -426,6 +426,10 @@ function Util.matches(str, pattern)
return t
end
function Util.startsWidth(s, match)
return string.sub(s, 1, #match) == match
end
function Util.widthify(s, len)
s = s or ''
local slen = #s

View File

@ -3,14 +3,26 @@ requireInjector(getfenv(1))
local class = require('class')
local Config = require('config')
local Event = require('event')
local FileUI = require('fileui')
local FileUI = require('ui.fileui')
local NFT = require('nft')
local SHA1 = require('sha1')
local Tween = require('tween')
local Tween = require('ui.tween')
local UI = require('ui')
local Util = require('util')
local REGISTRY_DIR = 'usr/.registry'
local TEMPLATE = [[
local env = { }
for k,v in pairs(getfenv(1)) do
env[k] = v
end
setmetatable(env, { __index = _G })
local s, m = os.run(env, 'sys/apps/appRun.lua', %s, ...)
if not s then
error(m)
end
]]
multishell.setTitle(multishell.getCurrent(), 'Overview')
UI:configure('Overview', ...)
@ -39,6 +51,11 @@ local function loadApplications()
Util.each(applications, function(v, k) v.key = k end)
applications = Util.filter(applications, function(_, a) return not a.disabled end)
applications = Util.filter(applications, function(_, a)
return Util.startsWidth(a.run, 'http') or shell.resolveProgram(a.run)
end)
end
loadApplications()
@ -285,6 +302,7 @@ function page:eventHandler(event)
end
Config.update('Overview', config)
multishell.openTab({
title = event.button.app.title,
path = 'sys/apps/shell',
args = { event.button.app.run },
focused = true,

View File

@ -7,7 +7,7 @@ local UI = require('ui')
local Util = require('util')
local GROUPS_PATH = 'usr/groups'
local SCRIPTS_PATH = 'sys/etc/scripts'
local SCRIPTS_PATH = 'usr/etc/scripts'
multishell.setTitle(multishell.getCurrent(), 'Script')
UI:configure('script', ...)

View File

@ -20,8 +20,7 @@ local options = {
desc = 'Displays the options' },
}
local USR_SCRIPTS_PATH = 'usr/scripts'
local SYS_SCRIPTS_PATH = 'sys/etc/scripts'
local SCRIPTS_PATH = 'usr/etc/scripts'
local nullTerm = Terminal.getNullTerm(term.current())
local turtles = { }
@ -228,21 +227,8 @@ end
function page.tabs.scripts:draw()
local function combineDirs(...)
local list = { }
for _,path in pairs({...}) do
if fs.exists(path) then
local files = fs.list(path)
for _,f in pairs(files) do
list[f] = fs.combine(path, f)
end
end
end
return list
end
Util.clear(self.values)
local files = combineDirs(SYS_SCRIPTS_PATH, USR_SCRIPTS_PATH)
local files = fs.list(SCRIPTS_PATH)
for f,path in pairs(files) do
table.insert(self.values, { label = f, path = path })
end

View File

@ -1,36 +0,0 @@
requireInjector(getfenv(1))
Base64 = require('base64')
local args = { ... }
if not args[2] then
error('Syntax: base64dl <file name> <url>')
end
local c = http.get(args[2])
if not c then
error('unable to open url')
end
local data = c.readAll()
c.close()
print('size: ' .. #data)
local decoded = Base64.decode(data)
print('decoded: ' .. #decoded)
local file = io.open(shell.resolve(args[1]), "wb")
if not file then
error('Unable to open ' .. args[1], 2)
end
for k,b in ipairs(decoded) do
if (k % 1000) == 0 then
os.sleep(0)
end
file:write(b)
end
file:close()
print('done')

File diff suppressed because it is too large Load Diff

View File

@ -1,939 +0,0 @@
requireInjector(getfenv(1))
local ChestAdapter = require('chestAdapter18')
local Config = require('config')
local Craft = require('turtle.craft')
local Event = require('event')
local itemDB = require('itemDB')
local Peripheral = require('peripheral')
local RefinedAdapter = require('refinedAdapter')
local Terminal = require('terminal')
local UI = require('ui')
local Util = require('util')
multishell.setTitle(multishell.getCurrent(), 'Resource Manager')
-- 3 wide monitor (any side of turtle)
-- Config location is /sys/config/resourceManager
-- adjust directions in that file if needed
local config = {
trashDirection = 'up', -- trash /chest in relation to chest
turtleDirection = 'down', -- turtle in relation to chest
}
Config.load('resourceManager', config)
local controller = RefinedAdapter()
if not controller:isValid() then
-- error('Refined storage controller not found')
controller = nil
end
local chestAdapter = ChestAdapter({ direction = 'west', wrapSide = 'back' })
local turtleChestAdapter = ChestAdapter({ direction = 'up', wrapSide = 'bottom' })
local RESOURCE_FILE = 'usr/etc/resources.db'
local RECIPES_FILE = 'sys/etc/recipes.db'
local jobListGrid
local craftingPaused = false
local recipes = Util.readTable(RECIPES_FILE) or { }
local resources = Util.readTable(RESOURCE_FILE) or { }
Craft.setRecipes(recipes)
for _,r in pairs(resources) do
r.maxDamage = nil
r.displayName = nil
r.count = nil
r.lname = nil
r.has_recipe = nil
if not r.ignoreDamage then
r.ignoreDamage = nil
end
if not r.auto then
r.auto = nil
end
end
Util.writeTable(RESOURCE_FILE, resources)
local function getItem(items, inItem, ignoreDamage)
for _,item in pairs(items) do
if item.name == inItem.name then
if ignoreDamage then
return item
elseif item.damage == inItem.damage and item.nbtHash == inItem.nbtHash then
return item
end
end
end
end
local function splitKey(key)
local t = Util.split(key, '(.-):')
local item = { }
if #t[#t] > 2 then
item.nbtHash = table.remove(t)
end
item.damage = tonumber(table.remove(t))
item.name = table.concat(t, ':')
return item
end
local function getItemQuantity(items, item)
item = getItem(items, item)
if item then
return item.count
end
return 0
end
local function getItemDetails(items, item)
local cItem = getItem(items, item)
if cItem then
return cItem
end
cItem = itemDB:get(itemDB:makeKey(item))
if cItem then
return { count = 0, maxCount = cItem.maxCount }
end
return { count = 0, maxCount = 64 }
end
local function uniqueKey(item)
return table.concat({ item.name, item.damage, item.nbtHash }, ':')
end
local function getName(item)
local detail = itemDB:get(itemDB:makeKey(item))
if detail then
return detail.displayName
end
return item.name .. ':' .. item.damage
end
local function mergeResources(t)
for _,v in pairs(resources) do
local item = getItem(t, v)
if item then
Util.merge(item, v)
else
item = Util.shallowCopy(v)
item.count = 0
table.insert(t, item)
end
end
for k in pairs(recipes) do
local v = splitKey(k)
local item = getItem(t, v)
if not item then
item = Util.shallowCopy(v)
item.count = 0
table.insert(t, item)
end
item.has_recipe = true
end
for _,v in pairs(t) do
if not v.displayName then
v.displayName = getName(v)
end
v.lname = v.displayName:lower()
end
end
local function filterItems(t, filter)
if filter then
local r = {}
filter = filter:lower()
for k,v in pairs(t) do
if string.find(v.lname, filter) then
table.insert(r, v)
end
end
return r
end
return t
end
local function sumItems3(ingredients, items, summedItems, count)
local canCraft = 0
for _,key in pairs(ingredients) do
local item = splitKey(key)
local summedItem = summedItems[key]
if not summedItem then
summedItem = Util.shallowCopy(item)
summedItem.recipe = recipes[key]
summedItem.count = getItemQuantity(items, summedItem)
summedItems[key] = summedItem
end
summedItem.count = summedItem.count - count
if summedItem.recipe and summedItem.count < 0 then
local need = math.ceil(-summedItem.count / summedItem.recipe.count)
summedItem.count = 0
sumItems3(summedItem.recipe.ingredients, items, summedItems, need)
end
end
end
local function isGridClear()
for i = 1, 16 do
if turtle.getItemCount(i) ~= 0 then
return false
end
end
return true
end
local function clearGrid()
for i = 1, 16 do
local count = turtle.getItemCount(i)
if count > 0 then
chestAdapter:insert(i, count)
if turtle.getItemCount(i) ~= 0 then
return false
end
end
end
return true
end
local function addCraftingRequest(item, craftList, count)
local key = uniqueKey(item)
local request = craftList[key]
if not craftList[key] then
request = { name = item.name, damage = item.damage, nbtHash = nbtHash, count = 0 }
request.displayName = getName(request)
craftList[key] = request
end
request.count = request.count + count
end
local function craftItem(recipe, items, originalItem, craftList, count)
if craftingPaused or not device.workbench or not isGridClear() then
return
end
local toCraft = Craft.getCraftableAmount(recipe, count, items)
if toCraft > 0 then
Craft.craftRecipe(recipe, toCraft, chestAdapter)
clearGrid()
items = chestAdapter:listItems()
end
count = count - toCraft
if count > 0 then
local summedItems = { }
sumItems3(recipe.ingredients, items, summedItems, math.ceil(count / recipe.count))
for key,ingredient in pairs(summedItems) do
if not ingredient.recipe and ingredient.count < 0 then
addCraftingRequest(ingredient, craftList, -ingredient.count)
end
end
end
end
local function craftItems(craftList, allItems)
for _,key in pairs(Util.keys(craftList)) do
local item = craftList[key]
local recipe = recipes[key]
if recipe then
craftItem(recipe, allItems, item, craftList, item.count)
allItems = chestAdapter:listItems() -- refresh counts
elseif item.rsControl then
item.status = 'Activated'
end
end
for key,item in pairs(craftList) do
if not recipes[key] then
if not controller then
item.status = '(no recipe)'
else
if controller:isCrafting(item) then
item.status = '(crafting)'
else
local count = item.count
while count >= 1 do -- try to request smaller quantities until successful
local s, m = pcall(function()
item.status = '(no recipe)'
if not controller:craft(item, count) then
item.status = '(missing ingredients)'
error('failed')
end
item.status = '(crafting)'
end)
if s then
break -- successfully requested crafting
end
count = math.floor(count / 2)
end
end
end
end
end
end
local function jobMonitor(jobList)
local mon = Peripheral.getByType('monitor')
if mon then
mon = UI.Device({
device = mon,
textScale = .5,
})
else
mon = UI.Device({
device = Terminal.getNullTerm(term.current())
})
end
jobListGrid = UI.Grid({
parent = mon,
sortColumn = 'displayName',
columns = {
{ heading = 'Qty', key = 'count', width = 6 },
{ heading = 'Crafting', key = 'displayName', width = mon.width / 2 - 10 },
{ heading = 'Status', key = 'status', width = mon.width - 10 },
},
})
end
local function getAutocraftItems()
local craftList = { }
for _,res in pairs(resources) do
if res.auto then
res.count = 4 -- this could be higher to increase autocrafting speed
local key = uniqueKey(res)
craftList[key] = res
end
end
return craftList
end
local function getItemWithQty(items, res, ignoreDamage)
local item = getItem(items, res, ignoreDamage)
if item then
if ignoreDamage then
local count = 0
for _,v in pairs(items) do
if item.name == v.name and item.nbtHash == v.nbtHash then
if item.maxDamage > 0 or item.damage == v.damage then
count = count + v.count
end
end
end
item.count = count
end
end
return item
end
local function watchResources(items)
local craftList = { }
for k, res in pairs(resources) do
local item = getItemWithQty(items, res, res.ignoreDamage)
if not item then
item = {
damage = res.damage,
nbtHash = res.nbtHash,
name = res.name,
displayName = getName(res),
count = 0
}
end
if res.limit and item.count > res.limit then
chestAdapter:provide(res, item.count - res.limit, nil, config.trashDirection)
elseif res.low and item.count < res.low then
if res.ignoreDamage then
item.damage = 0
end
local key = uniqueKey(res)
craftList[key] = {
damage = item.damage,
nbtHash = item.nbtHash,
count = res.low - item.count,
name = item.name,
displayName = item.displayName,
status = '',
rsControl = res.rsControl,
}
end
if res.rsControl and res.rsDevice and res.rsSide then
pcall(function()
device[res.rsDevice].setOutput(res.rsSide, item.count < res.low)
end)
end
end
return craftList
end
local itemPage = UI.Page {
backgroundColor = colors.lightGray,
titleBar = UI.TitleBar {
title = 'Limit Resource',
previousPage = true,
event = 'form_cancel',
backgroundColor = colors.green
},
displayName = UI.Window {
x = 2, y = 2, width = UI.term.width - 4, height = 3,
},
form = UI.Form {
x = 4, y = 5, height = 8, rex = -4,
[1] = UI.TextEntry {
width = 7,
backgroundColor = colors.gray,
backgroundFocusColor = colors.gray,
formLabel = 'Min', formKey = 'low', help = 'Craft if below min'
},
[2] = UI.TextEntry {
width = 7,
backgroundColor = colors.gray,
backgroundFocusColor = colors.gray,
formLabel = 'Max', formKey = 'limit', help = 'Eject if above max'
},
[3] = UI.Chooser {
width = 7,
formLabel = 'Autocraft', formKey = 'auto',
nochoice = 'No',
choices = {
{ name = 'Yes', value = true },
{ name = 'No', value = false },
},
help = 'Craft until out of ingredients'
},
[4] = UI.Chooser {
width = 7,
formLabel = 'Ignore Dmg', formKey = 'ignore_dmg',
nochoice = 'No',
choices = {
{ name = 'Yes', value = true },
{ name = 'No', value = false },
},
help = 'Ignore damage of item'
},
[5] = UI.Chooser {
width = 7,
formLabel = 'RS Control', formKey = 'rsControl',
nochoice = 'No',
choices = {
{ name = 'Yes', value = true },
{ name = 'No', value = false },
},
help = 'Control via redstone'
},
[6] = UI.Chooser {
width = 25,
formLabel = 'RS Device', formKey = 'rsDevice',
--choices = devices,
help = 'Redstone Device'
},
[7] = UI.Chooser {
width = 10,
formLabel = 'RS Side', formKey = 'rsSide',
--nochoice = 'No',
choices = {
{ name = 'up', value = 'up' },
{ name = 'down', value = 'down' },
{ name = 'east', value = 'east' },
{ name = 'north', value = 'north' },
{ name = 'west', value = 'west' },
{ name = 'south', value = 'south' },
},
help = 'Output side'
},
},
statusBar = UI.StatusBar { }
}
function itemPage.displayName:draw()
local item = self.parent.item
local str = string.format('Name: %s\nDamage: %d', item.displayName, item.damage)
if item.nbtHash then
str = str .. string.format('\n%s', item.nbtHash)
end
self:setCursorPos(1, 1)
self:print(str)
end
function itemPage:enable(item)
self.item = item
self.form:setValues(item)
self.titleBar.title = item.name
local devices = self.form[6].choices
Util.clear(devices)
for _,device in pairs(device) do
if device.setOutput then
table.insert(devices, { name = device.name, value = device.name })
end
end
if Util.size(devices) == 0 then
table.insert(devices, { name = 'None found', values = '' })
end
UI.Page.enable(self)
self:focusFirst()
end
function itemPage:eventHandler(event)
if event.type == 'form_cancel' then
UI:setPreviousPage()
elseif event.type == 'focus_change' then
self.statusBar:setStatus(event.focused.help)
self.statusBar:draw()
elseif event.type == 'form_complete' then
local values = self.form.values
local keys = { 'name', 'auto', 'low', 'limit', 'damage',
'nbtHash', 'ignoreDamage',
'rsControl', 'rsDevice', 'rsSide', }
local filtered = { }
for _,key in pairs(keys) do
filtered[key] = values[key]
end
filtered.low = tonumber(filtered.low)
filtered.limit = tonumber(filtered.limit)
--filtered.ignoreDamage = filtered.ignoreDamage == true
--filtered.auto = filtered.auto == true
--filtered.rsControl = filtered.rsControl == true
if filtered.auto ~= true then
filtered.auto = nil
end
if filtered.rsControl ~= true then
filtered.rsControl = nil
filtered.rsSide = nil
filtered.rsDevice = nil
end
if values.ignoreDamage == true then
filtered.damage = 0
end
resources[uniqueKey(filtered)] = filtered
Util.writeTable(RESOURCE_FILE, resources)
UI:setPreviousPage()
else
return UI.Page.eventHandler(self, event)
end
return true
end
local listingPage = UI.Page {
menuBar = UI.MenuBar {
buttons = {
{ text = 'Learn', event = 'learn' },
{ text = 'Forget', event = 'forget' },
{ text = 'Craft', event = 'craft' },
},
},
grid = UI.Grid {
y = 2, height = UI.term.height - 2,
columns = {
{ heading = 'Name', key = 'displayName' , width = 22 },
{ heading = 'Qty', key = 'count' , width = 5 },
{ heading = 'Min', key = 'low' , width = 4 },
{ heading = 'Max', key = 'limit' , width = 4 },
},
sortColumn = 'displayName',
},
statusBar = UI.StatusBar {
backgroundColor = colors.gray,
width = UI.term.width,
filterText = UI.Text {
x = 2, width = 6,
value = 'Filter',
},
filter = UI.TextEntry {
x = 9, width = 19,
limit = 50,
},
refresh = UI.Button {
x = 31, width = 8,
text = 'Refresh',
event = 'refresh',
},
},
accelerators = {
r = 'refresh',
q = 'quit',
}
}
function listingPage.grid:getRowTextColor(row, selected)
if row.is_craftable then
return colors.yellow
end
if row.has_recipe then
if selected then
return colors.blue
end
return colors.lightBlue
end
return UI.Grid:getRowTextColor(row, selected)
end
function listingPage.grid:getDisplayValues(row)
row = Util.shallowCopy(row)
row.count = Util.toBytes(row.count)
if row.low then
row.low = Util.toBytes(row.low)
end
if row.limit then
row.limit = Util.toBytes(row.limit)
end
return row
end
function listingPage.statusBar:draw()
return UI.Window.draw(self)
end
function listingPage.statusBar.filter:eventHandler(event)
if event.type == 'mouse_rightclick' then
self.value = ''
self:draw()
local page = UI:getCurrentPage()
page.filter = nil
page:applyFilter()
page.grid:draw()
page:setFocus(self)
end
return UI.TextEntry.eventHandler(self, event)
end
function listingPage:eventHandler(event)
if event.type == 'quit' then
UI:exitPullEvents()
elseif event.type == 'grid_select' then
local selected = event.selected
UI:setPage('item', selected)
elseif event.type == 'refresh' then
self:refresh()
self.grid:draw()
self.statusBar.filter:focus()
elseif event.type == 'learn' then
UI:setPage('learn')
elseif event.type == 'craft' then
UI:setPage('craft')
elseif event.type == 'forget' then
local item = self.grid:getSelected()
if item then
local key = uniqueKey(item)
if recipes[key] then
recipes[key] = nil
Util.writeTable(RECIPES_FILE, recipes)
end
if resources[key] then
resources[key] = nil
Util.writeTable(RESOURCE_FILE, resources)
end
self.statusBar:timedStatus('Forgot: ' .. item.name, 3)
self:refresh()
self.grid:draw()
end
elseif event.type == 'text_change' then
self.filter = event.text
if #self.filter == 0 then
self.filter = nil
end
self:applyFilter()
self.grid:draw()
self.statusBar.filter:focus()
else
UI.Page.eventHandler(self, event)
end
return true
end
function listingPage:enable()
self:refresh()
self:setFocus(self.statusBar.filter)
UI.Page.enable(self)
end
function listingPage:refresh()
self.allItems = chestAdapter:listItems()
mergeResources(self.allItems)
self:applyFilter()
end
function listingPage:applyFilter()
local t = filterItems(self.allItems, self.filter)
self.grid:setValues(t)
end
-- without duck antenna
local function getTurtleInventoryOld()
local inventory = { }
for i = 1,16 do
if turtle.getItemCount(i) > 0 then
turtle.select(i)
local item = turtle.getItemDetail()
inventory[i] = {
name = item.name,
damage = item.damage,
count = item.count,
}
end
end
return inventory
end
local function getTurtleInventory()
local inventory = { }
for i = 1,16 do
local qty = turtle.getItemCount(i)
if qty > 0 then
turtleChestAdapter:insert(i, qty)
local items = turtleChestAdapter:listItems()
_, inventory[i] = next(items)
turtleChestAdapter:extract(1, qty, i)
end
end
return inventory
end
local function filter(t, filter)
local keys = Util.keys(t)
for _,key in pairs(keys) do
if not Util.key(filter, key) then
t[key] = nil
end
end
end
local function learnRecipe(page)
local recipe = { }
local ingredients = getTurtleInventory()
if ingredients then
turtle.select(1)
if device.workbench and turtle.craft() then
recipe = getTurtleInventory()
if recipe and recipe[1] then
clearGrid()
local key = uniqueKey(recipe[1])
local newRecipe = {
count = recipe[1].count,
ingredients = ingredients,
}
if recipe[1].maxCount ~= 64 then
newRecipe.maxCount = recipe[1].maxCount
end
for k,ingredient in pairs(ingredients) do
ingredients[k] = uniqueKey(ingredient)
end
recipes[key] = newRecipe
Util.writeTable(RECIPES_FILE, recipes)
local displayName = getName(recipe[1])
listingPage.statusBar.filter:setValue(displayName)
listingPage.statusBar:timedStatus('Learned: ' .. displayName, 3)
listingPage.filter = displayName
listingPage:refresh()
listingPage.grid:draw()
return true
end
else
page.statusBar:timedStatus('Failed to craft', 3)
end
else
page.statusBar:timedStatus('No recipe defined', 3)
end
end
local learnPage = UI.Dialog {
height = 7, width = UI.term.width - 6,
backgroundColor = colors.lightGray,
title = 'Learn Recipe',
idField = UI.Text {
x = 5,
y = 3,
width = UI.term.width - 10,
value = 'Place recipe in turtle'
},
accept = UI.Button {
rx = -13, ry = -2,
text = 'Ok', event = 'accept',
},
cancel = UI.Button {
rx = -8, ry = -2,
text = 'Cancel', event = 'cancel'
},
statusBar = UI.StatusBar {
status = 'Crafting paused'
}
}
function learnPage:enable()
craftingPaused = true
self:focusFirst()
UI.Dialog.enable(self)
end
function learnPage:disable()
craftingPaused = false
UI.Dialog.disable(self)
end
function learnPage:eventHandler(event)
if event.type == 'cancel' then
UI:setPreviousPage()
elseif event.type == 'accept' then
if learnRecipe(self) then
UI:setPreviousPage()
end
else
return UI.Dialog.eventHandler(self, event)
end
return true
end
local craftPage = UI.Dialog {
height = 6, width = UI.term.width - 10,
backgroundColor = colors.lightGray,
title = 'Enter amount to craft',
idField = UI.TextEntry {
x = 15,
y = 3,
width = 10,
limit = 6,
value = '1',
backgroundColor = colors.black,
backgroundFocusColor = colors.black,
},
accept = UI.Button {
rx = -7, ry = -1,
backgroundColor = colors.green,
text = '+', event = 'accept',
},
cancel = UI.Button {
rx = -3, ry = -1,
backgroundColor = colors.red,
backgroundFocusColor = colors.red,
text = '\215', event = 'cancel'
},
}
function craftPage:draw()
UI.Dialog.draw(self)
self:write(6, 3, 'Quantity')
end
function craftPage:enable()
craftingPaused = true
self:focusFirst()
UI.Dialog.enable(self)
end
function craftPage:disable()
craftingPaused = false
UI.Dialog.disable(self)
end
function craftPage:eventHandler(event)
if event.type == 'cancel' then
UI:setPreviousPage()
elseif event.type == 'accept' then
else
return UI.Dialog.eventHandler(self, event)
end
return true
end
UI:setPages({
listing = listingPage,
item = itemPage,
learn = learnPage,
craft = craftPage,
})
UI:setPage(listingPage)
listingPage:setFocus(listingPage.statusBar.filter)
clearGrid()
jobMonitor()
jobListGrid:draw()
jobListGrid:sync()
Event.onInterval(5, function()
if not craftingPaused then
local items = chestAdapter:listItems()
if Util.size(items) == 0 then
jobListGrid.parent:clear()
jobListGrid.parent:centeredWrite(math.ceil(jobListGrid.parent.height/2), 'No items in system')
jobListGrid:sync()
else
local craftList = watchResources(items)
jobListGrid:setValues(craftList)
--jobListGrid:draw()
--jobListGrid:sync()
craftItems(craftList, items)
jobListGrid:update()
jobListGrid:draw()
jobListGrid:sync()
craftList = getAutocraftItems(items) -- autocrafted items don't show on job monitor
craftItems(craftList, items)
end
end
end)
UI:pullEvents()
jobListGrid.parent:reset()

View File

@ -1,101 +0,0 @@
requireInjector(getfenv(1))
local Event = require('event')
local Message = require('message')
local UI = require('ui')
local Util = require('util')
multishell.setTitle(multishell.getCurrent(), 'Log Monitor')
if not device.wireless_modem then
error('Wireless modem is required')
end
device.wireless_modem.open(59998)
local ids = { }
local messages = { }
local terminal = UI.term
if device.openperipheral_bridge then
UI.Glasses = require('glasses')
terminal = UI.Glasses({
x = 4,
y = 175,
height = 40,
width = 64,
textScale = .5,
backgroundOpacity = .65,
})
elseif device.monitor then
terminal = UI.Device({
deviceType = 'monitor',
textScale = .5
})
end
terminal:clear()
function getClient(id)
if not ids[id] then
ids[id] = {
titleBar = UI.TitleBar({ title = 'ID: ' .. id, parent = terminal }),
scrollingText = UI.ScrollingText({ parent = terminal })
}
local clientCount = Util.size(ids)
local clientHeight = math.floor((terminal.height - clientCount) / clientCount)
terminal:clear()
local y = 1
for k,v in pairs(ids) do
v.titleBar.y = y
y = y + 1
v.scrollingText.height = clientHeight
v.scrollingText.y = y
y = y + clientHeight
v.scrollingText:clear()
v.titleBar:draw()
v.scrollingText:draw()
end
end
return ids[id]
end
Event.on('logMessage', function()
local t = { }
while #messages > 0 do
local msg = messages[1]
table.remove(messages, 1)
local client = getClient(msg.id)
client.scrollingText:appendLine(string.format('%d %s', math.floor(os.clock()), msg.text))
t[msg.id] = client
end
for _,client in pairs(t) do
client.scrollingText:draw()
end
terminal:sync()
end)
Message.addHandler('log', function(h, id, msg)
table.insert(messages, { id = id, text = msg.contents })
os.queueEvent('logMessage')
end)
Event.on('monitor_touch', function()
terminal:reset()
ids = { }
end)
Event.on('mouse_click', function()
terminal:reset()
ids = { }
end)
Event.on('char', function()
Event.exitPullEvents()
end)
Event.pullEvents(logWriter)
terminal:reset()

View File

@ -1,25 +0,0 @@
requireInjector(getfenv(1))
local Terminal = require('terminal')
local args = { ... }
local mon = device[table.remove(args, 1) or 'monitor']
if not mon then
error('mirror: Invalid device')
end
mon.clear()
mon.setTextScale(.5)
mon.setCursorPos(1, 1)
local oterm = Terminal.copy(term.current())
Terminal.mirror(term.current(), mon)
term.current().getSize = mon.getSize
if #args > 0 then
shell.run(unpack(args))
Terminal.copy(oterm, term.current())
mon.setCursorBlink(false)
end

View File

@ -1,86 +0,0 @@
requireInjector(getfenv(1))
local Event = require('event')
local Logger = require('logger')
local Socket = require('socket')
local Terminal = require('terminal')
local Util = require('util')
Logger.setScreenLogging()
local remoteId
local args = { ... }
if #args == 1 then
remoteId = tonumber(args[1])
else
print('Enter host ID')
remoteId = tonumber(read())
end
if not remoteId then
error('Syntax: mirrorClient <host ID>')
end
local function wrapTerm(socket)
local methods = { 'blit', 'clear', 'clearLine', 'setCursorPos', 'write',
'setTextColor', 'setTextColour', 'setBackgroundColor',
'setBackgroundColour', 'scroll', 'setCursorBlink', }
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
end
while true do
print('connecting...')
local socket
while true do
socket = Socket.connect(remoteId, 5901)
if socket then
break
end
os.sleep(3)
end
print('connected')
wrapTerm(socket)
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
for k,v in pairs(socket.oldTerm) do
socket.term[k] = v
end
socket:close()
end

View File

@ -1,53 +0,0 @@
requireInjector(getfenv(1))
local Event = require('event')
local Logger = require('logger')
local Socket = require('socket')
Logger.setScreenLogging()
local args = { ... }
local mon = device[args[1] or 'monitor']
if not mon then
error('Monitor not attached')
end
mon.setBackgroundColor(colors.black)
mon.clear()
while true do
local socket = Socket.server(5901)
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)
-- ensure socket is connected
Event.onInterval(3, function(h)
if not socket:ping() then
Event.off(h)
end
end)
while true do
Event.pullEvent()
if not socket.connected then
break
end
end
print('connection lost')
socket:close()
end

View File

@ -1,335 +0,0 @@
requireInjector(getfenv(1))
local Event = require('event')
local GPS = require('gps')
local Logger = require('logger')
local MEProvider = require('meProvider')
local Point = require('point')
local Socket = require('socket')
local Util = require('util')
if not device.wireless_modem then
error('Modem is required')
end
Logger.setWirelessLogging()
if not turtle then
error('Can only be run on a turtle')
end
local blocks = { }
local meProvider = MEProvider()
local items = { }
local pickups = Util.readTable('pickup.tbl') or { }
local cells = Util.readTable('cells.tbl') or { }
local refills = Util.readTable('refills.tbl') or { }
local fluids = Util.readTable('fluids.tbl') or { }
local chestPt = turtle.loadLocation('chest')
local chargePt = turtle.loadLocation('charge')
local fuel = {
item = {
id = 'minecraft:coal',
dmg = 0,
},
qty = 64
}
local slots
turtle.setMoveCallback(function(action, pt)
if slots then
for _,slot in pairs(slots) do
if turtle.getItemCount(slot.index) ~= slot.qty then
printError('Slots changed')
Event.exitPullEvents()
end
end
end
end)
function refuel()
if turtle.getFuelLevel() < 5000 then
print('refueling')
turtle.status = 'refueling'
gotoPoint(chestPt, true)
dropOff(chestPt)
while turtle.getFuelLevel() < 5000 do
turtle.select(1)
meProvider:provide(fuel.item, fuel.qty, 1)
turtle.refuel(64)
print(turtle.getFuelLevel())
os.sleep(1)
end
end
end
function pickUp(pt)
turtle.status = 'picking up'
gotoPoint(pt, true)
while true do
if not turtle.selectOpenSlot() then
dropOff(chestPt)
gotoPoint(pt, true)
end
turtle.select(1)
if not turtle.suckDown(64) then
return
end
end
end
function dropOff(pt)
if turtle.selectSlotWithItems() then
gotoPoint(pt, true)
turtle.emptyInventory(turtle.dropDown)
if pt == chestPt then
print('refreshing items')
items = meProvider:refresh()
end
end
end
function gotoPoint(pt, doDetect)
slots = turtle.getInventory()
while not turtle.pathfind(pt, blocks) do
if turtle.abort then
error('aborted')
end
turtle.status = 'blocked'
os.sleep(5)
end
if doDetect and not turtle.detectDown() then
printError('Missing target')
Event.exitPullEvents()
end
end
function checkCell(pt)
if not turtle.selectOpenSlot() then
dropOff(chestPt)
end
print('checking cell')
turtle.status = 'recharging'
gotoPoint(pt, true)
local c = peripheral.wrap('bottom')
local energy = c.getMaxEnergyStored() -
c.getEnergyStored()
if energy > 20000 then
print('charging cell')
turtle.selectOpenSlot()
turtle.digDown()
gotoPoint(chargePt, true)
turtle.dropDown()
os.sleep(energy / 20000)
turtle.suckDown()
print('replacing cell')
gotoPoint(pt)
if not turtle.placeDown() then
error('could not place down cell')
end
end
end
function fluid(points)
print('checking fluid')
turtle.status = 'fluiding'
gotoPoint(points.source, true)
turtle.select(1)
turtle.digDown()
gotoPoint(points.target)
if not turtle.placeDown() then
error('could not place fluid container')
end
os.sleep(5)
turtle.digDown()
gotoPoint(points.source)
turtle.placeDown()
end
function refill(entry)
dropOff(chestPt)
turtle.status = 'refilling'
gotoPoint(chestPt)
for _,item in pairs(entry.items) do
meProvider:provide(item, tonumber(item.qty), turtle.selectOpenSlot())
end
if turtle.selectSlotWithItems() then
if entry.point then
dropOff(entry.point)
end
end
end
function oldRefill(points)
gotoPoint(points.source)
repeat until not turtle.suckDown(64)
if points.target then
dropOff(points.target)
end
if points.targets then
for k,target in pairs(points.targets) do
dropOff(target)
end
end
dropOff(points.source)
dropOff(chestPt)
end
local function makeKey(pt)
return string.format('%d:%d:%d', pt.x, pt.y, pt.z)
end
local function pickupHost(socket)
while true do
local data = socket:read()
if not data then
print('pickup: closing connection to ' .. socket.dhost)
return
end
print('command: ' .. data.type)
if data.type == 'pickup' then
local key = makeKey(data.point)
pickups[key] = data.point
Util.writeTable('pickup.tbl', pickups)
socket:write( { type = "response", response = 'added' })
elseif data.type == 'items' then
socket:write( { type = "response", response = items })
elseif data.type == 'refill' then
local key = makeKey(data.entry.point)
refills[key] = data.entry
Util.writeTable('refills.tbl', refills)
socket:write( { type = "response", response = 'added' })
elseif data.type == 'setPickup' then
chestPt = data.point
turtle.storeLocation('chest', chestPt)
socket:write( { type = "response", response = 'Location set' })
elseif data.type == 'setRecharge' then
chargePt = data.point
turtle.storeLocation('charge', chargePt)
socket:write( { type = "response", response = 'Location set' })
elseif data.type == 'charge' then
local key = makeKey(data.point)
cells[key] = data.point
Util.writeTable('cells.tbl', cells)
socket:write( { type = "response", response = 'added' })
elseif data.type == 'fluid' then
elseif data.type == 'clear' then
local key = makeKey(data.point)
refills[key] = nil
cells[key] = nil
fluids[key] = nil
pickups[key] = nil
Util.writeTable('refills.tbl', refills)
Util.writeTable('cells.tbl', cells)
Util.writeTable('fluids.tbl', fluids)
Util.writeTable('pickup.tbl', pickups)
socket:write( { type = "response", response = 'cleared' })
else
print('unknown command')
end
end
end
Event.addRoutine(function()
while true do
print('waiting for connection on port 5222')
local socket = Socket.server(5222)
print('pickup: connection from ' .. socket.dhost)
Event.addRoutine(function() pickupHost(socket) end)
end
end)
local function eachEntry(t, fn)
local keys = Util.keys(t)
for _,key in pairs(keys) do
if t[key] then
if turtle.abort then
return
end
fn(t[key])
end
end
end
local function eachClosestEntry(t, fn)
local points = { }
for k,v in pairs(t) do
v = Util.shallowCopy(v)
v.key = k
table.insert(points, v)
end
while not Util.empty(points) do
local closest = Point.closest(turtle.point, points)
if turtle.abort then
return
end
if t[closest.key] then
fn(closest)
end
for k,v in pairs(points) do
if v.key == closest.key then
table.remove(points, k)
break
end
end
end
end
Event.addRoutine(function()
while true do
if chestPt then
eachClosestEntry(pickups, pickUp)
eachEntry(refills, refill)
refuel()
end
eachEntry(fluids, fluid)
if chargePt then
eachEntry(cells, checkCell)
end
print('sleeping')
turtle.status = 'sleeping'
if turtle.abort then
printError('aborted')
break
end
os.sleep(60)
end
Event.exitPullEvents()
end)
turtle.run(function()
if not turtle.enableGPS() then
error('turtle: No GPS found')
end
refuel()
Event.pullEvents()
end)

View File

@ -1,231 +0,0 @@
if not device.wireless_modem then
error('Wireless modem is required')
end
requireInjector(getfenv(1))
local Event = require('event')
local GPS = require('gps')
local Socket = require('socket')
local UI = require('ui')
local Util = require('util')
multishell.setTitle(multishell.getCurrent(), 'Pickup Remote')
local id
local mainPage = UI.Page({
menu = UI.Menu({
centered = true,
y = 2,
menuItems = {
{ prompt = 'Pickup', event = 'pickup', help = 'Pickup items from this location' },
{ prompt = 'Charge cell', event = 'charge', help = 'Recharge this cell' },
{ prompt = 'Refill', event = 'refill', help = 'Recharge this cell' },
{ prompt = 'Set pickup location', event = 'setPickup', help = 'Recharge this cell' },
{ prompt = 'Set recharge location', event = 'setRecharge', help = 'Recharge this cell' },
{ prompt = 'Clear', event = 'clear', help = 'Remove this location' },
},
}),
statusBar = UI.StatusBar(),
accelerators = {
q = 'quit',
},
})
local refillPage = UI.Page({
menuBar = UI.MenuBar({
y = 1,
buttons = {
{ text = 'Done', event = 'done', help = 'Pickup items from this location' },
{ text = 'Back', event = 'back', help = 'Recharge this cell' },
},
}),
grid1 = UI.ScrollingGrid({
columns = {
{ heading = 'Name', key = 'name', width = UI.term.width-9 },
{ heading = 'Qty', key = 'fQty', width = 5 },
},
sortColumn = 'name',
height = 8,
y = 3,
}),
grid2 = UI.ScrollingGrid({
columns = {
{ heading = 'Name', key = 'name', width = UI.term.width-9 },
{ heading = 'Qty', key = 'qty', width = 5 },
},
sortColumn = 'name',
height = 4,
y = 12,
}),
statusBar = UI.StatusBar(),
accelerators = {
q = 'quit',
},
})
refillPage.menuBar:add({
filter = UI.TextEntry({
x = UI.term.width-10,
width = 10,
})
})
local function sendCommand(cmd)
local socket = Socket.connect(id, 5222)
if not socket then
mainPage.statusBar:timedStatus('Unable to connect', 3)
return
end
socket:write(cmd)
local m = socket:read(3)
socket:close()
if m then
return m.response
end
mainPage.statusBar:timedStatus('No response', 3)
end
local function getPoint()
local gpt = GPS.getPoint()
if not gpt then
mainPage.statusBar:timedStatus('Unable to get location', 3)
end
return gpt
end
function refillPage:eventHandler(event)
if event.type == 'grid_select' then
local item = {
name = event.selected.name,
id = event.selected.id,
dmg = event.selected.dmg,
qty = 0,
}
local dialog = UI.Dialog({
x = 1,
width = UI.term.width,
text = UI.Text({ x = 3, y = 3, value = 'Quantity' }),
textEntry = UI.TextEntry({ x = 14, y = 3 })
})
dialog.eventHandler = function(self, event)
if event.type == 'accept' then
local l = tonumber(self.textEntry.value)
if l and l <= 1024 and l > 0 then
item.qty = self.textEntry.value
table.insert(refillPage.grid2.values, item)
refillPage.grid2:update()
UI:setPreviousPage()
else
self.statusBar:timedStatus('Invalid Quantity', 3)
end
return true
end
return UI.Dialog.eventHandler(self, event)
end
dialog.titleBar.title = item.name
dialog:setFocus(dialog.textEntry)
UI:setPage(dialog)
elseif event.type == 'text_change' then
local text = event.text
if #text == 0 then
self.grid1.values = self.allItems
else
self.grid1.values = { }
for _,item in pairs(self.allItems) do
if string.find(item.lname, text) then
table.insert(self.grid1.values, item)
end
end
end
--self.grid:adjustWidth()
self.grid1:update()
self.grid1:setIndex(1)
self.grid1:draw()
elseif event.type == 'back' then
UI:setPreviousPage()
elseif event.type == 'done' then
UI:setPage(mainPage)
local pt = getPoint()
if pt then
local response = sendCommand({ type = 'refill', entry = { point = pt, items = self.grid2.values } })
if response then
mainPage.statusBar:timedStatus(response, 3)
end
end
elseif event.type == 'grid_focus_row' then
self.statusBar:setStatus(event.selected.id .. ':' .. event.selected.dmg)
self.statusBar:draw()
end
return UI.Page.eventHandler(self, event)
end
function refillPage:enable()
for _,item in pairs(self.allItems) do
item.lname = string.lower(item.name)
item.fQty = Util.toBytes(item.qty)
end
self.grid1:setValues(self.allItems)
self.menuBar.filter.value = ''
self.menuBar.filter.pos = 1
self:setFocus(self.menuBar.filter)
UI.Page.enable(self)
end
function mainPage:eventHandler(event)
if event.type == 'quit' then
Event.exitPullEvents()
elseif event.type == 'refill' then
local response = sendCommand({ type = 'items' })
if response then
refillPage.allItems = response
refillPage.grid2:setValues({ })
UI:setPage(refillPage)
end
elseif event.type == 'pickup' or event.type == 'setPickup' or
event.type == 'setRecharge' or event.type == 'charge' or
event.type == 'clear' then
local pt = getPoint()
if pt then
local response = sendCommand({ type = event.type, point = pt })
if response then
self.statusBar:timedStatus(response, 3)
end
end
end
return UI.Page.eventHandler(self, event)
end
local args = { ... }
if #args == 1 then
id = tonumber(args[1])
end
if not id then
error('Syntax: pickupRemote <turtle ID>')
end
UI:setPage(mainPage)
Event.pullEvents()
UI.term:reset()

View File

@ -1,538 +0,0 @@
-- +---------------------+------------+---------------------+
-- | | | |
-- | | RecGif | |
-- | | | |
-- +---------------------+------------+---------------------+
local version = "Version 1.1.6"
-- Records your terminal and saves the result as an animating GIF.
-- http://www.computercraft.info/forums2/index.php?/topic/24840-recgif/
-- ----------------------------------------------------------
-- Original code by Bomb Bloke
-- Modified to integrate with opus os
requireInjector(getfenv(1))
local Util = require('util')
local recTerm, oldTerm, arg, showInput, skipLast, lastDelay, curInput = {}, Util.shallowCopy(multishell.term), {...}, false, false, 2, ""
local curBlink, oldBlink, tTerm, buffer, colourNum, xPos, yPos, oldXPos, oldYPos, tCol, bCol, xSize, ySize = false, false, {}, {}, {}, 1, 1, 1, 1, colours.white, colours.black, oldTerm.getSize()
local greys, buttons = {["0"] = true, ["7"] = true, ["8"] = true, ["f"] = true}, {"l", "r", "m"}
local charW, charH, chars, resp
local filename
local calls = { }
local curCalls = { delay = 0 }
local callListCount = 0
local callCount = 0
local function showSyntax()
print('Gif Recorder by Bomb Bloke\n')
print('Syntax: recGif [-i] [-s] [-ld:<delay>] filename')
print(' -i : show input')
print(' -s : skip last')
print(' -ld : last delay')
end
for i = #arg, 1, -1 do
local curArg = arg[i]:lower()
if curArg == "-i" then
showInput, ySize = true, ySize + 1
table.remove(arg, i)
elseif curArg == "-s" then
skipLast = true
table.remove(arg, i)
elseif curArg:sub(1, 4) == "-ld:" then
curArg = tonumber(curArg:sub(5))
if curArg then lastDelay = curArg end
table.remove(arg, i)
elseif curArg == '-?' then
showSyntax()
return
elseif i ~= #arg then
showSyntax()
printError('\nInvalid argument')
return
end
end
print('Press control-p to stop recording')
local filename = arg[#arg]
if not filename then
print('Enter file name:')
filename = read()
end
if #filename == 0 then
showSyntax()
print()
error('Invalid file name')
end
print('Initializing...')
-- don't pollute global env
-- convert these to require style apis
local function loadAPI(url, env)
local apiEnv = Util.shallowCopy(env)
apiEnv.shell = nil
apiEnv.multishell = nil
setmetatable(apiEnv, { __index = _G })
local fn = Util.loadUrl(url, apiEnv)
fn()
return apiEnv
end
bbpack = loadAPI('http://pastebin.com/raw/PdrJjb5S', getfenv(1))
GIF = loadAPI('http://pastebin.com/raw/5uk9uRjC', getfenv(1))
Util.runUrl(getfenv(1), 'http://pastebin.com/raw/cUYTGbpb', 'get', 'Y0eLUPtr')
local function snooze()
local myEvent = tostring({})
os.queueEvent(myEvent)
os.pullEvent(myEvent)
end
local function safeString(text)
local newText = {}
for i = 1, #text do
local val = text:byte(i)
newText[i] = (val > 31 and val < 127) and val or 63
end
return string.char(unpack(newText))
end
local function safeCol(text, subst)
local newText = {}
for i = 1, #text do
local val = text:sub(i, i)
newText[i] = greys[val] and val or subst
end
return table.concat(newText)
end
-- Build a terminal that records stuff:
recTerm = multishell.term
for key, func in pairs(oldTerm) do
recTerm[key] = function(...)
local result = { func(...) }
if callCount == 0 then
os.queueEvent('capture_frame')
end
callCount = callCount + 1
curCalls[callCount] = { key, ... }
return unpack(result)
end
end
local tabId = multishell.getCurrent()
multishell.addHotkey(25, function()
os.queueEvent('recorder_stop')
end)
local tabs = multishell.getTabs()
for _,tab in pairs(tabs) do
if tab.isOverview then
multishell.hideTab(tabId)
multishell.setFocus(tab.tabId)
os.queueEvent('term_resize')
break
end
end
local curTime = os.clock() - 1
while true do
local event = { os.pullEventRaw() }
if event[1] == 'recorder_stop' or event[1] == 'terminate' then
break
end
if event[1] == 'capture_frame' then
local newTime = os.clock()
if callListCount > 0 then
calls[callListCount].delay = (newTime - curTime)
end
curTime = newTime
callListCount = callListCount + 1
calls[callListCount] = curCalls
curCalls, callCount = { delay = 0 }, 0
end
end
multishell.removeHotkey(25)
for k,fn in pairs(oldTerm) do
multishell.term[k] = fn
end
multishell.unhideTab(tabId)
multishell.setFocus(tabId)
if #calls[#calls] == 0 then calls[#calls] = nil end
if skipLast and #calls > 1 then calls[#calls] = nil end
calls[#calls].delay = lastDelay
print(string.format("Encoding %d frames...", #calls))
--Util.writeTable('tmp/raw.txt', calls)
-- Perform a quick re-parse of the recorded data (adding frames for when the cursor blinks):
do
local callListCount, tempCalls, blink, oldBlink, curBlink, blinkDelay = 1, {}, false, false, true, 0
for i = 1, #calls - 1 do
curCalls = calls[i]
tempCalls[callListCount] = curCalls
for j = 1, #curCalls do if curCalls[j][1] == "setCursorBlink" then blink = curCalls[j][2] end end
if blink then
if blinkDelay == 0 then
curCalls[#curCalls + 1] = {"toggleCur", curBlink}
blinkDelay, curBlink = 0.4, not curBlink
end
while tempCalls[callListCount].delay > blinkDelay do
local remainder = tempCalls[callListCount].delay - blinkDelay
tempCalls[callListCount].delay = blinkDelay
callListCount = callListCount + 1
tempCalls[callListCount] = {{"toggleCur", curBlink}, ["delay"] = remainder}
blinkDelay, curBlink = 0.4, not curBlink
end
blinkDelay = blinkDelay - tempCalls[callListCount].delay
else
if oldBlink then curCalls[#curCalls + 1] = {"toggleCur", false} end
blinkDelay = (curCalls.delay - blinkDelay) % 0.4
end
callListCount, oldBlink = callListCount + 1, blink
end
tempCalls[callListCount] = calls[#calls]
tempCalls[callListCount][#tempCalls[callListCount] + 1] = {"toggleCur", false}
calls, curCalls = tempCalls, nil
end
snooze()
-- Load font data:
do
local ascii, counter = GIF.toPaintutils(GIF.flattenGIF(GIF.loadGIF("ascii.gif"))), 0
local newFont, ybump, xbump = #ascii ~= #ascii[1], 0, 0
charW, charH, chars = newFont and #ascii[1] / 16 or #ascii[1] * 3 / 64, #ascii / 16, {}
for yy = 0, newFont and 15 or 7 do
for xx = 0, 15 do
local newChar, length = {}, 0
-- Place in 2d grid of bools:
for y = 1, charH do
local newRow = {}
for x = 1, charW do
local set = ascii[y + ybump][x + xbump] == 1
if set and x > length then length = x end
newRow[x] = set
end
newChar[y] = newRow
end
-- Center:
if not newFont then for y = 1, charH do for x = 1, math.floor((charW - length) / 2) do table.insert(newChar[y], 1, false) end end end
chars[counter] = newChar
counter, xbump = counter + 1, xbump + (newFont and charW or charH)
end
xbump, ybump = 0, ybump + charH
end
end
snooze()
-- Terminal data translation:
do
local hex, counter = "0123456789abcdef", 1
for i = 1, 16 do
colourNum[counter] = hex:sub(i, i)
counter = counter * 2
end
end
for y = 1, ySize do
buffer[y] = {}
for x = 1, xSize do buffer[y][x] = {" ", colourNum[tCol], colourNum[bCol]} end
end
if showInput then for x = 1, xSize do buffer[ySize][x][3] = colourNum[colours.lightGrey] end end
tTerm.blit = function(text, fgCol, bgCol)
if xPos > xSize or xPos + #text - 1 < 1 or yPos < 1 or yPos > ySize then return end
if not _HOST then text = safeString(text) end
if not term.isColour() then
fgCol = safeCol(fgCol, "0")
bgCol = safeCol(bgCol, "f")
end
if xPos < 1 then
text = text:sub(2 - xPos)
fgCol = fgCol:sub(2 - xPos)
bgCol = bgCol:sub(2 - xPos)
xPos = 1
end
if xPos + #text - 1 > xSize then
text = text:sub(1, xSize - xPos + 1)
fgCol = fgCol:sub(1, xSize - xPos + 1)
bgCol = bgCol:sub(1, xSize - xPos + 1)
end
for x = 1, #text do
buffer[yPos][xPos + x - 1][1] = text:sub(x, x)
buffer[yPos][xPos + x - 1][2] = fgCol:sub(x, x)
buffer[yPos][xPos + x - 1][3] = bgCol:sub(x, x)
end
xPos = xPos + #text
end
tTerm.write = function(text)
text = tostring(text)
tTerm.blit(text, string.rep(colourNum[tCol], #text), string.rep(colourNum[bCol], #text))
end
tTerm.clearLine = function()
local oldXPos = xPos
xPos = 1
tTerm.write(string.rep(" ", xSize))
xPos = oldXPos
end
tTerm.clear = function()
local oldXPos, oldYPos = xPos, yPos
for y = 1, ySize do
xPos, yPos = 1, y
tTerm.write(string.rep(" ", xSize))
end
xPos, yPos = oldXPos, oldYPos
end
tTerm.setCursorPos = function(x, y)
xPos, yPos = math.floor(x), math.floor(y)
end
tTerm.setTextColour = function(col)
tCol = col
end
tTerm.setTextColor = function(col)
tCol = col
end
tTerm.setBackgroundColour = function(col)
bCol = col
end
tTerm.setBackgroundColor = function(col)
bCol = col
end
tTerm.scroll = function(lines)
if math.abs(lines) < ySize then
local oldXPos, oldYPos = xPos, yPos
for y = 1, ySize do
if y + lines > 0 and y + lines <= ySize then
for x = 1, xSize do
xPos, yPos = x, y
tTerm.blit(buffer[y + lines][x][1], buffer[y + lines][x][2], buffer[y + lines][x][3])
end
else
yPos = y
tTerm.clearLine()
end
end
xPos, yPos = oldXPos, oldYPos
else tTerm.clear() end
end
tTerm.toggleCur = function(newBlink)
curBlink = newBlink
end
tTerm.newInput = function(input)
local oldTC, oldBC, oldX, oldY = tCol, bCol, xPos, yPos
tCol, bCol, xPos, yPos, ySize, input = colours.grey, colours.lightGrey, 1, ySize + 1, ySize + 1, input .. " "
while #curInput + #input + 1 > xSize do curInput = curInput:sub(curInput:find(" ") + 1) end
curInput = curInput .. input .. " "
tTerm.clearLine()
tTerm.write(curInput)
tCol, bCol, xPos, yPos, ySize = oldTC, oldBC, oldX, oldY, ySize - 1
end
tTerm.key = function(key)
tTerm.newInput((not keys.getName(key)) and "unknownKey" or keys.getName(key))
end
tTerm.mouse_click = function(button, x, y)
tTerm.newInput(buttons[button] .. "C@" .. tostring(x) .. "x" .. tostring(y))
end
local image = {["width"] = xSize * charW, ["height"] = ySize * charH}
for i = 1, #calls do
local xMin, yMin, xMax, yMax, oldBuffer, curCalls, changed = xSize + 1, ySize + 1, 0, 0, {}, calls[i], false
calls[i] = nil
for y = 1, ySize do
oldBuffer[y] = {}
for x = 1, xSize do oldBuffer[y][x] = {buffer[y][x][1], buffer[y][x][2], buffer[y][x][3], buffer[y][x][4]} end
end
snooze()
if showInput then ySize = ySize - 1 end
for j = 1, #curCalls do if tTerm[curCalls[j][1]] then tTerm[curCalls[j][1]](unpack(curCalls[j], 2)) end end
if showInput then ySize = ySize + 1 end
if i > 1 then
for yy = 1, ySize do for xx = 1, xSize do if buffer[yy][xx][1] ~= oldBuffer[yy][xx][1] or (buffer[yy][xx][2] ~= oldBuffer[yy][xx][2] and buffer[yy][xx][1] ~= " ") or buffer[yy][xx][3] ~= oldBuffer[yy][xx][3] then
changed = true
if xx < xMin then xMin = xx end
if xx > xMax then xMax = xx end
if yy < yMin then yMin = yy end
if yy > yMax then yMax = yy end
end end end
else xMin, yMin, xMax, yMax, changed = 1, 1, xSize, ySize, true end
if oldBlink and (xPos ~= oldXPos or yPos ~= oldYPos or not curBlink) and oldXPos > 0 and oldYPos > 0 and oldXPos <= xSize and oldYPos <= ySize then
changed = true
if oldXPos < xMin then xMin = oldXPos end
if oldXPos > xMax then xMax = oldXPos end
if oldYPos < yMin then yMin = oldYPos end
if oldYPos > yMax then yMax = oldYPos end
buffer[oldYPos][oldXPos][4] = false
end
if curBlink and (xPos ~= oldXPos or yPos ~= oldYPos or not oldBlink) and xPos > 0 and yPos > 0 and xPos <= xSize and yPos <= ySize then
changed = true
if xPos < xMin then xMin = xPos end
if xPos > xMax then xMax = xPos end
if yPos < yMin then yMin = yPos end
if yPos > yMax then yMax = yPos end
buffer[yPos][xPos][4] = true
end
oldBlink, oldXPos, oldYPos = curBlink, xPos, yPos
local thisFrame = {
["xstart"] = (xMin - 1) * charW,
["ystart"] = (yMin - 1) * charH,
["xend"] = (xMax - xMin + 1) * charW,
["yend"] = (yMax - yMin + 1) * charH,
["delay"] = curCalls.delay,
["disposal"] = 1
}
for y = 1, (yMax - yMin + 1) * charH do
local row = {}
for x = 1, (xMax - xMin + 1) * charW do row[x] = " " end
thisFrame[y] = row
end
snooze()
for yy = yMin, yMax do
local yBump = (yy - yMin) * charH
for xx = xMin, xMax do if buffer[yy][xx][1] ~= oldBuffer[yy][xx][1] or (buffer[yy][xx][2] ~= oldBuffer[yy][xx][2] and buffer[yy][xx][1] ~= " ") or buffer[yy][xx][3] ~= oldBuffer[yy][xx][3] or buffer[yy][xx][4] ~= oldBuffer[yy][xx][4] or i == 1 then
local thisChar, thisT, thisB, xBump = chars[buffer[yy][xx][1]:byte()], buffer[yy][xx][2], buffer[yy][xx][3], (xx - xMin) * charW
if thisChar then
for y = 1, charH do
for x = 1, charW do
local ch = thisChar[y][x] and thisT or thisB
thisFrame[y + yBump][x + xBump] = ch
end
end
end
if buffer[yy][xx][4] then
thisT, thisChar = colourNum[tCol], chars[95]
for y = 1, charH do for x = 1, charW do if thisChar[y][x] then thisFrame[y + yBump][x + xBump] = thisT end end end
end
end end
for y = yBump + 1, yBump + charH do
local skip, chars, row = 0, {}, {}
for x = 1, #thisFrame[y] do
if thisFrame[y][x] == " " then
if #chars > 0 then
row[#row + 1] = table.concat(chars)
chars = {}
end
skip = skip + 1
else
if skip > 0 then
row[#row + 1] = skip
skip = 0
end
chars[#chars + 1] = thisFrame[y][x]
end
end
if #chars > 0 then row[#row + 1] = table.concat(chars) end
thisFrame[y] = row
end
snooze()
end
if changed then
image[#image + 1] = thisFrame
else
image[#image].delay = image[#image].delay + curCalls.delay
end
end
buffer = nil
GIF.saveGIF(image, filename)
fs.delete('ascii.gif')
print("Encode complete")

View File

@ -1,517 +0,0 @@
requireInjector(getfenv(1))
local GPS = require('gps')
local Socket = require('socket')
local UI = require('ui')
local Util = require('util')
multishell.setTitle(multishell.getCurrent(), 'Shapes')
local args = { ... }
local turtleId = args[1] or error('Supply turtle ID')
turtleId = tonumber(turtleId)
local script = [[
requireInjector(getfenv(1))
local GPS = require('gps')
local ChestAdapter = require('chestAdapter18')
local Point = require('point')
local Util = require('util')
local itemAdapter
function dumpInventory()
for i = 1, 16 do
local qty = turtle.getItemCount(i)
if qty > 0 then
itemAdapter:insert(i, qty)
end
if turtle.getItemCount(i) ~= 0 then
print('Adapter is full or missing - make space or replace')
print('Press enter to continue')
read()
end
end
turtle.select(1)
end
local function refuel()
while turtle.getFuelLevel() < 4000 do
print('Refueling')
turtle.select(1)
itemAdapter:provide({ name = 'minecraft:coal', damage = 0 }, 64, 1)
if turtle.getItemCount(1) == 0 then
print('Out of fuel, add fuel to chest/ME system')
turtle.status = 'waiting'
os.sleep(5)
else
turtle.refuel(64)
end
end
end
local function goto(pt)
while not turtle.gotoPoint(pt) do
print('stuck')
os.sleep(5)
end
end
local function pathTo(pt)
while not turtle.pathfind(pt) do
print('stuck')
os.sleep(5)
end
end
local function resupply()
if data.suppliesPt then
pathTo(data.suppliesPt)
itemAdapter = ChestAdapter({ direction = 'up', wrapSide = 'bottom' })
dumpInventory()
refuel()
end
end
local function makePlane(y)
local pt = { x = math.min(data.startPt.x, data.endPt.x),
ex = math.max(data.startPt.x, data.endPt.x),
z = math.min(data.startPt.z, data.endPt.z),
ez = math.max(data.startPt.z, data.endPt.z) }
local blocks = { }
for z = pt.z, pt.ez do
for x = pt.x, pt.ex do
table.insert(blocks, { x = x, y = y, z = z })
end
end
return blocks
end
local function optimizeRoute(plane, ptb)
local maxDistance = 99999999
local function getNearestNeighbor(p, pt, threshold)
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)
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)
return not b.u
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 <= threshold 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 <= threshold 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 throttle = Util.throttle()
local t = { }
ptb.index = 0
local threshold = 0
for i = 1, #plane do
local b = getNearestNeighbor(plane, ptb, threshold)
table.insert(t, b)
throttle()
threshold = 1
end
return t
end
local function clear()
local pt = Util.shallowCopy(data.startPt)
pt.y = math.min(data.startPt.y, data.endPt.y)
pt.heading = 0
local osy = pt.y
local sy = osy + 1
local ey = math.max(data.startPt.y, data.endPt.y)
local firstPlane = true
resupply()
while true do
if sy > ey then
sy = ey
end
local plane = makePlane(sy)
plane = optimizeRoute(plane, pt)
if firstPlane then
turtle.pathfind(plane[1])
turtle.setPolicy(turtle.policies.digAttack)
firstPlane = false
end
for _,b in ipairs(plane) do
turtle.gotoPoint(b)
if sy < ey then
turtle.digUp()
end
if sy > osy then
turtle.digDown()
end
if turtle.abort then
break
end
end
if turtle.abort then
break
end
if sy + 1 >= ey then
break
end
sy = sy + 3
end
turtle.setPolicy(turtle.policies.none)
resupply()
end
turtle.run(function()
turtle.status = 'Clearing'
if turtle.enableGPS() then
local pt = Util.shallowCopy(turtle.point)
local s, m = pcall(clear)
pathTo(pt)
if not s and m then
error(m)
read()
end
end
end)
]]
local levelScript = [[
requireInjector(getfenv(1))
local Point = require('point')
local Util = require('util')
local checkedNodes = { }
local nodes = { }
local box = { }
local function inBox(pt, box)
return pt.x >= box.x and
pt.y >= box.y and
pt.z >= box.z and
pt.x <= box.ex and
pt.y <= box.ey and
pt.z <= box.ez
end
local function toKey(pt)
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 }
if 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',
}
-- 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 }
if inBox(node, box) then
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
end
local function move(action)
if action == 'turn' then
dig(turtle.getAction('forward'))
elseif action == 'up' then
dig(turtle.getAction('up'))
elseif action == 'down' then
dig(turtle.getAction('down'))
elseif action == 'back' then
dig(turtle.getAction('up'))
dig(turtle.getAction('down'))
end
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
return Point.closest2(turtle.getPoint(), t)
end
local function level()
box.x = math.min(data.startPt.x, data.endPt.x)
box.y = math.min(data.startPt.y, data.endPt.y)
box.z = math.min(data.startPt.z, data.endPt.z)
box.ex = math.max(data.startPt.x, data.endPt.x)
box.ey = math.max(data.startPt.y, data.endPt.y)
box.ez = math.max(data.startPt.z, data.endPt.z)
turtle.pathfind(data.firstPt)
turtle.setPolicy("attack", { dig = dig }, "assuredMove")
turtle.setMoveCallback(move)
repeat
local key = toKey(turtle.point)
checkedNodes[key] = true
nodes[key] = nil
dig(turtle.getAction('down'))
dig(turtle.getAction('up'))
dig(turtle.getAction('forward'))
print(string.format('%d nodes remaining', Util.size(nodes)))
if Util.size(nodes) == 0 then
break
end
local node = Point.closest2(turtle.point, nodes)
node = getAdjacentPoint(node)
if not turtle.gotoPoint(node) then
break
end
until turtle.abort
turtle.resetState()
end
local s, m = turtle.run(function()
turtle.status = 'Leveling'
if turtle.enableGPS() then
local pt = Util.shallowCopy(turtle.point)
local s, m = pcall(level)
turtle.pathfind(pt)
if not s and m then
error(m)
end
end
end)
if not s then
error(m)
end
]]
local data = Util.readTable('/usr/config/shapes') or { }
local page = UI.Page {
titleBar = UI.TitleBar { title = 'Shapes' },
info = UI.Window { x = 5, y = 3, height = 1 },
startCoord = UI.Button { x = 2, y = 6, text = 'Start ', event = 'startCoord' },
endCoord = UI.Button { x = 2, y = 8, text = 'End ', event = 'endCoord' },
supplies = UI.Button { x = 2, y = 10, text = 'Supplies', event = 'supplies' },
first = UI.Button { x = 2, y = 11, text = 'First', event = 'firstCoord' },
cancel = UI.Button { rx = 2, ry = -2, text = 'Abort', event = 'cancel' },
begin = UI.Button { rx = -7, ry = -2, text = 'Begin', event = 'begin' },
accelerators = { q = 'quit' },
notification = UI.Notification(),
statusBar = UI.StatusBar(),
}
function page.info:draw()
local function size(a, b)
return (math.abs(a.x - b.x) + 1) *
(math.abs(a.y - b.y) + 1) *
(math.abs(a.z - b.z) + 1)
end
self:clear()
if not data.startPt then
self:write(1, 1, 'Set starting corner')
elseif not data.endPt then
self:write(1, 1, 'Set ending corner')
else
self:write(1, 1, 'Blocks: ' .. size(data.startPt, data.endPt))
end
end
function page:getPoint()
local pt = GPS.getPoint()
if not pt then
self.notification:error('GPS not available')
end
return pt
end
function page:runFunction(id, script)
Util.writeFile('script.tmp', script)
self.notification:info('Connecting')
local fn, msg = loadstring(script, 'script')
if not fn then
self.notification:error('Error in script')
debug(msg)
return
end
local socket = Socket.connect(id, 161)
if not socket then
self.notification:error('Unable to connect')
return
end
socket:write({ type = 'script', args = script })
socket:close()
self.notification:success('Sent')
end
function page:eventHandler(event)
if event.type == 'startCoord' then
data.startPt = self:getPoint()
if data.startPt then
self.statusBar:setStatus('starting corner set')
Util.writeTable('/usr/config/shapes', data)
end
self:draw()
elseif event.type == 'endCoord' then
data.endPt = self:getPoint()
if data.endPt then
self.statusBar:setStatus('ending corner set')
Util.writeTable('/usr/config/shapes', data)
end
self:draw()
elseif event.type == 'firstCoord' then
data.firstPt = self:getPoint()
if data.firstPt then
self.statusBar:setStatus('first point set')
Util.writeTable('/usr/config/shapes', data)
end
self:draw()
elseif event.type == 'supplies' then
data.suppliesPt = self:getPoint()
if data.suppliesPt then
self.statusBar:setStatus('supplies location set')
Util.writeTable('/usr/config/shapes', data)
end
elseif event.type == 'begin' then
if data.startPt and data.endPt then
local s = 'local data = ' .. textutils.serialize(data) .. levelScript
self:runFunction(turtleId, s)
else
self.notification:error('Corners not set')
end
self.statusBar:setStatus('')
elseif event.type == 'cancel' then
self:runFunction(turtleId, 'turtle.abortAction()')
self.statusBar:setStatus('')
else
return UI.Page.eventHandler(self, event)
end
return true
end
UI:setPage(page)
UI:pullEvents()
UI.term:reset()

View File

@ -41,7 +41,12 @@ end
function shell.run(...)
local path, args = parseCommandLine(...)
path = shell.resolveProgram(path)
local isUrl = not not path:match("^(https?:)//(([^/:]+):?([0-9]*))(/?.*)$")
if not isUrl then
path = shell.resolveProgram(path)
end
if path then
tProgramStack[#tProgramStack + 1] = path
local oldTitle
@ -51,7 +56,15 @@ function shell.run(...)
multishell.setTitle(multishell.getCurrent(), fs.getName(path))
end
local result, err = os.run(Util.shallowCopy(sandboxEnv), path, unpack(args))
local result, err
if isUrl then
local env = Util.shallowCopy(sandboxEnv)
setmetatable(env, { __index = _G })
result, err = Util.runUrl(env, path, unpack(args))
else
result, err = os.run(Util.shallowCopy(sandboxEnv), path, unpack(args))
end
if multishell then
local title = 'shell'

View File

@ -1,633 +0,0 @@
requireInjector(getfenv(1))
local Logger = require('logger')
local Point = require('point')
local Util = require('util')
if device and device.wireless_modem then
Logger.setWirelessLogging()
end
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' },
-- 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' },
}
local fortuneBlocks = {
[ 'minecraft:redstone_ore' ] = true,
[ 'minecraft:lapis_ore' ] = true,
[ 'minecraft:coal_ore' ] = true,
[ 'minecraft:diamond_ore' ] = true,
[ 'minecraft:emerald_ore' ] = true,
}
local MIN_FUEL = 7500
local LOW_FUEL = 1500
local MAX_FUEL = 100000
if not term.isColor() then
MAX_FUEL = 20000
end
local mining = {
diameter = 1,
chunkIndex = 0,
chunks = -1,
}
local trash
local boreDirection
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
dir = dirs[math.floor(index / (diameter - 1)) + 1]
dir.x = x + dir.xd * 16
dir.z = z + dir.zd * 16
return dir
end
function getBoreLocations(x, z)
local locations = {}
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
end
-- get the bore location closest to the miner
local function getClosestLocation(points, b)
local key = 1
local leastMoves = 9000
for k,pt in pairs(points) do
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)
end
function getCornerOf(c)
return math.floor(c.x / 16) * 16, math.floor(c.z / 16) * 16
end
function nextChunk()
local x, z = getCornerOf({ x = mining.x, z = mining.z })
local points = math.pow(mining.diameter, 2) - math.pow(mining.diameter-2, 2)
mining.chunkIndex = mining.chunkIndex + 1
if mining.chunkIndex >= points then
mining.diameter = mining.diameter + 2
mining.chunkIndex = 0
end
if mining.chunks ~= -1 then
local chunks = math.pow(mining.diameter-2, 2) + mining.chunkIndex
if chunks >= mining.chunks then
return false
end
end
local nc = getChunkCoordinates(mining.diameter, mining.chunkIndex, x, z)
mining.locations = getBoreLocations(nc.x, nc.z)
-- enter next chunk
mining.x = nc.x
mining.z = nc.z
Util.writeTable('mining.progress', mining)
return true
end
function addTrash()
if not trash then
trash = { }
end
local slots = turtle.getFilledSlots()
for k,slot in pairs(slots) do
trash[slot.iddmg] = true
end
trash['minecraft:bucket:0'] = nil
Util.writeTable('mining.trash', trash)
end
function log(text)
print(text)
Logger.log('mineWorker', text)
end
function status(status)
turtle.status = status
log(status)
end
function refuel()
if turtle.getFuelLevel() < MIN_FUEL then
local oldStatus = turtle.status
status('refueling')
if turtle.selectSlot('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.select(1)
end
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)
turtle.select(1)
turtle.drop(64)
turtle.digDown()
end
end
function safeGoto(x, z, y, h)
local oldStatus = turtle.status
while not turtle.pathfind({ x = x, z = z, y = y, heading = h }) do
--status('stuck')
if turtle.abort then
return false
end
--os.sleep(1)
end
turtle.status = oldStatus
return true
end
function safeGotoY(y)
local oldStatus = turtle.status
while not turtle.gotoY(y) do
status('stuck')
if turtle.abort then
return false
end
os.sleep(1)
end
turtle.status = oldStatus
return true
end
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
end
function normalChestUnload()
local oldStatus = turtle.status
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)
safeGoto(0, 0)
if not turtle.detectUp() then
error('no chest')
end
local slots = turtle.getFilledSlots()
for _,slot in pairs(slots) do
if not trash[slot.iddmg] and
slot.iddmg ~= 'minecraft:bucket:0' and
slot.id ~= 'minecraft:diamond_pickaxe' and
slot.id ~= 'cctweaks:toolHost' then
if slot.id ~= options.fortunePick.value then
turtle.select(slot.index)
turtle.dropUp(64)
end
end
end
turtle.select(1)
safeGoto(pt.x, pt.z, 0, pt.heading)
turtle.clearMoveCallback()
safeGotoY(pt.y)
status(oldStatus)
end
function ejectTrash()
local cobbleSlotCount = 0
turtle.eachFilledSlot(function(slot)
if slot.iddmg == 'minecraft:cobblestone:0' then
cobbleSlotCount = cobbleSlotCount + 1
end
if trash[slot.iddmg] then
-- retain 1 slot with cobble in order to indicate active mining
if slot.iddmg ~= 'minecraft:cobblestone:0' or cobbleSlotCount > 1 then
turtle.select(slot.index)
turtle.dropDown(64)
end
end
end)
end
function mineable(action)
local r, block = action.inspect()
if not r then
return false
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.selectSlot('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 trash[block.name .. ':0'] then
return false
end
return block.name
end
function fortuneDig(action, blockName)
if options.fortunePick.value and fortuneBlocks[blockName] then
turtle.selectSlot('cctweaks:toolHost')
turtle.equipRight()
turtle.selectSlot(options.fortunePick.value)
repeat until not turtle.dig()
turtle.selectSlot('minecraft:diamond_pickaxe')
turtle.equipRight()
turtle.select(1)
return true
end
end
function mine(action)
local blockName = mineable(action)
if blockName then
checkSpace()
--collectDrops(action.suck)
if not fortuneDig(action, blockName) then
action.dig()
end
end
end
function bore()
local loc = turtle.point
local level = loc.y
turtle.select(1)
status('boring down')
boreDirection = 'down'
while true do
if turtle.abort then
status('aborting')
return false
end
if loc.y <= -mining.depth then
break
end
if turtle.point.y < -2 then
-- turtle.setDigPolicy(turtle.digPolicies.turtleSafe)
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
boreDirection = 'up'
status('boring up')
turtle.turnRight()
mine(turtle.getAction('forward'))
turtle.turnRight()
mine(turtle.getAction('forward'))
turtle.turnLeft()
while true do
if turtle.abort then
status('aborting')
return false
end
if turtle.point.y > -2 then
-- turtle.setDigPolicy(turtle.digPolicies.turtleSafe)
end
while not Util.tryTimed(3, turtle.up) do
status('stuck')
end
if turtle.status == 'stuck' then
status('boring up')
end
if loc.y >= level - 1 then
break
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
return true
end
function checkSpace()
if turtle.getItemCount(16) > 0 then
refuel()
local oldStatus = turtle.status
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
function collectDrops(suckAction)
for i = 1, 50 do
if not suckAction() then
break
end
checkSpace()
end
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
end
function inspect(action, name)
local r, block = action.inspect()
if r and block.name == name then
return true
end
end
function boreCommand()
local pt = getClosestLocation(mining.locations, turtle.point)
turtle.setMoveCallback(function(action, tpt)
makeWalkableTunnel(action, tpt, pt)
end)
safeGotoY(0)
safeGoto(pt.x, pt.z, 0)
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
turtle.digUp()
turtle.placeUp('minecraft:cobblestone:0')
local success = bore()
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
return success
end
if not Util.getOptions(options, args) then
return
end
mining.depth = options.depth.value
mining.chunks = options.chunks.value
unload = normalChestUnload
--if options.enderChest.value then
-- unload = enderChestUnload
--end
mining.x = 0
mining.z = 0
mining.locations = getBoreLocations(0, 0)
trash = Util.readTable('mining.trash')
if options.resume.value then
mining = Util.readTable('mining.progress')
elseif fs.exists('mining.progress') then
print('use -r to resume')
read()
end
if not trash or options.setTrash.value then
print('Add trash blocks, press 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()
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.iddmg] = nil
trash['minecraft:diamond_pickaxe:0'] = nil
trash['cctweaks:toolHost:0'] = nil
end
_G._p = trash
local function main()
repeat
while #mining.locations > 0 do
status('searching')
if not boreCommand() then
return
end
Util.writeTable('mining.progress', mining)
end
until not nextChunk()
end
turtle.run(function()
turtle.reset()
turtle.setPolicy(turtle.policies.digAttack)
turtle.setDigPolicy(turtle.digPolicies.turtleSafe)
unload()
status('mining')
local s, m = pcall(function() main() end)
if not s and m then
printError(m)
end
safeGotoY(0)
safeGoto(0, 0, 0, 0)
unload()
turtle.reset()
end)

View File

@ -1,174 +0,0 @@
requireInjector(getfenv(1))
local ChestAdapter = require('chestAdapter18')
local Event = require('event')
local MEAdapter = require('meAdapter')
local RefinedAdapter = require('refinedAdapter')
local UI = require('ui')
local Util = require('util')
local storage = RefinedAdapter()
if not storage:isValid() then
storage = MEAdapter()
if not storage:isValid() then
storage = ChestAdapter()
end
end
if not storage:isValid() then
error('Not connected to a storage device')
end
multishell.setTitle(multishell.getCurrent(), 'Storage Activity')
UI:configure('StorageActivity', ...)
local changedPage = UI.Page({
grid = UI.Grid({
columns = {
{ heading = 'Qty', key = 'count', width = 5 },
{ heading = 'Change', key = 'change', width = 6 },
{ heading = 'Name', key = 'displayName', width = UI.term.width - 15 },
},
sortColumn = 'displayName',
rey = -6,
}),
buttons = UI.Window({
ry = -4,
height = 5,
backgroundColor = colors.gray,
prevButton = UI.Button({
event = 'previous',
backgroundColor = colors.lightGray,
x = 2,
y = 2,
height = 3,
width = 5,
text = ' < '
}),
resetButton = UI.Button({
event = 'reset',
backgroundColor = colors.lightGray,
x = 8,
y = 2,
height = 3,
rex = -8,
text = 'Reset'
}),
nextButton = UI.Button({
event = 'next',
backgroundColor = colors.lightGray,
rx = -5,
y = 2,
height = 3,
width = 5,
text = ' > '
})
}),
accelerators = {
q = 'quit',
}
})
function changedPage.grid:getDisplayValues(row)
row = Util.shallowCopy(row)
local ind = '+'
if row.change < 0 then
ind = ''
end
row.change = ind .. Util.toBytes(row.change)
row.count = Util.toBytes(row.count)
return row
end
function changedPage:eventHandler(event)
if event.type == 'reset' then
self.lastItems = nil
self.grid:setValues({ })
self.grid:clear()
self.grid:draw()
elseif event.type == 'next' then
self.grid:nextPage()
elseif event.type == 'previous' then
self.grid:previousPage()
elseif event.type == 'quit' then
Event.exitPullEvents()
else
return UI.Page.eventHandler(self, event)
end
return true
end
local function uniqueKey(item)
return table.concat({ item.name, item.damage, item.nbtHash }, ':')
end
function changedPage:refresh()
local t = storage:listItems()
if not t or Util.empty(t) then
self:clear()
self:centeredWrite(math.ceil(self.height/2), 'Communication failure')
return
end
for k,v in pairs(t) do
t[k] = Util.shallowCopy(v)
end
if not self.lastItems then
self.lastItems = t
self.grid:setValues({ })
else
local changedItems = {}
for _,v in pairs(self.lastItems) do
found = false
for k2,v2 in pairs(t) do
if uniqueKey(v) == uniqueKey(v2) then
if v.count ~= v2.count then
local c = Util.shallowCopy(v2)
c.lastCount = v.count
table.insert(changedItems, c)
end
table.remove(t, k2)
found = true
break
end
end
-- New item
if not found then
local c = Util.shallowCopy(v)
c.lastCount = v.count
c.count = 0
table.insert(changedItems, c)
end
end
-- No items left
for k,v in pairs(t) do
v.lastCount = 0
table.insert(changedItems, v)
end
for k,v in pairs(changedItems) do
v.change = v.count - v.lastCount
end
self.grid:setValues(changedItems)
end
self.grid:draw()
end
Event.onInterval(5, function()
changedPage:refresh()
changedPage:sync()
end)
UI:setPage(changedPage)
UI:pullEvents()

View File

@ -1,901 +0,0 @@
requireInjector(getfenv(1))
local Config = require('config')
local Event = require('event')
local Logger = require('logger')
local ME = require('me')
local UI = require('ui')
local Util = require('util')
-- Must be a crafty turtle with duck antenna !
-- 3 wide monitor (any side of turtle)
-- Config location is /sys/config/storageMonitor
-- adjust directions in that file if needed
local config = {
trashDirection = 'up', -- trash /chest in relation to interface
turtleDirection = 'down', -- turtle in relation to interface
noCraftingStorage = 'false' -- no ME crafting (or ability to tell if powered - use with caution)
}
Config.load('storageMonitor', config)
if not device.tileinterface then
error('ME interface not found')
end
local duckAntenna
if device.workbench then
local oppositeSide = {
[ 'left' ] = 'right',
[ 'right' ] = 'left'
}
local duckAntennaSide = oppositeSide[device.workbench.side]
duckAntenna = peripheral.wrap(duckAntennaSide)
end
--if not device.monitor then
-- error('Monitor not found')
--end
ME.setDevice(device.tileinterface)
local jobListGrid
local craftingPaused = false
multishell.setTitle(multishell.getCurrent(), 'Storage Manager')
Logger.disable()
function getItem(items, inItem, ignore_dmg)
for _,item in pairs(items) do
if item.id == inItem.id then
if ignore_dmg and ignore_dmg == 'yes' then
return item
elseif item.dmg == inItem.dmg and item.nbt_hash == inItem.nbt_hash then
return item
end
end
end
end
local function uniqueKey(item)
local key = item.id .. ':' .. item.dmg
if item.nbt_hash then
key = key .. ':' .. item.nbt_hash
end
return key
end
function mergeResources(t)
local resources = Util.readTable('resource.limits')
resources = resources or { }
for _,item in pairs(t) do
item.has_recipe = false
end
for _,v in pairs(resources) do
local item = getItem(t, v)
if item then
item.limit = tonumber(v.limit)
item.low = tonumber(v.low)
item.auto = v.auto
item.ignore_dmg = v.ignore_dmg
else
v.qty = 0
v.limit = tonumber(v.limit)
v.low = tonumber(v.low)
v.auto = v.auto
v.ignore_dmg = v.ignore_dmg
table.insert(t, v)
end
end
recipes = Util.readTable('recipes') or { }
for _,v in pairs(recipes) do
local item = getItem(t, v)
if item then
item.has_recipe = true
else
v.qty = 0
v.limit = nil
v.low = nil
v.has_recipe = true
v.auto = 'no'
v.ignore_dmg = 'no'
v.has_recipe = 'true'
table.insert(t, v)
end
end
end
function filterItems(t, filter)
local r = {}
if filter then
filter = filter:lower()
for k,v in pairs(t) do
if string.find(v.lname, filter) then
table.insert(r, v)
end
end
else
return t
end
return r
end
function sumItems(items)
local t = {}
for _,item in pairs(items) do
local key = uniqueKey(item)
local summedItem = t[key]
if summedItem then
summedItem.qty = summedItem.qty + item.qty
else
summedItem = Util.shallowCopy(item)
t[key] = summedItem
end
end
return t
end
function isGridClear()
for i = 1, 16 do
if turtle.getItemCount(i) ~= 0 then
return false
end
end
return true
end
local function clearGrid()
for i = 1, 16 do
local count = turtle.getItemCount(i)
if count > 0 then
ME.insert(i, count, config.turtleDirection)
if turtle.getItemCount(i) ~= 0 then
return false
end
end
end
return true
end
function turtleCraft(recipe, originalItem)
for k,v in pairs(recipe.ingredients) do
-- ugh
local dmg = v.dmg
if v.max_dmg and v.max_dmg > 0 then
local item = ME.getItemDetail({ id = v.id, nbt_hash = v.nbt_hash }, false)
if item then
dmg = item.dmg
end
end
if not ME.extract(v.id, dmg, v.nbt_hash, v.qty, config.turtleDirection, k) then
clearGrid()
originalItem.status = v.name .. ' (extract failed)'
return false
end
end
if not turtle.craft() then
clearGrid()
return false
end
clearGrid()
return true
end
function craftItem(items, recipes, item, originalItem, itemList)
local key = uniqueKey(item)
local recipe = recipes[key]
if recipe then
if not isGridClear() then
return
end
local summedItems = sumItems(recipe.ingredients)
for i = 1, math.ceil(item.qty / recipe.qty) do
local failed = false -- try to craft all components (use all CPUs available)
for _,ingredient in pairs(summedItems) do
local ignore_dmg = 'no'
if ingredient.max_dmg and ingredient.max_dmg > 0 then
ignore_dmg = 'yes'
end
local qty = ME.getItemCount(ingredient.id, ingredient.dmg, ingredient.nbt_hash, ignore_dmg)
if qty < ingredient.qty then
originalItem.status = ingredient.name .. ' (crafting)'
ingredient.qty = ingredient.qty - qty
if not craftItem(items, recipes, ingredient, originalItem, itemList) then
failed = true
end
end
end
if failed then
return false
end
if not failed and not turtleCraft(recipe, originalItem) then
Logger.debug('turtle failed to craft ' .. item.name)
return false
end
end
return true
else
local meItem = getItem(items, item)
if not meItem or not meItem.is_craftable then
if item.id == originalItem.id and item.dmg == originalItem.dmg then
originalItem.status = '(not craftable)'
else
originalItem.status = item.name .. ' (missing)'
end
else
if item.id == originalItem.id and item.dmg == originalItem.dmg then
item.meCraft = true
return false
end
-- find it in the list of items to be crafted
for _,v in pairs(itemList) do
if v.id == item.id and v.dmg == item.dmg and v.nbt_hash == item.nbt_hash then
v.qty = item.qty + v.qty
return false
end
end
-- add to the item list
table.insert(itemList, {
id = item.id,
dmg = item.dmg,
nbt_hash = item.nbt_hash,
qty = item.qty,
name = item.name,
meCraft = true,
status = ''
})
end
end
return false
end
function craftItems(itemList)
local recipes = Util.readTable('recipes') or { }
local items = ME.getAvailableItems()
-- turtle craft anything we can, build up list for ME items
local keys = Util.keys(itemList)
for _,key in pairs(keys) do
local item = itemList[key]
craftItem(items, recipes, item, item, itemList)
end
-- second pass is to request crafting from ME with aggregated items
for _,item in pairs(itemList) do
if item.meCraft then
local alreadyCrafting = false
local jobList = ME.getJobList()
for _,v in pairs(jobList) do
if v.id == item.id and v.dmg == item.dmg and v.nbt_hash == item.nbt_hash then
alreadyCrafting = true
end
end
if alreadyCrafting then
item.status = '(crafting)'
elseif not ME.isCPUAvailable() then
item.status = '(waiting)'
else
item.status = '(failed)'
local qty = item.qty
while qty >= 1 do -- try to request smaller quantities until successful
if ME.craft(item.id, item.dmg, item.nbt_hash, qty) then
item.status = '(crafting)'
break -- successfully requested crafting
end
qty = math.floor(qty / 2)
end
end
end
end
end
-- AE 1 (obsolete)
function isCrafting(jobList, id, dmg)
for _, job in pairs(jobList) do
if job.id == id and job.dmg == dmg then
return job
end
end
end
local nullDevice = {
setCursorPos = function(...) end,
write = function(...) end,
getSize = function() return 13, 20 end,
isColor = function() return false end,
setBackgroundColor = function(...) end,
setTextColor = function(...) end,
clear = function(...) end,
}
local function jobMonitor(jobList)
local mon
if device.monitor then
mon = UI.Device({
deviceType = 'monitor',
textScale = .5,
})
else
mon = UI.Device({
device = nullDevice
})
end
jobListGrid = UI.Grid({
parent = mon,
sortColumn = 'name',
columns = {
{ heading = 'Qty', key = 'qty', width = 6 },
{ heading = 'Crafting', key = 'name', width = mon.width / 2 - 10 },
{ heading = 'Status', key = 'status', width = mon.width - 10 },
},
})
end
function getAutocraftItems(items)
local t = Util.readTable('resource.limits') or { }
local itemList = { }
for _,res in pairs(t) do
if res.auto and res.auto == 'yes' then
res.qty = 4 -- this could be higher to increase autocrafting speed
table.insert(itemList, res)
end
end
return itemList
end
local function getItemWithQty(items, res, ignore_dmg)
local item = getItem(items, res, ignore_dmg)
if item then
if ignore_dmg and ignore_dmg == 'yes' then
local qty = 0
for _,v in pairs(items) do
if item.id == v.id and item.nbt_hash == v.nbt_hash then
if item.max_dmg > 0 or item.dmg == v.dmg then
qty = qty + v.qty
end
end
end
item.qty = qty
end
end
return item
end
function watchResources(items)
local itemList = { }
local t = Util.readTable('resource.limits') or { }
for k, res in pairs(t) do
local item = getItemWithQty(items, res, res.ignore_dmg)
res.limit = tonumber(res.limit)
res.low = tonumber(res.low)
if not item then
item = {
id = res.id,
dmg = res.dmg,
nbt_hash = res.nbt_hash,
name = res.name,
qty = 0
}
end
if res.limit and item.qty > res.limit then
Logger.debug("Purging " .. item.qty-res.limit .. " " .. res.name)
if not ME.extract(item.id, item.dmg, item.nbt_hash, item.qty - res.limit, config.trashDirection) then
Logger.debug('Failed to purge ' .. res.name)
end
elseif res.low and item.qty < res.low then
if res.ignore_dmg and res.ignore_dmg == 'yes' then
item.dmg = 0
end
table.insert(itemList, {
id = item.id,
dmg = item.dmg,
nbt_hash = item.nbt_hash,
qty = res.low - item.qty,
name = item.name,
status = ''
})
end
end
return itemList
end
itemPage = UI.Page {
backgroundColor = colors.lightGray,
titleBar = UI.TitleBar {
title = 'Limit Resource',
previousPage = true,
event = 'form_cancel',
backgroundColor = colors.green
},
idField = UI.Text {
x = 5, y = 3, width = UI.term.width - 10,
},
form = UI.Form {
x = 4, y = 4, height = 8, rex = -4,
[1] = UI.TextEntry {
width = 7,
backgroundColor = colors.gray,
backgroundFocusColor = colors.gray,
formLabel = 'Min', formKey = 'low', help = 'Craft if below min'
},
[2] = UI.TextEntry {
width = 7,
backgroundColor = colors.gray,
backgroundFocusColor = colors.gray,
formLabel = 'Max', formKey = 'limit', help = 'Eject if above max'
},
[3] = UI.Chooser {
width = 7,
formLabel = 'Autocraft', formKey = 'auto',
nochoice = 'No',
choices = {
{ name = 'Yes', value = 'yes' },
{ name = 'No', value = 'no' },
},
help = 'Craft until out of ingredients'
},
[4] = UI.Chooser {
width = 7,
formLabel = 'Ignore Dmg', formKey = 'ignore_dmg',
nochoice = 'No',
choices = {
{ name = 'Yes', value = 'yes' },
{ name = 'No', value = 'no' },
},
help = 'Ignore damage of item'
},
},
statusBar = UI.StatusBar { }
}
function itemPage:enable()
UI.Page.enable(self)
self:focusFirst()
end
function itemPage:eventHandler(event)
if event.type == 'form_cancel' then
UI:setPreviousPage()
elseif event.type == 'focus_change' then
self.statusBar:setStatus(event.focused.help)
self.statusBar:draw()
elseif event.type == 'form_complete' then
local values = self.form.values
local t = Util.readTable('resource.limits') or { }
for k,v in pairs(t) do
if v.id == values.id and v.dmg == values.dmg then
table.remove(t, k)
break
end
end
local keys = { 'name', 'auto', 'id', 'low', 'dmg', 'max_dmg', 'nbt_hash', 'limit', 'ignore_dmg' }
local filtered = { }
for _,key in pairs(keys) do
filtered[key] = values[key]
end
table.insert(t, filtered)
Util.writeTable('resource.limits', t)
UI:setPreviousPage()
else
return UI.Page.eventHandler(self, event)
end
return true
end
listingPage = UI.Page {
menuBar = UI.MenuBar {
buttons = {
{ text = 'Learn', event = 'learn' },
{ text = 'Forget', event = 'forget' },
},
},
grid = UI.Grid {
y = 2, height = UI.term.height - 2,
columns = {
{ heading = 'Name', key = 'name' , width = 22 },
{ heading = 'Qty', key = 'qty' , width = 5 },
{ heading = 'Min', key = 'low' , width = 4 },
{ heading = 'Max', key = 'limit', width = 4 },
},
sortColumn = 'name',
},
statusBar = UI.StatusBar {
backgroundColor = colors.gray,
width = UI.term.width,
filterText = UI.Text {
x = 2, width = 6,
value = 'Filter',
},
filter = UI.TextEntry {
x = 9, width = 19,
limit = 50,
},
refresh = UI.Button {
x = 31, width = 8,
text = 'Refresh',
event = 'refresh',
},
},
accelerators = {
r = 'refresh',
q = 'quit',
}
}
function listingPage.grid:getRowTextColor(row, selected)
if row.is_craftable then
return colors.yellow
end
if row.has_recipe then
if selected then
return colors.blue
end
return colors.lightBlue
end
return UI.Grid:getRowTextColor(row, selected)
end
function listingPage.grid:getDisplayValues(row)
row = Util.shallowCopy(row)
row.qty = Util.toBytes(row.qty)
if row.low then
row.low = Util.toBytes(row.low)
end
if row.limit then
row.limit = Util.toBytes(row.limit)
end
return row
end
function listingPage.statusBar:draw()
return UI.Window.draw(self)
end
function listingPage.statusBar.filter:eventHandler(event)
if event.type == 'mouse_rightclick' then
self.value = ''
self:draw()
local page = UI:getCurrentPage()
page.filter = nil
page:applyFilter()
page.grid:draw()
page:setFocus(self)
end
return UI.TextEntry.eventHandler(self, event)
end
function listingPage:eventHandler(event)
if event.type == 'quit' then
Event.exitPullEvents()
elseif event.type == 'grid_select' then
local selected = event.selected
itemPage.form:setValues(selected)
itemPage.titleBar.title = selected.name
itemPage.idField.value = selected.id
UI:setPage('item')
elseif event.type == 'refresh' then
self:refresh()
self.grid:draw()
elseif event.type == 'learn' then
if not duckAntenna then
self.statusBar:timedStatus('Missing peripherals', 3)
else
UI:setPage('craft')
end
elseif event.type == 'forget' then
local item = self.grid:getSelected()
if item then
local recipes = Util.readTable('recipes') or { }
local key = uniqueKey(item)
local recipe = recipes[key]
if recipe then
recipes[key] = nil
Util.writeTable('recipes', recipes)
end
local resources = Util.readTable('resource.limits') or { }
for k,v in pairs(resources) do
if v.id == item.id and v.dmg == item.dmg then
table.remove(resources, k)
Util.writeTable('resource.limits', resources)
break
end
end
self.statusBar:timedStatus('Forgot: ' .. item.name, 3)
self:refresh()
self.grid:draw()
end
elseif event.type == 'text_change' then
self.filter = event.text
if #self.filter == 0 then
self.filter = nil
end
self:applyFilter()
self.grid:draw()
self.statusBar.filter:focus()
else
UI.Page.eventHandler(self, event)
end
return true
end
function listingPage:enable()
self:refresh()
self:setFocus(self.statusBar.filter)
UI.Page.enable(self)
end
function listingPage:refresh()
self.allItems = ME.getAvailableItems('all')
mergeResources(self.allItems)
Util.each(self.allItems, function(item)
item.lname = item.name:lower()
end)
self:applyFilter()
end
function listingPage:applyFilter()
local t = filterItems(self.allItems, self.filter)
self.grid:setValues(t)
end
-- without duck antenna
local function getTurtleInventory()
local inventory = { }
for i = 1,16 do
if turtle.getItemCount(i) > 0 then
turtle.select(i)
local item = turtle.getItemDetail()
inventory[i] = {
id = item.name,
dmg = item.damage,
qty = item.count,
name = item.name,
}
end
end
return inventory
end
-- Strip off color prefix
local function safeString(text)
local val = text:byte(1)
if val < 32 or val > 128 then
local newText = {}
for i = 4, #text do
local 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
end
local function filter(t, filter)
local keys = Util.keys(t)
for _,key in pairs(keys) do
if not Util.key(filter, key) then
t[key] = nil
end
end
end
local function learnRecipe(page)
local t = Util.readTable('recipes') or { }
local recipe = { }
local ingredients = duckAntenna.getAllStacks(false) -- getTurtleInventory()
if ingredients then
turtle.select(1)
if turtle.craft() then
recipe = duckAntenna.getAllStacks(false) -- getTurtleInventory()
if recipe and recipe[1] then
recipe = recipe[1]
local key = uniqueKey(recipe)
clearGrid()
recipe.name = safeString(recipe.display_name)
filter(recipe, { 'name', 'id', 'dmg', 'nbt_hash', 'qty', 'max_size' })
for _,ingredient in pairs(ingredients) do
ingredient.name = safeString(ingredient.display_name)
filter(ingredient, { 'name', 'id', 'dmg', 'nbt_hash', 'qty', 'max_size', 'max_dmg' })
if ingredient.max_dmg > 0 then -- let's try this...
ingredient.dmg = 0
end
end
recipe.ingredients = ingredients
recipe.ignore_dmg = 'no'
t[key] = recipe
Util.writeTable('recipes', t)
listingPage.statusBar.filter:setValue(recipe.name)
listingPage.statusBar:timedStatus('Learned: ' .. recipe.name, 3)
listingPage.filter = recipe.name
listingPage:refresh()
listingPage.grid:draw()
return true
end
else
page.statusBar:timedStatus('Failed to craft', 3)
end
else
page.statusBar:timedStatus('No recipe defined', 3)
end
end
craftPage = UI.Dialog {
height = 7, width = UI.term.width - 6,
backgroundColor = colors.lightGray,
titleBar = UI.TitleBar {
title = 'Learn Recipe',
previousPage = true,
},
idField = UI.Text {
x = 5,
y = 3,
width = UI.term.width - 10,
value = 'Place recipe in turtle'
},
accept = UI.Button {
rx = -13, ry = -2,
text = 'Ok', event = 'accept',
},
cancel = UI.Button {
rx = -8, ry = -2,
text = 'Cancel', event = 'cancel'
},
statusBar = UI.StatusBar {
status = 'Crafting paused'
}
}
function craftPage:enable()
craftingPaused = true
self:focusFirst()
UI.Dialog.enable(self)
end
function craftPage:disable()
craftingPaused = false
UI.Dialog.disable(self)
end
function craftPage:eventHandler(event)
if event.type == 'cancel' then
UI:setPreviousPage()
elseif event.type == 'accept' then
if learnRecipe(self) then
UI:setPreviousPage()
end
else
return UI.Dialog.eventHandler(self, event)
end
return true
end
UI:setPages({
listing = listingPage,
item = itemPage,
craft = craftPage,
})
UI:setPage(listingPage)
listingPage:setFocus(listingPage.statusBar.filter)
clearGrid()
jobMonitor()
jobListGrid:draw()
jobListGrid:sync()
Event.onInterval(5, function()
if not craftingPaused then
local items = ME.getAvailableItems()
if Util.size(items) == 0 then
jobListGrid.parent:clear()
jobListGrid.parent:centeredWrite(math.ceil(jobListGrid.parent.height/2), 'No items in system')
jobListGrid:sync()
elseif config.noCraftingStorage ~= 'true' and #ME.getCraftingCPUs() <= 0 then -- only way to determine if AE is online
jobListGrid.parent:clear()
jobListGrid.parent:centeredWrite(math.ceil(jobListGrid.parent.height/2), 'Power failure')
jobListGrid:sync()
else
local itemList = watchResources(items)
jobListGrid:setValues(itemList)
jobListGrid:draw()
jobListGrid:sync()
craftItems(itemList)
jobListGrid:update()
jobListGrid:draw()
jobListGrid:sync()
itemList = getAutocraftItems(items) -- autocrafted items don't show on job monitor
craftItems(itemList)
end
end
end)
UI:pullEvents()
jobListGrid.parent:reset()

View File

@ -1,438 +0,0 @@
requireInjector(getfenv(1))
local Event = require('event')
local Logger = require('logger')
local MEProvider = require('meProvider')
local Message = require('message')
local Point = require('point')
local TableDB = require('tableDB')
local Util = require('util')
--[[
A supplier turtle for the builder turtle. For larger builds, use
ender modems.
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
]]--
local ChestProvider = require('chestProvider')
if Util.getVersion() == 1.8 then
ChestProvider = require('chestProvider18')
end
if not device.wireless_modem then
error('No wireless modem detected')
end
Logger.filter('modem_send', 'event', 'ui')
Logger.setWirelessLogging()
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,
}
--[[-- 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 }
}
}
})
function maxStackDB:get(id, dmg)
return self.data[id .. ':' .. dmg] or 64
end
function Builder:dumpInventory()
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)
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
read()
end
self.ready = true
end
function Builder:autocraft(supplies)
local t = { }
for i,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)
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
end
function Builder:log(...)
Logger.log('supplier', ...)
Util.print(...)
end
function Builder:getSupplies()
Builder.itemProvider:refresh()
local t = { }
for _,s in ipairs(self.slots) do
if s.need > 0 then
local item = Builder.itemProvider:getItemInfo(s.id, s.dmg)
if item then
if item.name then
s.name = item.name
end
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
Builder:log('Need %d %s', s.need - s.qty, name)
end
end
return t
end
local function moveTowardsX(dx)
local direction = dx - turtle.point.x
local move
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
return move()
end
local function moveTowardsZ(dz)
local direction = dz - turtle.point.z
local move
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
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
end
function Builder:gotoBuilder()
if Builder.lastPoint then
turtle.status = '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
end
Message.addHandler('builder',
function(h, id, msg, distance)
if not id or id ~= __BUILDER_ID then
return
end
if not Builder.resupplying then
local pt = msg.contents
pt.y = pt.y + 3
turtle.status = 'supervising'
turtle.gotoYfirst(pt)
end
end)
Message.addHandler('supplyList',
function(h, id, msg, distance)
if not id or id ~= __BUILDER_ID then
return
end
turtle.status = 'resupplying'
Builder.resupplying = true
Builder.slots = msg.contents.slots
Builder.slotUid = msg.contents.uid
Builder:log('Received supply list ' .. Builder.slotUid)
os.sleep(0)
if not turtle.gotoLocation('supplies') then
Builder:log('Failed to go to supply location')
self.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.status = 'waiting'
os.sleep(5)
end
Builder:log('Got all supplies')
os.sleep(0)
Builder:gotoBuilder()
Builder.resupplying = false
end)
Message.addHandler('needSupplies',
function(h, id, msg, distance)
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.status = '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')
--self.ready = false
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
Message.send(__BUILDER_ID, 'gotSupplies', { supplies = true, point = pt })
Message.waitForMessage('thanks', 5, __BUILDER_ID)
--os.sleep(0)
--p.condenseItems()
for i = 1, 16 do
p:extract(i, 64)
end
turtle.digDown()
turtle.status = 'waiting'
end
end)
Message.addHandler('finished',
function(h, id)
if not id or id ~= __BUILDER_ID then
return
end
Builder:finish()
end)
Event.on('turtle_abort',
function()
turtle.abort = false
turtle.status = 'aborting'
Builder:finish()
end)
local function onTheWay() -- parallel routine
while true do
local e, side, _id, id, msg, distance = 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: <builder id> <facing>')
end
__BUILDER_ID = tonumber(args[1])
maxStackDB:load()
itemInfoDB = TableDB({
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',
}
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.saveLocation('supplies')
Event.pullEvents(onTheWay)
end)

View File

@ -1,98 +0,0 @@
function doCommand(command, moves)
--[[
if command == 'sl' then
local pt = GPS.getPoint()
if pt then
turtle.storeLocation(moves, pt)
end
return
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
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 repCmds = {
[ 'u' ] = turtle.up,
[ 'd' ] = turtle.down,
[ 'f' ] = turtle.forward,
[ 'r' ] = turtle.turnRight,
[ 'l' ] = turtle.turnLeft,
[ 'ta' ] = turtle.turnAround,
[ '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 i = 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)
else
print('Enter command (q to quit):')
while true do
local cmd = 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

View File

@ -1,709 +0,0 @@
requireInjector(getfenv(1))
--[[
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
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.
]]--
local ChestAdapter = require('chestAdapter18')
local Craft = require('turtle.craft')
local Level = require('turtle.level')
local Point = require('point')
local Util = require('util')
local FUEL_BASE = 0
local FUEL_DIRE = FUEL_BASE + 10
local FUEL_GOOD = FUEL_BASE + 2000
local MIN_CHARCOAL = 24
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 },
}
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 COBBLESTONE = 'minecraft:cobblestone:0'
local CHARCOAL = 'minecraft:coal:1'
local OAK_LOG = 'minecraft:log:0'
local OAK_PLANK = 'minecraft:planks:0'
local CHEST = 'minecraft:chest:0'
local FURNACE = 'minecraft:furnace:0'
local SAPLING = 'minecraft:sapling:0'
local STONE = 'minecraft:stone:0'
local TORCH = 'minecraft:torch:0'
local DIRT = 'minecraft:dirt:0'
local APPLE = 'minecraft:apple:0'
local STICK = 'minecraft:stick:0'
local ALL_SAPLINGS = {
SAPLING
}
local state = Util.readTable('usr/config/treefarm') or {
trees = {
{ x = 1, y = 0, z = 0 }
}
}
local clock = os.clock()
local recipes = Util.readTable('sys/etc/recipes.db') or { }
Craft.setRecipes(recipes)
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/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
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
if safePlaceBlock(CHEST) then
if turtle.equip('left', 'minecraft:crafting_table') then
local chestAdapter = ChestAdapter({
wrapSide = 'top',
direction = 'down',
})
if not chestAdapter:isValid() then
print('invalid chestAdapter')
read()
end
-- turtle.emptyInventory(turtle.dropUp)
Util.print('Crafting %d %s', (qty or 1), item)
success = Craft.craftRecipe(recipes[item], qty or 1, chestAdapter)
repeat until not turtle.suckUp()
end
turtle.equip('left', 'minecraft:diamond_pickaxe')
turtle.digUp()
end
return success
end
local function cook(item, count, result, fuel, fuelCount)
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))
repeat
os.sleep(1)
turtle.suckUp()
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
end
local function makeCharcoal()
local slots = turtle.getSummedInventory()
if not state.furnace or
not slots[CHARCOAL] or
slots[CHARCOAL].count >= MIN_CHARCOAL then
return true
end
local function getLogSlot(slots)
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
local slots = turtle.getSummedInventory()
local charcoal = slots[CHARCOAL].count
local slot = getLogSlot(slots)
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 emptyFurnace()
if state.cooking then
print('Emptying furnace')
turtle.suckDownAt(state.furnace)
turtle.suckForwardAt(state.furnace)
turtle.suckUpAt(state.furnace)
setState('cooking')
end
end
local function getCobblestone(count)
local slots = turtle.getSummedInventory()
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 i = 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.gotoPoint(pt)
turtle.placeDown(DIRT)
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 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
print('Creating a perimeter')
getCobblestone(GRID_WIDTH * 2 + 1)
cook(COBBLESTONE, 2, STONE, OAK_PLANK, 2)
turtle.refuel(OAK_PLANK)
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
if inspect(turtle.inspectDown) ~= target then
turtle.digDown()
turtle.placeDown(target)
end
end)
turtle.pathfind(GRID.BR)
turtle.clearMoveCallback()
turtle.drop(COBBLESTONE)
turtle.drop(DIRT)
setState('perimeter', true)
end
end
local function createChests()
if state.chest_1 then
return false
end
if state.perimeter and
turtle.getFuelLevel() > FUEL_GOOD and
Craft.canCraft(CHEST, 4, turtle.getSummedInventory()) then
print('Adding storage')
if craftItem(CHEST, 4) then
local pt = Point.copy(GRID.BL)
pt.x = pt.x + 1
pt.y = pt.y - 1
for i = 1, 2 do
pt.z = pt.z + 1
turtle.digDownAt(pt)
turtle.placeDown(CHEST)
pt.z = pt.z + 1
turtle.digDownAt(pt)
turtle.placeDown(CHEST)
setState('chest_' .. i, Util.shallowCopy(pt))
pt.z = pt.z + 1
end
turtle.drop(DIRT)
turtle.refuel(OAK_PLANK)
end
end
return true
end
local function dropOffItems()
if state.chest_1 then
local slots = turtle.getSummedInventory()
if state.chest_1 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(state.chest_1)
turtle.dropDown('minecraft:log')
turtle.dropDown('minecraft:log2')
end
if slots[APPLE] then
print('Storing apples')
turtle.dropDownAt(state.chest_2, APPLE)
end
end
return true
end
local function eatSaplings()
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
end
local function placeTorches()
if state.torches then
return
end
if turtle.getFuelLevel() > 100 and
Craft.canCraft(TORCH, 4, turtle.getSummedInventory()) then
print('Placing torches')
if 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
end
local function randomSapling()
local sapling = SAPLING
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
sapling = ALL_SAPLINGS[math.random(1, #ALL_SAPLINGS)]
end
return sapling
end
local function fellTree(pt)
local function desparateRefuel(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() desparateRefuel(FUEL_DIRE) end)
desparateRefuel(FUEL_DIRE)
if turtle.digUpAt(Point.above(pt)) then
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
desparateRefuel(FUEL_BASE + 100)
turtle.clearMoveCallback()
turtle.setPolicy("attack")
return true
end
local function fell()
local pts = Util.shallowCopy(state.trees)
local pt = table.remove(pts, math.random(1, #pts))
if not turtle.faceAgainst(pt) or
not string.match(inspect(turtle.inspect), 'minecraft:log') then
return true
end
print('Chopping')
local fuel = turtle.getFuelLevel()
table.insert(pts, 1, pt)
Point.eachClosest(turtle.point, pts, function(pt)
if turtle.faceAgainst(pt) 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
end
local function moreTrees()
if #state.trees > 1 then
return
end
if not state.chest_1 or turtle.getItemCount(SAPLING) < 9 then
return true
end
print('Adding more trees')
local singleTree = state.trees[1]
state.trees = { }
for x = -2, 2, 2 do
for z = -2, 2, 2 do
table.insert(state.trees, { x = x, y = 0, z = z })
end
end
turtle.digAt(singleTree)
fellTree(singleTree)
setState('trees', state.trees)
Point.eachClosest(turtle.point, state.trees, function(pt)
turtle.placeDownAt(pt, randomSapling())
end)
end
function getTurtleFacing(block)
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]
end
function saveTurtleFacing()
if not state.facing then
setState('facing', getTurtleFacing(CHEST))
end
end
local function findGround()
print('Locating ground level')
turtle.setPoint(HOME_PT)
while true do
local s, block = turtle.inspectDown()
if not s then block = { name = 'minecraft:air', metadata = 0 } end
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 == COBBLESTONE or b == STONE then
error('lost')
end
if b == TORCH or b == FURNACE then
turtle.forward()
else
turtle.digDown()
turtle.down()
end
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
print('Determining location')
turtle.point.heading = getTurtleFacing(CHEST)
turtle.setHeading(state.facing)
turtle.point.heading = 0
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
turtle.setPoint({
x = -(GRID_LENGTH),
y = 0,
z = -GRID_WIDTH,
heading = turtle.point.heading
})
end
local function updateClock()
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
return true
end
local tasks = {
{ desc = 'Finding ground', fn = findGround },
{ desc = 'Determine facing', fn = saveTurtleFacing },
{ desc = 'Finding home', fn = findHome },
{ desc = 'Adding trees', fn = moreTrees },
{ desc = 'Chopping', fn = fell },
{ desc = 'Snacking', fn = eatSaplings },
{ desc = 'Creating chest', fn = createChests },
{ desc = 'Creating furnace', fn = createFurnace },
{ desc = 'Emptying furnace', fn = emptyFurnace },
{ 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 },
}
turtle.run(function()
turtle.setPolicy("attack")
while not turtle.abort do
print('fuel: ' .. turtle.getFuelLevel())
for _,task in ipairs(Util.shallowCopy(tasks)) do
--print(task.desc)
turtle.status = 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)

View File

@ -1,6 +1,6 @@
print('\nStarting Opus..')
LUA_PATH = '/sys/apis'
LUA_PATH = '/sys/apis:/usr/apis'
local Util = dofile('sys/apis/util.lua')
_G.debug = function(...) Util.print(...) end
@ -15,7 +15,7 @@ if not s then
end
-- process fstab
local mounts = Util.readFile('sys/etc/fstab')
local mounts = Util.readFile('usr/etc/fstab')
if mounts then
for _,l in ipairs(Util.split(mounts)) do
if l:sub(1, 1) ~= '#' then
@ -25,6 +25,8 @@ if mounts then
end
end
fs.mount('usr', 'gitfs', 'kepler155c', 'opus-apps', 'develop')
-- user environment
if not fs.exists('usr/apps') then
fs.makeDir('usr/apps')

View File

@ -103,7 +103,7 @@
icon = "\030f \
\030f\0310lua>\031 \
\030f ",
run = "Lua.lua",
run = "sys/apps/Lua.lua",
},
[ "89307d419a2fe4fbb69af92b3d3af27b6ec14d3e" ] = {
title = "Telnet",
@ -177,14 +177,6 @@
\031e\\/\031 \0319c",
run = "vnc.lua",
},
[ "4759bf14d7a511508e86c343c934894b6e8db533" ] = {
title = "Builder",
category = "Apps",
icon = "\0317_____\
\030e\031c###\0308\0317=\030e\031c#\
\030e\031c#\0307\031f.\030e\031c###",
run = "builder.lua",
},
[ "6ce6c512ea433a7fc5c8841628e7696cd0ff7f2b" ] = {
title = "Files",
category = "Apps",
@ -217,4 +209,36 @@
\0304 \030f \030 ",
run = "shell",
},
[ "131f0b008f44298812221d120d982940609be781" ] = {
title = "Builder",
category = "Apps",
icon = "\0317_____\
\030e\031c###\0308\0317=\030e\031c#\
\030e\031c#\0307\031f.\030e\031c###",
run = "usr/apps/builder.lua",
},
[ "d8c298dd41e4a4ec20e8307901797b64688b3b77" ] = {
title = "GPS Deploy",
category = "Apps",
run = "http://pastebin.com/raw/qLthLak5",
requires = "turtle",
},
[ "53a5d150062b1e03206b9e15854b81060e3c7552" ] = {
apptype = "url",
title = "Minesweeper",
category = "Games",
run = "http://pastebin.com/raw/nsKrHTbN",
},
[ "8d59207c8a84153b3e9f035cc3b6ec7a23671323" ] = {
apptype = "url",
title = "Micropaint",
category = "Apps",
run = "http://pastebin.com/raw/tMRzJXx2",
},
[ "d78f28759f255a0db76604ee560b87c4715a0da5" ] = {
apptype = "url",
title = "Sketch",
category = "Apps",
run = "http://pastebin.com/raw/Mm5hd97E",
},
}

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +0,0 @@
sys/apps/gpsdeploy urlfs http://pastebin.com/raw/qLthLak5
sys/apps/Minesweeper urlfs http://pastebin.com/raw/nsKrHTbN
sys/apps/Micropaint urlfs http://pastebin.com/raw/tMRzJXx2
sys/apps/sketch urlfs http://pastebin.com/raw/Mm5hd97E

File diff suppressed because it is too large Load Diff

View File

@ -28,7 +28,7 @@ local function findObsidian()
local _,b = turtle.inspectDown()
if b and (b.name == 'minecraft:lava' or b.name == 'minecraft:flowing_lava') then
if turtle.selectSlot('minecraft:water_bucket') then
if turtle.select('minecraft:water_bucket') then
while true do
if turtle.up() then
break
@ -84,7 +84,7 @@ local s, m = turtle.run(function()
end
findObsidian()
if not turtle.selectSlot('minecraft:water_bucket') then
if not turtle.select('minecraft:water_bucket') then
break
end
turtle.goto(0, 0)

View File

@ -7,10 +7,11 @@ requireInjector(getfenv(1))
local Point = require('point')
local synchronized = require('sync')
local Util = require('util')
turtle.pathfind = require('turtle.pathfind')
local Pathing = require('turtle.pathfind')
local function noop() end
turtle.pathfind = Pathing.pathfind
turtle.point = { x = 0, y = 0, z = 0, heading = 0 }
turtle.status = 'idle'
turtle.abort = false
@ -46,7 +47,7 @@ function turtle.resetState()
state.digPolicy = noop
state.movePolicy = _defaultMove
state.moveCallback = noop
state.locations = { }
Pathing.reset()
return true
end
@ -355,40 +356,6 @@ function turtle.setMoveCallback(cb) state.moveCallback = cb end
function turtle.clearMoveCallback() state.moveCallback = noop end
function turtle.getMoveCallback() return state.moveCallback end
-- [[ Locations ]] --
function turtle.getLocation(name)
return state.locations[name]
end
function turtle.saveLocation(name, pt)
pt = pt or turtle.point
state.locations[name] = { x = pt.x, y = pt.y, z = pt.z }
end
function turtle.gotoLocation(name)
local pt = turtle.getLocation(name)
if pt then
return turtle.goto(pt.x, pt.z, pt.y, pt.heading)
end
end
function turtle.storeLocation(name, pt)
pt = pt or turtle.point
Util.writeTable(name .. '.pt', pt)
return true
end
function turtle.loadLocation(name)
return Util.readTable(name .. '.pt')
end
function turtle.gotoStoredLocation(name)
local pt = turtle.loadLocation(name)
if pt then
return turtle.gotoPoint(pt)
end
end
-- [[ Heading ]] --
function turtle.getHeading()
return turtle.point.heading
@ -813,10 +780,6 @@ function turtle.select(indexOrId)
return false, 'Inventory does not contain item'
end
function turtle.selectSlot(indexOrId) -- deprecated
return turtle.select(indexOrId)
end
function turtle.getInventory(slots)
slots = slots or { }
for i = 1, 16 do
@ -851,24 +814,6 @@ function turtle.getSummedInventory()
return t
end
function turtle.emptyInventory(dropAction)
dropAction = dropAction or turtle.drop
for i = 1, 16 do
turtle.emptySlot(i, dropAction)
end
turtle.select(1)
end
function turtle.emptySlot(slot, dropAction)
dropAction = dropAction or turtle.drop
local count = turtle.getItemCount(slot)
if count > 0 then
turtle.select(slot)
return dropAction(count)
end
return false, 'No items to drop'
end
function turtle.getFilledSlots(startSlot)
startSlot = startSlot or 1
@ -889,6 +834,15 @@ function turtle.eachFilledSlot(fn)
end
end
function turtle.emptyInventory(dropAction)
dropAction = dropAction or turtle.native.drop
turtle.eachFilledSlot(function(slot)
turtle.select(slot.index)
dropAction()
end)
turtle.select(1)
end
function turtle.reconcileInventory(slots, dropAction)
dropAction = dropAction or turtle.native.drop
for _,s in pairs(slots) do
@ -910,10 +864,6 @@ function turtle.selectSlotWithItems(startSlot)
end
end
function turtle.selectOpenSlot(startSlot)
return turtle.selectSlotWithQuantity(0, startSlot)
end
function turtle.selectSlotWithQuantity(qty, startSlot)
startSlot = startSlot or 1
@ -925,6 +875,10 @@ function turtle.selectSlotWithQuantity(qty, startSlot)
end
end
function turtle.selectOpenSlot(startSlot)
return turtle.selectSlotWithQuantity(0, startSlot)
end
function turtle.condense()
local slots = turtle.getInventory()
@ -1007,14 +961,15 @@ function turtle.abortAction()
end
-- [[ Pathing ]] --
function turtle.faceAgainst(pt) -- 4 sided
function turtle.faceAgainst(pt, options) -- 4 sided
local pts = { }
options = options or { }
options.dest = { }
for i = 0, 3 do
local hi = turtle.getHeadingInfo(i)
table.insert(pts, {
table.insert(options.dest, {
x = pt.x + hi.xd,
z = pt.z + hi.zd,
y = pt.y + hi.yd,
@ -1022,12 +977,13 @@ function turtle.faceAgainst(pt) -- 4 sided
})
end
return turtle.pathfind(Point.closest(turtle.point, pts), { dest = pts })
return turtle.pathfind(Point.closest(turtle.point, options.dest), options)
end
function turtle.moveAgainst(pt) -- 6 sided
function turtle.moveAgainst(pt, options) -- 6 sided
local pts = { }
options = options or { }
options.dest = { }
for i = 0, 5 do
local hi = turtle.getHeadingInfo(i)
@ -1041,7 +997,7 @@ function turtle.moveAgainst(pt) -- 6 sided
direction = 'up'
end
table.insert(pts, {
table.insert(options.dest, {
x = pt.x + hi.xd,
z = pt.z + hi.zd,
y = pt.y + hi.yd,
@ -1050,7 +1006,7 @@ function turtle.moveAgainst(pt) -- 6 sided
})
end
return turtle.pathfind(Point.closest(turtle.point, pts), { dest = pts })
return turtle.pathfind(Point.closest(turtle.point, options.dest), options)
end
local actionsAt = {