mirror of
https://github.com/kepler155c/opus
synced 2026-05-01 19:11:26 +00:00
Initial commit
This commit is contained in:
8
sys/extensions/device.lua
Normal file
8
sys/extensions/device.lua
Normal 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
134
sys/extensions/os.lua
Normal 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
223
sys/extensions/pathfind.lua
Normal 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
|
||||
78
sys/extensions/scheduler.lua
Normal file
78
sys/extensions/scheduler.lua
Normal 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
40
sys/extensions/tgps.lua
Normal 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
861
sys/extensions/tl3.lua
Normal 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
311
sys/extensions/vfs.lua
Normal 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
|
||||
Reference in New Issue
Block a user