1
0
mirror of https://github.com/kepler155c/opus synced 2026-05-01 19:11:26 +00:00

Initial commit

This commit is contained in:
kepler155c@gmail.com
2016-12-11 14:24:52 -05:00
commit fc243a9c12
110 changed files with 25577 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
_G.device = { }
require = requireInjector(getfenv(1))
local Peripheral = require('peripheral')
for _,side in pairs(peripheral.getNames()) do
Peripheral.addDevice(side)
end

134
sys/extensions/os.lua Normal file
View File

@@ -0,0 +1,134 @@
require = requireInjector(getfenv(1))
local Config = require('config')
local config = {
enable = false,
pocketId = 10,
distance = 8,
}
Config.load('lock', config)
local lockId
function lockScreen()
require = requireInjector(getfenv(1))
local UI = require('ui')
local Event = require('event')
local SHA1 = require('sha1')
local center = math.floor(UI.term.width / 2)
local page = UI.Page({
backgroundColor = colors.blue,
prompt = UI.Text({
x = center - 9,
y = math.floor(UI.term.height / 2),
value = 'Password',
}),
password = UI.TextEntry({
x = center,
y = math.floor(UI.term.height / 2),
width = 8,
limit = 8
}),
statusBar = UI.StatusBar(),
accelerators = {
q = 'back',
},
})
function page:eventHandler(event)
if event.type == 'key' and event.key == 'enter' then
if SHA1.sha1(self.password.value) == config.password then
os.locked = false
Event.exitPullEvents()
lockId = false
return true
else
self.statusBar:timedStatus('Invalid Password', 3)
end
end
UI.Page.eventHandler(self, event)
end
UI:setPage(page)
Event.pullEvents()
end
os.lock = function()
--os.locked = true
if not lockId then
lockId = multishell.openTab({
title = 'Lock',
env = getfenv(1),
fn = lockScreen,
focused = true,
})
end
end
os.unlock = function()
os.locked = false
if lockId then
multishell.terminate(lockId)
lockId = nil
end
end
function os.isTurtle()
return not not turtle
end
function os.isAdvanced()
return term.native().isColor()
end
function os.isPocket()
return not not pocket
end
function os.registerApp(entry)
local apps = { }
Config.load('apps', apps)
local run = fs.combine(entry.run, '')
for k,app in pairs(apps) do
if app.run == run then
table.remove(apps, k)
break
end
end
table.insert(apps, {
run = run,
title = entry.title,
args = entry.args,
category = entry.category,
icon = entry.icon,
})
Config.update('apps', apps)
os.queueEvent('os_register_app')
end
function os.unregisterApp(run)
local apps = { }
Config.load('apps', apps)
local run = fs.combine(run, '')
for k,app in pairs(apps) do
if app.run == run then
table.remove(apps, k)
Config.update('apps', apps)
os.queueEvent('os_register_app')
break
end
end
end

223
sys/extensions/pathfind.lua Normal file
View File

