new release

This commit is contained in:
kepler155c@gmail.com 2018-12-08 13:25:30 -05:00
commit 3953ef08cc
35 changed files with 1356 additions and 369 deletions

View File

@ -15,8 +15,13 @@ function Config.load(fname, data)
if not fs.exists(filename) then
Util.writeTable(filename, data)
else
Util.merge(data, Util.readTable(filename) or { })
local contents = Util.readTable(filename) or
error('Configuration file is corrupt:' .. filename)
Util.merge(data, contents)
end
return data
end
function Config.loadWithCheck(fname, data)
@ -33,7 +38,7 @@ function Config.loadWithCheck(fname, data)
shell.run('edit ' .. filename)
end
Config.load(fname, data)
return Config.load(fname, data)
end
function Config.update(fname, data)

View File

@ -1,4 +1,5 @@
local os = _G.os
local os = _G.os
local table = _G.table
local Event = {
uid = 1, -- unique id for handlers
@ -6,8 +7,29 @@ local Event = {
types = { }, -- event handlers
timers = { }, -- named timers
terminate = false,
free = { },
}
-- Use a pool of coroutines for event handlers
local function createCoroutine(h)
local co = table.remove(Event.free)
if not co then
co = coroutine.create(function(_, ...)
local args = { ... }
while true do
h.fn(table.unpack(args))
h.co = nil
table.insert(Event.free, co)
args = { coroutine.yield() }
h = table.remove(args, 1)
h.co = co
end
end)
end
h.primeCo = true -- TODO: fix...
return co
end
local Routine = { }
function Routine:isDead()
@ -24,18 +46,20 @@ function Routine:terminate()
end
function Routine:resume(event, ...)
--if coroutine.status(self.co) == 'running' then
--return
--end
if not self.co then
error('Cannot resume a dead routine')
end
if not self.filter or self.filter == event or event == "terminate" then
local s, m = coroutine.resume(self.co, event, ...)
if coroutine.status(self.co) == 'dead' then
local s, m
if self.primeCo then
-- Only need self passed when using a coroutine from the pool
s, m = coroutine.resume(self.co, self, event, ...)
self.primeCo = nil
else
s, m = coroutine.resume(self.co, event, ...)
end
if self:isDead() then
self.co = nil
self.filter = nil
Event.routines[self.uid] = nil
@ -83,8 +107,14 @@ end
function Event.off(h)
if h and h.event then
for _,event in pairs(h.event) do
local handler = Event.types[event][h.uid]
if handler then
handler:terminate()
end
Event.types[event][h.uid] = nil
end
elseif h and h.co then
h:terminate()
end
end
@ -107,7 +137,12 @@ local function addTimer(interval, recurring, fn)
end
function Event.onInterval(interval, fn)
return addTimer(interval, true, fn)
return Event.addRoutine(function()
while true do
os.sleep(interval)
fn()
end
end)
end
function Event.onTimeout(timeout, fn)
@ -136,6 +171,18 @@ function Event.waitForEvent(event, timeout)
until e[1] == 'timer' and e[2] == timerId
end
-- Set a handler for the terminate event. Within the function, return
-- true or false to indicate whether the event should be propagated to
-- all sub-threads
function Event.onTerminate(fn)
Event.termFn = fn
end
function Event.termFn()
Event.terminate = true
return true -- propagate
end
function Event.addRoutine(fn)
local r = setmetatable({
co = coroutine.create(fn),
@ -171,7 +218,7 @@ local function processHandlers(event)
for _,h in pairs(handlers) do
if not h.co then
-- callbacks are single threaded (only 1 co per handler)
h.co = coroutine.create(h.fn)
h.co = createCoroutine(h)
Event.routines[h.uid] = h
end
end
@ -204,11 +251,16 @@ end
function Event.pullEvent(eventType)
while true do
local e = { os.pullEventRaw() }
local propagate = true -- don't like this...
Event.terminate = Event.terminate or e[1] == 'terminate'
if e[1] == 'terminate' then
propagate = Event.termFn()
end
processHandlers(e[1])
processRoutines(table.unpack(e))
if propagate then
processHandlers(e[1])
processRoutines(table.unpack(e))
end
if Event.terminate then
return { 'terminate' }

View File

@ -1,5 +1,5 @@
local Socket = require('socket')
local synchronized = require('sync')
local synchronized = require('sync').sync
local fs = _G.fs

View File

@ -1,27 +1,51 @@
local json = require('json')
local Util = require('util')
-- Limit queries to once per minecraft day
-- TODO: will not work if time is stopped
local TREE_URL = 'https://api.github.com/repos/%s/%s/git/trees/%s?recursive=1'
local FILE_URL = 'https://raw.githubusercontent.com/%s/%s/%s/%s'
local git = { }
function git.list(repository)
local fs = _G.fs
local os = _G.os
if not _G.GIT then
_G.GIT = { }
end
function git.list(repository)
local t = Util.split(repository, '(.-)/')
local user = t[1]
local repo = t[2]
local branch = t[3] or 'master'
local user = table.remove(t, 1)
local repo = table.remove(t, 1)
local branch = table.remove(t, 1) or 'master'
local path
local dataUrl = string.format(TREE_URL, user, repo, branch)
local contents = Util.download(dataUrl)
if not contents then
error('Invalid repository')
if not Util.empty(t) then
path = table.concat(t, '/') .. '/'
end
local data = json.decode(contents)
local cacheKey = table.concat({ user, repo, branch }, '-')
local fname = fs.combine('.git', cacheKey)
local function getContents()
if fs.exists(fname) then
local contents = Util.readTable(fname)
if contents and contents.data == os.day() then
return contents.data
end
fs.delete(fname)
end
local dataUrl = string.format(TREE_URL, user, repo, branch)
local contents = Util.download(dataUrl)
if contents then
return json.decode(contents)
end
end
local data = getContents() or error('Invalid repository')
if data.message and data.message:find("API rate limit exceeded") then
error("Out of API calls, try again later")
@ -31,15 +55,26 @@ function git.list(repository)
error("Invalid repository")
end
local list = { }
if not fs.exists(fname) then
Util.writeTable('.git/' .. cacheKey, { day = os.day(), data = data })
end
local list = { }
for _,v in pairs(data.tree) do
if v.type == "blob" then
v.path = v.path:gsub("%s","%%20")
list[v.path] = {
url = string.format(FILE_URL, user, repo, branch, v.path),
size = v.size,
}
if not path then
list[v.path] = {
url = string.format(FILE_URL, user, repo, branch, v.path),
size = v.size,
}
elseif Util.startsWith(v.path, path) then
local p = string.sub(v.path, #path)
list[p] = {
url = string.format(FILE_URL, user, repo, branch, path .. p),
size = v.size,
}
end
end
end

View File

@ -68,7 +68,6 @@ local function encodeCommon(val, pretty, tabLevel, tTracking)
str = str .. encodeCommon(v, pretty, tabLevel, tTracking)
end)
else
debug(val)
arrEncoding(val, "{", "}", pairs, function(k,v)
assert(type(k) == "string", "JSON object keys must be strings", 2)
str = str .. encodeCommon(k, pretty, tabLevel, tTracking)

60
sys/apis/packages.lua Normal file
View File

@ -0,0 +1,60 @@
local Util = require('util')
local fs = _G.fs
local textutils = _G.textutils
local PACKAGE_DIR = 'packages'
local Packages = { }
function Packages:installed()
self.cache = { }
if fs.exists(PACKAGE_DIR) then
for _, dir in pairs(fs.list(PACKAGE_DIR)) do
local path = fs.combine(fs.combine(PACKAGE_DIR, dir), '.package')
self.cache[dir] = Util.readTable(path)
end
end
return self.cache
end
function Packages:list()
if self.packageList then
return self.packageList
end
self.packageList = Util.readTable('usr/config/packages') or { }
return self.packageList
end
function Packages:isInstalled(package)
return self:installed()[package]
end
function Packages:getManifest(package)
local fname = 'packages/' .. package .. '/.package'
if fs.exists(fname) then
local c = Util.readTable(fname)
if c then
c.repository = c.repository:gsub('{{OPUS_BRANCH}}', _G.OPUS_BRANCH)
return c
end
end
local list = self:list()
local url = list and list[package]
if url then
local c = Util.httpGet(url)
if c then
c = textutils.unserialize(c)
if c then
c.repository = c.repository:gsub('{{OPUS_BRANCH}}', _G.OPUS_BRANCH)
return c
end
end
end
end
return Packages

View File

@ -28,9 +28,9 @@ function Peripheral.addDevice(deviceList, side)
end
if ptype == 'modem' then
if Peripheral.call(name, 'isWireless') then
ptype = 'wireless_modem'
else
if not Peripheral.call(name, 'isWireless') then
-- ptype = 'wireless_modem'
-- else
ptype = 'wired_modem'
end
end
@ -55,17 +55,21 @@ function Peripheral.addDevice(deviceList, side)
end
-- this can randomly fail
pcall(function() deviceList[name] = Peripheral.wrap(side) end)
if not deviceList[name] then
pcall(function()
deviceList[name] = Peripheral.wrap(side)
end)
if deviceList[name] then
Util.merge(deviceList[name], {
name = name,
type = ptype,
side = side,
})
return deviceList[name]
if deviceList[name] then
Util.merge(deviceList[name], {
name = name,
type = ptype,
side = side,
})
end
end
return deviceList[name]
end
function Peripheral.getBySide(side)

View File

@ -48,6 +48,13 @@ function Point.copy(pt)
return { x = pt.x, y = pt.y, z = pt.z }
end
function Point.round(pt)
pt.x = Util.round(pt.x)
pt.y = Util.round(pt.y)
pt.z = Util.round(pt.z)
return pt
end
function Point.same(pta, ptb)
return pta.x == ptb.x and
pta.y == ptb.y and
@ -144,14 +151,21 @@ function Point.calculateMoves(pta, ptb, distance)
end
end
if ptb.heading then
if heading ~= ptb.heading then
moves = moves + Point.calculateTurns(heading, ptb.heading)
heading = ptb.heading
end
if not ptb.heading then
return moves, heading, moves
end
return moves, heading
-- calc turns as slightly less than moves
local weighted = moves
if heading ~= ptb.heading then
local turns = Point.calculateTurns(heading, ptb.heading)
moves = moves + turns
local wturns = { [0] = 0, [1] = .9, [2] = 1.9 }
weighted = weighted + wturns[turns]
heading = ptb.heading
end
return moves, heading, weighted
end
-- given a set of points, find the one taking the least moves
@ -164,7 +178,7 @@ function Point.closest(reference, pts)
for _,pt in pairs(pts) do
local distance = Point.turtleDistance(reference, pt)
if distance < lm then
local m = Point.calculateMoves(reference, pt, distance)
local _, _, m = Point.calculateMoves(reference, pt, distance)
if m < lm then
lpt = pt
lm = m

View File

@ -81,7 +81,8 @@ local function loopback(port, sport, msg)
end
local function newSocket(isLoopback)
for i = 16384, 32767 do
for _ = 16384, 32767 do
local i = math.random(16384, 32767)
if not device.wireless_modem.isOpen(i) then
local socket = {
shost = os.getComputerID(),
@ -128,6 +129,7 @@ function Socket.connect(host, port)
local e, id, sport, dport, msg = os.pullEvent()
if e == 'modem_message' and
sport == socket.sport and
type(msg) == 'table' and
msg.dhost == socket.shost then
os.cancelTimer(timerId)
@ -171,7 +173,7 @@ local function trusted(msg, port)
local data = Crypto.decrypt(msg.t or '', pubKey)
--local sharedKey = modexp(pubKey, exchange.secretKey, public.primeMod)
return data.ts and tonumber(data.ts) and math.abs(os.time() - data.ts) < 1
return data.ts and tonumber(data.ts) and math.abs(os.time() - data.ts) < 24
end
end
@ -184,6 +186,7 @@ function Socket.server(port)
if sport == port and
msg and
type(msg) == 'table' and
msg.dhost == os.getComputerID() and
msg.type == 'OPEN' then

18
sys/apis/sound.lua Normal file
View File

@ -0,0 +1,18 @@
local peripheral = _G.peripheral
local Sound = {
_volume = 1,
}
function Sound.play(sound, vol)
local speaker = peripheral.find('speaker')
if speaker then
speaker.playSound('minecraft:' .. sound, vol or Sound._volume)
end
end
function Sound.setVolume(volume)
Sound._volume = math.max(0, math.min(volume, 1))
end
return Sound

View File

@ -1,26 +1,61 @@
local syncLocks = { }
local Sync = {
syncLocks = { }
}
local os = _G.os
return function(obj, fn)
function Sync.sync(obj, fn)
local key = tostring(obj)
if syncLocks[key] then
if Sync.syncLocks[key] then
local cos = tostring(coroutine.running())
table.insert(syncLocks[key], cos)
table.insert(Sync.syncLocks[key], cos)
repeat
local _, co = os.pullEvent('sync_lock')
until co == cos
else
syncLocks[key] = { }
Sync.syncLocks[key] = { }
end
local s, m = pcall(fn)
local co = table.remove(syncLocks[key], 1)
local co = table.remove(Sync.syncLocks[key], 1)
if co then
os.queueEvent('sync_lock', co)
else
syncLocks[key] = nil
Sync.syncLocks[key] = nil
end
if not s then
error(m)
end
end
function Sync.lock(obj)
local key = tostring(obj)
if Sync.syncLocks[key] then
local cos = tostring(coroutine.running())
table.insert(Sync.syncLocks[key], cos)
repeat
local _, co = os.pullEvent('sync_lock')
until co == cos
else
Sync.syncLocks[key] = { }
end
end
function Sync.release(obj)
local key = tostring(obj)
if not Sync.syncLocks[key] then
error('Sync.release: Lock was not obtained', 2)
end
local co = table.remove(Sync.syncLocks[key], 1)
if co then
os.queueEvent('sync_lock', co)
else
Sync.syncLocks[key] = nil
end
end
function Sync.isLocked(obj)
local key = tostring(obj)
return not not Sync.syncLocks[key]
end
return Sync

View File

@ -3,6 +3,7 @@ local class = require('class')
local Event = require('event')
local Input = require('input')
local Peripheral = require('peripheral')
local Sound = require('sound')
local Transition = require('ui.transition')
local Util = require('util')
@ -306,6 +307,9 @@ function Manager:setDefaultDevice(dev)
end
function Manager:addPage(name, page)
if not self.pages then
self.pages = { }
end
self.pages[name] = page
end
@ -940,6 +944,7 @@ function UI.Device:postInit()
end
function UI.Device:resize()
self.device.setTextScale(self.textScale)
self.width, self.height = self.device.getSize()
self.lines = { }
self.canvas:resize(self.width, self.height)
@ -1055,6 +1060,17 @@ function UI.StringBuffer:insert(s, width)
end
end
function UI.StringBuffer:insertRight(s, width)
local len = #tostring(s or '')
if len > width then
s = _sub(s, 1, width)
end
if len < width then
table.insert(self.buffer, _rep(' ', width - len))
end
table.insert(self.buffer, s)
end
function UI.StringBuffer:get(sep)
return Util.widthify(table.concat(self.buffer, sep or ''), self.bufSize)
end
@ -1205,7 +1221,7 @@ function UI.Page:focusNext()
end
function UI.Page:setFocus(child)
if not child.focus then
if not child or not child.focus then
return
end
@ -1218,7 +1234,8 @@ function UI.Page:setFocus(child)
self.focused = child
if not child.focused then
child.focused = true
self:emit({ type = 'focus_change', focused = child })
child:emit({ type = 'focus_change', focused = child })
--self:emit({ type = 'focus_change', focused = child })
end
child:focus()
@ -1250,6 +1267,7 @@ UI.Grid.defaults = {
backgroundSelectedColor = colors.gray,
headerBackgroundColor = colors.cyan,
headerTextColor = colors.white,
headerSortColor = colors.yellow,
unfocusedTextSelectedColor = colors.white,
unfocusedBackgroundSelectedColor = colors.gray,
focusIndicator = '>',
@ -1407,6 +1425,18 @@ function UI.Grid:getSelected()
end
end
function UI.Grid:setSelected(name, value)
if self.sorted then
for k,v in pairs(self.sorted) do
if self.values[v][name] == value then
self:setIndex(k)
return
end
end
end
self:setIndex(1)
end
function UI.Grid:focus()
self:drawRows()
end
@ -1459,7 +1489,7 @@ function UI.Grid:update()
end
function UI.Grid:drawHeadings()
local sb = UI.StringBuffer(self.width)
local x = 1
for _,col in ipairs(self.columns) do
local ind = ' '
if col.key == self.sortColumn then
@ -1469,9 +1499,13 @@ function UI.Grid:drawHeadings()
ind = self.sortIndicator
end
end
sb:insert(ind .. col.heading, col.cw + 1)
self:write(x,
1,
Util.widthify(ind .. col.heading, col.cw + 1),
self.headerBackgroundColor,
col.key == self.sortColumn and self.headerSortColor or self.headerTextColor)
x = x + col.cw + 1
end
self:write(1, 1, sb:get(), self.headerBackgroundColor, self.headerTextColor)
end
function UI.Grid:sortCompare(a, b)
@ -1508,7 +1542,11 @@ function UI.Grid:drawRows()
end
for _,col in pairs(self.columns) do
sb:insert(ind .. safeValue(row[col.key] or ''), col.cw + 1)
if col.justify == 'right' then
sb:insertRight(ind .. safeValue(row[col.key] or ''), col.cw + 1)
else
sb:insert(ind .. safeValue(row[col.key] or ''), col.cw + 1)
end
ind = ' '
end
@ -1603,13 +1641,12 @@ function UI.Grid:eventHandler(event)
local col = 2
for _,c in ipairs(self.columns) do
if event.x < col + c.cw then
if self.sortColumn == c.key then
self:setInverseSort(not self.inverseSort)
else
self.sortColumn = c.key
self:setInverseSort(false)
end
self:draw()
self:emit({
type = 'grid_sort',
sortColumn = c.key,
inverseSort = self.sortColumn == c.key and not self.inverseSort,
element = self,
})
break
end
col = col + c.cw + 1
@ -1632,6 +1669,10 @@ function UI.Grid:eventHandler(event)
end
return false
elseif event.type == 'grid_sort' then
self.sortColumn = event.sortColumn
self:setInverseSort(event.inverseSort)
self:draw()
elseif event.type == 'scroll_down' then
self:setIndex(self.index + 1)
elseif event.type == 'scroll_up' then
@ -1854,6 +1895,8 @@ function UI.TitleBar:draw()
sb:center(string.format(' %s ', self.title))
if self.previousPage or self.event then
sb:insert(-1, self.closeInd)
else
sb:insert(-2, self.frameChar)
end
self:write(1, 1, sb:get())
end
@ -2286,56 +2329,52 @@ function UI.Wizard:add(pages)
end
end
function UI.Wizard:enable()
function UI.Wizard:getPage(index)
return Util.find(self.pages, 'index', index)
end
function UI.Wizard:enable(...)
self.enabled = true
for _,child in ipairs(self.children) do
if not child.index then
child:enable()
elseif child.index == 1 then
child:enable()
self.index = 1
local initial = self:getPage(1)
for _,child in pairs(self.children) do
if child == initial or not child.index then
child:enable(...)
else
child:disable()
end
end
self:emit({ type = 'enable_view', next = Util.find(self.pages, 'index', 1) })
self:emit({ type = 'enable_view', next = initial })
end
function UI.Wizard:nextView()
local currentView = Util.find(self.pages, 'enabled', true)
local nextView = Util.find(self.pages, 'index', currentView.index + 1)
if nextView then
self:emit({ type = 'enable_view', view = nextView })
self:addTransition('slideLeft')
currentView:disable()
nextView:enable()
end
end
function UI.Wizard:prevView()
local currentView = Util.find(self.pages, 'enabled', true)
local nextView = Util.find(self.pages, 'index', currentView.index - 1)
if nextView then
self:emit({ type = 'enable_view', view = nextView })
self:addTransition('slideRight')
currentView:disable()
nextView:enable()
end
function UI.Wizard:isViewValid()
local currentView = self:getPage(self.index)
return not currentView.validate and true or currentView:validate()
end
function UI.Wizard:eventHandler(event)
if event.type == 'nextView' then
local currentView = Util.find(self.pages, 'enabled', true)
local nextView = Util.find(self.pages, 'index', currentView.index + 1)
currentView:emit({ type = 'enable_view', next = nextView, current = currentView })
local currentView = self:getPage(self.index)
if self:isViewValid() then
self.index = self.index + 1
local nextView = self:getPage(self.index)
currentView:emit({ type = 'enable_view', next = nextView, current = currentView })
end
elseif event.type == 'previousView' then
local currentView = Util.find(self.pages, 'enabled', true)
local nextView = Util.find(self.pages, 'index', currentView.index - 1)
currentView:emit({ type = 'enable_view', prev = nextView, current = currentView })
local currentView = self:getPage(self.index)
local nextView = self:getPage(self.index - 1)
if nextView then
self.index = self.index - 1
currentView:emit({ type = 'enable_view', prev = nextView, current = currentView })
end
return true
elseif event.type == 'wizard_complete' then
if self:isViewValid() then
self:emit({ type = 'accept' })
end
elseif event.type == 'enable_view' then
if event.current then
if event.next then
@ -2347,18 +2386,20 @@ function UI.Wizard:eventHandler(event)
end
local current = event.next or event.prev
if Util.find(self.pages, 'index', current.index - 1) then
if not current then error('property "index" is required on wizard pages') end
if self:getPage(self.index - 1) then
self.previousButton:enable()
else
self.previousButton:disable()
end
if Util.find(self.pages, 'index', current.index + 1) then
if self:getPage(self.index + 1) then
self.nextButton.text = 'Next >'
self.nextButton.event = 'nextView'
else
self.nextButton.text = 'Accept'
self.nextButton.event = 'accept'
self.nextButton.event = 'wizard_complete'
end
-- a new current view
current:enable()
@ -2381,12 +2422,12 @@ function UI.SlideOut:enable()
self.enabled = false
end
function UI.SlideOut:show()
function UI.SlideOut:show(...)
self:addTransition('expandUp')
self.canvas:setVisible(true)
self.enabled = true
for _,child in pairs(self.children) do
child:enable()
child:enable(...)
end
self:draw()
self:capture(self)
@ -2494,6 +2535,7 @@ end
function UI.Notification:error(value, timeout)
self.backgroundColor = colors.red
Sound.play('entity.villager.no', .5)
self:display(value, timeout)
end
@ -2544,9 +2586,10 @@ UI.Throttle = class(UI.Window)
UI.Throttle.defaults = {
UIElement = 'Throttle',
backgroundColor = colors.gray,
height = 6,
bordercolor = colors.cyan,
height = 4,
width = 10,
timeout = .095,
timeout = .075,
ctr = 0,
image = {
' //) (O )~@ &~&-( ?Q ',
@ -2562,6 +2605,7 @@ function UI.Throttle:setParent()
end
function UI.Throttle:enable()
self.c = os.clock()
self.enabled = false
end
@ -2570,27 +2614,26 @@ function UI.Throttle:disable()
self.enabled = false
self.canvas:removeLayer()
self.canvas = nil
self.c = nil
self.ctr = 0
end
end
function UI.Throttle:update()
local cc = os.clock()
if not self.c then
self.c = cc
elseif cc > self.c + self.timeout then
if cc > self.c + self.timeout then
os.sleep(0)
self.c = os.clock()
self.enabled = true
if not self.canvas then
self.canvas = self:addLayer(self.backgroundColor, colors.cyan)
self.canvas = self:addLayer(self.backgroundColor, self.borderColor)
self.canvas:setVisible(true)
self:clear(colors.cyan)
self:clear(self.borderColor)
end
local image = self.image[self.ctr + 1]
local width = self.width - 2
for i = 0, #self.image do
self:write(2, i + 2, image:sub(width * i + 1, width * i + width), colors.black, colors.white)
self:write(2, i + 1, image:sub(width * i + 1, width * i + width),
self.backgroundColor, self.textColor)
end
self.ctr = (self.ctr + 1) % #self.image
@ -2913,6 +2956,9 @@ UI.Chooser.defaults = {
choices = { },
nochoice = 'Select',
backgroundFocusColor = colors.lightGray,
textInactiveColor = colors.gray,
leftIndicator = '<',
rightIndicator = '>',
height = 1,
}
function UI.Chooser:setParent()
@ -2933,14 +2979,15 @@ function UI.Chooser:draw()
if self.focused then
bg = self.backgroundFocusColor
end
local fg = self.inactive and self.textInactiveColor or self.textColor
local choice = Util.find(self.choices, 'value', self.value)
local value = self.nochoice
if choice then
value = choice.name
end
self:write(1, 1, '<', bg, colors.black)
self:write(2, 1, ' ' .. Util.widthify(value, self.width-4) .. ' ', bg)
self:write(self.width, 1, '>', bg, colors.black)
self:write(1, 1, self.leftIndicator, self.backgroundColor, colors.black)
self:write(2, 1, ' ' .. Util.widthify(tostring(value), self.width-4) .. ' ', bg, fg)
self:write(self.width, 1, self.rightIndicator, self.backgroundColor, colors.black)
end
function UI.Chooser:focus()
@ -2951,22 +2998,26 @@ function UI.Chooser:eventHandler(event)
if event.type == 'key' then
if event.key == 'right' or event.key == 'space' then
local _,k = Util.find(self.choices, 'value', self.value)
local choice
if k and k < #self.choices then
self.value = self.choices[k+1].value
choice = self.choices[k+1]
else
self.value = self.choices[1].value
choice = self.choices[1]
end
self:emit({ type = 'choice_change', value = self.value })
self.value = choice.value
self:emit({ type = 'choice_change', value = self.value, element = self, choice = choice })
self:draw()
return true
elseif event.key == 'left' then
local _,k = Util.find(self.choices, 'value', self.value)
local choice
if k and k > 1 then
self.value = self.choices[k-1].value
choice = self.choices[k-1]
else
self.value = self.choices[#self.choices].value
choice = self.choices[#self.choices]
end
self:emit({ type = 'choice_change', value = self.value })
self.value = choice.value
self:emit({ type = 'choice_change', value = self.value, element = self, choice = choice })
self:draw()
return true
end
@ -2981,6 +3032,57 @@ function UI.Chooser:eventHandler(event)
end
end
--[[-- Chooser --]]--
UI.Checkbox = class(UI.Window)
UI.Checkbox.defaults = {
UIElement = 'Checkbox',
nochoice = 'Select',
checkedIndicator = 'X',
leftMarker = '[',
rightMarker = ']',
value = false,
textColor = colors.white,
backgroundColor = colors.black,
backgroundFocusColor = colors.lightGray,
height = 1,
width = 3,
accelerators = {
space = 'checkbox_toggle',
mouse_click = 'checkbox_toggle',
}
}
function UI.Checkbox:draw()
local bg = self.backgroundColor
if self.focused then
bg = self.backgroundFocusColor
end
if type(self.value) == 'string' then
self.value = nil -- TODO: fix form
end
local text = string.format('[%s]', not self.value and ' ' or self.checkedIndicator)
self:write(1, 1, text, bg)
self:write(1, 1, self.leftMarker, self.backgroundColor, self.textColor)
self:write(2, 1, not self.value and ' ' or self.checkedIndicator, bg)
self:write(3, 1, self.rightMarker, self.backgroundColor, self.textColor)
end
function UI.Checkbox:focus()
self:draw()
end
function UI.Checkbox:reset()
self.value = false
end
function UI.Checkbox:eventHandler(event)
if event.type == 'checkbox_toggle' then
self.value = not self.value
self:emit({ type = 'checkbox_change', checked = self.value, element = self })
self:draw()
return true
end
end
--[[-- Text --]]--
UI.Text = class(UI.Window)
UI.Text.defaults = {
@ -3180,16 +3282,18 @@ function UI.Form:createForm()
end
end
table.insert(self.children, UI.Button {
y = -self.margin, x = -12 - self.margin,
text = 'Ok',
event = 'form_ok',
})
table.insert(self.children, UI.Button {
y = -self.margin, x = -7 - self.margin,
text = 'Cancel',
event = 'form_cancel',
})
if not self.manualControls then
table.insert(self.children, UI.Button {
y = -self.margin, x = -12 - self.margin,
text = 'Ok',
event = 'form_ok',
})
table.insert(self.children, UI.Button {
y = -self.margin, x = -7 - self.margin,
text = 'Cancel',
event = 'form_cancel',
})
end
end
function UI.Form:validateField(field)
@ -3198,25 +3302,47 @@ function UI.Form:validateField(field)
return false, 'Field is required'
end
end
if field.validate == 'numeric' then
if #tostring(field.value) > 0 then
if not tonumber(field.value) then
return false, 'Invalid number'
end
end
end
return true
end
function UI.Form:save()
for _,child in pairs(self.children) do
if child.formKey then
local s, m = self:validateField(child)
if not s then
self:setFocus(child)
self:emit({ type = 'form_invalid', message = m, field = child })
return false
end
end
end
for _,child in pairs(self.children) do
if child.formKey then
if (child.pruneEmpty and type(child.value) == 'string' and #child.value == 0) or
(child.pruneEmpty and type(child.value) == 'boolean' and not child.value) then
self.values[child.formKey] = nil
elseif child.validate == 'numeric' then
self.values[child.formKey] = tonumber(child.value)
else
self.values[child.formKey] = child.value
end
end
end
return true
end
function UI.Form:eventHandler(event)
if event.type == 'form_ok' then
for _,child in pairs(self.children) do
if child.formKey then
local s, m = self:validateField(child)
if not s then
self:setFocus(child)
self:emit({ type = 'form_invalid', message = m, field = child })
return false
end
end
end
for _,child in pairs(self.children) do
if child.formKey then
self.values[child.formKey] = child.value
end
if not self:save() then
return false
end
self:emit({ type = self.event, UIElement = self })
else

View File

@ -137,7 +137,7 @@ function Canvas:writeBlit(x, y, text, bg, fg)
if bg then
bg = _sub(bg, 2 - x)
end
if bg then
if fg then
fg = _sub(fg, 2 - x)
end
width = width + x - 1
@ -149,7 +149,7 @@ function Canvas:writeBlit(x, y, text, bg, fg)
if bg then
bg = _sub(bg, 1, self.width - x + 1)
end
if bg then
if fg then
fg = _sub(fg, 1, self.width - x + 1)
end
width = #text

View File

@ -172,6 +172,20 @@ function Util.deepMerge(obj, args)
end
end
-- remove table entries if passed function returns false
function Util.prune(t, fn)
for _,k in pairs(Util.keys(t)) do
local v = t[k]
if type(v) == 'table' then
t[k] = Util.prune(v, fn)
end
if not fn(t[k]) then
t[k] = nil
end
end
return t
end
function Util.transpose(t)
local tt = { }
for k,v in pairs(t) do
@ -207,6 +221,7 @@ function Util.findAll(t, name, value)
end
function Util.shallowCopy(t)
if not t then error('Util.shallowCopy: invalid table', 2) end
local t2 = { }
for k,v in pairs(t) do
t2[k] = v
@ -342,6 +357,7 @@ function Util.readFile(fname)
end
function Util.writeFile(fname, data)
if not fname or not data then error('Util.writeFile: invalid parameters', 2) end
local file = io.open(fname, "w")
if not file then
error('Unable to open ' .. fname, 2)
@ -415,7 +431,7 @@ end
function Util.download(url, filename)
local contents, msg = Util.httpGet(url)
if not contents then
error(string.format('Failed to download %s\n%s', url, msg))
error(string.format('Failed to download %s\n%s', url, msg), 2)
end
if filename then
@ -475,6 +491,7 @@ function Util.insertString(str, istr, pos)
end
function Util.split(str, pattern)
if not str then error('Util.split: Invalid parameters', 2) end
pattern = pattern or "(.-)\n"
local t = {}
local function helper(line) table.insert(t, line) return "" end
@ -491,7 +508,7 @@ function Util.matches(str, pattern)
return t
end
function Util.startsWidth(s, match)
function Util.startsWith(s, match)
return string.sub(s, 1, #match) == match
end
@ -653,7 +670,6 @@ function Util.getOptions(options, args, ignoreInvalid)
end
end
return true, Util.size(rawOptions)
end
return Util

View File

@ -1,5 +1,6 @@
_G.requireInjector(_ENV)
local Config = require('config')
local Event = require('event')
local Socket = require('socket')
local UI = require('ui')
@ -20,6 +21,9 @@ local gridColumns = {
{ heading = 'Status', key = 'status' },
}
local trusted = Util.readTable('usr/.known_hosts')
local config = Config.load('network', { })
if UI.term.width >= 30 then
table.insert(gridColumns, { heading = 'Fuel', key = 'fuel', width = 5 })
table.insert(gridColumns, { heading = 'Uptime', key = 'uptime' })
@ -39,7 +43,16 @@ local page = UI.Page {
{ text = 'Establish', event = 'trust' },
{ text = 'Remove', event = 'untrust' },
} },
{ text = 'Help', event = 'help' },
{ text = 'Help', event = 'help', noCheck = true },
{
text = '\206',
x = -3,
dropdown = {
{ text = 'Show all', event = 'show_all', noCheck = true },
UI.MenuBar.spacer,
{ text = 'Show trusted', event = 'show_trusted', noCheck = true },
},
},
},
},
grid = UI.ScrollingGrid {
@ -143,6 +156,16 @@ This only needs to be done once.
q = 'cancel',
}
})
elseif event.type == 'show_all' then
config.showTrusted = false
self.grid:setValues(network)
Config.update('network', config)
elseif event.type == 'show_trusted' then
config.showTrusted = true
Config.update('network', config)
elseif event.type == 'quit' then
Event.exitPullEvents()
end
@ -155,7 +178,7 @@ function page.menuBar:getActive(menuItem)
local trustList = Util.readTable('usr/.known_hosts') or { }
return t and trustList[t.id]
end
return not not t
return menuItem.noCheck or not not t
end
function page.grid:getRowTextColor(row, selected)
@ -184,7 +207,17 @@ function page.grid:getDisplayValues(row)
end
Event.onInterval(1, function()
page.grid:update()
local t = { }
if config.showTrusted then
for k,v in pairs(network) do
if trusted[k] then
t[k] = v
end
end
page.grid:setValues(t)
else
page.grid:update()
end
page.grid:draw()
page:sync()
end)

View File

@ -1,14 +1,15 @@
_G.requireInjector(_ENV)
local class = require('class')
local Config = require('config')
local Event = require('event')
local FileUI = require('ui.fileui')
local NFT = require('nft')
local SHA1 = require('sha1')
local Tween = require('ui.tween')
local UI = require('ui')
local Util = require('util')
local class = require('class')
local Config = require('config')
local Event = require('event')
local FileUI = require('ui.fileui')
local NFT = require('nft')
local Packages = require('packages')
local SHA1 = require('sha1')
local Tween = require('ui.tween')
local UI = require('ui')
local Util = require('util')
local colors = _G.colors
local fs = _G.fs
@ -32,25 +33,28 @@ local config = {
Config.load('Overview', config)
local applications = { }
local extSupport = Util.getVersion() >= 1.76
local function loadApplications()
local requirements = {
turtle = function() return turtle end,
advancedTurtle = function() return turtle and term.isColor() end,
advanced = function() return term.isColor() end,
pocket = function() return pocket end,
advancedPocket = function() return pocket and term.isColor() end,
advancedComputer = function() return not turtle and not pocket and term.isColor() end,
turtle = not not turtle,
advancedTurtle = turtle and term.isColor(),
advanced = term.isColor(),
pocket = not not pocket,
advancedPocket = pocket and term.isColor(),
advancedComputer = not turtle and not pocket and term.isColor(),
}
applications = Util.readTable('sys/etc/app.db')
if fs.exists('usr/etc/apps') then
local dbs = fs.list('usr/etc/apps')
for _, db in pairs(dbs) do
local apps = Util.readTable('usr/etc/apps/' .. db) or { }
Util.merge(applications, apps)
for dir in pairs(Packages:installed()) do
local path = fs.combine('packages/' .. dir, 'etc/apps')
if fs.exists(path) then
local dbs = fs.list(path)
for _, db in pairs(dbs) do
local apps = Util.readTable(fs.combine(path, db)) or { }
Util.merge(applications, apps)
end
end
end
@ -72,13 +76,10 @@ local function loadApplications()
end
if a.requires then
local fn = requirements[a.requires]
if fn and not fn() then
return false
end
return requirements[a.requires]
end
return true -- Util.startsWidth(a.run, 'http') or shell.resolveProgram(a.run)
return true -- Util.startsWith(a.run, 'http') or shell.resolveProgram(a.run)
end)
end
@ -117,7 +118,7 @@ local function parseIcon(iconText)
icon = NFT.parse(iconText)
if icon then
if icon.height > 3 or icon.width > 8 then
error('Invalid size')
error('Must be an NFT image - 3 rows, 8 cols max')
end
end
return icon
@ -174,6 +175,10 @@ local page = UI.Page {
},
}
if extSupport then
page.container.backgroundColor = colors.black
end
UI.Icon = class(UI.Window)
UI.Icon.defaults = {
UIElement = 'Icon',
@ -194,7 +199,6 @@ function UI.Icon:eventHandler(event)
end
function page.container:setCategory(categoryName, animate)
-- reset the viewport window
self.children = { }
self.offy = 0
@ -231,7 +235,10 @@ function page.container:setCategory(categoryName, animate)
for _,program in ipairs(filtered) do
local icon
if program.icon then
if extSupport and program.iconExt then
icon = parseIcon(program.iconExt)
end
if not icon and program.icon then
icon = parseIcon(program.icon)
end
if not icon then
@ -344,7 +351,6 @@ function page:resize()
end
function page:eventHandler(event)
if event.type == 'tab_select' then
self.container:setCategory(event.button.text, true)
self.container:draw()
@ -455,7 +461,10 @@ function editor:enable(app)
self.form:setValues(app)
local icon
if app.icon then
if extSupport and app.iconExt then
icon = parseIcon(app.iconExt)
end
if not icon and app.icon then
icon = parseIcon(app.icon)
end
self.form.image:setImage(icon)
@ -479,7 +488,6 @@ function editor:updateApplications(app)
end
function editor:eventHandler(event)
if event.type == 'form_cancel' or event.type == 'cancel' then
UI:setPreviousPage()
@ -501,13 +509,17 @@ function editor:eventHandler(event)
local s, m = pcall(function()
local iconLines = Util.readFile(fileName)
if not iconLines then
error('Unable to load file')
error('Must be an NFT image - 3 rows, 8 cols max')
end
local icon, m = parseIcon(iconLines)
if not icon then
error(m)
end
self.form.values.icon = iconLines
if extSupport then
self.form.values.iconExt = iconLines
else
self.form.values.icon = iconLines
end
self.form.image:setImage(icon)
self.form.image:draw()
end)

158
sys/apps/PackageManager.lua Normal file
View File

@ -0,0 +1,158 @@
_G.requireInjector(_ENV)
local Ansi = require('ansi')
local Packages = require('packages')
local UI = require('ui')
local colors = _G.colors
local shell = _ENV.shell
local term = _G.term
UI:configure('PackageManager', ...)
local page = UI.Page {
grid = UI.ScrollingGrid {
y = 2, ey = 7, x = 2, ex = -6,
values = { },
columns = {
{ heading = 'Package', key = 'name' },
},
sortColumn = 'name',
autospace = true,
help = 'Select a package',
},
add = UI.Button {
x = -4, y = 4,
text = '+',
event = 'action',
help = 'Install or update',
},
remove = UI.Button {
x = -4, y = 6,
text = '-',
event = 'action',
operation = 'uninstall',
operationText = 'Remove',
help = 'Remove',
},
description = UI.TextArea {
x = 2, y = 9, ey = -2,
--backgroundColor = colors.white,
},
statusBar = UI.StatusBar { },
action = UI.SlideOut {
backgroundColor = colors.cyan,
titleBar = UI.TitleBar {
event = 'hide-action',
},
button = UI.Button {
ex = -4, y = 4, width = 7,
text = 'Begin', event = 'begin',
},
output = UI.Embedded {
y = 6, ey = -2, x = 2, ex = -2,
},
statusBar = UI.StatusBar {
backgroundColor = colors.cyan,
},
},
}
function page.grid:getRowTextColor(row, selected)
if row.installed then
return colors.yellow
end
return UI.Grid.getRowTextColor(self, row, selected)
end
function page.action:show()
UI.SlideOut.show(self)
self.output:draw()
self.output.win.redraw()
end
function page:run(operation, name)
local oterm = term.redirect(self.action.output.win)
self.action.output:clear()
local cmd = string.format('package %s %s', operation, name)
--for _ = 1, 3 do
-- print(cmd .. '\n')
-- os.sleep(1)
--end
term.setCursorPos(1, 1)
term.clear()
term.setTextColor(colors.yellow)
print(cmd .. '\n')
term.setTextColor(colors.white)
shell.run(cmd)
term.redirect(oterm)
self.action.output:draw()
end
function page:updateSelection(selected)
self.add.operation = selected.installed and 'update' or 'install'
self.add.operationText = selected.installed and 'Update' or 'Install'
end
function page:eventHandler(event)
if event.type == 'focus_change' then
self.statusBar:setStatus(event.focused.help)
elseif event.type == 'grid_focus_row' then
local manifest = event.selected.manifest
self.description.value = string.format('%s%s\n\n%s%s',
Ansi.yellow, manifest.title,
Ansi.white, manifest.description)
self.description:draw()
self:updateSelection(event.selected)
elseif event.type == 'action' then
local selected = self.grid:getSelected()
if selected then
self.operation = event.button.operation
self.action.button.text = event.button.operationText
self.action.titleBar.title = selected.manifest.title
self.action.button.text = 'Begin'
self.action.button.event = 'begin'
self.action:show()
end
elseif event.type == 'hide-action' then
self.action:hide()
elseif event.type == 'begin' then
local selected = self.grid:getSelected()
self:run(self.operation, selected.name)
selected.installed = Packages:isInstalled(selected.name)
self:updateSelection(selected)
self.action.button.text = 'Done'
self.action.button.event = 'hide-action'
self.action.button:draw()
elseif event.type == 'quit' then
UI:exitPullEvents()
end
UI.Page.eventHandler(self, event)
end
for k in pairs(Packages:list()) do
local manifest = Packages:getManifest(k)
if not manifest then
manifest = {
invalid = true,
description = 'Unable to download manifest',
title = '',
}
end
table.insert(page.grid.values, {
installed = not not Packages:isInstalled(k),
name = k,
manifest = manifest,
})
end
page.grid:update()
UI:setPage(page)
UI:pullEvents()

View File

@ -141,61 +141,63 @@ local systemPage = UI.Page {
}
if turtle then
local Home = require('turtle.home')
pcall(function()
local Home = require('turtle.home')
-- TODO: dont rely on turtle.home
local values = { }
Config.load('gps', values.home and { values.home } or { })
local values = { }
Config.load('gps', values.home and { values.home } or { })
systemPage.tabs:add({
gpsTab = UI.Window {
tabTitle = 'GPS',
labelText = UI.Text {
x = 3, y = 2,
value = 'On restart, return to this location'
},
grid = UI.Grid {
x = 3, ex = -3, y = 4,
height = 2,
values = values,
inactive = true,
columns = {
{ heading = 'x', key = 'x' },
{ heading = 'y', key = 'y' },
{ heading = 'z', key = 'z' },
systemPage.tabs:add({
gpsTab = UI.Window {
tabTitle = 'GPS',
labelText = UI.Text {
x = 3, y = 2,
value = 'On restart, return to this location'
},
grid = UI.Grid {
x = 3, ex = -3, y = 4,
height = 2,
values = values,
inactive = true,
columns = {
{ heading = 'x', key = 'x' },
{ heading = 'y', key = 'y' },
{ heading = 'z', key = 'z' },
},
},
button1 = UI.Button {
x = 3, y = 7,
text = 'Set home',
event = 'gps_set',
},
button2 = UI.Button {
ex = -3, y = 7, width = 7,
text = 'Clear',
event = 'gps_clear',
},
},
button1 = UI.Button {
x = 3, y = 7,
text = 'Set home',
event = 'gps_set',
},
button2 = UI.Button {
ex = -3, y = 7, width = 7,
text = 'Clear',
event = 'gps_clear',
},
},
})
function systemPage.tabs.gpsTab:eventHandler(event)
if event.type == 'gps_set' then
systemPage.notification:info('Determining location', 10)
systemPage:sync()
if Home.set() then
Config.load('gps', values)
self.grid:setValues(values.home and { values.home } or { })
})
function systemPage.tabs.gpsTab:eventHandler(event)
if event.type == 'gps_set' then
systemPage.notification:info('Determining location', 10)
systemPage:sync()
if Home.set() then
Config.load('gps', values)
self.grid:setValues(values.home and { values.home } or { })
self.grid:draw()
systemPage.notification:success('Location set')
else
systemPage.notification:error('Unable to determine location')
end
return true
elseif event.type == 'gps_clear' then
fs.delete('usr/config/gps')
self.grid:setValues({ })
self.grid:draw()
systemPage.notification:success('Location set')
else
systemPage.notification:error('Unable to determine location')
return true
end
return true
elseif event.type == 'gps_clear' then
fs.delete('usr/config/gps')
self.grid:setValues({ })
self.grid:draw()
return true
end
end
end)
end
if settings then

97
sys/apps/package.lua Normal file
View File

@ -0,0 +1,97 @@
_G.requireInjector(_ENV)
local Git = require('git')
local Packages = require('packages')
local Util = require('util')
local fs = _G.fs
local term = _G.term
local args = { ... }
local action = table.remove(args, 1)
local function Syntax(msg)
_G.printError(msg)
print('\nSyntax: Package list | install [name] ... | update [name] | uninstall [name]')
error(0)
end
local function progress(max)
-- modified from: https://pastebin.com/W5ZkVYSi (apemanzilla)
local _, y = term.getCursorPos()
local wide, _ = term.getSize()
term.setCursorPos(1, y)
term.write("[")
term.setCursorPos(wide - 6, y)
term.write("]")
local done = 0
return function()
done = done + 1
local value = done / max
term.setCursorPos(2,y)
term.write(("="):rep(math.floor(value * (wide - 8))))
local percent = math.floor(value * 100) .. "%"
term.setCursorPos(wide - percent:len(),y)
term.write(percent)
end
end
local function install(name)
local manifest = Packages:getManifest(name) or error('Invalid package')
local packageDir = fs.combine('packages', name)
local method = args[2] or 'local'
if method == 'remote' then
Util.writeTable(packageDir .. '/.install', {
mount = string.format('%s gitfs %s', packageDir, manifest.repository),
})
Util.writeTable(fs.combine(packageDir, '.package'), manifest)
else
local list = Git.list(manifest.repository)
local showProgress = progress(Util.size(list))
for path, entry in pairs(list) do
Util.download(entry.url, fs.combine(packageDir, path))
showProgress()
end
end
return
end
if action == 'list' then
for k in pairs(Packages:list()) do
Util.print('[%s] %s', Packages:isInstalled(k) and 'x' or ' ', k)
end
return
end
if action == 'install' then
local name = args[1] or Syntax('Invalid package')
if Packages:isInstalled(name) then
error('Package is already installed')
end
install(name)
print('installation complete')
return
end
if action == 'update' then
local name = args[1] or Syntax('Invalid package')
if not Packages:isInstalled(name) then
error('Package is not installed')
end
install(name)
print('update complete')
return
end
if action == 'uninstall' then
local name = args[1] or Syntax('Invalid package')
if not Packages:isInstalled(name) then
error('Package is not installed')
end
local packageDir = fs.combine('packages', name)
fs.delete(packageDir)
print('removed: ' .. packageDir)
return
end
Syntax('Invalid command')

View File

@ -365,7 +365,7 @@ local term = _G.term
local textutils = _G.textutils
local terminal = term.current()
Terminal.scrollable(terminal, 100)
--Terminal.scrollable(terminal, 100)
terminal.noAutoScroll = true
local config = {
@ -568,10 +568,10 @@ local function shellRead(history)
local ie = Input:translate(event, p1, p2, p3)
if ie then
if ie.code == 'scroll_up' then
terminal.scrollUp()
--terminal.scrollUp()
elseif ie.code == 'scroll_down' then
terminal.scrollDown()
--terminal.scrollDown()
elseif ie.code == 'terminate' then
bExit = true

View File

@ -10,7 +10,7 @@ local os = _G.os
local read = _G.read
local term = _G.term
local options, args = Util.args({ ... })
local args = { ... }
local remoteId = tonumber(table.remove(args, 1) or '')
if not remoteId then
@ -19,11 +19,11 @@ if not remoteId then
end
if not remoteId then
error('Syntax: telnet [-title TITLE] ID [PROGRAM]')
error('Syntax: telnet ID [PROGRAM] [ARGS]')
end
if options.title and multishell then
multishell.setTitle(multishell.getCurrent(), options.title)
if multishell then
multishell.setTitle(multishell.getCurrent(), 'Telnet ' .. remoteId)
end
local socket, msg = Socket.connect(remoteId, 23)

View File

@ -1,12 +1,13 @@
_G.requireInjector(_ENV)
local Event = require('event')
local Socket = require('socket')
local Terminal = require('terminal')
local Util = require('util')
local Event = require('event')
local Socket = require('socket')
local Terminal = require('terminal')
local Util = require('util')
local colors = _G.colors
local multishell = _ENV.multishell
local os = _G.os
local term = _G.term
local remoteId
@ -26,75 +27,104 @@ if multishell then
multishell.setTitle(multishell.getCurrent(), 'VNC-' .. remoteId)
end
print('connecting...')
local socket, msg = Socket.connect(remoteId, 5900)
local function connect()
local socket, msg = Socket.connect(remoteId, 5900)
if not socket then
error(msg)
end
if not socket then
return false, msg
end
local function writeTermInfo()
local w, h = term.getSize()
socket:write({
type = 'termInfo',
width = w,
height = h,
isColor = term.isColor(),
local function writeTermInfo()
local w, h = term.getSize()
socket:write({
type = 'termInfo',
width = w,
height = h,
isColor = term.isColor(),
})
end
writeTermInfo()
local ct = Util.shallowCopy(term.current())
if not ct.isColor() then
Terminal.toGrayscale(ct)
end
ct.clear()
ct.setCursorPos(1, 1)
Event.addRoutine(function()
while true do
local data = socket:read()
if not data then
break
end
for _,v in ipairs(data) do
ct[v.f](unpack(v.args))
end
end
end)
local filter = Util.transpose({
'char', 'paste', 'key', 'key_up',
'mouse_scroll', 'mouse_click', 'mouse_drag', 'mouse_up',
})
end
writeTermInfo()
local ct = Util.shallowCopy(term.current())
if not ct.isColor() then
Terminal.toGrayscale(ct)
end
Event.addRoutine(function()
while true do
local data = socket:read()
if not data then
local e = Event.pullEvent()
local event = e[1]
if not socket.connected then
break
end
for _,v in ipairs(data) do
ct[v.f](unpack(v.args))
if filter[event] then
socket:write({
type = 'shellRemote',
event = e,
})
elseif event == 'term_resize' then
writeTermInfo()
elseif event == 'terminate' then
socket:close()
ct.setBackgroundColor(colors.black)
ct.clear()
ct.setCursorPos(1, 1)
return true
end
end
end)
ct.clear()
ct.setCursorPos(1, 1)
local filter = Util.transpose({
'char', 'paste', 'key', 'key_up',
'mouse_scroll', 'mouse_click', 'mouse_drag', 'mouse_up',
})
return false, "Connection Lost"
end
while true do
local e = Event.pullEvent()
local event = e[1]
term.clear()
term.setCursorPos(1, 1)
if not socket.connected then
print()
print('Connection lost')
print('Press enter to exit')
_G.read()
print('connecting...')
local s, m = connect()
if s then
break
end
if filter[event] then
socket:write({
type = 'shellRemote',
event = e,
})
elseif event == 'term_resize' then
writeTermInfo()
elseif event == 'terminate' then
socket:close()
ct.setBackgroundColor(colors.black)
ct.clear()
ct.setCursorPos(1, 1)
break
term.setBackgroundColor(colors.black)
term.setTextColor(colors.white)
term.clear()
term.setCursorPos(1, 1)
print(m)
print('\nPress any key to exit')
print('\nRetrying in ... ')
local x, y = term.getCursorPos()
for i = 5, 1, -1 do
local timerId = os.startTimer(1)
term.setCursorPos(x, y)
term.write(i)
repeat
local e, id = os.pullEvent()
if e == 'char' or e == 'key' then
return
end
until e == 'timer' and id == timerId
end
end

View File

@ -11,7 +11,7 @@ for k,v in pairs(_ENV) do
sandboxEnv[k] = v
end
_G.debug = function() end
_G._debug = function() end
local function makeEnv()
local env = setmetatable({ }, { __index = _G })

View File

@ -1,10 +1,21 @@
{
[ "0a999012ffb87b3edac99adbdfc498b12831a1e2" ] = {
title = "Packages",
category = "System",
run = "PackageManager.lua",
iconExt = "\030c\0317\151\131\131\131\0307\031c\148\
\030c\0317\151\131\0310\143\0317\131\0307\031c\148\
\0307\031c\138\030f\0317\151\131\131\131",
},
[ "53ebc572b4a44802ba114729f07bdaaf5409a9d7" ] = {
title = "Network",
category = "Apps",
icon = "\0304 \030 \
\030f \0304 \0307 \030 \031 \031f)\
\030f \0304 \0307 \030 \031f)",
title = "Network",
iconExt = "\030 \031f \0305\031f\140\030f\0315\137\144\
\030 \031f\030f\0314\131\131\0304\031f\148\030 \0305\155\150\149\
\030 \031f\030f\0310\147\0300\031f\141\0304\149\0307\0318\149\030 ",
run = "Network.lua",
},
c7116629a6a855cb774d9c7c8ad822fd83c71fb5 = {
@ -13,6 +24,9 @@
icon = "\0304\031f \030f\0310o..\0304\031f \
\0304\031f \030f\0310.o.\0304\031f \
\0304\031f - ",
iconExt = "\0307\031f\135\0300\0317\159\0307\0310\144\031f\139\
\0300\0317\131\0307\0310\147\0300\0317\156\131\
\130\143\143\129",
run = "rom/programs/reboot",
},
fb91e24fa52d8d2b32937bf04d843f730319a902 = {
@ -21,6 +35,9 @@
icon = "\0301\03171\03180\030 \031 \
\0301\03181\030 \031 \
\0301\03170\03180\03171\0307\031f>",
iconExt = "\031f\128\0313\152\131\131\132\031f\128\
\0313\139\159\129\0303\031f\159\129\139\
\031f\128\0313\136\0303\031f\143\143\030f\0313\134\031f\128",
run = "http://pastebin.com/raw/UzGHLbNC",
},
c47ae15370cfe1ed2781eedc1dc2547d12d9e972 = {
@ -29,6 +46,9 @@
icon = " \031f?\031 \
\031f?\031 \
\031f?",
iconExt = "\0300\031f\129\030f\0310\131\0300\031f\148\030f\0310\148\
\030 \031 \0300\031f\131\030f\0310\142\129\
\030 \031 \0300\031f\131\030f\128",
run = "Help.lua",
},
b0832074630eb731d7fbe8074de48a90cd9bb220 = {
@ -37,46 +57,77 @@
icon = "\030f \
\030f\0310lua>\031 \
\030f ",
iconExt = "\0300\031f\151\030f\128\0300\159\159\159\030f\0310\144\0304\031f\159\030f\128\
\0300\031f\149\030f\128\0300\149\149\151\145\030f\128\0314\153\
\130\131\130\131\130\131\0314\130\031f\128",
run = "sys/apps/Lua.lua",
},
df485c871329671f46570634d63216761441bcd6 = {
title = "Devices",
category = "System",
icon = "\0304 \030 \
\030f \0304 \0307 \030 \031 \031f_\
\030f \0304 \0307 \030 \031f/",
run = "Devices.lua",
},
bc0792d8dc81e8aa30b987246a5ce97c40cd6833 = {
title = "System",
category = "System",
icon = " \0307\031f| \
\0307\031f---o\030 \031 \
\0307\031f| ",
iconExt = "\0318\138\0308\031f\130\0318\128\031f\129\030f\0318\133\
\0318\143\0308\128\0317\143\0318\128\030f\143\
\0318\138\135\143\139\133",
run = "System.lua",
},
c5497bca58468ae64aed6c0fd921109217988db3 = {
title = "Events",
category = "System",
icon = "\0304\031f \030 \0311e\
\030f\031f \0304 \030 \0311ee\031f \
\030f\031f \0304 \030 \0311e\031f ",
run = "Events.lua",
},
[ "2a4d562b1d9a9c90bdede6fac8ce4f7402462b86" ] = {
title = "Tasks",
category = "System",
icon = "\030f\031f \0315/\
\030f\031f \0315/\\/ \
\030f\0315/\031f ",
iconExt = "\031f\128\128\0305\159\030f\128\0305\159\030f\0315\134\031f\128\
\031f\128\0315\152\129\137\0305\031f\158\139\030f\0317 \
\0315\134\031f\128\128\128\128\0305\154\030f\0317 ",
run = "Tasks.lua",
},
[ "a0365977708b7387ee9ce2c13e5820e6e11732cb" ] = {
title = "Pain",
category = "Apps",
icon = "\030 \031f\0307\031f\159\030 \159\030 \
\030 \031f\0308\031f\135\0307\0318\144\140\030f\0317\159\143\031c\139\0302\135\030f\0312\157\
\030 \031f\030f\0318\143\133\0312\136\0302\031f\159\159\143\131\030f\0312\132",
run = "http://pastebin.com/raw/wJQ7jav0",
},
[ "48d6857f6b2869d031f463b13aa34df47e18c548" ] = {
title = "Breakout",
category = "Games",
icon = "\0301\031f \0309 \030c \030b \030e \030c \0306 \
\030 \031f \
\030 \031f \0300 \0310 ",
iconExt = "\030 \031f\030f\0319\144\030d\031f\159\030b\159\030f\0311\144\031b\144\030c\031f\159\030f\0311\144\
\030 \031f\030f\0311\130\031b\129\0319\130\031e\130\0310\144\031d\129\0316\129\
\030 \031f\030f\0310\136\140\140\030 ",
run = "https://gist.github.com/LDDestroier/c7528d95bc0103545c2a/raw",
},
[ "53a5d150062b1e03206b9e15854b81060e3c7552" ] = {
title = "Minesweeper",
category = "Games",
icon = "\030f\031f \03131\0308\031f \030f\031d2\
\030f\031f \031d2\03131\0308\031f \030f\03131\
\030f\03131\0308\031f \030f\03131\031e3",
run = "https://pastebin.com/raw/nsKrHTbN",
},
[ "01c933b2a36ad8ed2d54089cb2903039046c1216" ] = {
title = "Enchat",
icon = "\030e\031f\151\030f\031e\156\0311\140\0314\140\0315\140\031d\140\031b\140\031a\132\
\030f\0314\128\030e\031f\132\030f\031e\132\0318nchat\
\030f\031e\138\141\0311\140\0314\140\0315\132\0317v\03183\031a\132",
category = "Apps",
run = "https://raw.githubusercontent.com/LDDestroier/enchat/master/enchat3.lua",
},
[ "6ce6c512ea433a7fc5c8841628e7696cd0ff7f2b" ] = {
title = "Files",
category = "Apps",
icon = "\0300\0317==\031 \0307 \
\0300\0317====\
\0300\0317====",
iconExt = "\030 \031f\0300\031f\136\140\132\0308\130\030f\0318\144\
\030 \031f\030f\0310\157\0300\031f\147\030f\0310\142\143\149\
\030 \031f\0300\031f\136\140\132\140\030f\0310\149",
run = "Files.lua",
},
[ "7fddb7d8d1d60b1eeefa9af01082e0811d4b484d" ] = {
@ -85,12 +136,18 @@
icon = "\0304\031f \
\0304\031f \030f\0310zz\031 \
\0304\031f \030f ",
iconExt = "\030e\031f\135\030f\031e\148\030e\128\031f\151\139\
\030e\031e\128\030f\031f\128\031e\143\031f\128\030e\031e\128\
\031e\139\030e\031f\130\131\129\030f\031e\135",
run = "/rom/programs/shutdown",
},
bdc1fd5d3c0f3dcfd55d010426e61bf9451e680d = {
title = "Shell",
category = "Apps",
icon = "\030f\0314\151\131\131\131\131\
icon = "\0304 \030 \
\0304 \030f\0314> \0310_\031 \
\0304 \030f \030 ",
iconExt = "\030f\0314\151\131\131\131\131\
\030f\0314\149\030f\0314> \0310_ \
\030f\0314\149\030f ",
run = "shell",
@ -127,6 +184,9 @@
icon = "\030d \030 \030e \030 \
\030d \030 \
\030d ",
iconExt = "\030 \031f\0305\031f\151\030f\0315\135\131\0305\031f\146\
\030 \031f\030f\0315\130\141\0305\031f\139\030f\0315\130\
\030 \031f\0305\031f\146\143\030f\0315\158\031e\130",
run = "/rom/programs/fun/worm",
},
[ "9f46ca3ef617166776ef6014a58d4e66859caa62" ] = {
@ -135,6 +195,17 @@
icon = " \030f \
\030f \0307 \
\030f \0307 \0300 ",
iconExt = "\031f\128\0307\143\131\131\131\131\143\030f\128\
\0307\031f\129\0317\128\0319\136\0309\031b\136\132\0307\0319\132\0317\128\031f\130\
\0317\130\143\0307\128\128\128\128\030f\143\129",
run = "/rom/programs/fun/dj",
},
[ "76b849f460640bc789c433894382fb5acbac42a2" ] = {
title = "Tron",
category = "Games",
iconExt = "\030 \031f\030b\031f\143\030f\128\128\030b\143\143\143\030f\128\128\
\030 \031f\0309\031b\140\030b\031f\151\030f\031b\131\0307\148\0317\128\030b\151\030f\031b\131\148\
\030 \031f\030f\031b\131\031f\128\031b\131\0317\131\031f\128\0317\131\031b\131\031f\128",
run = "https://raw.githubusercontent.com/LDDestroier/CC/master/tron.lua",
},
}

View File

@ -8,6 +8,15 @@
Button = {
--focusIndicator = '\183',
},
Checkbox = {
checkedIndicator = '\4',
leftMarker = '\124',
rightMarker = '\124',
},
Chooser = {
leftIndicator = '\17',
rightIndicator = '\16',
},
Grid = {
focusIndicator = '\183',
inverseSortIndicator = '\24',

View File

@ -33,12 +33,25 @@ local keyboard = _G.device.keyboard
local mouse = _G.device.mouse
local os = _G.os
local drivers = { }
kernel.hook('peripheral', function(_, eventData)
local side = eventData[1]
if side then
local dev = Peripheral.addDevice(device, side)
if dev then
os.queueEvent('device_attach', dev.name)
if drivers[dev.type] then
local e = drivers[dev.type](dev)
if type(e) == 'table' then
for _, v in pairs(e) do
os.queueEvent('device_attach', v.name)
end
elseif e then
os.queueEvent('device_attach', e.name)
end
end
os.queueEvent('device_attach', dev.name, dev)
end
end
end)
@ -48,7 +61,12 @@ kernel.hook('peripheral_detach', function(_, eventData)
if side then
local dev = Util.find(device, 'side', side)
if dev then
os.queueEvent('device_detach', dev.name)
os.queueEvent('device_detach', dev.name, dev)
if dev._children then
for _,v in pairs(dev._children) do
os.queueEvent('peripheral_detach', v.name)
end
end
device[dev.name] = nil
end
end
@ -109,3 +127,60 @@ kernel.hook('monitor_touch', function(event, eventData)
return true -- stop propagation
end
end)
local function createDevice(name, devType, method, manipulator)
local dev = {
name = name,
side = name,
type = devType,
}
local methods = {
'drop', 'getDocs', 'getItem', 'getItemMeta', 'getTransferLocations',
'list', 'pullItems', 'pushItems', 'size', 'suck',
}
if manipulator[method] then
for _,k in pairs(methods) do
dev[k] = function(...)
return manipulator[method]()[k](...)
end
end
if not manipulator._children then
manipulator._children = { dev }
else
table.insert(manipulator._children, dev)
end
device[name] = dev
end
end
drivers['manipulator'] = function(dev)
if dev.getName then
local name
pcall(function()
name = dev.getName()
end)
if name then
if dev.getInventory then
createDevice(name .. ':inventory', 'inventory', 'getInventory', dev)
end
if dev.getEquipment then
createDevice(name .. ':equipment', 'equipment', 'getEquipment', dev)
end
if dev.getEnder then
createDevice(name .. ':enderChest', 'enderChest', 'getEnder', dev)
end
return dev._children
end
end
end
-- initialize drivers
for _,v in pairs(device) do
if drivers[v.type] then
local s, m = pcall(drivers[v.type], v)
if not s and m then
_G.printError(m)
end
end
end

View File

@ -11,10 +11,10 @@ end
if not fs.exists('usr/autorun') then
fs.makeDir('usr/autorun')
end
if not fs.exists('usr/config/fstab') then
Util.writeFile('usr/config/fstab',
'usr gitfs kepler155c/opus-apps/' .. _G.OPUS_BRANCH)
end
--if not fs.exists('usr/config/fstab') then
-- Util.writeFile('usr/config/fstab',
-- 'usr gitfs kepler155c/opus-apps/' .. _G.OPUS_BRANCH)
--end
if not fs.exists('usr/config/shell') then
Util.writeTable('usr/config/shell', {
@ -24,6 +24,17 @@ if not fs.exists('usr/config/shell') then
})
end
if not fs.exists('usr/config/packages') then
local packages = {
[ 'develop-1.8' ] = 'https://pastebin.com/raw/WhEiNGZE',
[ 'master-1.8' ] = 'https://pastebin.com/raw/pexZpAxt',
}
if packages[_G.OPUS_BRANCH] then
Util.download(packages[_G.OPUS_BRANCH], 'usr/config/packages')
end
end
local config = Util.readTable('usr/config/shell')
if config.aliases then
for k in pairs(shell.aliases()) do

View File

@ -1,5 +1,10 @@
local kernel = _G.kernel
local os = _G.os
_G.requireInjector(_ENV)
local Config = require('config')
local device = _G.device
local kernel = _G.kernel
local os = _G.os
_G.network = { }
@ -11,15 +16,44 @@ local function startNetwork()
})
end
local function setModem(dev)
if not device.wireless_modem and dev.isWireless() then
local config = Config.load('os', { })
if not config.wirelessModem or dev.name == config.wirelessModem then
device.wireless_modem = dev
os.queueEvent('device_attach', 'wireless_modem')
return dev
end
end
end
-- create a psuedo-device named 'wireleess_modem'
kernel.hook('device_attach', function(_, eventData)
if eventData[1] == 'wireless_modem' then
startNetwork()
local dev = device[eventData[1]]
if dev and dev.type == 'modem' then
if setModem(dev) then
startNetwork()
end
end
end)
if _G.device.wireless_modem then
kernel.hook('device_detach', function(_, eventData)
if device.wireless_modem and eventData[1] == device.wireless_modem.name then
device['wireless_modem'] = nil
os.queueEvent('device_detach', 'wireless_modem')
end
end)
for _,dev in pairs(device) do
if dev.type == 'modem' then
if setModem(dev) then
break
end
end
end
if device.wireless_modem then
print('waiting for network...')
startNetwork()
os.pullEvent('network_up')
end

View File

@ -0,0 +1,45 @@
_G.requireInjector(_ENV)
local Packages = require('packages')
local Util = require('util')
local shell = _ENV.shell
local fs = _G.fs
local appPaths = Util.split(shell.path(), '(.-):')
local luaPaths = Util.split(_G.LUA_PATH, '(.-):')
local function addPath(t, e)
local function hasEntry()
for _,v in ipairs(t) do
if v == e then
return true
end
end
end
if not hasEntry() then
table.insert(t, 1, e)
end
end
-- dependency graph
-- https://github.com/mpeterv/depgraph/blob/master/src/depgraph/init.lua
for name in pairs(Packages:installed()) do
local packageDir = fs.combine('packages', name)
if fs.exists(fs.combine(packageDir, '.install')) then
local install = Util.readTable(fs.combine(packageDir, '.install'))
if install and install.mount then
fs.mount(table.unpack(Util.matches(install.mount)))
end
end
addPath(appPaths, packageDir)
local apiPath = fs.combine(fs.combine('packages', name), 'apis')
if fs.exists(apiPath) then
addPath(luaPaths, apiPath)
end
end
shell.setPath(table.concat(appPaths, ':'))
_G.LUA_PATH = table.concat(luaPaths, ':')

View File

@ -7,7 +7,7 @@ _G.requireInjector(_ENV)
local Pathing = require('turtle.pathfind')
local GPS = require('gps')
local Point = require('point')
local synchronized = require('sync')
local synchronized = require('sync').sync
local Util = require('util')
local os = _G.os
@ -458,6 +458,7 @@ function turtle.back()
end
local function moveTowardsX(dx)
if not tonumber(dx) then error('moveTowardsX: Invalid arguments') end
local direction = dx - turtle.point.x
local move
@ -662,11 +663,11 @@ function turtle._goto(pt)
local dx, dy, dz, dh = pt.x, pt.y, pt.z, pt.heading
if not turtle.gotoSingleTurn(dx, dy, dz, dh) then
if not gotoMultiTurn(dx, dy, dz) then
return false
return false, 'Failed to reach location'
end
end
turtle.setHeading(dh)
return true
return pt
end
-- avoid lint errors
@ -738,6 +739,8 @@ function turtle.getSlot(indexOrId, slots)
}
end
-- inconsistent return value
-- null is returned if indexOrId is a string and no item is present
return {
qty = 0, -- deprecate
count = 0,
@ -794,8 +797,12 @@ function turtle.getSummedInventory()
end
function turtle.has(item, count)
local slot = turtle.getSummedInventory()[item]
return slot and slot.count >= (count or 1)
if item:match('.*:%d') then
local slot = turtle.getSummedInventory()[item]
return slot and slot.count >= (count or 1)
end
local slot = turtle.getSlot(item)
return slot and slot.count > 0
end
function turtle.getFilledSlots(startSlot)
@ -966,6 +973,18 @@ function turtle.addWorldBlock(pt)
Pathing.addBlock(pt)
end
local movementStrategy = turtle.pathfind
function turtle.setMovementStrategy(strategy)
if strategy == 'pathing' then
movementStrategy = turtle.pathfind
elseif strategy == 'goto' then
movementStrategy = turtle._goto
else
error('Invalid movement strategy')
end
end
function turtle.faceAgainst(pt, options) -- 4 sided
options = options or { }
options.dest = { }
@ -980,7 +999,7 @@ function turtle.faceAgainst(pt, options) -- 4 sided
})
end
return turtle.pathfind(Point.closest(turtle.point, options.dest), options)
return movementStrategy(Point.closest(turtle.point, options.dest), options)
end
-- move against this point
@ -1012,7 +1031,7 @@ function turtle.moveAgainst(pt, options) -- 6 sided
})
end
return turtle.pathfind(Point.closest(turtle.point, options.dest), options)
return movementStrategy(Point.closest(turtle.point, options.dest), options)
end
local actionsAt = {
@ -1097,7 +1116,7 @@ local function _actionAt(action, pt, ...)
direction = 'up'
end
if turtle.pathfind(apt) then
if movementStrategy(apt) then
return action[direction](...)
end
end

View File

@ -1,7 +1,8 @@
_G.requireInjector(_ENV)
local Config = require('config')
local Util = require('util')
local Config = require('config')
local Packages = require('packages')
local Util = require('util')
local colors = _G.colors
local fs = _G.fs
@ -32,6 +33,7 @@ local config = {
backgroundColor = colors.gray,
tabBarBackgroundColor = colors.gray,
focusBackgroundColor = colors.gray,
errorColor = colors.black,
},
color = {
textColor = colors.lightGray,
@ -40,6 +42,7 @@ local config = {
backgroundColor = colors.gray,
tabBarBackgroundColor = colors.gray,
focusBackgroundColor = colors.gray,
errorColor = colors.red,
},
}
Config.load('multishell', config)
@ -129,6 +132,7 @@ function multishell.openTab(tab)
print('\nPress enter to close')
routine.isDead = true
routine.hidden = false
redrawMenu()
while true do
local e, code = os.pullEventRaw('key')
if e == 'terminate' or e == 'key' and code == keys.enter then
@ -252,8 +256,9 @@ kernel.hook('multishell_redraw', function()
tab.ex = tabX + tab.width
tabX = tabX + tab.width
if tab ~= currentTab then
local textColor = tab.isDead and _colors.errorColor or _colors.textColor
write(tab.sx, tab.title:sub(1, tab.width - 1),
_colors.backgroundColor, _colors.textColor)
_colors.backgroundColor, textColor)
end
end
end
@ -371,6 +376,10 @@ local function startup()
end
runDir('sys/autorun', shell.run)
for name in pairs(Packages:installed()) do
local packageDir = 'packages/' .. name .. '/autorun'
runDir(packageDir, shell.run)
end
runDir('usr/autorun', shell.run)
if not success then

View File

@ -28,12 +28,18 @@ local focusedRoutineEvents = Util.transpose {
'paste', 'terminate',
}
_G.debug = function(pattern, ...)
_G._debug = function(pattern, ...)
local oldTerm = term.redirect(kernel.window)
Util.print(pattern, ...)
term.redirect(oldTerm)
end
if not _G.debug then -- don't clobber lua debugger
function _G.debug(...)
_G._debug(...)
end
end
-- any function that runs in a kernel hook does not run in
-- a separate coroutine or have a window. an error in a hook
-- function will crash the system.

View File

@ -83,11 +83,15 @@ Event.on('modem_message', function(_, _, dport, dhost, request)
if dport == 80 and dhost == computerId and type(request) == 'table' then
if request.method == 'GET' then
local query
if not request.path or type(request.path) ~= 'string' then
return
end
local path = request.path:gsub('%?(.*)', function(v)
query = parseQuery(v)
return ''
end)
if fs.isDir(path) then
-- TODO: more validation
modem.transmit(request.replyPort, request.replyAddress, {
statusCode = 200,
contentType = 'table/directory',

View File

@ -150,11 +150,13 @@ local function sendInfo()
end
if device.neuralInterface then
info.status = device.neuralInterface.status
if not info.status and device.neuralInterface.getMetaOwner then
info.status = 'health: ' ..
math.floor(device.neuralInterface.getMetaOwner().health /
device.neuralInterface.getMetaOwner().maxHealth * 100)
end
pcall(function()
if not info.status and device.neuralInterface.getMetaOwner then
info.status = 'health: ' ..
math.floor(device.neuralInterface.getMetaOwner().health /
device.neuralInterface.getMetaOwner().maxHealth * 100)
end
end)
end
device.wireless_modem.transmit(999, os.getComputerID(), info)
end

View File

@ -33,7 +33,7 @@ function transport.read(socket)
end
function transport.write(socket, data)
--debug('>> ' .. Util.tostring({ type = 'DATA', seq = socket.wseq }))
--_debug('>> ' .. Util.tostring({ type = 'DATA', seq = socket.wseq }))
socket.transmit(socket.dport, socket.dhost, data)
--local timerId = os.startTimer(3)
@ -45,7 +45,7 @@ function transport.write(socket, data)
end
function transport.ping(socket)
--debug('>> ' .. Util.tostring({ type = 'DATA', seq = socket.wseq }))
--_debug('>> ' .. Util.tostring({ type = 'DATA', seq = socket.wseq }))
if os.clock() - socket.activityTimer > 10 then
socket.activityTimer = os.clock()
socket.transmit(socket.dport, socket.dhost, {
@ -78,9 +78,12 @@ Event.on('modem_message', function(_, _, dport, dhost, msg, distance)
local socket = transport.sockets[dport]
if socket and socket.connected then
--if msg.type then debug('<< ' .. Util.tostring(msg)) end
--if msg.type then _debug('<< ' .. Util.tostring(msg)) end
if socket.co and coroutine.status(socket.co) == 'dead' then
_G._debug('socket coroutine dead')
socket:close()
if msg.type == 'DISC' then
elseif msg.type == 'DISC' then
-- received disconnect from other end
if socket.connected then
os.queueEvent('transport_' .. socket.uid)
@ -108,9 +111,9 @@ Event.on('modem_message', function(_, _, dport, dhost, msg, distance)
socket.activityTimer = os.clock()
if msg.seq ~= socket.rseq then
print('transport seq error - closing socket ' .. socket.sport)
debug(msg.data)
debug('current ' .. socket.rseq)
debug('expected ' .. msg.seq)
_debug(msg.data)
_debug('current ' .. socket.rseq)
_debug('expected ' .. msg.seq)
-- socket:close()
-- os.queueEvent('transport_' .. socket.uid)
else
@ -122,7 +125,7 @@ Event.on('modem_message', function(_, _, dport, dhost, msg, distance)
os.queueEvent('transport_' .. socket.uid)
end
--debug('>> ' .. Util.tostring({ type = 'ACK', seq = msg.seq }))
--_debug('>> ' .. Util.tostring({ type = 'ACK', seq = msg.seq }))
--socket.transmit(socket.dport, socket.dhost, {
-- type = 'ACK',
-- seq = msg.seq,