@@ -0,0 +1,223 @@
if not turtle then
return
end
require = requireInjector(getfenv(1))
local Grid = require ("jumper.grid")
local Pathfinder = require ("jumper.pathfinder")
local Point = require('point')
local WALKABLE = 0
local function createMap(dim)
local map = { }
for z = 0, dim.ez do
local row = {}
for x = 0, dim.ex do
local col = { }
for y = 0, dim.ey do
table.insert(col, WALKABLE)
end
table.insert(row, col)
end
table.insert(map, row)
end
return map
end
local function addBlock(map, dim, b)
map[b.z + dim.oz][b.x + dim.ox][b.y + dim.oy] = 1
end
-- map shrinks/grows depending upon blocks encountered
-- the map will encompass any blocks encountered, the turtle position, and the destination
local function mapDimensions(dest, blocks, boundingBox)
local sx, sz, sy = turtle.point.x, turtle.point.z, turtle.point.y
local ex, ez, ey = turtle.point.x, turtle.point.z, turtle.point.y
local function adjust(pt)
if pt.x < sx then
sx = pt.x
end
if pt.z < sz then
sz = pt.z
end
if pt.y < sy then
sy = pt.y
end
if pt.x > ex then
ex = pt.x
end
if pt.z > ez then
ez = pt.z
end
if pt.y > ey then
ey = pt.y
end
end
adjust(dest)
for _,b in ipairs(blocks) do
adjust(b)
end
-- expand one block out in all directions
sx = math.max(sx - 1, boundingBox.sx)
sz = math.max(sz - 1, boundingBox.sz)
sy = math.max(sy - 1, boundingBox.sy)
ex = math.min(ex + 1, boundingBox.ex)
ez = math.min(ez + 1, boundingBox.ez)
ey = math.min(ey + 1, boundingBox.ey)
return {
ex = ex - sx + 1,
ez = ez - sz + 1,
ey = ey - sy + 1,
ox = -sx + 1,
oz = -sz + 1,
oy = -sy + 1
}
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 }
end
local function nodeToPoint(dim, node)
return { x = node:getX() - dim.ox, z = node:getY() - dim.oz, y = node:getZ() - dim.oy }
end
local heuristic = function(n, node)
local m, h = Point.calculateMoves(
{ x = node._x, z = node._y, y = node._z, heading = node._heading },
{ x = n._x, z = n._y, y = n._z, heading = n._heading })
return m, h
end
local function dimsAreEqual(d1, d2)
return d1.ex == d2.ex and
d1.ey == d2.ey and
d1.ez == d2.ez and
d1.ox == d2.ox and
d1.oy == d2.oy and
d1.oz == d2.oz
end
-- turtle sensor returns blocks in relation to the world - not turtle orientation
-- so cannot figure out block location unless we know our orientation in the world
-- really kinda dumb since it returns the coordinates as offsets of our location
-- instead of true coordinates
local function addSensorBlocks(blocks, sblocks)
for _,b in pairs(sblocks) do
if b.type ~= 'AIR' then
local pt = { x = turtle.point.x, y = turtle.point.y + b.y, z = turtle.point.z }
pt.x = pt.x - b.x
pt.z = pt.z - b.z -- this will only work if we were originally facing west
local found = false
for _,ob in pairs(blocks) do
if pt.x == ob.x and pt.y == ob.y and pt.z == ob.z then
found = true
break
end
end
if not found then
table.insert(blocks, pt)
end
end
end
end
local function pathTo(dest, blocks, maxRadius)
blocks = blocks or { }
maxRadius = maxRadius or 1000000
local lastDim = nil
local map = nil
local grid = nil
local boundingBox = {
sx = math.min(turtle.point.x, dest.x) - maxRadius,
sy = math.min(turtle.point.y, dest.y) - maxRadius,
sz = math.min(turtle.point.z, dest.z) - maxRadius,
ex = math.max(turtle.point.x, dest.x) + maxRadius,
ey = math.max(turtle.point.y, dest.y) + maxRadius,
ez = math.max(turtle.point.z, dest.z) + maxRadius,
}
-- Creates a pathfinder object
local myFinder = Pathfinder(grid, 'ASTAR', walkable)
myFinder:setMode('ORTHOGONAL')
myFinder:setHeuristic(heuristic)
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, boundingBox)
-- reuse map if possible
if not lastDim or not dimsAreEqual(dim, lastDim) then
map = createMap(dim)
-- Creates a grid object
grid = Grid(map)
myFinder:setGrid(grid)
myFinder:setWalkable(WALKABLE)
lastDim = dim
end
for _,b in ipairs(blocks) do
addBlock(map, dim, b)
end
-- Define start and goal locations coordinates
local startPt = pointToMap(dim, turtle.point)
local endPt = pointToMap(dim, dest)
-- Calculates the path, and its length
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
return false, 'failed to recalculate'
end
for node, count in path:nodes() do
local pt = nodeToPoint(dim, node)
if turtle.abort then
return false, 'aborted'
end
-- use single turn method so the turtle doesn't turn around when encountering obstacles
if not turtle.gotoSingleTurn(pt.x, pt.z, pt.y) then
table.insert(blocks, pt)
--if device.turtlesensorenvironment then
-- addSensorBlocks(blocks, device.turtlesensorenvironment.sonicScan())
--end
break
end
end
end
if dest.heading then
turtle.setHeading(dest.heading)
end
return true
end
turtle.pathfind = function(dest, blocks, maxRadius)
if not blocks and turtle.gotoPoint(dest) then
return true
end
return pathTo(dest, blocks, maxRadius)
end

View File

@@ -0,0 +1,78 @@
if not turtle then
return
end
local Scheduler = {
uid = 0,
queue = { },
idle = true,
}
function turtle.abortAction()
if turtle.status ~= 'idle' then
turtle.abort = true
os.queueEvent('turtle_abort')
end
Util.clear(Scheduler.queue)
os.queueEvent('turtle_ticket', 0, true)
end
local function getTicket(fn, ...)
Scheduler.uid = Scheduler.uid + 1
if Scheduler.idle then
Scheduler.idle = false
turtle.status = 'busy'
os.queueEvent('turtle_ticket', Scheduler.uid)
else
table.insert(Scheduler.queue, Scheduler.uid)
end
return Scheduler.uid
end
local function releaseTicket(id)
for k,v in ipairs(Scheduler.queue) do
if v == id then
table.remove(Scheduler.queue, k)
return
end
end
local id = table.remove(Scheduler.queue, 1)
if id then
os.queueEvent('turtle_ticket', id)
else
Scheduler.idle = true
turtle.status = 'idle'
end
end
function turtle.run(fn, ...)
local ticketId = getTicket()
if type(fn) == 'string' then
fn = turtle[fn]
end
while true do
local e, id, abort = os.pullEventRaw('turtle_ticket')
if e == 'terminate' then
releaseTicket(ticketId)
error('Terminated')
end
if abort then
-- the function was queued, but the queue was cleared
return false, 'aborted'
end
if id == ticketId then
turtle.abort = false
local args = { ... }
local s, m = pcall(function() fn(unpack(args)) end)
turtle.abort = false
releaseTicket(ticketId)
if not s and m then
printError(m)
end
return s, m
end
end
end

40
sys/extensions/tgps.lua Normal file
View File

@@ -0,0 +1,40 @@
if not turtle then
return
end
require = requireInjector(getfenv(1))
local GPS = require('gps')
function turtle.enableGPS()
local pt = GPS.getPointAndHeading()
if pt then
turtle.setPoint(pt)
return true
end
end
function turtle.gotoGPSHome()
local homePt = turtle.loadLocation('gpsHome')
if homePt then
local pt = GPS.getPointAndHeading()
if pt then
turtle.setPoint(pt)
turtle.pathfind(homePt)
end
end
end
function turtle.setGPSHome()
local GPS = require('gps')
local pt = GPS.getPoint()
if pt then
turtle.setPoint(pt)
pt.heading = GPS.getHeading()
if pt.heading then
turtle.point.heading = pt.heading
turtle.storeLocation('gpsHome', pt)
turtle.gotoPoint(pt)
end
end
end

861
sys/extensions/tl3.lua Normal file
View File

@@ -0,0 +1,861 @@
if not turtle then
return
end
local function noop() end
turtle.point = { x = 0, y = 0, z = 0, heading = 0 }
turtle.status = 'idle'
turtle.abort = false
function turtle.getPoint()
return turtle.point
end
local state = {
moveAttack = noop,
moveDig = noop,
moveCallback = noop,
locations = {},
}
function turtle.getState()
return state
end
function turtle.setPoint(pt)
turtle.point.x = pt.x
turtle.point.y = pt.y
turtle.point.z = pt.z
if pt.heading then
turtle.point.heading = pt.heading
end
return true
end
function turtle.reset()
turtle.point.x = 0
turtle.point.y = 0
turtle.point.z = 0
turtle.point.heading = 0
turtle.abort = false -- should be part of state
--turtle.status = 'idle' -- should be part of state
state.moveAttack = noop
state.moveDig = noop
state.moveCallback = noop
state.locations = {}
return true
end
local actions = {
up = {
detect = turtle.native.detectUp,
dig = turtle.native.digUp,
move = turtle.native.up,
attack = turtle.native.attackUp,
place = turtle.native.placeUp,
drop = turtle.native.dropUp,
suck = turtle.native.suckUp,
compare = turtle.native.compareUp,
inspect = turtle.native.inspectUp,
side = 'top'
},
down = {
detect = turtle.native.detectDown,
dig = turtle.native.digDown,
move = turtle.native.down,
attack = turtle.native.attackDown,
place = turtle.native.placeDown,
drop = turtle.native.dropDown,
suck = turtle.native.suckDown,
compare = turtle.native.compareDown,
inspect = turtle.native.inspectDown,
side = 'bottom'
},
forward = {
detect = turtle.native.detect,
dig = turtle.native.dig,
move = turtle.native.forward,
attack = turtle.native.attack,
place = turtle.native.place,
drop = turtle.native.drop,
suck = turtle.native.suck,
compare = turtle.native.compare,
inspect = turtle.native.inspect,
side = 'front'
},
back = {
detect = noop,
dig = noop,
move = turtle.native.back,
attack = noop,
place = noop,
suck = noop,
compare = noop,
side = 'back'
},
}
function turtle.getAction(direction)
return actions[direction]
end
-- [[ Heading data ]] --
local headings = {
[ 0 ] = { xd = 1, zd = 0, yd = 0, heading = 0, direction = 'east' },
[ 1 ] = { xd = 0, zd = 1, yd = 0, heading = 1, direction = 'south' },
[ 2 ] = { xd = -1, zd = 0, yd = 0, heading = 2, direction = 'west' },
[ 3 ] = { xd = 0, zd = -1, yd = 0, heading = 3, direction = 'north' },
[ 4 ] = { xd = 0, zd = 0, yd = 1, heading = 4, direction = 'up' },
[ 5 ] = { xd = 0, zd = 0, yd = -1, heading = 5, direction = 'down' }
}
local namedHeadings = {
east = headings[0],
south = headings[1],
west = headings[2],
north = headings[3],
up = headings[4],
down = headings[5]
}
function turtle.getHeadings()
return headings
end
function turtle.getHeadingInfo(heading)
if heading and type(heading) == 'string' then
return namedHeadings[heading]
end
heading = heading or turtle.point.heading
return headings[heading]
end
-- [[ Basic turtle actions ]] --
local function _attack(action)
if action.attack() then
repeat until not action.attack()
return true
end
return false
end
function turtle.attack() return _attack(actions.forward) end
function turtle.attackUp() return _attack(actions.up) end
function turtle.attackDown() return _attack(actions.down) end
local function _place(action, indexOrId)
local slot
if indexOrId then
slot = turtle.getSlot(indexOrId)
if not slot then
return false, 'No items to place'
end
end
if slot and slot.qty == 0 then
return false, 'No items to place'
end
return Util.tryTimes(3, function()
if slot then
turtle.select(slot.index)
end
local result = { action.place() }
if result[1] then
return true
end
if not state.moveDig(action) then
state.moveAttack(action)
end
return unpack(result)
end)
end
function turtle.place(slot) return _place(actions.forward, slot) end
function turtle.placeUp(slot) return _place(actions.up, slot) end
function turtle.placeDown(slot) return _place(actions.down, slot) end
local function _drop(action, count, indexOrId)
if indexOrId then
local slot = turtle.getSlot(indexOrId)
if not slot or slot.qty == 0 then
return false, 'No items to drop'
end
turtle.select(slot.index)
end
if not count then
return action.drop() -- wtf
end
return action.drop(count)
end
function turtle.drop(count, slot) return _drop(actions.forward, count, slot) end
function turtle.dropUp(count, slot) return _drop(actions.up, count, slot) end
function turtle.dropDown(count, slot) return _drop(actions.down, count, slot) end
--[[
function turtle.dig() return state.dig(actions.forward) end
function turtle.digUp() return state.dig(actions.up) end
function turtle.digDown() return state.dig(actions.down) end
--]]
function turtle.isTurtleAtSide(side)
local sideType = peripheral.getType(side)
return sideType and sideType == 'turtle'
end
turtle.attackPolicies = {
none = noop,
attack = function(action)
return _attack(action)
end,
}
turtle.digPolicies = {
none = noop,
dig = function(action)
return action.dig()
end,
turtleSafe = function(action)
if action.side == 'back' then
return false
end
if not turtle.isTurtleAtSide(action.side) then
return action.dig()
end
return Util.tryTimes(6, function()
-- if not turtle.isTurtleAtSide(action.side) then
-- return true --action.dig()
-- end
os.sleep(.25)
if not action.detect() then
return true
end
end)
end,
digAndDrop = function(action)
if action.detect() then
local slots = turtle.getInventory()
if action.dig() then
turtle.reconcileInventory(slots)
return true
end
end
return false
end
}
turtle.policies = {
none = { dig = turtle.digPolicies.none, attack = turtle.attackPolicies.none },
digOnly = { dig = turtle.digPolicies.dig, attack = turtle.attackPolicies.none },
attackOnly = { dig = turtle.digPolicies.none, attack = turtle.attackPolicies.attack },
digAttack = { dig = turtle.digPolicies.dig, attack = turtle.attackPolicies.attack },
turtleSafe = { dig = turtle.digPolicies.turtleSafe, attack = turtle.attackPolicies.attack },
}
function turtle.setPolicy(policy)
if type(policy) == 'string' then
policy = turtle.policies[policy]
end
if not policy then
return false, 'Invalid policy'
end
state.moveDig = policy.dig
state.moveAttack = policy.attack
return true
end
function turtle.setDigPolicy(policy)
state.moveDig = policy
end
function turtle.setAttackPolicy(policy)
state.moveAttack = policy
end
function turtle.setMoveCallback(cb)
state.moveCallback = cb
end
function turtle.clearMoveCallback()
state.moveCallback = noop
end
local function infoMoveCallback()
local pt = turtle.point
print(string.format('x:%d y:%d z:%d heading:%d', pt.x, pt.y, pt.z, pt.heading))
end
-- TESTING
--turtle.setMoveCallback(infoMoveCallback)
-- [[ 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
end
function turtle.turnRight()
turtle.setHeading(turtle.point.heading + 1)
return turtle.point
end
function turtle.turnLeft()
turtle.setHeading(turtle.point.heading - 1)
return turtle.point
end
function turtle.turnAround()
turtle.setHeading(turtle.point.heading + 2)
return turtle.point
end
function turtle.setNamedHeading(headingName)
local headingInfo = namedHeadings[headingName]
if headingInfo then
return turtle.setHeading(headingInfo.heading)
end
return false, 'Invalid heading'
end
function turtle.setHeading(heading)
if not heading then
return
end
heading = heading % 4
if heading ~= turtle.point.heading then
while heading < turtle.point.heading do
heading = heading + 4
end
if heading - turtle.point.heading == 3 then
turtle.native.turnLeft()
turtle.point.heading = turtle.point.heading - 1
else
local turns = heading - turtle.point.heading
while turns > 0 do
turns = turns - 1
turtle.point.heading = turtle.point.heading + 1
turtle.native.turnRight()
end
end
turtle.point.heading = turtle.point.heading % 4
state.moveCallback('turn', turtle.point)
end
return turtle.point
end
function turtle.headTowardsX(dx)
if turtle.point.x ~= dx then
if turtle.point.x > dx then
turtle.setHeading(2)
else
turtle.setHeading(0)
end
end
end
function turtle.headTowardsZ(dz)
if turtle.point.z ~= dz then
if turtle.point.z > dz then
turtle.setHeading(3)
else
turtle.setHeading(1)
end
end
end
function turtle.headTowards(pt)
local xd = math.abs(turtle.point.x - pt.x)
local zd = math.abs(turtle.point.z - pt.z)
if xd > zd then
turtle.headTowardsX(pt.x)
else
turtle.headTowardsZ(pt.z)
end
end
-- [[ move ]] --
local function _move(action)
while not action.move() do
if not state.moveDig(action) and not state.moveAttack(action) then
return false
end
end
return true
end
function turtle.up()
if _move(actions.up) then
turtle.point.y = turtle.point.y + 1
state.moveCallback('up', turtle.point)
return true, turtle.point
end
end
function turtle.down()
if _move(actions.down) then
turtle.point.y = turtle.point.y - 1
state.moveCallback('down', turtle.point)
return true, turtle.point
end
end
function turtle.forward()
if _move(actions.forward) then
turtle.point.x = turtle.point.x + headings[turtle.point.heading].xd
turtle.point.z = turtle.point.z + headings[turtle.point.heading].zd
state.moveCallback('forward', turtle.point)
return true, turtle.point
end
end
function turtle.back()
if _move(actions.back) then
turtle.point.x = turtle.point.x - headings[turtle.point.heading].xd
turtle.point.z = turtle.point.z - headings[turtle.point.heading].zd
state.moveCallback('back', turtle.point)
return true, turtle.point
end
end
function turtle.moveTowardsX(dx)
local direction = dx - turtle.point.x
local move
if direction == 0 then
return true
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
repeat
if not move() then
return false
end
until turtle.point.x == dx
return true
end
function turtle.moveTowardsZ(dz)
local direction = dz - turtle.point.z
local move
if direction == 0 then
return true
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
repeat
if not move() then
return false
end
until turtle.point.z == dz
return true
end
-- [[ go ]] --
-- 1 turn goto (going backwards if possible)
function turtle.gotoSingleTurn(dx, dz, dy, dh)
dy = dy or turtle.point.y
local function gx()
if turtle.point.x ~= dx then
turtle.moveTowardsX(dx)
end
if turtle.point.z ~= dz then
if dh and dh % 2 == 1 then
turtle.setHeading(dh)
else
turtle.headTowardsZ(dz)
end
end
end
local function gz()
if turtle.point.z ~= dz then
turtle.moveTowardsZ(dz)
end
if turtle.point.x ~= dx then
if dh and dh % 2 == 0 then
turtle.setHeading(dh)
else
turtle.headTowardsX(dx)
end
end
end
repeat
local x, z
local y = turtle.point.y
repeat
x, z = turtle.point.x, turtle.point.z
if turtle.point.heading % 2 == 0 then
gx()
gz()
else
gz()
gx()
end
until x == turtle.point.x and z == turtle.point.z
if turtle.point.y ~= dy then
turtle.gotoY(dy)
end
if turtle.point.x == dx and turtle.point.z == dz and turtle.point.y == dy then
return true
end
until x == turtle.point.x and z == turtle.point.z and y == turtle.point.y
return false
end
local function gotoEx(dx, dz, dy)
-- determine the heading to ensure the least amount of turns
-- first check is 1 turn needed - remaining require 2 turns
if turtle.point.heading == 0 and turtle.point.x <= dx or
turtle.point.heading == 2 and turtle.point.x >= dx or
turtle.point.heading == 1 and turtle.point.z <= dz or
turtle.point.heading == 3 and turtle.point.z >= dz then
-- maintain current heading
-- nop
elseif dz > turtle.point.z and turtle.point.heading == 0 or
dz < turtle.point.z and turtle.point.heading == 2 or
dx < turtle.point.x and turtle.point.heading == 1 or
dx > turtle.point.x and turtle.point.heading == 3 then
turtle.turnRight()
else
turtle.turnLeft()
end
if (turtle.point.heading % 2) == 1 then
if not turtle.gotoZ(dz) then return false end
if not turtle.gotoX(dx) then return false end
else
if not turtle.gotoX(dx) then return false end
if not turtle.gotoZ(dz) then return false end
end
if dy then
if not turtle.gotoY(dy) then return false end
end
return true
end
-- fallback goto - will turn around if was previously moving backwards
local function gotoMultiTurn(dx, dz, dy)
if gotoEx(dx, dz, dy) then
return true
end
local moved
repeat
local x, y, z = turtle.point.x, turtle.point.y, turtle.point.z
-- try going the other way
if (turtle.point.heading % 2) == 1 then
turtle.headTowardsX(dx)
else
turtle.headTowardsZ(dz)
end
if gotoEx(dx, dz, dy) then
return true
end
if dy then
turtle.gotoY(dy)
end
moved = x ~= turtle.point.x or y ~= turtle.point.y or z ~= turtle.point.z
until not moved
return false
end
function turtle.gotoPoint(pt)
return turtle.goto(pt.x, pt.z, pt.y, pt.heading)
end
-- go backwards - turning around if necessary to fight mobs / break blocks
function turtle.goback()
local hi = headings[turtle.point.heading]
return turtle.goto(turtle.point.x - hi.xd, turtle.point.z - hi.zd, turtle.point.y, turtle.point.heading)
end
function turtle.gotoYfirst(pt)
if turtle.gotoY(pt.y) then
if turtle.goto(pt.x, pt.z, nil, pt.heading) then
turtle.setHeading(pt.heading)
return true
end
end
end
function turtle.gotoYlast(pt)
if turtle.goto(pt.x, pt.z, nil, pt.heading) then
if turtle.gotoY(pt.y) then
turtle.setHeading(pt.heading)
return true
end
end
end
function turtle.goto(dx, dz, dy, dh)
if not turtle.gotoSingleTurn(dx, dz, dy, dh) then
if not gotoMultiTurn(dx, dz, dy) then
return false
end
end
turtle.setHeading(dh)
return true
end
function turtle.gotoX(dx)
turtle.headTowardsX(dx)
while turtle.point.x ~= dx do
if not turtle.forward() then
return false
end
end
return true
end
function turtle.gotoZ(dz)
turtle.headTowardsZ(dz)
while turtle.point.z ~= dz do
if not turtle.forward() then
return false
end
end
return true
end
function turtle.gotoY(dy)
while turtle.point.y > dy do
if not turtle.down() then
return false
end
end
while turtle.point.y < dy do
if not turtle.up() then
return false
end
end
return true
end
-- [[ Slot management ]] --
function turtle.getSlot(indexOrId, slots)
if type(indexOrId) == 'string' then
slots = slots or turtle.getInventory()
local _,c = string.gsub(indexOrId, ':', '')
if c == 2 then -- combined id and dmg .. ie. minecraft:coal:0
return Util.find(slots, 'iddmg', indexOrId)
end
return Util.find(slots, 'id', indexOrId)
end
local detail = turtle.getItemDetail(indexOrId)
if detail then
return {
qty = detail.count,
dmg = detail.damage,
id = detail.name,
iddmg = detail.name .. ':' .. detail.damage,
index = indexOrId,
}
end
return {
qty = 0,
index = indexOrId,
}
end
function turtle.selectSlot(indexOrId)
local s = turtle.getSlot(indexOrId)
if s then
turtle.select(s.index)
return s
end
return false, 'Inventory does not contain item'
end
function turtle.getInventory(slots)
slots = slots or { }
for i = 1, 16 do
slots[i] = turtle.getSlot(i)
end
return slots
end
function turtle.emptyInventory(dropAction)
dropAction = dropAction or turtle.drop
for i = 1, 16 do
turtle.emptySlot(i, dropAction)
end
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
local slots = { }
for i = startSlot, 16 do
local count = turtle.getItemCount(i)
if count > 0 then
slots[i] = turtle.getSlot(i)
end
end
return slots
end
function turtle.eachFilledSlot(fn)
local slots = turtle.getFilledSlots()
for _,slot in pairs(slots) do
fn(slot)
end
end
function turtle.reconcileInventory(slots, dropAction)
dropAction = dropAction or turtle.native.drop
for _,s in pairs(slots) do
local qty = turtle.getItemCount(s.index)
if qty > s.qty then
turtle.select(s.index)
dropAction(qty-s.qty, s)
end
end
end
function turtle.selectSlotWithItems(startSlot)
startSlot = startSlot or 1
for i = startSlot, 16 do
if turtle.getItemCount(i) > 0 then
turtle.select(i)
return i
end
end
end
function turtle.selectOpenSlot(startSlot)
return turtle.selectSlotWithQuantity(0, startSlot)
end
function turtle.selectSlotWithQuantity(qty, startSlot)
startSlot = startSlot or 1
for i = startSlot, 16 do
if turtle.getItemCount(i) == qty then
turtle.select(i)
return i
end
end
end
function turtle.condense(startSlot)
startSlot = startSlot or 1
local aslots = turtle.getInventory()
for _,slot in ipairs(aslots) do
if slot.qty < 64 then
for i = slot.index + 1, 16 do
local fslot = aslots[i]
if fslot.qty > 0 then
if slot.qty == 0 or slot.iddmg == fslot.iddmg then
turtle.select(fslot.index)
turtle.transferTo(slot.index, 64)
local transferred = turtle.getItemCount(slot.index) - slot.qty
slot.qty = slot.qty + transferred
fslot.qty = fslot.qty - transferred
slot.iddmg = fslot.iddmg
if slot.qty == 64 then
break
end
end
end
end
end
end
end

311
sys/extensions/vfs.lua Normal file
View File

@@ -0,0 +1,311 @@
if fs.native then
return
end
require = requireInjector(getfenv(1))
fs.native = Util.shallowCopy(fs)
local fstypes = { }
local nativefs = { }
for k,fn in pairs(fs) do
if type(fn) == 'function' then
nativefs[k] = function(node, ...)
return fn(...)
end
end
end
function nativefs.list(node, dir, full)
local files
if fs.native.isDir(dir) then
files = fs.native.list(dir)
end
local function inList(l, e)
for _,v in ipairs(l) do
if v == e then
return true
end
end
end
if dir == node.mountPoint and node.nodes then
files = files or { }
for k in pairs(node.nodes) do
if not inList(files, k) then
table.insert(files, k)
end
end
end
if not files then
error('Not a directory')
end
if full then
local t = { }
pcall(function()
for _,f in ipairs(files) do
local fullName = fs.combine(dir, f)
local file = {
name = f,
isDir = fs.isDir(fullName),
isReadOnly = fs.isReadOnly(fullName),
}
if not file.isDir then
file.size = fs.getSize(fullName)
end
table.insert(t, file)
end
end)
return t
end
return files
end
function nativefs.getSize(node, dir)
if node.mountPoint == dir and node.nodes then
return 0
end
return fs.native.getSize(dir)
end
function nativefs.isDir(node, dir)
if node.mountPoint == dir then
return not not node.nodes
end
return fs.native.isDir(dir)
end
function nativefs.exists(node, dir)
if node.mountPoint == dir then
return true
end
return fs.native.exists(dir)
end
function nativefs.delete(node, dir)
if node.mountPoint == dir then
fs.unmount(dir)
else
fs.native.delete(dir)
end
end
fstypes.nativefs = nativefs
fs.nodes = {
fs = nativefs,
mountPoint = '',
fstype = 'nativefs',
nodes = { },
}
local function splitpath(path)
local parts = { }
for match in string.gmatch(path, "[^/]+") do
table.insert(parts, match)
end
return parts
end
local function getNode(dir)
local cd = fs.combine(dir, '')
local parts = splitpath(cd)
local node = fs.nodes
for _,d in ipairs(parts) do
if node.nodes and node.nodes[d] then
node = node.nodes[d]
else
break
end
end
return node
end
local methods = { 'delete', 'getFreeSpace', 'exists', 'isDir', 'getSize',
'isReadOnly', 'makeDir', 'getDrive', 'list', 'open' }
for _,m in pairs(methods) do
fs[m] = function(dir, ...)
dir = fs.combine(dir, '')
local node = getNode(dir)
return node.fs[m](node, dir, ...)
end
end
function fs.complete(partial, dir, includeFiles, includeSlash)
dir = fs.combine(dir, '')
local node = getNode(dir)
if node.fs.complete then
return node.fs.complete(node, partial, dir, includeFiles, includeSlash)
end
return fs.native.complete(partial, dir, includeFiles, includeSlash)
end
function fs.copy(s, t)
local sp = getNode(s)
local tp = getNode(t)
if sp == tp and sp.fs.copy then
return sp.fs.copy(sp, s, t)
end
if fs.exists(t) then
error('File exists')
end
if fs.isDir(s) then
fs.makeDir(t)
local list = fs.list(s)
for _,f in ipairs(list) do
fs.copy(fs.combine(s, f), fs.combine(t, f))
end
else
local sf = Util.readFile(s)
if not sf then
error('No such file')
end
Util.writeFile(t, sf)
end
end
function fs.find(spec) -- not optimized
-- local node = getNode(spec)
-- local files = node.fs.find(node, spec)
local files = { }
-- method from https://github.com/N70/deltaOS/blob/dev/vfs
local function recurse_spec(results, path, spec)
local segment = spec:match('([^/]*)'):gsub('/', '')
local pattern = '^' .. segment:gsub("[%.%[%]%(%)%%%+%-%?%^%$]","%%%1"):gsub("%z","%%z"):gsub("%*","[^/]-") .. '$'
if fs.isDir(path) then
for _, file in ipairs(fs.list(path)) do
if file:match(pattern) then
local f = fs.combine(path, file)
if spec == segment then
table.insert(results, f)
end
if fs.isDir(f) then
recurse_spec(results, f, spec:sub(#segment + 2))
end
end
end
end
end
recurse_spec(files, '', spec)
table.sort(files)
return files
end
function fs.move(s, t)
local sp = getNode(s)
local tp = getNode(t)
if sp == tp and sp.fs.move then
return sp.fs.move(sp, s, t)
end
fs.copy(s, t)
fs.delete(s)
end
local function getfstype(fstype)
local vfs = fstypes[fstype]
if not vfs then
vfs = require('fs.' .. fstype)
fs.registerType(fstype, vfs)
end
return vfs
end
function fs.mount(path, fstype, ...)
local vfs = getfstype(fstype)
if not vfs then
error('Invalid file system type')
end
local node = vfs.mount(path, ...)
if node then
local parts = splitpath(path)
local targetName = table.remove(parts, #parts)
local tp = fs.nodes
for _,d in ipairs(parts) do
if not tp.nodes then
tp.nodes = { }
end
if not tp.nodes[d] then
tp.nodes[d] = Util.shallowCopy(tp)
tp.nodes[d].nodes = { }
tp.nodes[d].mountPoint = fs.combine(tp.mountPoint, d)
end
tp = tp.nodes[d]
end
node.fs = vfs
node.fstype = fstype
if not targetName then
node.mountPoint = ''
fs.nodes = node
else
node.mountPoint = fs.combine(tp.mountPoint, targetName)
tp.nodes[targetName] = node
end
end
return node
end
local function getNodeByParts(parts)
local node = fs.nodes
for _,d in ipairs(parts) do
if not node.nodes[d] then
return
end
node = node.nodes[d]
end
return node
end
function fs.unmount(path)
local parts = splitpath(path)
local targetName = table.remove(parts, #parts)
local node = getNodeByParts(parts)
if not node or not node.nodes[targetName] then
error('Invalid node')
end
node.nodes[targetName] = nil
--[[
-- remove the shadow directories
while #parts > 0 do
targetName = table.remove(parts, #parts)
node = getNodeByParts(parts)
if not Util.empty(node.nodes[targetName].nodes) then
break
end
node.nodes[targetName] = nil
end
--]]
end
function fs.registerType(name, fs)
fstypes[name] = fs
end
function fs.getTypes()
return fstypes
end
function fs.restore()
local native = fs.native
Util.clear(fs)
Util.merge(fs, native)
end