mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-10-26 19:37:39 +00:00
Merge branch 'master' into mc-1.14.x
This also deletes display list support - MC 1.14 now requires VBOs to be supported in some capacity.
This commit is contained in:
@@ -21,7 +21,7 @@ if _VERSION == "Lua 5.1" then
|
||||
local nativeloadstring = loadstring
|
||||
local nativesetfenv = setfenv
|
||||
|
||||
--- Historically load/loadstring would handle the chunk name as if it has
|
||||
-- Historically load/loadstring would handle the chunk name as if it has
|
||||
-- been prefixed with "=". We emulate that behaviour here.
|
||||
local function prefix(chunkname)
|
||||
if type(chunkname) ~= "string" then return chunkname end
|
||||
@@ -33,45 +33,43 @@ if _VERSION == "Lua 5.1" then
|
||||
end
|
||||
end
|
||||
|
||||
function load( x, name, mode, env )
|
||||
function load(x, name, mode, env)
|
||||
expect(1, x, "function", "string")
|
||||
expect(2, name, "string", "nil")
|
||||
expect(3, mode, "string", "nil")
|
||||
expect(4, env, "table", "nil")
|
||||
|
||||
local ok, p1, p2 = pcall( function()
|
||||
local ok, p1, p2 = pcall(function()
|
||||
if type(x) == "string" then
|
||||
local result, err = nativeloadstring( x, name )
|
||||
local result, err = nativeloadstring(x, name)
|
||||
if result then
|
||||
if env then
|
||||
env._ENV = env
|
||||
nativesetfenv( result, env )
|
||||
nativesetfenv(result, env)
|
||||
end
|
||||
return result
|
||||
else
|
||||
return nil, err
|
||||
end
|
||||
else
|
||||
local result, err = nativeload( x, name )
|
||||
local result, err = nativeload(x, name)
|
||||
if result then
|
||||
if env then
|
||||
env._ENV = env
|
||||
nativesetfenv( result, env )
|
||||
nativesetfenv(result, env)
|
||||
end
|
||||
return result
|
||||
else
|
||||
return nil, err
|
||||
end
|
||||
end
|
||||
end )
|
||||
end)
|
||||
if ok then
|
||||
return p1, p2
|
||||
else
|
||||
error( p1, 2 )
|
||||
error(p1, 2)
|
||||
end
|
||||
end
|
||||
table.unpack = unpack
|
||||
table.pack = function( ... ) return { n = select( "#", ... ), ... } end
|
||||
|
||||
if _CC_DISABLE_LUA51_FEATURES then
|
||||
-- Remove the Lua 5.1 features that will be removed when we update to Lua 5.2, for compatibility testing.
|
||||
@@ -83,7 +81,7 @@ if _VERSION == "Lua 5.1" then
|
||||
math.log10 = nil
|
||||
table.maxn = nil
|
||||
else
|
||||
loadstring = function(string, chunkname) return nativeloadstring(string, prefix( chunkname )) end
|
||||
loadstring = function(string, chunkname) return nativeloadstring(string, prefix(chunkname)) end
|
||||
|
||||
-- Inject a stub for the old bit library
|
||||
_G.bit = {
|
||||
@@ -98,97 +96,33 @@ if _VERSION == "Lua 5.1" then
|
||||
end
|
||||
end
|
||||
|
||||
if _VERSION == "Lua 5.3" and not bit32 then
|
||||
-- If we're on Lua 5.3, install the bit32 api from Lua 5.2
|
||||
-- (Loaded from a string so this file will still parse on <5.3 lua)
|
||||
load( [[
|
||||
bit32 = {}
|
||||
|
||||
function bit32.arshift( n, bits )
|
||||
if type(n) ~= "number" or type(bits) ~= "number" then
|
||||
error( "Expected number, number", 2 )
|
||||
end
|
||||
return n >> bits
|
||||
end
|
||||
|
||||
function bit32.band( m, n )
|
||||
if type(m) ~= "number" or type(n) ~= "number" then
|
||||
error( "Expected number, number", 2 )
|
||||
end
|
||||
return m & n
|
||||
end
|
||||
|
||||
function bit32.bnot( n )
|
||||
if type(n) ~= "number" then
|
||||
error( "Expected number", 2 )
|
||||
end
|
||||
return ~n
|
||||
end
|
||||
|
||||
function bit32.bor( m, n )
|
||||
if type(m) ~= "number" or type(n) ~= "number" then
|
||||
error( "Expected number, number", 2 )
|
||||
end
|
||||
return m | n
|
||||
end
|
||||
|
||||
function bit32.btest( m, n )
|
||||
if type(m) ~= "number" or type(n) ~= "number" then
|
||||
error( "Expected number, number", 2 )
|
||||
end
|
||||
return (m & n) ~= 0
|
||||
end
|
||||
|
||||
function bit32.bxor( m, n )
|
||||
if type(m) ~= "number" or type(n) ~= "number" then
|
||||
error( "Expected number, number", 2 )
|
||||
end
|
||||
return m ~ n
|
||||
end
|
||||
|
||||
function bit32.lshift( n, bits )
|
||||
if type(n) ~= "number" or type(bits) ~= "number" then
|
||||
error( "Expected number, number", 2 )
|
||||
end
|
||||
return n << bits
|
||||
end
|
||||
|
||||
function bit32.rshift( n, bits )
|
||||
if type(n) ~= "number" or type(bits) ~= "number" then
|
||||
error( "Expected number, number", 2 )
|
||||
end
|
||||
return n >> bits
|
||||
end
|
||||
]] )()
|
||||
end
|
||||
|
||||
-- Install lua parts of the os api
|
||||
function os.version()
|
||||
return "CraftOS 1.8"
|
||||
end
|
||||
|
||||
function os.pullEventRaw( sFilter )
|
||||
return coroutine.yield( sFilter )
|
||||
function os.pullEventRaw(sFilter)
|
||||
return coroutine.yield(sFilter)
|
||||
end
|
||||
|
||||
function os.pullEvent( sFilter )
|
||||
local eventData = table.pack( os.pullEventRaw( sFilter ) )
|
||||
function os.pullEvent(sFilter)
|
||||
local eventData = table.pack(os.pullEventRaw(sFilter))
|
||||
if eventData[1] == "terminate" then
|
||||
error( "Terminated", 0 )
|
||||
error("Terminated", 0)
|
||||
end
|
||||
return table.unpack( eventData, 1, eventData.n )
|
||||
return table.unpack(eventData, 1, eventData.n)
|
||||
end
|
||||
|
||||
-- Install globals
|
||||
function sleep( nTime )
|
||||
function sleep(nTime)
|
||||
expect(1, nTime, "number", "nil")
|
||||
local timer = os.startTimer( nTime or 0 )
|
||||
local timer = os.startTimer(nTime or 0)
|
||||
repeat
|
||||
local _, param = os.pullEvent( "timer" )
|
||||
local _, param = os.pullEvent("timer")
|
||||
until param == timer
|
||||
end
|
||||
|
||||
function write( sText )
|
||||
function write(sText)
|
||||
expect(1, sText, "string", "number")
|
||||
|
||||
local w, h = term.getSize()
|
||||
@@ -209,32 +143,32 @@ function write( sText )
|
||||
-- Print the line with proper word wrapping
|
||||
sText = tostring(sText)
|
||||
while #sText > 0 do
|
||||
local whitespace = string.match( sText, "^[ \t]+" )
|
||||
local whitespace = string.match(sText, "^[ \t]+")
|
||||
if whitespace then
|
||||
-- Print whitespace
|
||||
term.write( whitespace )
|
||||
term.write(whitespace)
|
||||
x, y = term.getCursorPos()
|
||||
sText = string.sub( sText, #whitespace + 1 )
|
||||
sText = string.sub(sText, #whitespace + 1)
|
||||
end
|
||||
|
||||
local newline = string.match( sText, "^\n" )
|
||||
local newline = string.match(sText, "^\n")
|
||||
if newline then
|
||||
-- Print newlines
|
||||
newLine()
|
||||
sText = string.sub( sText, 2 )
|
||||
sText = string.sub(sText, 2)
|
||||
end
|
||||
|
||||
local text = string.match( sText, "^[^ \t\n]+" )
|
||||
local text = string.match(sText, "^[^ \t\n]+")
|
||||
if text then
|
||||
sText = string.sub( sText, #text + 1 )
|
||||
sText = string.sub(sText, #text + 1)
|
||||
if #text > w then
|
||||
-- Print a multiline word
|
||||
while #text > 0 do
|
||||
if x > w then
|
||||
newLine()
|
||||
end
|
||||
term.write( text )
|
||||
text = string.sub( text, w - x + 2 )
|
||||
term.write(text)
|
||||
text = string.sub(text, w - x + 2)
|
||||
x, y = term.getCursorPos()
|
||||
end
|
||||
else
|
||||
@@ -242,7 +176,7 @@ function write( sText )
|
||||
if x + #text - 1 > w then
|
||||
newLine()
|
||||
end
|
||||
term.write( text )
|
||||
term.write(text)
|
||||
x, y = term.getCursorPos()
|
||||
end
|
||||
end
|
||||
@@ -251,42 +185,42 @@ function write( sText )
|
||||
return nLinesPrinted
|
||||
end
|
||||
|
||||
function print( ... )
|
||||
function print(...)
|
||||
local nLinesPrinted = 0
|
||||
local nLimit = select("#", ... )
|
||||
local nLimit = select("#", ...)
|
||||
for n = 1, nLimit do
|
||||
local s = tostring( select( n, ... ) )
|
||||
local s = tostring(select(n, ...))
|
||||
if n < nLimit then
|
||||
s = s .. "\t"
|
||||
end
|
||||
nLinesPrinted = nLinesPrinted + write( s )
|
||||
nLinesPrinted = nLinesPrinted + write(s)
|
||||
end
|
||||
nLinesPrinted = nLinesPrinted + write( "\n" )
|
||||
nLinesPrinted = nLinesPrinted + write("\n")
|
||||
return nLinesPrinted
|
||||
end
|
||||
|
||||
function printError( ... )
|
||||
function printError(...)
|
||||
local oldColour
|
||||
if term.isColour() then
|
||||
oldColour = term.getTextColour()
|
||||
term.setTextColour( colors.red )
|
||||
term.setTextColour(colors.red)
|
||||
end
|
||||
print( ... )
|
||||
print(...)
|
||||
if term.isColour() then
|
||||
term.setTextColour( oldColour )
|
||||
term.setTextColour(oldColour)
|
||||
end
|
||||
end
|
||||
|
||||
function read( _sReplaceChar, _tHistory, _fnComplete, _sDefault )
|
||||
function read(_sReplaceChar, _tHistory, _fnComplete, _sDefault)
|
||||
expect(1, _sReplaceChar, "string", "nil")
|
||||
expect(2, _tHistory, "table", "nil")
|
||||
expect(3, _fnComplete, "function", "nil")
|
||||
expect(4, _sDefault, "string", "nil")
|
||||
|
||||
term.setCursorBlink( true )
|
||||
term.setCursorBlink(true)
|
||||
|
||||
local sLine
|
||||
if type( _sDefault ) == "string" then
|
||||
if type(_sDefault) == "string" then
|
||||
sLine = _sDefault
|
||||
else
|
||||
sLine = ""
|
||||
@@ -294,14 +228,14 @@ function read( _sReplaceChar, _tHistory, _fnComplete, _sDefault )
|
||||
local nHistoryPos
|
||||
local nPos, nScroll = #sLine, 0
|
||||
if _sReplaceChar then
|
||||
_sReplaceChar = string.sub( _sReplaceChar, 1, 1 )
|
||||
_sReplaceChar = string.sub(_sReplaceChar, 1, 1)
|
||||
end
|
||||
|
||||
local tCompletions
|
||||
local nCompletion
|
||||
local function recomplete()
|
||||
if _fnComplete and nPos == #sLine then
|
||||
tCompletions = _fnComplete( sLine )
|
||||
tCompletions = _fnComplete(sLine)
|
||||
if tCompletions and #tCompletions > 0 then
|
||||
nCompletion = 1
|
||||
else
|
||||
@@ -321,7 +255,7 @@ function read( _sReplaceChar, _tHistory, _fnComplete, _sDefault )
|
||||
local w = term.getSize()
|
||||
local sx = term.getCursorPos()
|
||||
|
||||
local function redraw( _bClear )
|
||||
local function redraw(_bClear)
|
||||
local cursor_pos = nPos - nScroll
|
||||
if sx + cursor_pos >= w then
|
||||
-- We've moved beyond the RHS, ensure we're on the edge.
|
||||
@@ -332,39 +266,39 @@ function read( _sReplaceChar, _tHistory, _fnComplete, _sDefault )
|
||||
end
|
||||
|
||||
local _, cy = term.getCursorPos()
|
||||
term.setCursorPos( sx, cy )
|
||||
term.setCursorPos(sx, cy)
|
||||
local sReplace = _bClear and " " or _sReplaceChar
|
||||
if sReplace then
|
||||
term.write( string.rep( sReplace, math.max( #sLine - nScroll, 0 ) ) )
|
||||
term.write(string.rep(sReplace, math.max(#sLine - nScroll, 0)))
|
||||
else
|
||||
term.write( string.sub( sLine, nScroll + 1 ) )
|
||||
term.write(string.sub(sLine, nScroll + 1))
|
||||
end
|
||||
|
||||
if nCompletion then
|
||||
local sCompletion = tCompletions[ nCompletion ]
|
||||
local sCompletion = tCompletions[nCompletion]
|
||||
local oldText, oldBg
|
||||
if not _bClear then
|
||||
oldText = term.getTextColor()
|
||||
oldBg = term.getBackgroundColor()
|
||||
term.setTextColor( colors.white )
|
||||
term.setBackgroundColor( colors.gray )
|
||||
term.setTextColor(colors.white)
|
||||
term.setBackgroundColor(colors.gray)
|
||||
end
|
||||
if sReplace then
|
||||
term.write( string.rep( sReplace, #sCompletion ) )
|
||||
term.write(string.rep(sReplace, #sCompletion))
|
||||
else
|
||||
term.write( sCompletion )
|
||||
term.write(sCompletion)
|
||||
end
|
||||
if not _bClear then
|
||||
term.setTextColor( oldText )
|
||||
term.setBackgroundColor( oldBg )
|
||||
term.setTextColor(oldText)
|
||||
term.setBackgroundColor(oldBg)
|
||||
end
|
||||
end
|
||||
|
||||
term.setCursorPos( sx + nPos - nScroll, cy )
|
||||
term.setCursorPos(sx + nPos - nScroll, cy)
|
||||
end
|
||||
|
||||
local function clear()
|
||||
redraw( true )
|
||||
redraw(true)
|
||||
end
|
||||
|
||||
recomplete()
|
||||
@@ -376,7 +310,7 @@ function read( _sReplaceChar, _tHistory, _fnComplete, _sDefault )
|
||||
clear()
|
||||
|
||||
-- Find the common prefix of all the other suggestions which start with the same letter as the current one
|
||||
local sCompletion = tCompletions[ nCompletion ]
|
||||
local sCompletion = tCompletions[nCompletion]
|
||||
sLine = sLine .. sCompletion
|
||||
nPos = #sLine
|
||||
|
||||
@@ -390,7 +324,7 @@ function read( _sReplaceChar, _tHistory, _fnComplete, _sDefault )
|
||||
if sEvent == "char" then
|
||||
-- Typed key
|
||||
clear()
|
||||
sLine = string.sub( sLine, 1, nPos ) .. param .. string.sub( sLine, nPos + 1 )
|
||||
sLine = string.sub(sLine, 1, nPos) .. param .. string.sub(sLine, nPos + 1)
|
||||
nPos = nPos + 1
|
||||
recomplete()
|
||||
redraw()
|
||||
@@ -398,7 +332,7 @@ function read( _sReplaceChar, _tHistory, _fnComplete, _sDefault )
|
||||
elseif sEvent == "paste" then
|
||||
-- Pasted text
|
||||
clear()
|
||||
sLine = string.sub( sLine, 1, nPos ) .. param .. string.sub( sLine, nPos + 1 )
|
||||
sLine = string.sub(sLine, 1, nPos) .. param .. string.sub(sLine, nPos + 1)
|
||||
nPos = nPos + #param
|
||||
recomplete()
|
||||
redraw()
|
||||
@@ -489,7 +423,7 @@ function read( _sReplaceChar, _tHistory, _fnComplete, _sDefault )
|
||||
-- Backspace
|
||||
if nPos > 0 then
|
||||
clear()
|
||||
sLine = string.sub( sLine, 1, nPos - 1 ) .. string.sub( sLine, nPos + 1 )
|
||||
sLine = string.sub(sLine, 1, nPos - 1) .. string.sub(sLine, nPos + 1)
|
||||
nPos = nPos - 1
|
||||
if nScroll > 0 then nScroll = nScroll - 1 end
|
||||
recomplete()
|
||||
@@ -509,7 +443,7 @@ function read( _sReplaceChar, _tHistory, _fnComplete, _sDefault )
|
||||
-- Delete
|
||||
if nPos < #sLine then
|
||||
clear()
|
||||
sLine = string.sub( sLine, 1, nPos ) .. string.sub( sLine, nPos + 2 )
|
||||
sLine = string.sub(sLine, 1, nPos) .. string.sub(sLine, nPos + 2)
|
||||
recomplete()
|
||||
redraw()
|
||||
end
|
||||
@@ -546,14 +480,14 @@ function read( _sReplaceChar, _tHistory, _fnComplete, _sDefault )
|
||||
end
|
||||
|
||||
local _, cy = term.getCursorPos()
|
||||
term.setCursorBlink( false )
|
||||
term.setCursorPos( w + 1, cy )
|
||||
term.setCursorBlink(false)
|
||||
term.setCursorPos(w + 1, cy)
|
||||
print()
|
||||
|
||||
return sLine
|
||||
end
|
||||
|
||||
function loadfile( filename, mode, env )
|
||||
function loadfile(filename, mode, env)
|
||||
-- Support the previous `loadfile(filename, env)` form instead.
|
||||
if type(mode) == "table" and env == nil then
|
||||
mode, env = nil, mode
|
||||
@@ -563,81 +497,81 @@ function loadfile( filename, mode, env )
|
||||
expect(2, mode, "string", "nil")
|
||||
expect(3, env, "table", "nil")
|
||||
|
||||
local file = fs.open( filename, "r" )
|
||||
local file = fs.open(filename, "r")
|
||||
if not file then return nil, "File not found" end
|
||||
|
||||
local func, err = load( file.readAll(), "@" .. fs.getName( filename ), mode, env )
|
||||
local func, err = load(file.readAll(), "@" .. fs.getName(filename), mode, env)
|
||||
file.close()
|
||||
return func, err
|
||||
end
|
||||
|
||||
function dofile( _sFile )
|
||||
function dofile(_sFile)
|
||||
expect(1, _sFile, "string")
|
||||
|
||||
local fnFile, e = loadfile( _sFile, nil, _G )
|
||||
local fnFile, e = loadfile(_sFile, nil, _G)
|
||||
if fnFile then
|
||||
return fnFile()
|
||||
else
|
||||
error( e, 2 )
|
||||
error(e, 2)
|
||||
end
|
||||
end
|
||||
|
||||
-- Install the rest of the OS api
|
||||
function os.run( _tEnv, _sPath, ... )
|
||||
function os.run(_tEnv, _sPath, ...)
|
||||
expect(1, _tEnv, "table")
|
||||
expect(2, _sPath, "string")
|
||||
|
||||
local tArgs = table.pack( ... )
|
||||
local tArgs = table.pack(...)
|
||||
local tEnv = _tEnv
|
||||
setmetatable( tEnv, { __index = _G } )
|
||||
local fnFile, err = loadfile( _sPath, nil, tEnv )
|
||||
setmetatable(tEnv, { __index = _G })
|
||||
local fnFile, err = loadfile(_sPath, nil, tEnv)
|
||||
if fnFile then
|
||||
local ok, err = pcall( function()
|
||||
fnFile( table.unpack( tArgs, 1, tArgs.n ) )
|
||||
end )
|
||||
local ok, err = pcall(function()
|
||||
fnFile(table.unpack(tArgs, 1, tArgs.n))
|
||||
end)
|
||||
if not ok then
|
||||
if err and err ~= "" then
|
||||
printError( err )
|
||||
printError(err)
|
||||
end
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
if err and err ~= "" then
|
||||
printError( err )
|
||||
printError(err)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local tAPIsLoading = {}
|
||||
function os.loadAPI( _sPath )
|
||||
function os.loadAPI(_sPath)
|
||||
expect(1, _sPath, "string")
|
||||
local sName = fs.getName( _sPath )
|
||||
local sName = fs.getName(_sPath)
|
||||
if sName:sub(-4) == ".lua" then
|
||||
sName = sName:sub(1, -5)
|
||||
end
|
||||
if tAPIsLoading[sName] == true then
|
||||
printError( "API " .. sName .. " is already being loaded" )
|
||||
printError("API " .. sName .. " is already being loaded")
|
||||
return false
|
||||
end
|
||||
tAPIsLoading[sName] = true
|
||||
|
||||
local tEnv = {}
|
||||
setmetatable( tEnv, { __index = _G } )
|
||||
local fnAPI, err = loadfile( _sPath, nil, tEnv )
|
||||
setmetatable(tEnv, { __index = _G })
|
||||
local fnAPI, err = loadfile(_sPath, nil, tEnv)
|
||||
if fnAPI then
|
||||
local ok, err = pcall( fnAPI )
|
||||
local ok, err = pcall(fnAPI)
|
||||
if not ok then
|
||||
tAPIsLoading[sName] = nil
|
||||
return error( "Failed to load API " .. sName .. " due to " .. err, 1 )
|
||||
return error("Failed to load API " .. sName .. " due to " .. err, 1)
|
||||
end
|
||||
else
|
||||
tAPIsLoading[sName] = nil
|
||||
return error( "Failed to load API " .. sName .. " due to " .. err, 1 )
|
||||
return error("Failed to load API " .. sName .. " due to " .. err, 1)
|
||||
end
|
||||
|
||||
local tAPI = {}
|
||||
for k, v in pairs( tEnv ) do
|
||||
for k, v in pairs(tEnv) do
|
||||
if k ~= "_ENV" then
|
||||
tAPI[k] = v
|
||||
end
|
||||
@@ -648,15 +582,15 @@ function os.loadAPI( _sPath )
|
||||
return true
|
||||
end
|
||||
|
||||
function os.unloadAPI( _sName )
|
||||
function os.unloadAPI(_sName)
|
||||
expect(1, _sName, "string")
|
||||
if _sName ~= "_G" and type(_G[_sName]) == "table" then
|
||||
_G[_sName] = nil
|
||||
end
|
||||
end
|
||||
|
||||
function os.sleep( nTime )
|
||||
sleep( nTime )
|
||||
function os.sleep(nTime)
|
||||
sleep(nTime)
|
||||
end
|
||||
|
||||
local nativeShutdown = os.shutdown
|
||||
@@ -685,7 +619,7 @@ if http then
|
||||
PATCH = true, TRACE = true,
|
||||
}
|
||||
|
||||
local function checkKey( options, key, ty, opt )
|
||||
local function checkKey(options, key, ty, opt)
|
||||
local value = options[key]
|
||||
local valueTy = type(value)
|
||||
|
||||
@@ -694,24 +628,24 @@ if http then
|
||||
end
|
||||
end
|
||||
|
||||
local function checkOptions( options, body )
|
||||
checkKey( options, "url", "string")
|
||||
local function checkOptions(options, body)
|
||||
checkKey(options, "url", "string")
|
||||
if body == false then
|
||||
checkKey( options, "body", "nil" )
|
||||
checkKey(options, "body", "nil")
|
||||
else
|
||||
checkKey( options, "body", "string", not body )
|
||||
checkKey(options, "body", "string", not body)
|
||||
end
|
||||
checkKey( options, "headers", "table", true )
|
||||
checkKey( options, "method", "string", true )
|
||||
checkKey( options, "redirect", "boolean", true )
|
||||
checkKey(options, "headers", "table", true)
|
||||
checkKey(options, "method", "string", true)
|
||||
checkKey(options, "redirect", "boolean", true)
|
||||
|
||||
if options.method and not methods[options.method] then
|
||||
error( "Unsupported HTTP method", 3 )
|
||||
error("Unsupported HTTP method", 3)
|
||||
end
|
||||
end
|
||||
|
||||
local function wrapRequest( _url, ... )
|
||||
local ok, err = nativeHTTPRequest( ... )
|
||||
local function wrapRequest(_url, ...)
|
||||
local ok, err = nativeHTTPRequest(...)
|
||||
if ok then
|
||||
while true do
|
||||
local event, param1, param2, param3 = os.pullEvent()
|
||||
@@ -725,35 +659,35 @@ if http then
|
||||
return nil, err
|
||||
end
|
||||
|
||||
http.get = function( _url, _headers, _binary)
|
||||
if type( _url ) == "table" then
|
||||
checkOptions( _url, false )
|
||||
return wrapRequest( _url.url, _url )
|
||||
http.get = function(_url, _headers, _binary)
|
||||
if type(_url) == "table" then
|
||||
checkOptions(_url, false)
|
||||
return wrapRequest(_url.url, _url)
|
||||
end
|
||||
|
||||
expect(1, _url, "string")
|
||||
expect(2, _headers, "table", "nil")
|
||||
expect(3, _binary, "boolean", "nil")
|
||||
return wrapRequest( _url, _url, nil, _headers, _binary )
|
||||
return wrapRequest(_url, _url, nil, _headers, _binary)
|
||||
end
|
||||
|
||||
http.post = function( _url, _post, _headers, _binary)
|
||||
if type( _url ) == "table" then
|
||||
checkOptions( _url, true )
|
||||
return wrapRequest( _url.url, _url )
|
||||
http.post = function(_url, _post, _headers, _binary)
|
||||
if type(_url) == "table" then
|
||||
checkOptions(_url, true)
|
||||
return wrapRequest(_url.url, _url)
|
||||
end
|
||||
|
||||
expect(1, _url, "string")
|
||||
expect(2, _post, "string")
|
||||
expect(3, _headers, "table", "nil")
|
||||
expect(4, _binary, "boolean", "nil")
|
||||
return wrapRequest( _url, _url, _post, _headers, _binary )
|
||||
return wrapRequest(_url, _url, _post, _headers, _binary)
|
||||
end
|
||||
|
||||
http.request = function( _url, _post, _headers, _binary )
|
||||
http.request = function(_url, _post, _headers, _binary)
|
||||
local url
|
||||
if type( _url ) == "table" then
|
||||
checkOptions( _url )
|
||||
if type(_url) == "table" then
|
||||
checkOptions(_url)
|
||||
url = _url.url
|
||||
else
|
||||
expect(1, _url, "string")
|
||||
@@ -763,32 +697,32 @@ if http then
|
||||
url = _url.url
|
||||
end
|
||||
|
||||
local ok, err = nativeHTTPRequest( _url, _post, _headers, _binary )
|
||||
local ok, err = nativeHTTPRequest(_url, _post, _headers, _binary)
|
||||
if not ok then
|
||||
os.queueEvent( "http_failure", url, err )
|
||||
os.queueEvent("http_failure", url, err)
|
||||
end
|
||||
return ok, err
|
||||
end
|
||||
|
||||
local nativeCheckURL = http.checkURL
|
||||
http.checkURLAsync = nativeCheckURL
|
||||
http.checkURL = function( _url )
|
||||
local ok, err = nativeCheckURL( _url )
|
||||
http.checkURL = function(_url)
|
||||
local ok, err = nativeCheckURL(_url)
|
||||
if not ok then return ok, err end
|
||||
|
||||
while true do
|
||||
local _, url, ok, err = os.pullEvent( "http_check" )
|
||||
local _, url, ok, err = os.pullEvent("http_check")
|
||||
if url == _url then return ok, err end
|
||||
end
|
||||
end
|
||||
|
||||
local nativeWebsocket = http.websocket
|
||||
http.websocketAsync = nativeWebsocket
|
||||
http.websocket = function( _url, _headers )
|
||||
http.websocket = function(_url, _headers)
|
||||
expect(1, _url, "string")
|
||||
expect(2, _headers, "table", "nil")
|
||||
|
||||
local ok, err = nativeWebsocket( _url, _headers )
|
||||
local ok, err = nativeWebsocket(_url, _headers)
|
||||
if not ok then return ok, err end
|
||||
|
||||
while true do
|
||||
@@ -804,7 +738,7 @@ end
|
||||
|
||||
-- Install the lua part of the FS api
|
||||
local tEmpty = {}
|
||||
function fs.complete( sPath, sLocation, bIncludeFiles, bIncludeDirs )
|
||||
function fs.complete(sPath, sLocation, bIncludeFiles, bIncludeDirs)
|
||||
expect(1, sPath, "string")
|
||||
expect(2, sLocation, "string")
|
||||
expect(3, bIncludeFiles, "boolean", "nil")
|
||||
@@ -814,49 +748,49 @@ function fs.complete( sPath, sLocation, bIncludeFiles, bIncludeDirs )
|
||||
bIncludeDirs = bIncludeDirs ~= false
|
||||
local sDir = sLocation
|
||||
local nStart = 1
|
||||
local nSlash = string.find( sPath, "[/\\]", nStart )
|
||||
local nSlash = string.find(sPath, "[/\\]", nStart)
|
||||
if nSlash == 1 then
|
||||
sDir = ""
|
||||
nStart = 2
|
||||
end
|
||||
local sName
|
||||
while not sName do
|
||||
local nSlash = string.find( sPath, "[/\\]", nStart )
|
||||
local nSlash = string.find(sPath, "[/\\]", nStart)
|
||||
if nSlash then
|
||||
local sPart = string.sub( sPath, nStart, nSlash - 1 )
|
||||
sDir = fs.combine( sDir, sPart )
|
||||
local sPart = string.sub(sPath, nStart, nSlash - 1)
|
||||
sDir = fs.combine(sDir, sPart)
|
||||
nStart = nSlash + 1
|
||||
else
|
||||
sName = string.sub( sPath, nStart )
|
||||
sName = string.sub(sPath, nStart)
|
||||
end
|
||||
end
|
||||
|
||||
if fs.isDir( sDir ) then
|
||||
if fs.isDir(sDir) then
|
||||
local tResults = {}
|
||||
if bIncludeDirs and sPath == "" then
|
||||
table.insert( tResults, "." )
|
||||
table.insert(tResults, ".")
|
||||
end
|
||||
if sDir ~= "" then
|
||||
if sPath == "" then
|
||||
table.insert( tResults, bIncludeDirs and ".." or "../" )
|
||||
table.insert(tResults, bIncludeDirs and ".." or "../")
|
||||
elseif sPath == "." then
|
||||
table.insert( tResults, bIncludeDirs and "." or "./" )
|
||||
table.insert(tResults, bIncludeDirs and "." or "./")
|
||||
end
|
||||
end
|
||||
local tFiles = fs.list( sDir )
|
||||
local tFiles = fs.list(sDir)
|
||||
for n = 1, #tFiles do
|
||||
local sFile = tFiles[n]
|
||||
if #sFile >= #sName and string.sub( sFile, 1, #sName ) == sName then
|
||||
local bIsDir = fs.isDir( fs.combine( sDir, sFile ) )
|
||||
local sResult = string.sub( sFile, #sName + 1 )
|
||||
if #sFile >= #sName and string.sub(sFile, 1, #sName) == sName then
|
||||
local bIsDir = fs.isDir(fs.combine(sDir, sFile))
|
||||
local sResult = string.sub(sFile, #sName + 1)
|
||||
if bIsDir then
|
||||
table.insert( tResults, sResult .. "/" )
|
||||
table.insert(tResults, sResult .. "/")
|
||||
if bIncludeDirs and #sResult > 0 then
|
||||
table.insert( tResults, sResult )
|
||||
table.insert(tResults, sResult)
|
||||
end
|
||||
else
|
||||
if bIncludeFiles and #sResult > 0 then
|
||||
table.insert( tResults, sResult )
|
||||
table.insert(tResults, sResult)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -868,26 +802,26 @@ end
|
||||
|
||||
-- Load APIs
|
||||
local bAPIError = false
|
||||
local tApis = fs.list( "rom/apis" )
|
||||
for _, sFile in ipairs( tApis ) do
|
||||
if string.sub( sFile, 1, 1 ) ~= "." then
|
||||
local sPath = fs.combine( "rom/apis", sFile )
|
||||
if not fs.isDir( sPath ) then
|
||||
if not os.loadAPI( sPath ) then
|
||||
local tApis = fs.list("rom/apis")
|
||||
for _, sFile in ipairs(tApis) do
|
||||
if string.sub(sFile, 1, 1) ~= "." then
|
||||
local sPath = fs.combine("rom/apis", sFile)
|
||||
if not fs.isDir(sPath) then
|
||||
if not os.loadAPI(sPath) then
|
||||
bAPIError = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if turtle and fs.isDir( "rom/apis/turtle" ) then
|
||||
if turtle and fs.isDir("rom/apis/turtle") then
|
||||
-- Load turtle APIs
|
||||
local tApis = fs.list( "rom/apis/turtle" )
|
||||
for _, sFile in ipairs( tApis ) do
|
||||
if string.sub( sFile, 1, 1 ) ~= "." then
|
||||
local sPath = fs.combine( "rom/apis/turtle", sFile )
|
||||
if not fs.isDir( sPath ) then
|
||||
if not os.loadAPI( sPath ) then
|
||||
local tApis = fs.list("rom/apis/turtle")
|
||||
for _, sFile in ipairs(tApis) do
|
||||
if string.sub(sFile, 1, 1) ~= "." then
|
||||
local sPath = fs.combine("rom/apis/turtle", sFile)
|
||||
if not fs.isDir(sPath) then
|
||||
if not os.loadAPI(sPath) then
|
||||
bAPIError = true
|
||||
end
|
||||
end
|
||||
@@ -895,14 +829,14 @@ if turtle and fs.isDir( "rom/apis/turtle" ) then
|
||||
end
|
||||
end
|
||||
|
||||
if pocket and fs.isDir( "rom/apis/pocket" ) then
|
||||
if pocket and fs.isDir("rom/apis/pocket") then
|
||||
-- Load pocket APIs
|
||||
local tApis = fs.list( "rom/apis/pocket" )
|
||||
for _, sFile in ipairs( tApis ) do
|
||||
if string.sub( sFile, 1, 1 ) ~= "." then
|
||||
local sPath = fs.combine( "rom/apis/pocket", sFile )
|
||||
if not fs.isDir( sPath ) then
|
||||
if not os.loadAPI( sPath ) then
|
||||
local tApis = fs.list("rom/apis/pocket")
|
||||
for _, sFile in ipairs(tApis) do
|
||||
if string.sub(sFile, 1, 1) ~= "." then
|
||||
local sPath = fs.combine("rom/apis/pocket", sFile)
|
||||
if not fs.isDir(sPath) then
|
||||
if not os.loadAPI(sPath) then
|
||||
bAPIError = true
|
||||
end
|
||||
end
|
||||
@@ -910,18 +844,18 @@ if pocket and fs.isDir( "rom/apis/pocket" ) then
|
||||
end
|
||||
end
|
||||
|
||||
if commands and fs.isDir( "rom/apis/command" ) then
|
||||
if commands and fs.isDir("rom/apis/command") then
|
||||
-- Load command APIs
|
||||
if os.loadAPI( "rom/apis/command/commands.lua" ) then
|
||||
if os.loadAPI("rom/apis/command/commands.lua") then
|
||||
-- Add a special case-insensitive metatable to the commands api
|
||||
local tCaseInsensitiveMetatable = {
|
||||
__index = function( table, key )
|
||||
local value = rawget( table, key )
|
||||
__index = function(table, key)
|
||||
local value = rawget(table, key)
|
||||
if value ~= nil then
|
||||
return value
|
||||
end
|
||||
if type(key) == "string" then
|
||||
local value = rawget( table, string.lower(key) )
|
||||
local value = rawget(table, string.lower(key))
|
||||
if value ~= nil then
|
||||
return value
|
||||
end
|
||||
@@ -929,8 +863,8 @@ if commands and fs.isDir( "rom/apis/command" ) then
|
||||
return nil
|
||||
end,
|
||||
}
|
||||
setmetatable( commands, tCaseInsensitiveMetatable )
|
||||
setmetatable( commands.async, tCaseInsensitiveMetatable )
|
||||
setmetatable(commands, tCaseInsensitiveMetatable)
|
||||
setmetatable(commands.async, tCaseInsensitiveMetatable)
|
||||
|
||||
-- Add global "exec" function
|
||||
exec = commands.exec
|
||||
@@ -940,29 +874,77 @@ if commands and fs.isDir( "rom/apis/command" ) then
|
||||
end
|
||||
|
||||
if bAPIError then
|
||||
print( "Press any key to continue" )
|
||||
os.pullEvent( "key" )
|
||||
print("Press any key to continue")
|
||||
os.pullEvent("key")
|
||||
term.clear()
|
||||
term.setCursorPos( 1, 1 )
|
||||
term.setCursorPos(1, 1)
|
||||
end
|
||||
|
||||
-- Set default settings
|
||||
settings.set( "shell.allow_startup", true )
|
||||
settings.set( "shell.allow_disk_startup", commands == nil )
|
||||
settings.set( "shell.autocomplete", true )
|
||||
settings.set( "edit.autocomplete", true )
|
||||
settings.set( "edit.default_extension", "lua" )
|
||||
settings.set( "paint.default_extension", "nfp" )
|
||||
settings.set( "lua.autocomplete", true )
|
||||
settings.set( "list.show_hidden", false )
|
||||
settings.set( "motd.enable", false )
|
||||
settings.set( "motd.path", "/rom/motd.txt:/motd.txt" )
|
||||
settings.define("shell.allow_startup", {
|
||||
default = true,
|
||||
description = "Run startup files when the computer turns on.",
|
||||
type = "boolean",
|
||||
})
|
||||
settings.define("shell.allow_disk_startup", {
|
||||
default = commands == nil,
|
||||
description = "Run startup files from disk drives when the computer turns on.",
|
||||
type = "boolean",
|
||||
})
|
||||
|
||||
settings.define("shell.autocomplete", {
|
||||
default = true,
|
||||
description = "Autocomplete program and arguments in the shell.",
|
||||
type = "boolean",
|
||||
})
|
||||
settings.define("edit.autocomplete", {
|
||||
default = true,
|
||||
description = "Autocomplete API and function names in the editor.",
|
||||
type = "boolean",
|
||||
})
|
||||
settings.define("lua.autocomplete", {
|
||||
default = true,
|
||||
description = "Autocomplete API and function names in the Lua REPL.",
|
||||
type = "boolean",
|
||||
})
|
||||
|
||||
settings.define("edit.default_extension", {
|
||||
default = "lua",
|
||||
description = [[The file extension the editor will use if none is given. Set to "" to disable.]],
|
||||
type = "string",
|
||||
})
|
||||
settings.define("paint.default_extension", {
|
||||
default = "nfp",
|
||||
description = [[The file extension the paint program will use if none is given. Set to "" to disable.]],
|
||||
type = "string",
|
||||
})
|
||||
|
||||
settings.define("list.show_hidden", {
|
||||
default = false,
|
||||
description = [[Show hidden files (those starting with "." in the Lua REPL)]],
|
||||
type = "boolean",
|
||||
})
|
||||
|
||||
settings.define("motd.enable", {
|
||||
default = false,
|
||||
description = "Display a random message when the computer starts up.",
|
||||
type = "boolean",
|
||||
})
|
||||
settings.define("motd.path", {
|
||||
default = "/rom/motd.txt:/motd.txt",
|
||||
description = [[The path to load random messages from. Should be a colon (":") separated string of file paths.]],
|
||||
type = "string",
|
||||
})
|
||||
if term.isColour() then
|
||||
settings.set( "bios.use_multishell", true )
|
||||
settings.define("bios.use_multishell", {
|
||||
default = true,
|
||||
description = [[Allow running multiple programs at once, through the use of the "fg" and "bg" programs.]],
|
||||
type = "boolean",
|
||||
})
|
||||
end
|
||||
if _CC_DEFAULT_SETTINGS then
|
||||
for sPair in string.gmatch( _CC_DEFAULT_SETTINGS, "[^,]+" ) do
|
||||
local sName, sValue = string.match( sPair, "([^=]*)=(.*)" )
|
||||
for sPair in string.gmatch(_CC_DEFAULT_SETTINGS, "[^,]+") do
|
||||
local sName, sValue = string.match(sPair, "([^=]*)=(.*)")
|
||||
if sName and sValue then
|
||||
local value
|
||||
if sValue == "true" then
|
||||
@@ -977,46 +959,43 @@ if _CC_DEFAULT_SETTINGS then
|
||||
value = sValue
|
||||
end
|
||||
if value ~= nil then
|
||||
settings.set( sName, value )
|
||||
settings.set(sName, value)
|
||||
else
|
||||
settings.unset( sName )
|
||||
settings.unset(sName)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Load user settings
|
||||
if fs.exists( ".settings" ) then
|
||||
settings.load( ".settings" )
|
||||
if fs.exists(".settings") then
|
||||
settings.load(".settings")
|
||||
end
|
||||
|
||||
-- Run the shell
|
||||
local ok, err = pcall( function()
|
||||
parallel.waitForAny(
|
||||
function()
|
||||
local sShell
|
||||
if term.isColour() and settings.get( "bios.use_multishell" ) then
|
||||
sShell = "rom/programs/advanced/multishell.lua"
|
||||
else
|
||||
sShell = "rom/programs/shell.lua"
|
||||
end
|
||||
os.run( {}, sShell )
|
||||
os.run( {}, "rom/programs/shutdown.lua" )
|
||||
end,
|
||||
function()
|
||||
rednet.run()
|
||||
end )
|
||||
end )
|
||||
local ok, err = pcall(parallel.waitForAny,
|
||||
function()
|
||||
local sShell
|
||||
if term.isColour() and settings.get("bios.use_multishell") then
|
||||
sShell = "rom/programs/advanced/multishell.lua"
|
||||
else
|
||||
sShell = "rom/programs/shell.lua"
|
||||
end
|
||||
os.run({}, sShell)
|
||||
os.run({}, "rom/programs/shutdown.lua")
|
||||
end,
|
||||
rednet.run
|
||||
)
|
||||
|
||||
-- If the shell errored, let the user read it.
|
||||
term.redirect( term.native() )
|
||||
term.redirect(term.native())
|
||||
if not ok then
|
||||
printError( err )
|
||||
pcall( function()
|
||||
term.setCursorBlink( false )
|
||||
print( "Press any key to continue" )
|
||||
os.pullEvent( "key" )
|
||||
end )
|
||||
printError(err)
|
||||
pcall(function()
|
||||
term.setCursorBlink(false)
|
||||
print("Press any key to continue")
|
||||
os.pullEvent("key")
|
||||
end)
|
||||
end
|
||||
|
||||
-- End
|
||||
|
||||
@@ -1,24 +1,92 @@
|
||||
--- The Colors API allows you to manipulate sets of colors.
|
||||
--
|
||||
-- This is useful in conjunction with Bundled Cables from the RedPower mod,
|
||||
-- RedNet Cables from the MineFactory Reloaded mod, and colors on Advanced
|
||||
-- Computers and Advanced Monitors.
|
||||
--
|
||||
-- For the non-American English version just replace @{colors} with @{colours}
|
||||
-- and it will use the other API, colours which is exactly the same, except in
|
||||
-- British English (e.g. @{colors.gray} is spelt @{colours.grey}).
|
||||
--
|
||||
-- @see colours
|
||||
-- @module colors
|
||||
|
||||
local expect = dofile("rom/modules/main/cc/expect.lua").expect
|
||||
|
||||
-- Colors
|
||||
white = 1
|
||||
orange = 2
|
||||
magenta = 4
|
||||
lightBlue = 8
|
||||
yellow = 16
|
||||
lime = 32
|
||||
pink = 64
|
||||
gray = 128
|
||||
lightGray = 256
|
||||
cyan = 512
|
||||
purple = 1024
|
||||
blue = 2048
|
||||
brown = 4096
|
||||
green = 8192
|
||||
red = 16384
|
||||
black = 32768
|
||||
--- White: Written as `0` in paint files and @{term.blit}, has a default
|
||||
-- terminal colour of #F0F0F0.
|
||||
white = 0x1
|
||||
|
||||
function combine( ... )
|
||||
--- Orange: Written as `1` in paint files and @{term.blit}, has a
|
||||
-- default terminal colour of #F2B233.
|
||||
orange = 0x2
|
||||
|
||||
--- Magenta: Written as `2` in paint files and @{term.blit}, has a
|
||||
-- default terminal colour of #E57FD8.
|
||||
magenta = 0x4
|
||||
|
||||
--- Light blue: Written as `3` in paint files and @{term.blit}, has a
|
||||
-- default terminal colour of #99B2F2.
|
||||
lightBlue = 0x8
|
||||
|
||||
--- Yellow: Written as `4` in paint files and @{term.blit}, has a
|
||||
-- default terminal colour of #DEDE6C.
|
||||
yellow = 0x10
|
||||
|
||||
--- Lime: Written as `5` in paint files and @{term.blit}, has a default
|
||||
-- terminal colour of #7FCC19.
|
||||
lime = 0x20
|
||||
|
||||
--- Pink. Written as `6` in paint files and @{term.blit}, has a default
|
||||
-- terminal colour of #F2B2CC.
|
||||
pink = 0x40
|
||||
|
||||
--- Gray: Written as `7` in paint files and @{term.blit}, has a default
|
||||
-- terminal colour of #4C4C4C.
|
||||
gray = 0x80
|
||||
|
||||
--- Light gray: Written as `8` in paint files and @{term.blit}, has a
|
||||
-- default terminal colour of #999999.
|
||||
lightGray = 0x100
|
||||
|
||||
--- Cyan: Written as `9` in paint files and @{term.blit}, has a default
|
||||
-- terminal colour of #4C99B2.
|
||||
cyan = 0x200
|
||||
|
||||
--- Purple: Written as `a` in paint files and @{term.blit}, has a
|
||||
-- default terminal colour of #B266E5.
|
||||
purple = 0x400
|
||||
|
||||
--- Blue: Written as `b` in paint files and @{term.blit}, has a default
|
||||
-- terminal colour of #3366CC.
|
||||
blue = 0x800
|
||||
|
||||
--- Brown: Written as `c` in paint files and @{term.blit}, has a default
|
||||
-- terminal colour of #7F664C.
|
||||
brown = 0x1000
|
||||
|
||||
--- Green: Written as `d` in paint files and @{term.blit}, has a default
|
||||
-- terminal colour of #57A64E.
|
||||
green = 0x2000
|
||||
|
||||
--- Red: Written as `e` in paint files and @{term.blit}, has a default
|
||||
-- terminal colour of #CC4C4C.
|
||||
red = 0x4000
|
||||
|
||||
--- Black: Written as `f` in paint files and @{term.blit}, has a default
|
||||
-- terminal colour of #191919.
|
||||
black = 0x8000
|
||||
|
||||
--- Combines a set of colors (or sets of colors) into a larger set.
|
||||
--
|
||||
-- @tparam number ... The colors to combine.
|
||||
-- @treturn number The union of the color sets given in `...`
|
||||
-- @usage
|
||||
-- ```lua
|
||||
-- colors.combine(colors.white, colors.magenta, colours.lightBlue)
|
||||
-- -- => 13
|
||||
-- ```
|
||||
function combine(...)
|
||||
local r = 0
|
||||
for i = 1, select('#', ...) do
|
||||
local c = select(i, ...)
|
||||
@@ -28,7 +96,21 @@ function combine( ... )
|
||||
return r
|
||||
end
|
||||
|
||||
function subtract( colors, ... )
|
||||
--- Removes one or more colors (or sets of colors) from an initial set.
|
||||
--
|
||||
-- Each parameter beyond the first may be a single color or may be a set of
|
||||
-- colors (in the latter case, all colors in the set are removed from the
|
||||
-- original set).
|
||||
--
|
||||
-- @tparam number colors The color from which to subtract.
|
||||
-- @tparam number ... The colors to subtract.
|
||||
-- @treturn number The resulting color.
|
||||
-- @usage
|
||||
-- ```lua
|
||||
-- colours.subtract(colours.lime, colours.orange, colours.white)
|
||||
-- -- => 32
|
||||
-- ```
|
||||
function subtract(colors, ...)
|
||||
expect(1, colors, "number")
|
||||
local r = colors
|
||||
for i = 1, select('#', ...) do
|
||||
@@ -39,34 +121,91 @@ function subtract( colors, ... )
|
||||
return r
|
||||
end
|
||||
|
||||
function test( colors, color )
|
||||
--- Tests whether `color` is contained within `colors`.
|
||||
--
|
||||
-- @tparam number colors A color, or color set
|
||||
-- @tparam number color A color or set of colors that `colors` should contain.
|
||||
-- @treturn boolean If `colors` contains all colors within `color`.
|
||||
-- @usage
|
||||
-- ```lua
|
||||
-- colors.test(colors.combine(colors.white, colors.magenta, colours.lightBlue), colors.lightBlue)
|
||||
-- -- => true
|
||||
-- ```
|
||||
function test(colors, color)
|
||||
expect(1, colors, "number")
|
||||
expect(2, color, "number")
|
||||
return bit32.band(colors, color) == color
|
||||
end
|
||||
|
||||
function packRGB( r, g, b )
|
||||
--- Combine a three-colour RGB value into one hexadecimal representation.
|
||||
--
|
||||
-- @tparam number r The red channel, should be between 0 and 1.
|
||||
-- @tparam number g The red channel, should be between 0 and 1.
|
||||
-- @tparam number b The blue channel, should be between 0 and 1.
|
||||
-- @treturn number The combined hexadecimal colour.
|
||||
-- @usage
|
||||
-- ```lua
|
||||
-- colors.rgb(0.7, 0.2, 0.6)
|
||||
-- -- => 0xb23399
|
||||
-- ```
|
||||
function packRGB(r, g, b)
|
||||
expect(1, r, "number")
|
||||
expect(2, g, "number")
|
||||
expect(3, b, "number")
|
||||
return
|
||||
bit32.band( r * 255, 0xFF ) * 2 ^ 16 +
|
||||
bit32.band( g * 255, 0xFF ) * 2 ^ 8 +
|
||||
bit32.band( b * 255, 0xFF )
|
||||
bit32.band(r * 255, 0xFF) * 2 ^ 16 +
|
||||
bit32.band(g * 255, 0xFF) * 2 ^ 8 +
|
||||
bit32.band(b * 255, 0xFF)
|
||||
end
|
||||
|
||||
function unpackRGB( rgb )
|
||||
--- Separate a hexadecimal RGB colour into its three constituent channels.
|
||||
--
|
||||
-- @tparam number rgb The combined hexadecimal colour.
|
||||
-- @treturn number The red channel, will be between 0 and 1.
|
||||
-- @treturn number The red channel, will be between 0 and 1.
|
||||
-- @treturn number The blue channel, will be between 0 and 1.
|
||||
-- @usage
|
||||
-- ```lua
|
||||
-- colors.rgb(0xb23399)
|
||||
-- -- => 0.7, 0.2, 0.6
|
||||
-- ```
|
||||
-- @see colors.packRGB
|
||||
function unpackRGB(rgb)
|
||||
expect(1, rgb, "number")
|
||||
return
|
||||
bit32.band( bit32.rshift( rgb, 16 ), 0xFF ) / 255,
|
||||
bit32.band( bit32.rshift( rgb, 8 ), 0xFF ) / 255,
|
||||
bit32.band( rgb, 0xFF ) / 255
|
||||
bit32.band(bit32.rshift(rgb, 16), 0xFF) / 255,
|
||||
bit32.band(bit32.rshift(rgb, 8), 0xFF) / 255,
|
||||
bit32.band(rgb, 0xFF) / 255
|
||||
end
|
||||
|
||||
function rgb8( r, g, b )
|
||||
--- Either calls @{colors.packRGB} or @{colors.unpackRGB}, depending on how many
|
||||
-- arguments it receives.
|
||||
--
|
||||
-- **Note:** This function is deprecated, and it is recommended you use the
|
||||
-- specific pack/unpack function directly.
|
||||
--
|
||||
-- @tparam[1] number r The red channel, as an argument to @{colors.packRGB}.
|
||||
-- @tparam[1] number g The green channel, as an argument to @{colors.packRGB}.
|
||||
-- @tparam[1] number b The blue channel, as an argument to @{colors.packRGB}.
|
||||
-- @tparam[2] number rgb The combined hexadecimal color, as an argument to @{colors.unpackRGB}.
|
||||
-- @treturn[1] number The combined hexadecimal colour, as returned by @{colors.packRGB}.
|
||||
-- @treturn[2] number The red channel, as returned by @{colors.unpackRGB}
|
||||
-- @treturn[2] number The green channel, as returned by @{colors.unpackRGB}
|
||||
-- @treturn[2] number The blue channel, as returned by @{colors.unpackRGB}
|
||||
-- @usage
|
||||
-- ```lua
|
||||
-- colors.rgb(0xb23399)
|
||||
-- -- => 0.7, 0.2, 0.6
|
||||
-- ```
|
||||
-- @usage
|
||||
-- ```lua
|
||||
-- colors.rgb(0.7, 0.2, 0.6)
|
||||
-- -- => 0xb23399
|
||||
-- ```
|
||||
function rgb8(r, g, b)
|
||||
if g == nil and b == nil then
|
||||
return unpackRGB( r )
|
||||
return unpackRGB(r)
|
||||
else
|
||||
return packRGB( r, g, b )
|
||||
return packRGB(r, g, b)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,11 +1,23 @@
|
||||
-- Colours (for lovers of british spelling)
|
||||
--- Colours for lovers of British spelling.
|
||||
--
|
||||
-- @see colors
|
||||
-- @module colours
|
||||
|
||||
local colours = _ENV
|
||||
for k, v in pairs(colors) do
|
||||
colours[k] = v
|
||||
end
|
||||
|
||||
--- Grey. Written as `7` in paint files and @{term.blit}, has a default
|
||||
-- terminal colour of #4C4C4C.
|
||||
--
|
||||
-- @see colors.gray
|
||||
colours.grey = colors.gray
|
||||
colours.gray = nil
|
||||
colours.gray = nil --- @local
|
||||
|
||||
--- Light grey. Written as `8` in paint files and @{term.blit}, has a
|
||||
-- default terminal colour of #999999.
|
||||
--
|
||||
-- @see colors.lightGray
|
||||
colours.lightGrey = colors.lightGray
|
||||
colours.lightGray = nil
|
||||
colours.lightGray = nil --- @local
|
||||
|
||||
@@ -1,8 +1,26 @@
|
||||
--- The commands API allows your system to directly execute [Minecraft
|
||||
-- commands][mc] and gather data from the results.
|
||||
--
|
||||
-- While one may use @{commands.exec} directly to execute a command, the
|
||||
-- commands API also provides helper methods to execute every command. For
|
||||
-- instance, `commands.say("Hi!")` is equivalent to `commands.exec("say Hi!")`.
|
||||
--
|
||||
-- @{commands.async} provides a similar interface to execute asynchronous
|
||||
-- commands. `commands.async.say("Hi!")` is equivalent to
|
||||
-- `commands.execAsync("Hi!")`.
|
||||
--
|
||||
-- [mc]: https://minecraft.gamepedia.com/Commands
|
||||
--
|
||||
-- @module commands
|
||||
|
||||
if not commands then
|
||||
error( "Cannot load command API on normal computer", 2 )
|
||||
end
|
||||
|
||||
--- The builtin commands API, without any generated command helper functions
|
||||
--
|
||||
-- This may be useful if a built-in function (such as @{commands.list}) has been
|
||||
-- overwritten by a command.
|
||||
local native = commands.native or commands
|
||||
|
||||
local function collapseArgs( bJSONIsNBT, ... )
|
||||
|
||||
@@ -1,87 +1,171 @@
|
||||
--- The Disk API allows you to interact with disk drives.
|
||||
--
|
||||
-- These functions can operate on locally attached or remote disk drives. To use
|
||||
-- a locally attached drive, specify “side” as one of the six sides
|
||||
-- (e.g. `left`); to use a remote disk drive, specify its name as printed when
|
||||
-- enabling its modem (e.g. `drive_0`).
|
||||
--
|
||||
-- **Note:** All computers (except command computers), turtles and pocket
|
||||
-- computers can be placed within a disk drive to access it's internal storage
|
||||
-- like a disk.
|
||||
--
|
||||
-- @module disk
|
||||
|
||||
local function isDrive( name )
|
||||
if type( name ) ~= "string" then
|
||||
error( "bad argument #1 (expected string, got " .. type( name ) .. ")", 3 )
|
||||
local function isDrive(name)
|
||||
if type(name) ~= "string" then
|
||||
error("bad argument #1 (expected string, got " .. type(name) .. ")", 3)
|
||||
end
|
||||
return peripheral.getType( name ) == "drive"
|
||||
return peripheral.getType(name) == "drive"
|
||||
end
|
||||
|
||||
function isPresent( name )
|
||||
if isDrive( name ) then
|
||||
return peripheral.call( name, "isDiskPresent" )
|
||||
--- Checks whether any item at all is in the disk drive
|
||||
--
|
||||
-- @tparam string name The name of the disk drive.
|
||||
-- @treturn boolean If something is in the disk drive.
|
||||
-- @usage disk.isPresent(false)
|
||||
function isPresent(name)
|
||||
if isDrive(name) then
|
||||
return peripheral.call(name, "isDiskPresent")
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function getLabel( name )
|
||||
if isDrive( name ) then
|
||||
return peripheral.call( name, "getDiskLabel" )
|
||||
--- Get the label of the floppy disk, record, or other media within the given
|
||||
-- disk drive.
|
||||
--
|
||||
-- If there is a computer or turtle within the drive, this will set the label as
|
||||
-- read by `os.getComputerLabel`.
|
||||
--
|
||||
-- @tparam string name The name of the disk drive.
|
||||
-- @treturn string|nil The name of the current media, or `nil` if the drive is
|
||||
-- not present or empty.
|
||||
-- @see disk.setLabel
|
||||
function getLabel(name)
|
||||
if isDrive(name) then
|
||||
return peripheral.call(name, "getDiskLabel")
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function setLabel( name, label )
|
||||
if isDrive( name ) then
|
||||
peripheral.call( name, "setDiskLabel", label )
|
||||
--- Set the label of the floppy disk or other media
|
||||
--
|
||||
-- @tparam string name The name of the disk drive.
|
||||
-- @tparam string|nil label The new label of the disk
|
||||
function setLabel(name, label)
|
||||
if isDrive(name) then
|
||||
peripheral.call(name, "setDiskLabel", label)
|
||||
end
|
||||
end
|
||||
|
||||
function hasData( name )
|
||||
if isDrive( name ) then
|
||||
return peripheral.call( name, "hasData" )
|
||||
--- Check whether the current disk provides a mount.
|
||||
--
|
||||
-- This will return true for disks and computers, but not records.
|
||||
--
|
||||
-- @tparam string name The name of the disk drive.
|
||||
-- @treturn boolean If the disk is present and provides a mount.
|
||||
-- @see disk.getMountPath
|
||||
function hasData(name)
|
||||
if isDrive(name) then
|
||||
return peripheral.call(name, "hasData")
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function getMountPath( name )
|
||||
if isDrive( name ) then
|
||||
return peripheral.call( name, "getMountPath" )
|
||||
--- Find the directory name on the local computer where the contents of the
|
||||
-- current floppy disk (or other mount) can be found.
|
||||
--
|
||||
-- @tparam string name The name of the disk drive.
|
||||
-- @treturn string|nil The mount's directory, or `nil` if the drive does not
|
||||
-- contain a floppy or computer.
|
||||
-- @see disk.hasData
|
||||
function getMountPath(name)
|
||||
if isDrive(name) then
|
||||
return peripheral.call(name, "getMountPath")
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function hasAudio( name )
|
||||
if isDrive( name ) then
|
||||
return peripheral.call( name, "hasAudio" )
|
||||
--- Whether the current disk is a [music disk][disk] as opposed to a floppy disk
|
||||
-- or other item.
|
||||
--
|
||||
-- If this returns true, you will can @{disk.playAudio|play} the record.
|
||||
--
|
||||
-- [disk]: https://minecraft.gamepedia.com/Music_Disc
|
||||
--
|
||||
-- @tparam string name The name of the disk drive.
|
||||
-- @treturn boolean If the disk is present and has audio saved on it.
|
||||
function hasAudio(name)
|
||||
if isDrive(name) then
|
||||
return peripheral.call(name, "hasAudio")
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function getAudioTitle( name )
|
||||
if isDrive( name ) then
|
||||
return peripheral.call( name, "getAudioTitle" )
|
||||
--- Get the title of the audio track from the music record in the drive.
|
||||
--
|
||||
-- This generally returns the same as @{disk.getLabel} for records.
|
||||
--
|
||||
-- @tparam string name The name of the disk drive.
|
||||
-- @treturn string|false|nil The track title, `false` if there is not a music
|
||||
-- record in the drive or `nil` if no drive is present.
|
||||
function getAudioTitle(name)
|
||||
if isDrive(name) then
|
||||
return peripheral.call(name, "getAudioTitle")
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function playAudio( name )
|
||||
if isDrive( name ) then
|
||||
peripheral.call( name, "playAudio" )
|
||||
--- Starts playing the music record in the drive.
|
||||
--
|
||||
-- If any record is already playing on any disk drive, it stops before the
|
||||
-- target drive starts playing. The record stops when it reaches the end of the
|
||||
-- track, when it is removed from the drive, when @{disk.stopAudio} is called, or
|
||||
-- when another record is started.
|
||||
--
|
||||
-- @tparam string name The name of the disk drive.
|
||||
-- @usage disk.playAudio("bottom")
|
||||
function playAudio(name)
|
||||
if isDrive(name) then
|
||||
peripheral.call(name, "playAudio")
|
||||
end
|
||||
end
|
||||
|
||||
function stopAudio( name )
|
||||
--- Stops the music record in the drive from playing, if it was started with
|
||||
-- @{disk.playAudio}.
|
||||
--
|
||||
-- @tparam string name The name o the disk drive.
|
||||
function stopAudio(name)
|
||||
if not name then
|
||||
for _, sName in ipairs( peripheral.getNames() ) do
|
||||
stopAudio( sName )
|
||||
for _, sName in ipairs(peripheral.getNames()) do
|
||||
stopAudio(sName)
|
||||
end
|
||||
else
|
||||
if isDrive( name ) then
|
||||
peripheral.call( name, "stopAudio" )
|
||||
if isDrive(name) then
|
||||
peripheral.call(name, "stopAudio")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function eject( name )
|
||||
if isDrive( name ) then
|
||||
peripheral.call( name, "ejectDisk" )
|
||||
--- Ejects any item currently in the drive, spilling it into the world as a loose item.
|
||||
--
|
||||
-- @tparam string name The name of the disk drive.
|
||||
-- @usage disk.eject("bottom")
|
||||
function eject(name)
|
||||
if isDrive(name) then
|
||||
peripheral.call(name, "ejectDisk")
|
||||
end
|
||||
end
|
||||
|
||||
function getID( name )
|
||||
if isDrive( name ) then
|
||||
return peripheral.call( name, "getDiskID" )
|
||||
--- Returns a number which uniquely identifies the disk in the drive.
|
||||
--
|
||||
-- Note, unlike @{disk.getLabel}, this does not return anything for other media,
|
||||
-- such as computers or turtles.
|
||||
--
|
||||
-- @tparam string name The name of the disk drive.
|
||||
-- @treturn string|nil The disk ID, or `nil` if the drive does not contain a floppy disk.
|
||||
function getID(name)
|
||||
if isDrive(name) then
|
||||
return peripheral.call(name, "getDiskID")
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
@@ -1,21 +1,46 @@
|
||||
--- The GPS API provides a method for turtles and computers to retrieve their
|
||||
-- own locations.
|
||||
--
|
||||
-- It broadcasts a PING message over @{rednet} and wait for responses. In order
|
||||
-- for this system to work, there must be at least 4 computers used as gps hosts
|
||||
-- which will respond and allow trilateration. Three of these hosts should be in
|
||||
-- a plane, and the fourth should be either above or below the other three. The
|
||||
-- three in a plane should not be in a line with each other. You can set up
|
||||
-- hosts using the gps program.
|
||||
--
|
||||
-- **Note**: When entering in the coordinates for the host you need to put in
|
||||
-- the `x`, `y`, and `z` coordinates of the computer, not the modem, as all
|
||||
-- rednet distances are measured from the block the computer is in.
|
||||
--
|
||||
-- Also note that you may choose which axes x, y, or z refers to - so long as
|
||||
-- your systems have the same definition as any GPS servers that're in range, it
|
||||
-- works just the same. For example, you might build a GPS cluster according to
|
||||
-- [this tutorial][1], using z to account for height, or you might use y to
|
||||
-- account for height in the way that Minecraft's debug screen displays.
|
||||
--
|
||||
-- [1]: http://www.computercraft.info/forums2/index.php?/topic/3088-how-to-guide-gps-global-position-system/
|
||||
--
|
||||
-- @module gps
|
||||
|
||||
local expect = dofile("rom/modules/main/cc/expect.lua").expect
|
||||
|
||||
--- The channel which GPS requests and responses are broadcast on.
|
||||
CHANNEL_GPS = 65534
|
||||
|
||||
local function trilaterate( A, B, C )
|
||||
local function trilaterate(A, B, C)
|
||||
local a2b = B.vPosition - A.vPosition
|
||||
local a2c = C.vPosition - A.vPosition
|
||||
|
||||
if math.abs( a2b:normalize():dot( a2c:normalize() ) ) > 0.999 then
|
||||
if math.abs(a2b:normalize():dot(a2c:normalize())) > 0.999 then
|
||||
return nil
|
||||
end
|
||||
|
||||
local d = a2b:length()
|
||||
local ex = a2b:normalize( )
|
||||
local i = ex:dot( a2c )
|
||||
local i = ex:dot(a2c)
|
||||
local ey = (a2c - ex * i):normalize()
|
||||
local j = ey:dot( a2c )
|
||||
local ez = ex:cross( ey )
|
||||
local j = ey:dot(a2c)
|
||||
local ez = ex:cross(ey)
|
||||
|
||||
local r1 = A.nDistance
|
||||
local r2 = B.nDistance
|
||||
@@ -28,35 +53,44 @@ local function trilaterate( A, B, C )
|
||||
|
||||
local zSquared = r1 * r1 - x * x - y * y
|
||||
if zSquared > 0 then
|
||||
local z = math.sqrt( zSquared )
|
||||
local z = math.sqrt(zSquared)
|
||||
local result1 = result + ez * z
|
||||
local result2 = result - ez * z
|
||||
|
||||
local rounded1, rounded2 = result1:round( 0.01 ), result2:round( 0.01 )
|
||||
local rounded1, rounded2 = result1:round(0.01), result2:round(0.01)
|
||||
if rounded1.x ~= rounded2.x or rounded1.y ~= rounded2.y or rounded1.z ~= rounded2.z then
|
||||
return rounded1, rounded2
|
||||
else
|
||||
return rounded1
|
||||
end
|
||||
end
|
||||
return result:round( 0.01 )
|
||||
return result:round(0.01)
|
||||
|
||||
end
|
||||
|
||||
local function narrow( p1, p2, fix )
|
||||
local dist1 = math.abs( (p1 - fix.vPosition):length() - fix.nDistance )
|
||||
local dist2 = math.abs( (p2 - fix.vPosition):length() - fix.nDistance )
|
||||
local function narrow(p1, p2, fix)
|
||||
local dist1 = math.abs((p1 - fix.vPosition):length() - fix.nDistance)
|
||||
local dist2 = math.abs((p2 - fix.vPosition):length() - fix.nDistance)
|
||||
|
||||
if math.abs(dist1 - dist2) < 0.01 then
|
||||
return p1, p2
|
||||
elseif dist1 < dist2 then
|
||||
return p1:round( 0.01 )
|
||||
return p1:round(0.01)
|
||||
else
|
||||
return p2:round( 0.01 )
|
||||
return p2:round(0.01)
|
||||
end
|
||||
end
|
||||
|
||||
function locate( _nTimeout, _bDebug )
|
||||
--- Tries to retrieve the computer or turtles own location.
|
||||
--
|
||||
-- @tparam[opt] number timeout The maximum time taken to establish our
|
||||
-- position. Defaults to 2 seconds if not specified.
|
||||
-- @tparam[opt] boolean debug Print debugging messages
|
||||
-- @treturn[1] number This computer's `x` position.
|
||||
-- @treturn[1] number This computer's `y` position.
|
||||
-- @treturn[1] number This computer's `z` position.
|
||||
-- @treturn[2] nil If the position could not be established.
|
||||
function locate(_nTimeout, _bDebug)
|
||||
expect(1, _nTimeout, "number", "nil")
|
||||
expect(2, _bDebug, "boolean", "nil")
|
||||
-- Let command computers use their magic fourth-wall-breaking special abilities
|
||||
@@ -66,8 +100,8 @@ function locate( _nTimeout, _bDebug )
|
||||
|
||||
-- Find a modem
|
||||
local sModemSide = nil
|
||||
for _, sSide in ipairs( rs.getSides() ) do
|
||||
if peripheral.getType( sSide ) == "modem" and peripheral.call( sSide, "isWireless" ) then
|
||||
for _, sSide in ipairs(rs.getSides()) do
|
||||
if peripheral.getType(sSide) == "modem" and peripheral.call(sSide, "isWireless") then
|
||||
sModemSide = sSide
|
||||
break
|
||||
end
|
||||
@@ -75,30 +109,30 @@ function locate( _nTimeout, _bDebug )
|
||||
|
||||
if sModemSide == nil then
|
||||
if _bDebug then
|
||||
print( "No wireless modem attached" )
|
||||
print("No wireless modem attached")
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
if _bDebug then
|
||||
print( "Finding position..." )
|
||||
print("Finding position...")
|
||||
end
|
||||
|
||||
-- Open GPS channel to listen for ping responses
|
||||
local modem = peripheral.wrap( sModemSide )
|
||||
local modem = peripheral.wrap(sModemSide)
|
||||
local bCloseChannel = false
|
||||
if not modem.isOpen( CHANNEL_GPS ) then
|
||||
modem.open( CHANNEL_GPS )
|
||||
if not modem.isOpen(CHANNEL_GPS) then
|
||||
modem.open(CHANNEL_GPS)
|
||||
bCloseChannel = true
|
||||
end
|
||||
|
||||
-- Send a ping to listening GPS hosts
|
||||
modem.transmit( CHANNEL_GPS, CHANNEL_GPS, "PING" )
|
||||
modem.transmit(CHANNEL_GPS, CHANNEL_GPS, "PING")
|
||||
|
||||
-- Wait for the responses
|
||||
local tFixes = {}
|
||||
local pos1, pos2 = nil, nil
|
||||
local timeout = os.startTimer( _nTimeout or 2 )
|
||||
local timeout = os.startTimer(_nTimeout or 2)
|
||||
while true do
|
||||
local e, p1, p2, p3, p4, p5 = os.pullEvent()
|
||||
if e == "modem_message" then
|
||||
@@ -107,19 +141,19 @@ function locate( _nTimeout, _bDebug )
|
||||
if sSide == sModemSide and sChannel == CHANNEL_GPS and sReplyChannel == CHANNEL_GPS and nDistance then
|
||||
-- Received the correct message from the correct modem: use it to determine position
|
||||
if type(tMessage) == "table" and #tMessage == 3 and tonumber(tMessage[1]) and tonumber(tMessage[2]) and tonumber(tMessage[3]) then
|
||||
local tFix = { vPosition = vector.new( tMessage[1], tMessage[2], tMessage[3] ), nDistance = nDistance }
|
||||
local tFix = { vPosition = vector.new(tMessage[1], tMessage[2], tMessage[3]), nDistance = nDistance }
|
||||
if _bDebug then
|
||||
print( tFix.nDistance .. " metres from " .. tostring( tFix.vPosition ) )
|
||||
print(tFix.nDistance .. " metres from " .. tostring(tFix.vPosition))
|
||||
end
|
||||
if tFix.nDistance == 0 then
|
||||
pos1, pos2 = tFix.vPosition, nil
|
||||
else
|
||||
table.insert( tFixes, tFix )
|
||||
table.insert(tFixes, tFix)
|
||||
if #tFixes >= 3 then
|
||||
if not pos1 then
|
||||
pos1, pos2 = trilaterate( tFixes[1], tFixes[2], tFixes[#tFixes] )
|
||||
pos1, pos2 = trilaterate(tFixes[1], tFixes[2], tFixes[#tFixes])
|
||||
else
|
||||
pos1, pos2 = narrow( pos1, pos2, tFixes[#tFixes] )
|
||||
pos1, pos2 = narrow(pos1, pos2, tFixes[#tFixes])
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -141,24 +175,24 @@ function locate( _nTimeout, _bDebug )
|
||||
|
||||
-- Close the channel, if we opened one
|
||||
if bCloseChannel then
|
||||
modem.close( CHANNEL_GPS )
|
||||
modem.close(CHANNEL_GPS)
|
||||
end
|
||||
|
||||
-- Return the response
|
||||
if pos1 and pos2 then
|
||||
if _bDebug then
|
||||
print( "Ambiguous position" )
|
||||
print( "Could be " .. pos1.x .. "," .. pos1.y .. "," .. pos1.z .. " or " .. pos2.x .. "," .. pos2.y .. "," .. pos2.z )
|
||||
print("Ambiguous position")
|
||||
print("Could be " .. pos1.x .. "," .. pos1.y .. "," .. pos1.z .. " or " .. pos2.x .. "," .. pos2.y .. "," .. pos2.z)
|
||||
end
|
||||
return nil
|
||||
elseif pos1 then
|
||||
if _bDebug then
|
||||
print( "Position is " .. pos1.x .. "," .. pos1.y .. "," .. pos1.z )
|
||||
print("Position is " .. pos1.x .. "," .. pos1.y .. "," .. pos1.z)
|
||||
end
|
||||
return pos1.x, pos1.y, pos1.z
|
||||
else
|
||||
if _bDebug then
|
||||
print( "Could not determine position" )
|
||||
print("Could not determine position")
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -1,24 +1,46 @@
|
||||
--- Provides an API to read help files.
|
||||
--
|
||||
-- @module help
|
||||
|
||||
local expect = dofile("rom/modules/main/cc/expect.lua").expect
|
||||
|
||||
local sPath = "/rom/help"
|
||||
|
||||
--- Returns a colon-separated list of directories where help files are searched
|
||||
-- for. All directories are absolute.
|
||||
--
|
||||
-- @treturn string The current help search path, separated by colons.
|
||||
-- @see help.setPath
|
||||
function path()
|
||||
return sPath
|
||||
end
|
||||
|
||||
function setPath( _sPath )
|
||||
--- Sets the colon-seperated list of directories where help files are searched
|
||||
-- for to `newPath`
|
||||
--
|
||||
-- @tparam string newPath The new path to use.
|
||||
-- @usage help.setPath( "/disk/help/" )
|
||||
-- @usage help.setPath( help.path() .. ":/myfolder/help/" )
|
||||
-- @see help.path
|
||||
function setPath(_sPath)
|
||||
expect(1, _sPath, "string")
|
||||
sPath = _sPath
|
||||
end
|
||||
|
||||
function lookup( _sTopic )
|
||||
--- Returns the location of the help file for the given topic.
|
||||
--
|
||||
-- @tparam string topic The topic to find
|
||||
-- @treturn string|nil The path to the given topic's help file, or `nil` if it
|
||||
-- cannot be found.
|
||||
-- @usage print(help.lookup("disk"))
|
||||
function lookup(_sTopic)
|
||||
expect(1, _sTopic, "string")
|
||||
-- Look on the path variable
|
||||
for sPath in string.gmatch(sPath, "[^:]+") do
|
||||
sPath = fs.combine( sPath, _sTopic )
|
||||
if fs.exists( sPath ) and not fs.isDir( sPath ) then
|
||||
sPath = fs.combine(sPath, _sTopic)
|
||||
if fs.exists(sPath) and not fs.isDir(sPath) then
|
||||
return sPath
|
||||
elseif fs.exists( sPath .. ".txt" ) and not fs.isDir( sPath .. ".txt" ) then
|
||||
elseif fs.exists(sPath .. ".txt") and not fs.isDir(sPath .. ".txt") then
|
||||
return sPath .. ".txt"
|
||||
end
|
||||
end
|
||||
@@ -27,23 +49,26 @@ function lookup( _sTopic )
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns a list of topics that can be looked up and/or displayed.
|
||||
--
|
||||
-- @treturn table A list of topics in alphabetical order.
|
||||
function topics()
|
||||
-- Add index
|
||||
local tItems = {
|
||||
[ "index" ] = true,
|
||||
["index"] = true,
|
||||
}
|
||||
|
||||
-- Add topics from the path
|
||||
for sPath in string.gmatch(sPath, "[^:]+") do
|
||||
if fs.isDir( sPath ) then
|
||||
local tList = fs.list( sPath )
|
||||
for _, sFile in pairs( tList ) do
|
||||
if string.sub( sFile, 1, 1 ) ~= "." then
|
||||
if not fs.isDir( fs.combine( sPath, sFile ) ) then
|
||||
if fs.isDir(sPath) then
|
||||
local tList = fs.list(sPath)
|
||||
for _, sFile in pairs(tList) do
|
||||
if string.sub(sFile, 1, 1) ~= "." then
|
||||
if not fs.isDir(fs.combine(sPath, sFile)) then
|
||||
if #sFile > 4 and sFile:sub(-4) == ".txt" then
|
||||
sFile = sFile:sub(1, -5)
|
||||
end
|
||||
tItems[ sFile ] = true
|
||||
tItems[sFile] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -52,21 +77,26 @@ function topics()
|
||||
|
||||
-- Sort and return
|
||||
local tItemList = {}
|
||||
for sItem in pairs( tItems ) do
|
||||
table.insert( tItemList, sItem )
|
||||
for sItem in pairs(tItems) do
|
||||
table.insert(tItemList, sItem)
|
||||
end
|
||||
table.sort( tItemList )
|
||||
table.sort(tItemList)
|
||||
return tItemList
|
||||
end
|
||||
|
||||
function completeTopic( sText )
|
||||
--- Returns a list of topic endings that match the prefix. Can be used with
|
||||
-- `read` to allow input of a help topic.
|
||||
--
|
||||
-- @tparam string prefix The prefix to match
|
||||
-- @treturn table A list of matching topics.
|
||||
function completeTopic(sText)
|
||||
expect(1, sText, "string")
|
||||
local tTopics = topics()
|
||||
local tResults = {}
|
||||
for n = 1, #tTopics do
|
||||
local sTopic = tTopics[n]
|
||||
if #sTopic > #sText and string.sub( sTopic, 1, #sText ) == sText then
|
||||
table.insert( tResults, string.sub( sTopic, #sText + 1 ) )
|
||||
if #sTopic > #sText and string.sub(sTopic, 1, #sText) == sText then
|
||||
table.insert(tResults, string.sub(sTopic, #sText + 1))
|
||||
end
|
||||
end
|
||||
return tResults
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
-- Definition for the IO API
|
||||
--- Emulates Lua's standard [io library][io].
|
||||
--
|
||||
-- [io]: https://www.lua.org/manual/5.1/manual.html#5.7
|
||||
--
|
||||
-- @module io
|
||||
|
||||
local expect, typeOf = dofile("rom/modules/main/cc/expect.lua").expect, _G.type
|
||||
local expect, type_of = dofile("rom/modules/main/cc/expect.lua").expect, _G.type
|
||||
|
||||
--- If we return nil then close the file, as we've reached the end.
|
||||
-- We use this weird wrapper function as we wish to preserve the varargs
|
||||
@@ -9,6 +13,9 @@ local function checkResult(handle, ...)
|
||||
return ...
|
||||
end
|
||||
|
||||
--- A file handle which can be read or written to.
|
||||
--
|
||||
-- @type Handle
|
||||
local handleMetatable
|
||||
handleMetatable = {
|
||||
__name = "FILE*",
|
||||
@@ -20,10 +27,17 @@ handleMetatable = {
|
||||
return "file (" .. hash .. ")"
|
||||
end
|
||||
end,
|
||||
|
||||
__index = {
|
||||
--- Close this file handle, freeing any resources it uses.
|
||||
--
|
||||
-- @treturn[1] true If this handle was successfully closed.
|
||||
-- @treturn[2] nil If this file handle could not be closed.
|
||||
-- @treturn[2] string The reason it could not be closed.
|
||||
-- @throws If this handle was already closed.
|
||||
close = function(self)
|
||||
if typeOf(self) ~= "table" or getmetatable(self) ~= handleMetatable then
|
||||
error("bad argument #1 (FILE expected, got " .. typeOf(self) .. ")", 2)
|
||||
if type_of(self) ~= "table" or getmetatable(self) ~= handleMetatable then
|
||||
error("bad argument #1 (FILE expected, got " .. type_of(self) .. ")", 2)
|
||||
end
|
||||
if self._closed then error("attempt to use a closed file", 2) end
|
||||
|
||||
@@ -36,18 +50,24 @@ handleMetatable = {
|
||||
return nil, "attempt to close standard stream"
|
||||
end
|
||||
end,
|
||||
|
||||
--- Flush any buffered output, forcing it to be written to the file
|
||||
--
|
||||
-- @throws If the handle has been closed
|
||||
flush = function(self)
|
||||
if typeOf(self) ~= "table" or getmetatable(self) ~= handleMetatable then
|
||||
error("bad argument #1 (FILE expected, got " .. typeOf(self) .. ")", 2)
|
||||
if type_of(self) ~= "table" or getmetatable(self) ~= handleMetatable then
|
||||
error("bad argument #1 (FILE expected, got " .. type_of(self) .. ")", 2)
|
||||
end
|
||||
if self._closed then error("attempt to use a closed file", 2) end
|
||||
|
||||
local handle = self._handle
|
||||
if handle.flush then handle.flush() end
|
||||
return true
|
||||
end,
|
||||
|
||||
lines = function(self, ...)
|
||||
if typeOf(self) ~= "table" or getmetatable(self) ~= handleMetatable then
|
||||
error("bad argument #1 (FILE expected, got " .. typeOf(self) .. ")", 2)
|
||||
if type_of(self) ~= "table" or getmetatable(self) ~= handleMetatable then
|
||||
error("bad argument #1 (FILE expected, got " .. type_of(self) .. ")", 2)
|
||||
end
|
||||
if self._closed then error("attempt to use a closed file", 2) end
|
||||
|
||||
@@ -57,9 +77,10 @@ handleMetatable = {
|
||||
local args = table.pack(...)
|
||||
return function() return checkResult(self, self:read(table.unpack(args, 1, args.n))) end
|
||||
end,
|
||||
|
||||
read = function(self, ...)
|
||||
if typeOf(self) ~= "table" or getmetatable(self) ~= handleMetatable then
|
||||
error("bad argument #1 (FILE expected, got " .. typeOf(self) .. ")", 2)
|
||||
if type_of(self) ~= "table" or getmetatable(self) ~= handleMetatable then
|
||||
error("bad argument #1 (FILE expected, got " .. type_of(self) .. ")", 2)
|
||||
end
|
||||
if self._closed then error("attempt to use a closed file", 2) end
|
||||
|
||||
@@ -71,9 +92,9 @@ handleMetatable = {
|
||||
for i = 1, n do
|
||||
local arg = select(i, ...)
|
||||
local res
|
||||
if typeOf(arg) == "number" then
|
||||
if type_of(arg) == "number" then
|
||||
if handle.read then res = handle.read(arg) end
|
||||
elseif typeOf(arg) == "string" then
|
||||
elseif type_of(arg) == "string" then
|
||||
local format = arg:gsub("^%*", ""):sub(1, 1)
|
||||
|
||||
if format == "l" then
|
||||
@@ -88,7 +109,7 @@ handleMetatable = {
|
||||
error("bad argument #" .. i .. " (invalid format)", 2)
|
||||
end
|
||||
else
|
||||
error("bad argument #" .. i .. " (expected string, got " .. typeOf(arg) .. ")", 2)
|
||||
error("bad argument #" .. i .. " (expected string, got " .. type_of(arg) .. ")", 2)
|
||||
end
|
||||
|
||||
output[i] = res
|
||||
@@ -99,9 +120,10 @@ handleMetatable = {
|
||||
if n == 0 and handle.readLine then return handle.readLine() end
|
||||
return table.unpack(output, 1, n)
|
||||
end,
|
||||
|
||||
seek = function(self, whence, offset)
|
||||
if typeOf(self) ~= "table" or getmetatable(self) ~= handleMetatable then
|
||||
error("bad argument #1 (FILE expected, got " .. typeOf(self) .. ")", 2)
|
||||
if type_of(self) ~= "table" or getmetatable(self) ~= handleMetatable then
|
||||
error("bad argument #1 (FILE expected, got " .. type_of(self) .. ")", 2)
|
||||
end
|
||||
if self._closed then error("attempt to use a closed file", 2) end
|
||||
|
||||
@@ -111,10 +133,18 @@ handleMetatable = {
|
||||
-- It's a tail call, so error positions are preserved
|
||||
return handle.seek(whence, offset)
|
||||
end,
|
||||
|
||||
setvbuf = function(self, mode, size) end,
|
||||
|
||||
--- Write one or more values to the file
|
||||
--
|
||||
-- @tparam string|number ... The values to write.
|
||||
-- @treturn[1] Handle The current file, allowing chained calls.
|
||||
-- @treturn[2] nil If the file could not be written to.
|
||||
-- @treturn[2] string The error message which occurred while writing.
|
||||
write = function(self, ...)
|
||||
if typeOf(self) ~= "table" or getmetatable(self) ~= handleMetatable then
|
||||
error("bad argument #1 (FILE expected, got " .. typeOf(self) .. ")", 2)
|
||||
if type_of(self) ~= "table" or getmetatable(self) ~= handleMetatable then
|
||||
error("bad argument #1 (FILE expected, got " .. type_of(self) .. ")", 2)
|
||||
end
|
||||
if self._closed then error("attempt to use a closed file", 2) end
|
||||
|
||||
@@ -156,41 +186,88 @@ local defaultError = setmetatable({
|
||||
local currentInput = defaultInput
|
||||
local currentOutput = defaultOutput
|
||||
|
||||
--- A file handle representing the "standard input". Reading from this
|
||||
-- file will prompt the user for input.
|
||||
stdin = defaultInput
|
||||
|
||||
--- A file handle representing the "standard output". Writing to this
|
||||
-- file will display the written text to the screen.
|
||||
stdout = defaultOutput
|
||||
|
||||
--- A file handle representing the "standard error" stream.
|
||||
--
|
||||
-- One may use this to display error messages, writing to it will display
|
||||
-- them on the terminal.
|
||||
stderr = defaultError
|
||||
|
||||
function close(_file)
|
||||
if _file == nil then return currentOutput:close() end
|
||||
--- Closes the provided file handle.
|
||||
--
|
||||
-- @tparam[opt] Handle file The file handle to close, defaults to the
|
||||
-- current output file.
|
||||
--
|
||||
-- @see Handle:close
|
||||
-- @see io.output
|
||||
function close(file)
|
||||
if file == nil then return currentOutput:close() end
|
||||
|
||||
if typeOf(_file) ~= "table" or getmetatable(_file) ~= handleMetatable then
|
||||
error("bad argument #1 (FILE expected, got " .. typeOf(_file) .. ")", 2)
|
||||
if type_of(file) ~= "table" or getmetatable(file) ~= handleMetatable then
|
||||
error("bad argument #1 (FILE expected, got " .. type_of(file) .. ")", 2)
|
||||
end
|
||||
return _file:close()
|
||||
return file:close()
|
||||
end
|
||||
|
||||
--- Flushes the current output file.
|
||||
--
|
||||
-- @see Handle:flush
|
||||
-- @see io.output
|
||||
function flush()
|
||||
return currentOutput:flush()
|
||||
end
|
||||
|
||||
function input(_arg)
|
||||
if typeOf(_arg) == "string" then
|
||||
local res, err = open(_arg, "rb")
|
||||
--- Get or set the current input file.
|
||||
--
|
||||
-- @tparam[opt] Handle|string file The new input file, either as a file path or pre-existing handle.
|
||||
-- @treturn Handle The current input file.
|
||||
-- @throws If the provided filename cannot be opened for reading.
|
||||
function input(file)
|
||||
if type_of(file) == "string" then
|
||||
local res, err = open(file, "rb")
|
||||
if not res then error(err, 2) end
|
||||
currentInput = res
|
||||
elseif typeOf(_arg) == "table" and getmetatable(_arg) == handleMetatable then
|
||||
currentInput = _arg
|
||||
elseif _arg ~= nil then
|
||||
error("bad argument #1 (FILE expected, got " .. typeOf(_arg) .. ")", 2)
|
||||
elseif type_of(file) == "table" and getmetatable(file) == handleMetatable then
|
||||
currentInput = file
|
||||
elseif file ~= nil then
|
||||
error("bad fileument #1 (FILE expected, got " .. type_of(file) .. ")", 2)
|
||||
end
|
||||
|
||||
return currentInput
|
||||
end
|
||||
|
||||
function lines(_sFileName)
|
||||
expect(1, _sFileName, "string", "nil")
|
||||
if _sFileName then
|
||||
local ok, err = open(_sFileName, "rb")
|
||||
--- Opens the given file name in read mode and returns an iterator that,
|
||||
-- each time it is called, returns a new line from the file.
|
||||
--
|
||||
-- This can be used in a for loop to iterate over all lines of a file:
|
||||
--
|
||||
-- ```lua
|
||||
-- for line in io.lines(filename) do print(line) end
|
||||
-- ```
|
||||
--
|
||||
-- Once the end of the file has been reached, @{nil} will be
|
||||
-- returned. The file is automatically closed.
|
||||
--
|
||||
-- If no file name is given, the @{io.input|current input} will be used
|
||||
-- instead. In this case, the handle is not used.
|
||||
--
|
||||
-- @tparam[opt] string filename The name of the file to extract lines from
|
||||
-- @treturn function():string|nil The line iterator.
|
||||
-- @throws If the file cannot be opened for reading
|
||||
--
|
||||
-- @see Handle:lines
|
||||
-- @see io.input
|
||||
function lines(filename)
|
||||
expect(1, filename, "string", "nil")
|
||||
if filename then
|
||||
local ok, err = open(filename, "rb")
|
||||
if not ok then error(err, 2) end
|
||||
|
||||
-- We set this magic flag to mark this file as being opened by io.lines and so should be
|
||||
@@ -202,38 +279,72 @@ function lines(_sFileName)
|
||||
end
|
||||
end
|
||||
|
||||
function open(_sPath, _sMode)
|
||||
expect(1, _sPath, "string")
|
||||
expect(2, _sMode, "string", "nil")
|
||||
--- Open a file with the given mode, either returning a new file handle
|
||||
-- or @{nil}, plus an error message.
|
||||
--
|
||||
-- The `mode` string can be any of the following:
|
||||
-- - **"r"**: Read mode
|
||||
-- - **"w"**: Write mode
|
||||
-- - **"w"**: Append mode
|
||||
--
|
||||
-- The mode may also have a `b` at the end, which opens the file in "binary
|
||||
-- mode". This allows you to read binary files, as well as seek within a file.
|
||||
--
|
||||
-- @tparam string filename The name of the file to open.
|
||||
-- @tparam[opt] string mode The mode to open the file with. This defaults to `rb`.
|
||||
-- @treturn[1] Handle The opened file.
|
||||
-- @treturn[2] nil In case of an error.
|
||||
-- @treturn[2] string The reason the file could not be opened.
|
||||
function open(filename, mode)
|
||||
expect(1, filename, "string")
|
||||
expect(2, mode, "string", "nil")
|
||||
|
||||
local sMode = _sMode and _sMode:gsub("%+", "") or "rb"
|
||||
local file, err = fs.open(_sPath, sMode)
|
||||
local sMode = mode and mode:gsub("%+", "") or "rb"
|
||||
local file, err = fs.open(filename, sMode)
|
||||
if not file then return nil, err end
|
||||
|
||||
return setmetatable({ _handle = file }, handleMetatable)
|
||||
end
|
||||
|
||||
function output(_arg)
|
||||
if typeOf(_arg) == "string" then
|
||||
local res, err = open(_arg, "w")
|
||||
--- Get or set the current output file.
|
||||
--
|
||||
-- @tparam[opt] Handle|string file The new output file, either as a file path or pre-existing handle.
|
||||
-- @treturn Handle The current output file.
|
||||
-- @throws If the provided filename cannot be opened for writing.
|
||||
function output(file)
|
||||
if type_of(file) == "string" then
|
||||
local res, err = open(file, "w")
|
||||
if not res then error(err, 2) end
|
||||
currentOutput = res
|
||||
elseif typeOf(_arg) == "table" and getmetatable(_arg) == handleMetatable then
|
||||
currentOutput = _arg
|
||||
elseif _arg ~= nil then
|
||||
error("bad argument #1 (FILE expected, got " .. typeOf(_arg) .. ")", 2)
|
||||
elseif type_of(file) == "table" and getmetatable(file) == handleMetatable then
|
||||
currentOutput = file
|
||||
elseif file ~= nil then
|
||||
error("bad argument #1 (FILE expected, got " .. type_of(file) .. ")", 2)
|
||||
end
|
||||
|
||||
return currentOutput
|
||||
end
|
||||
|
||||
--- Read from the currently opened input file.
|
||||
--
|
||||
-- This is equivalent to `io.input():read(...)`. See @{Handle:read|the
|
||||
-- documentation} there for full details.
|
||||
--
|
||||
-- @tparam string ... The formats to read, defaulting to a whole line.
|
||||
-- @treturn (string|nil)... The data read, or @{nil} if nothing can be read.
|
||||
function read(...)
|
||||
return currentInput:read(...)
|
||||
end
|
||||
|
||||
function type(handle)
|
||||
if typeOf(handle) == "table" and getmetatable(handle) == handleMetatable then
|
||||
if handle._closed then
|
||||
--- Checks whether `handle` is a given file handle, and determine if it is open
|
||||
-- or not.
|
||||
--
|
||||
-- @param obj The value to check
|
||||
-- @treturn string|nil `"file"` if this is an open file, `"closed file"` if it
|
||||
-- is a closed file handle, or `nil` if not a file handle.
|
||||
function type(obj)
|
||||
if type_of(obj) == "table" and getmetatable(obj) == handleMetatable then
|
||||
if obj._closed then
|
||||
return "closed file"
|
||||
else
|
||||
return "file"
|
||||
@@ -242,6 +353,12 @@ function type(handle)
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Write to the currently opened output file.
|
||||
--
|
||||
-- This is equivalent to `io.output():write(...)`. See @{Handle:write|the
|
||||
-- documentation} there for full details.
|
||||
--
|
||||
-- @tparam string ... The strings to write
|
||||
function write(...)
|
||||
return currentOutput:write(...)
|
||||
end
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
--- Key codes for ComputerCraft
|
||||
--- The Keys API provides a table of numerical codes corresponding to keyboard
|
||||
-- keys, suitable for decoding key events.
|
||||
--
|
||||
-- This is derived from the GLFW list of key codes, and mostly created via
|
||||
-- a couple of regexes.
|
||||
-- These values are not guaranteed to remain the same between versions. It is
|
||||
-- recommended that you use the constants provided by this file, rather than
|
||||
-- the underlying numerical values.
|
||||
--
|
||||
-- Note that this is technically incompatible with previous versions of CC, as
|
||||
-- they relied on Minecraft's character mappings. However, if CC emulators have
|
||||
-- taught me anything, it's that emulating LWJGL's weird key handling is nigh-on
|
||||
-- impossible.
|
||||
-- @module keys
|
||||
|
||||
local expect = dofile("rom/modules/main/cc/expect.lua").expect
|
||||
|
||||
@@ -137,10 +136,15 @@ for nKey, sKey in pairs( tKeys ) do
|
||||
end
|
||||
|
||||
-- Alias some keys for ease-of-use and backwards compatibility
|
||||
keys["return"] = keys.enter
|
||||
keys.scollLock = keys.scrollLock
|
||||
keys.cimcumflex = keys.circumflex
|
||||
keys["return"] = keys.enter --- @local
|
||||
keys.scollLock = keys.scrollLock --- @local
|
||||
keys.cimcumflex = keys.circumflex --- @local
|
||||
|
||||
--- Translates a numerical key code to a human-readable name. The human-readable
|
||||
-- name is one of the constants in the keys API.
|
||||
--
|
||||
-- @tparam number code The key code to look up.
|
||||
-- @treturn string|nil The name of the key, or `nil` if not a valid key code.
|
||||
function getName( _nKey )
|
||||
expect(1, _nKey, "number")
|
||||
return tKeys[ _nKey ]
|
||||
|
||||
@@ -1,75 +1,117 @@
|
||||
--- An API for advanced systems which can draw pixels and lines, load and draw
|
||||
-- image files. You can use the `colors` API for easier color manipulation.
|
||||
--
|
||||
-- @module paintutils
|
||||
|
||||
local expect = dofile("rom/modules/main/cc/expect.lua").expect
|
||||
|
||||
local function drawPixelInternal( xPos, yPos )
|
||||
term.setCursorPos( xPos, yPos )
|
||||
local function drawPixelInternal(xPos, yPos)
|
||||
term.setCursorPos(xPos, yPos)
|
||||
term.write(" ")
|
||||
end
|
||||
|
||||
local tColourLookup = {}
|
||||
for n = 1, 16 do
|
||||
tColourLookup[ string.byte( "0123456789abcdef", n, n ) ] = 2 ^ (n - 1)
|
||||
tColourLookup[string.byte("0123456789abcdef", n, n)] = 2 ^ (n - 1)
|
||||
end
|
||||
|
||||
local function parseLine( tImageArg, sLine )
|
||||
local function parseLine(tImageArg, sLine)
|
||||
local tLine = {}
|
||||
for x = 1, sLine:len() do
|
||||
tLine[x] = tColourLookup[ string.byte(sLine, x, x) ] or 0
|
||||
tLine[x] = tColourLookup[string.byte(sLine, x, x)] or 0
|
||||
end
|
||||
table.insert( tImageArg, tLine )
|
||||
table.insert(tImageArg, tLine)
|
||||
end
|
||||
|
||||
function parseImage( sRawData )
|
||||
expect(1, sRawData, "string")
|
||||
--- Parses an image from a multi-line string
|
||||
--
|
||||
-- @tparam string image The string containing the raw-image data.
|
||||
-- @treturn table The parsed image data, suitable for use with
|
||||
-- @{paintutils.drawImage}.
|
||||
function parseImage(image)
|
||||
expect(1, image, "string")
|
||||
local tImage = {}
|
||||
for sLine in ( sRawData .. "\n" ):gmatch( "(.-)\n" ) do -- read each line like original file handling did
|
||||
parseLine( tImage, sLine )
|
||||
for sLine in (image .. "\n"):gmatch("(.-)\n") do
|
||||
parseLine(tImage, sLine)
|
||||
end
|
||||
return tImage
|
||||
end
|
||||
|
||||
function loadImage( sPath )
|
||||
expect(1, sPath, "string")
|
||||
--- Loads an image from a file.
|
||||
--
|
||||
-- You can create a file suitable for being loaded using the `paint` program.
|
||||
--
|
||||
-- @tparam string path The file to load.
|
||||
--
|
||||
-- @treturn table|nil The parsed image data, suitable for use with
|
||||
-- @{paintutils.drawImage}, or `nil` if the file does not exist.
|
||||
function loadImage(path)
|
||||
expect(1, path, "string")
|
||||
|
||||
if fs.exists( sPath ) then
|
||||
local file = io.open( sPath, "r" )
|
||||
if fs.exists(path) then
|
||||
local file = io.open(path, "r")
|
||||
local sContent = file:read("*a")
|
||||
file:close()
|
||||
return parseImage( sContent ) -- delegate image parse to parseImage
|
||||
return parseImage(sContent)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function drawPixel( xPos, yPos, nColour )
|
||||
--- Draws a single pixel to the current term at the specified position.
|
||||
--
|
||||
-- Be warned, this may change the position of the cursor and the current
|
||||
-- background colour. You should not expect either to be preserved.
|
||||
--
|
||||
-- @tparam number xPos The x position to draw at, where 1 is the far left.
|
||||
-- @tparam number yPos The y position to draw at, where 1 is the very top.
|
||||
-- @tparam[opt] number colour The @{colors|color} of this pixel. This will be
|
||||
-- the current background colour if not specified.
|
||||
function drawPixel(xPos, yPos, colour)
|
||||
expect(1, xPos, "number")
|
||||
expect(2, yPos, "number")
|
||||
expect(3, nColour, "number", "nil")
|
||||
if nColour then
|
||||
term.setBackgroundColor( nColour )
|
||||
expect(3, colour, "number", "nil")
|
||||
|
||||
if type(xPos) ~= "number" then error("bad argument #1 (expected number, got " .. type(xPos) .. ")", 2) end
|
||||
if type(yPos) ~= "number" then error("bad argument #2 (expected number, got " .. type(yPos) .. ")", 2) end
|
||||
if colour ~= nil and type(colour) ~= "number" then error("bad argument #3 (expected number, got " .. type(colour) .. ")", 2) end
|
||||
if colour then
|
||||
term.setBackgroundColor(colour)
|
||||
end
|
||||
return drawPixelInternal( xPos, yPos )
|
||||
return drawPixelInternal(xPos, yPos)
|
||||
end
|
||||
|
||||
function drawLine( startX, startY, endX, endY, nColour )
|
||||
--- Draws a straight line from the start to end position.
|
||||
--
|
||||
-- Be warned, this may change the position of the cursor and the current
|
||||
-- background colour. You should not expect either to be preserved.
|
||||
--
|
||||
-- @tparam number startX The starting x position of the line.
|
||||
-- @tparam number startY The starting y position of the line.
|
||||
-- @tparam number endX The end x position of the line.
|
||||
-- @tparam number endY The end y position of the line.
|
||||
-- @tparam[opt] number colour The @{colors|color} of this pixel. This will be
|
||||
-- the current background colour if not specified.
|
||||
function drawLine(startX, startY, endX, endY, colour)
|
||||
expect(1, startX, "number")
|
||||
expect(2, startY, "number")
|
||||
expect(3, endX, "number")
|
||||
expect(4, endY, "number")
|
||||
expect(5, nColour, "number", "nil")
|
||||
expect(5, colour, "number", "nil")
|
||||
|
||||
startX = math.floor(startX)
|
||||
startY = math.floor(startY)
|
||||
endX = math.floor(endX)
|
||||
endY = math.floor(endY)
|
||||
|
||||
if nColour then
|
||||
term.setBackgroundColor( nColour )
|
||||
if colour then
|
||||
term.setBackgroundColor(colour)
|
||||
end
|
||||
if startX == endX and startY == endY then
|
||||
drawPixelInternal( startX, startY )
|
||||
drawPixelInternal(startX, startY)
|
||||
return
|
||||
end
|
||||
|
||||
local minX = math.min( startX, endX )
|
||||
local minX = math.min(startX, endX)
|
||||
local maxX, minY, maxY
|
||||
if minX == startX then
|
||||
minY = startY
|
||||
@@ -90,7 +132,7 @@ function drawLine( startX, startY, endX, endY, nColour )
|
||||
local y = minY
|
||||
local dy = yDiff / xDiff
|
||||
for x = minX, maxX do
|
||||
drawPixelInternal( x, math.floor( y + 0.5 ) )
|
||||
drawPixelInternal(x, math.floor(y + 0.5))
|
||||
y = y + dy
|
||||
end
|
||||
else
|
||||
@@ -98,19 +140,31 @@ function drawLine( startX, startY, endX, endY, nColour )
|
||||
local dx = xDiff / yDiff
|
||||
if maxY >= minY then
|
||||
for y = minY, maxY do
|
||||
drawPixelInternal( math.floor( x + 0.5 ), y )
|
||||
drawPixelInternal(math.floor(x + 0.5), y)
|
||||
x = x + dx
|
||||
end
|
||||
else
|
||||
for y = minY, maxY, -1 do
|
||||
drawPixelInternal( math.floor( x + 0.5 ), y )
|
||||
drawPixelInternal(math.floor(x + 0.5), y)
|
||||
x = x - dx
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function drawBox( startX, startY, endX, endY, nColour )
|
||||
--- Draws the outline of a box on the current term from the specified start
|
||||
-- position to the specified end position.
|
||||
--
|
||||
-- Be warned, this may change the position of the cursor and the current
|
||||
-- background colour. You should not expect either to be preserved.
|
||||
--
|
||||
-- @tparam number startX The starting x position of the line.
|
||||
-- @tparam number startY The starting y position of the line.
|
||||
-- @tparam number endX The end x position of the line.
|
||||
-- @tparam number endY The end y position of the line.
|
||||
-- @tparam[opt] number colour The @{colors|color} of this pixel. This will be
|
||||
-- the current background colour if not specified.
|
||||
function drawBox(startX, startY, endX, endY, nColour)
|
||||
expect(1, startX, "number")
|
||||
expect(2, startY, "number")
|
||||
expect(3, endX, "number")
|
||||
@@ -123,14 +177,14 @@ function drawBox( startX, startY, endX, endY, nColour )
|
||||
endY = math.floor(endY)
|
||||
|
||||
if nColour then
|
||||
term.setBackgroundColor( nColour )
|
||||
term.setBackgroundColor(nColour)
|
||||
end
|
||||
if startX == endX and startY == endY then
|
||||
drawPixelInternal( startX, startY )
|
||||
drawPixelInternal(startX, startY)
|
||||
return
|
||||
end
|
||||
|
||||
local minX = math.min( startX, endX )
|
||||
local minX = math.min(startX, endX)
|
||||
local maxX, minY, maxY
|
||||
if minX == startX then
|
||||
minY = startY
|
||||
@@ -143,19 +197,30 @@ function drawBox( startX, startY, endX, endY, nColour )
|
||||
end
|
||||
|
||||
for x = minX, maxX do
|
||||
drawPixelInternal( x, minY )
|
||||
drawPixelInternal( x, maxY )
|
||||
drawPixelInternal(x, minY)
|
||||
drawPixelInternal(x, maxY)
|
||||
end
|
||||
|
||||
if maxY - minY >= 2 then
|
||||
for y = minY + 1, maxY - 1 do
|
||||
drawPixelInternal( minX, y )
|
||||
drawPixelInternal( maxX, y )
|
||||
drawPixelInternal(minX, y)
|
||||
drawPixelInternal(maxX, y)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function drawFilledBox( startX, startY, endX, endY, nColour )
|
||||
--- Draws a filled box on the current term from the specified start position to
|
||||
-- the specified end position.
|
||||
--
|
||||
-- Be warned, this may change the position of the cursor and the current
|
||||
-- background colour. You should not expect either to be preserved.
|
||||
--
|
||||
-- @tparam number startX The starting x position of the line.
|
||||
-- @tparam number startY The starting y position of the line.
|
||||
-- @tparam number endX The end x position of the line.
|
||||
-- @tparam number endY The end y position of the line.
|
||||
-- @tparam[opt] number colour The @{colors|color} of this pixel. This will be
|
||||
-- the current background colour if not specified.
|
||||
function drawFilledBox(startX, startY, endX, endY, nColour)
|
||||
expect(1, startX, "number")
|
||||
expect(2, startY, "number")
|
||||
expect(3, endX, "number")
|
||||
@@ -168,14 +233,14 @@ function drawFilledBox( startX, startY, endX, endY, nColour )
|
||||
endY = math.floor(endY)
|
||||
|
||||
if nColour then
|
||||
term.setBackgroundColor( nColour )
|
||||
term.setBackgroundColor(nColour)
|
||||
end
|
||||
if startX == endX and startY == endY then
|
||||
drawPixelInternal( startX, startY )
|
||||
drawPixelInternal(startX, startY)
|
||||
return
|
||||
end
|
||||
|
||||
local minX = math.min( startX, endX )
|
||||
local minX = math.min(startX, endX)
|
||||
local maxX, minY, maxY
|
||||
if minX == startX then
|
||||
minY = startY
|
||||
@@ -189,21 +254,26 @@ function drawFilledBox( startX, startY, endX, endY, nColour )
|
||||
|
||||
for x = minX, maxX do
|
||||
for y = minY, maxY do
|
||||
drawPixelInternal( x, y )
|
||||
drawPixelInternal(x, y)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function drawImage( tImage, xPos, yPos )
|
||||
expect(1, tImage, "table")
|
||||
--- Draw an image loaded by @{paintutils.parseImage} or @{paintutils.loadImage}.
|
||||
--
|
||||
-- @tparam table image The parsed image data.
|
||||
-- @tparam number xPos The x position to start drawing at.
|
||||
-- @tparam number xPos The y position to start drawing at.
|
||||
function drawImage(image, xPos, yPos)
|
||||
expect(1, image, "table")
|
||||
expect(2, xPos, "number")
|
||||
expect(3, yPos, "number")
|
||||
for y = 1, #tImage do
|
||||
local tLine = tImage[y]
|
||||
for y = 1, #image do
|
||||
local tLine = image[y]
|
||||
for x = 1, #tLine do
|
||||
if tLine[x] > 0 then
|
||||
term.setBackgroundColor( tLine[x] )
|
||||
drawPixelInternal( x + xPos - 1, y + yPos - 1 )
|
||||
term.setBackgroundColor(tLine[x])
|
||||
drawPixelInternal(x + xPos - 1, y + yPos - 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,11 +1,26 @@
|
||||
--- Provides a simple implementation of multitasking.
|
||||
--
|
||||
-- Functions are not actually executed simultaniously, but rather this API will
|
||||
-- automatically switch between them whenever they yield (eg whenever they call
|
||||
-- @{coroutine.yield}, or functions that call that - eg `os.pullEvent` - or
|
||||
-- functions that call that, etc - basically, anything that causes the function
|
||||
-- to "pause").
|
||||
--
|
||||
-- Each function executed in "parallel" gets its own copy of the event queue,
|
||||
-- and so "event consuming" functions (again, mostly anything that causes the
|
||||
-- script to pause - eg `sleep`, `rednet.receive`, most of the `turtle` API,
|
||||
-- etc) can safely be used in one without affecting the event queue accessed by
|
||||
-- the other.
|
||||
--
|
||||
-- @module parallel
|
||||
|
||||
local function create( ... )
|
||||
local function create(...)
|
||||
local tFns = table.pack(...)
|
||||
local tCos = {}
|
||||
for i = 1, tFns.n, 1 do
|
||||
local fn = tFns[i]
|
||||
if type( fn ) ~= "function" then
|
||||
error( "bad argument #" .. i .. " (expected function, got " .. type( fn ) .. ")", 3 )
|
||||
if type(fn) ~= "function" then
|
||||
error("bad argument #" .. i .. " (expected function, got " .. type(fn) .. ")", 3)
|
||||
end
|
||||
|
||||
tCos[i] = coroutine.create(fn)
|
||||
@@ -14,7 +29,7 @@ local function create( ... )
|
||||
return tCos
|
||||
end
|
||||
|
||||
local function runUntilLimit( _routines, _limit )
|
||||
local function runUntilLimit(_routines, _limit)
|
||||
local count = #_routines
|
||||
local living = count
|
||||
|
||||
@@ -25,13 +40,13 @@ local function runUntilLimit( _routines, _limit )
|
||||
local r = _routines[n]
|
||||
if r then
|
||||
if tFilters[r] == nil or tFilters[r] == eventData[1] or eventData[1] == "terminate" then
|
||||
local ok, param = coroutine.resume( r, table.unpack( eventData, 1, eventData.n ) )
|
||||
local ok, param = coroutine.resume(r, table.unpack(eventData, 1, eventData.n))
|
||||
if not ok then
|
||||
error( param, 0 )
|
||||
error(param, 0)
|
||||
else
|
||||
tFilters[r] = param
|
||||
end
|
||||
if coroutine.status( r ) == "dead" then
|
||||
if coroutine.status(r) == "dead" then
|
||||
_routines[n] = nil
|
||||
living = living - 1
|
||||
if living <= _limit then
|
||||
@@ -43,7 +58,7 @@ local function runUntilLimit( _routines, _limit )
|
||||
end
|
||||
for n = 1, count do
|
||||
local r = _routines[n]
|
||||
if r and coroutine.status( r ) == "dead" then
|
||||
if r and coroutine.status(r) == "dead" then
|
||||
_routines[n] = nil
|
||||
living = living - 1
|
||||
if living <= _limit then
|
||||
@@ -51,16 +66,26 @@ local function runUntilLimit( _routines, _limit )
|
||||
end
|
||||
end
|
||||
end
|
||||
eventData = table.pack( os.pullEventRaw() )
|
||||
eventData = table.pack(os.pullEventRaw())
|
||||
end
|
||||
end
|
||||
|
||||
function waitForAny( ... )
|
||||
local routines = create( ... )
|
||||
return runUntilLimit( routines, #routines - 1 )
|
||||
--- Switches between execution of the functions, until any of them
|
||||
-- finishes. If any of the functions errors, the message is propagated upwards
|
||||
-- from the @{parallel.waitForAny} call.
|
||||
--
|
||||
-- @tparam function ... The functions this task will run
|
||||
function waitForAny(...)
|
||||
local routines = create(...)
|
||||
return runUntilLimit(routines, #routines - 1)
|
||||
end
|
||||
|
||||
function waitForAll( ... )
|
||||
local routines = create( ... )
|
||||
runUntilLimit( routines, 0 )
|
||||
--- Switches between execution of the functions, until all of them are
|
||||
-- finished. If any of the functions errors, the message is propagated upwards
|
||||
-- from the @{parallel.waitForAll} call.
|
||||
--
|
||||
-- @tparam function ... The functions this task will run
|
||||
function waitForAll(...)
|
||||
local routines = create(...)
|
||||
return runUntilLimit(routines, 0)
|
||||
end
|
||||
|
||||
@@ -1,110 +1,184 @@
|
||||
--- The Peripheral API is for interacting with peripherals connected to the
|
||||
-- computer, such as the Disk Drive, the Advanced Monitor and Monitor.
|
||||
--
|
||||
-- Each peripheral block has a name, either referring to the side the peripheral
|
||||
-- can be found on, or a name on an adjacent wired network.
|
||||
--
|
||||
-- If the peripheral is next to the computer, its side is either `front`,
|
||||
-- `back`, `left`, `right`, `top` or `bottom`. If the peripheral is attached by
|
||||
-- a cable, its side will follow the format `type_id`, for example `printer_0`.
|
||||
--
|
||||
-- Peripheral functions are called *methods*, a term borrowed from Java.
|
||||
--
|
||||
-- @module peripheral
|
||||
|
||||
local expect = dofile("rom/modules/main/cc/expect.lua").expect
|
||||
|
||||
local native = peripheral
|
||||
local sides = rs.getSides()
|
||||
|
||||
--- Provides a list of all peripherals available.
|
||||
--
|
||||
-- If a device is located directly next to the system, then its name will be
|
||||
-- listed as the side it is attached to. If a device is attached via a Wired
|
||||
-- Modem, then it'll be reported according to its name on the wired network.
|
||||
--
|
||||
-- @treturn table A list of the names of all attached peripherals.
|
||||
function getNames()
|
||||
local tResults = {}
|
||||
for _, sSide in ipairs( rs.getSides() ) do
|
||||
if native.isPresent( sSide ) then
|
||||
table.insert( tResults, sSide )
|
||||
if native.getType( sSide ) == "modem" and not native.call( sSide, "isWireless" ) then
|
||||
local tRemote = native.call( sSide, "getNamesRemote" )
|
||||
for _, sName in ipairs( tRemote ) do
|
||||
table.insert( tResults, sName )
|
||||
local results = {}
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.isPresent(side) then
|
||||
table.insert(results, side)
|
||||
if native.getType(side) == "modem" and not native.call(side, "isWireless") then
|
||||
local remote = native.call(side, "getNamesRemote")
|
||||
for _, name in ipairs(remote) do
|
||||
table.insert(results, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return tResults
|
||||
return results
|
||||
end
|
||||
|
||||
function isPresent( _sSide )
|
||||
expect(1, _sSide, "string")
|
||||
if native.isPresent( _sSide ) then
|
||||
--- Determines if a peripheral is present with the given name.
|
||||
--
|
||||
-- @tparam string name The side or network name that you want to check.
|
||||
-- @treturn boolean If a peripheral is present with the given name.
|
||||
-- @usage peripheral.isPresent("top")
|
||||
-- @usage peripheral.isPresent("monitor_0")
|
||||
function isPresent(name)
|
||||
expect(1, name, "string")
|
||||
if native.isPresent(name) then
|
||||
return true
|
||||
end
|
||||
for _, sSide in ipairs( rs.getSides() ) do
|
||||
if native.getType( sSide ) == "modem" and not native.call( sSide, "isWireless" ) then
|
||||
if native.call( sSide, "isPresentRemote", _sSide ) then
|
||||
return true
|
||||
end
|
||||
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.getType(side) == "modem" and not native.call(side, "isWireless") and
|
||||
native.call(side, "isPresentRemote", name)
|
||||
then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function getType( _sSide )
|
||||
expect(1, _sSide, "string")
|
||||
if native.isPresent( _sSide ) then
|
||||
return native.getType( _sSide )
|
||||
--- Get the type of the peripheral with the given name.
|
||||
--
|
||||
-- @tparam string name The name of the peripheral to find.
|
||||
-- @treturn string|nil The peripheral's type, or `nil` if it is not present.
|
||||
function getType(name)
|
||||
expect(1, name, "string")
|
||||
if native.isPresent(name) then
|
||||
return native.getType(name)
|
||||
end
|
||||
for _, sSide in ipairs( rs.getSides() ) do
|
||||
if native.getType( sSide ) == "modem" and not native.call( sSide, "isWireless" ) then
|
||||
if native.call( sSide, "isPresentRemote", _sSide ) then
|
||||
return native.call( sSide, "getTypeRemote", _sSide )
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.getType(side) == "modem" and not native.call(side, "isWireless") and
|
||||
native.call(side, "isPresentRemote", name)
|
||||
then
|
||||
return native.call(side, "getTypeRemote", name)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function getMethods( _sSide )
|
||||
expect(1, _sSide, "string")
|
||||
if native.isPresent( _sSide ) then
|
||||
return native.getMethods( _sSide )
|
||||
--- Get all available methods for the peripheral with the given name.
|
||||
--
|
||||
-- @tparam string name The name of the peripheral to find.
|
||||
-- @treturn table|nil A list of methods provided by this peripheral, or `nil` if
|
||||
-- it is not present.
|
||||
function getMethods(name)
|
||||
expect(1, name, "string")
|
||||
if native.isPresent(name) then
|
||||
return native.getMethods(name)
|
||||
end
|
||||
for _, sSide in ipairs( rs.getSides() ) do
|
||||
if native.getType( sSide ) == "modem" and not native.call( sSide, "isWireless" ) then
|
||||
if native.call( sSide, "isPresentRemote", _sSide ) then
|
||||
return native.call( sSide, "getMethodsRemote", _sSide )
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.getType(side) == "modem" and not native.call(side, "isWireless") and
|
||||
native.call(side, "isPresentRemote", name)
|
||||
then
|
||||
return native.call(side, "getMethodsRemote", name)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function call( _sSide, _sMethod, ... )
|
||||
expect(1, _sSide, "string")
|
||||
expect(2, _sMethod, "string")
|
||||
if native.isPresent( _sSide ) then
|
||||
return native.call( _sSide, _sMethod, ... )
|
||||
--- Call a method on the peripheral with the given name.
|
||||
--
|
||||
-- @tparam string name The name of the peripheral to invoke the method on.
|
||||
-- @tparam string method The name of the method
|
||||
-- @param ... Additional arguments to pass to the method
|
||||
-- @return The return values of the peripheral method.
|
||||
--
|
||||
-- @usage Open the modem on the top of this computer.
|
||||
--
|
||||
-- peripheral.call("top", "open", 1)
|
||||
function call(name, method, ...)
|
||||
expect(1, name, "string")
|
||||
expect(2, method, "string")
|
||||
if native.isPresent(name) then
|
||||
return native.call(name, method, ...)
|
||||
end
|
||||
for _, sSide in ipairs( rs.getSides() ) do
|
||||
if native.getType( sSide ) == "modem" and not native.call( sSide, "isWireless" ) then
|
||||
if native.call( sSide, "isPresentRemote", _sSide ) then
|
||||
return native.call( sSide, "callRemote", _sSide, _sMethod, ... )
|
||||
end
|
||||
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.getType(side) == "modem" and not native.call(side, "isWireless") and
|
||||
native.call(side, "isPresentRemote", name)
|
||||
then
|
||||
return native.call(side, "callRemote", name, method, ...)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function wrap( _sSide )
|
||||
expect(1, _sSide, "string")
|
||||
if peripheral.isPresent( _sSide ) then
|
||||
local tMethods = peripheral.getMethods( _sSide )
|
||||
local tResult = {}
|
||||
for _, sMethod in ipairs( tMethods ) do
|
||||
tResult[sMethod] = function( ... )
|
||||
return peripheral.call( _sSide, sMethod, ... )
|
||||
end
|
||||
end
|
||||
return tResult
|
||||
--- Get a table containing functions pointing to the peripheral's methods, which
|
||||
-- can then be called as if using @{peripheral.call}.
|
||||
--
|
||||
-- @tparam string name The name of the peripheral to wrap.
|
||||
-- @treturn table|nil The table containing the peripheral's methods, or `nil` if
|
||||
-- there is no peripheral present with the given name.
|
||||
-- @usage peripheral.wrap("top").open(1)
|
||||
function wrap(name)
|
||||
expect(1, name, "string")
|
||||
|
||||
local methods = peripheral.getMethods(name)
|
||||
if not methods then
|
||||
return nil
|
||||
end
|
||||
return nil
|
||||
|
||||
local result = {}
|
||||
for _, method in ipairs(methods) do
|
||||
result[method] = function(...)
|
||||
return peripheral.call(name, method, ...)
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
function find( sType, fnFilter )
|
||||
expect(1, sType, "string")
|
||||
expect(2, fnFilter, "function", "nil")
|
||||
local tResults = {}
|
||||
for _, sName in ipairs( peripheral.getNames() ) do
|
||||
if peripheral.getType( sName ) == sType then
|
||||
local wrapped = peripheral.wrap( sName )
|
||||
if fnFilter == nil or fnFilter( sName, wrapped ) then
|
||||
table.insert( tResults, wrapped )
|
||||
--- Find all peripherals of a specific type, and return the
|
||||
-- @{peripheral.wrap|wrapped} peripherals.
|
||||
--
|
||||
-- @tparam string ty The type of peripheral to look for.
|
||||
-- @tparam[opt] function(name:string, wrapped:table):boolean filter A
|
||||
-- filter function, which takes the peripheral's name and wrapped table
|
||||
-- and returns if it should be included in the result.
|
||||
-- @treturn table... 0 or more wrapped peripherals matching the given filters.
|
||||
-- @usage local monitors = { peripheral.find("monitor") }
|
||||
-- @usage peripheral.find("modem", rednet.open)
|
||||
function find(ty, filter)
|
||||
expect(1, ty, "string")
|
||||
expect(2, filter, "function", "nil")
|
||||
|
||||
local results = {}
|
||||
for _, name in ipairs(peripheral.getNames()) do
|
||||
if peripheral.getType(name) == ty then
|
||||
local wrapped = peripheral.wrap(name)
|
||||
if filter == nil or filter(name, wrapped) then
|
||||
table.insert(results, wrapped)
|
||||
end
|
||||
end
|
||||
end
|
||||
return table.unpack( tResults )
|
||||
return table.unpack(results)
|
||||
end
|
||||
|
||||
@@ -1,51 +1,92 @@
|
||||
--- The Rednet API allows systems to communicate between each other without
|
||||
-- using redstone. It serves as a wrapper for the modem API, offering ease of
|
||||
-- functionality (particularly in regards to repeating signals) with some
|
||||
-- expense of fine control.
|
||||
--
|
||||
-- In order to send and receive data, a modem (either wired, wireless, or ender)
|
||||
-- is required. The data reaches any possible destinations immediately after
|
||||
-- sending it, but is range limited.
|
||||
--
|
||||
-- Rednet also allows you to use a "protocol" - simple string names indicating
|
||||
-- what messages are about. Receiving systems may filter messages according to
|
||||
-- their protocols, thereby automatically ignoring incoming messages which don't
|
||||
-- specify an identical string. It's also possible to @{rednet.lookup|lookup}
|
||||
-- which systems in the area use certain protocols, hence making it easier to
|
||||
-- determine where given messages should be sent in the first place.
|
||||
--
|
||||
-- @module rednet
|
||||
|
||||
local expect = dofile("rom/modules/main/cc/expect.lua").expect
|
||||
|
||||
--- The channel used by the Rednet API to @{broadcast} messages.
|
||||
CHANNEL_BROADCAST = 65535
|
||||
|
||||
--- The channel used by the Rednet API to repeat messages.
|
||||
CHANNEL_REPEAT = 65533
|
||||
|
||||
local tReceivedMessages = {}
|
||||
local tReceivedMessageTimeouts = {}
|
||||
local tHostnames = {}
|
||||
|
||||
function open( sModem )
|
||||
expect(1, sModem, "string")
|
||||
if peripheral.getType( sModem ) ~= "modem" then
|
||||
error( "No such modem: " .. sModem, 2 )
|
||||
--- Opens a modem with the given @{peripheral} name, allowing it to send and
|
||||
--- receive messages over rednet.
|
||||
--
|
||||
-- This will open the modem on two channels: one which has the same
|
||||
-- @{os.getComputerID|ID} as the computer, and another on
|
||||
-- @{CHANNEL_BROADCAST|the broadcast channel}.
|
||||
--
|
||||
-- @tparam string modem The name of the modem to open.
|
||||
-- @throws If there is no such modem with the given name
|
||||
function open(modem)
|
||||
expect(1, modem, "string")
|
||||
if peripheral.getType(modem) ~= "modem" then
|
||||
error("No such modem: " .. modem, 2)
|
||||
end
|
||||
peripheral.call( sModem, "open", os.getComputerID() )
|
||||
peripheral.call( sModem, "open", CHANNEL_BROADCAST )
|
||||
peripheral.call(modem, "open", os.getComputerID())
|
||||
peripheral.call(modem, "open", CHANNEL_BROADCAST)
|
||||
end
|
||||
|
||||
function close( sModem )
|
||||
expect(1, sModem, "string", "nil")
|
||||
if sModem then
|
||||
--- Close a modem with the given @{peripheral} name, meaning it can no longer
|
||||
-- send and receive rednet messages.
|
||||
--
|
||||
-- @tparam[opt] string modem The side the modem exists on. If not given, all
|
||||
-- open modems will be closed.
|
||||
-- @throws If there is no such modem with the given name
|
||||
function close(modem)
|
||||
expect(1, modem, "string", "nil")
|
||||
if modem then
|
||||
-- Close a specific modem
|
||||
if peripheral.getType( sModem ) ~= "modem" then
|
||||
error( "No such modem: " .. sModem, 2 )
|
||||
if peripheral.getType(modem) ~= "modem" then
|
||||
error("No such modem: " .. modem, 2)
|
||||
end
|
||||
peripheral.call( sModem, "close", os.getComputerID() )
|
||||
peripheral.call( sModem, "close", CHANNEL_BROADCAST )
|
||||
peripheral.call(modem, "close", os.getComputerID())
|
||||
peripheral.call(modem, "close", CHANNEL_BROADCAST)
|
||||
else
|
||||
-- Close all modems
|
||||
for _, sModem in ipairs( peripheral.getNames() ) do
|
||||
if isOpen( sModem ) then
|
||||
close( sModem )
|
||||
for _, modem in ipairs(peripheral.getNames()) do
|
||||
if isOpen(modem) then
|
||||
close(modem)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function isOpen( sModem )
|
||||
expect(1, sModem, "string", "nil")
|
||||
if sModem then
|
||||
--- Determine if rednet is currently open.
|
||||
--
|
||||
-- @tparam[opt] string modem Which modem to check. If not given, all connected
|
||||
-- modems will be checked.
|
||||
-- @treturn boolean If the given modem is open.
|
||||
function isOpen(modem)
|
||||
expect(1, modem, "string", "nil")
|
||||
if modem then
|
||||
-- Check if a specific modem is open
|
||||
if peripheral.getType( sModem ) == "modem" then
|
||||
return peripheral.call( sModem, "isOpen", os.getComputerID() ) and peripheral.call( sModem, "isOpen", CHANNEL_BROADCAST )
|
||||
if peripheral.getType(modem) == "modem" then
|
||||
return peripheral.call(modem, "isOpen", os.getComputerID()) and peripheral.call(modem, "isOpen", CHANNEL_BROADCAST)
|
||||
end
|
||||
else
|
||||
-- Check if any modem is open
|
||||
for _, sModem in ipairs( peripheral.getNames() ) do
|
||||
if isOpen( sModem ) then
|
||||
for _, modem in ipairs(peripheral.getNames()) do
|
||||
if isOpen(modem) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
@@ -53,15 +94,32 @@ function isOpen( sModem )
|
||||
return false
|
||||
end
|
||||
|
||||
function send( nRecipient, message, sProtocol )
|
||||
--- Allows a computer or turtle with an attached modem to send a message
|
||||
-- intended for a system with a specific ID. At least one such modem must first
|
||||
-- be @{rednet.open|opened} before sending is possible.
|
||||
--
|
||||
-- Assuming the target was in range and also had a correctly opened modem, it
|
||||
-- may then use @{rednet.receive} to collect the message.
|
||||
--
|
||||
-- @tparam number nRecipient The ID of the receiving computer.
|
||||
-- @param message The message to send. This should not contain coroutines or
|
||||
-- functions, as they will be converted to @{nil}.
|
||||
-- @tparam[opt] string sProtocol The "protocol" to send this message under. When
|
||||
-- using @{rednet.receive} one can filter to only receive messages sent under a
|
||||
-- particular protocol.
|
||||
-- @treturn boolean If this message was successfully sent (i.e. if rednet is
|
||||
-- currently @{rednet.open|open}). Note, this does not guarantee the message was
|
||||
-- actually _received_.
|
||||
-- @see rednet.receive
|
||||
function send(nRecipient, message, sProtocol)
|
||||
expect(1, nRecipient, "number")
|
||||
expect(3, sProtocol, "string", "nil")
|
||||
-- Generate a (probably) unique message ID
|
||||
-- We could do other things to guarantee uniqueness, but we really don't need to
|
||||
-- Store it to ensure we don't get our own messages back
|
||||
local nMessageID = math.random( 1, 2147483647 )
|
||||
tReceivedMessages[ nMessageID ] = true
|
||||
tReceivedMessageTimeouts[ os.startTimer( 30 ) ] = nMessageID
|
||||
local nMessageID = math.random(1, 2147483647)
|
||||
tReceivedMessages[nMessageID] = true
|
||||
tReceivedMessageTimeouts[os.startTimer(30)] = nMessageID
|
||||
|
||||
-- Create the message
|
||||
local nReplyChannel = os.getComputerID()
|
||||
@@ -75,14 +133,14 @@ function send( nRecipient, message, sProtocol )
|
||||
local sent = false
|
||||
if nRecipient == os.getComputerID() then
|
||||
-- Loopback to ourselves
|
||||
os.queueEvent( "rednet_message", nReplyChannel, message, sProtocol )
|
||||
os.queueEvent("rednet_message", nReplyChannel, message, sProtocol)
|
||||
sent = true
|
||||
else
|
||||
-- Send on all open modems, to the target and to repeaters
|
||||
for _, sModem in ipairs( peripheral.getNames() ) do
|
||||
if isOpen( sModem ) then
|
||||
peripheral.call( sModem, "transmit", nRecipient, nReplyChannel, tMessage )
|
||||
peripheral.call( sModem, "transmit", CHANNEL_REPEAT, nReplyChannel, tMessage )
|
||||
for _, sModem in ipairs(peripheral.getNames()) do
|
||||
if isOpen(sModem) then
|
||||
peripheral.call(sModem, "transmit", nRecipient, nReplyChannel, tMessage)
|
||||
peripheral.call(sModem, "transmit", CHANNEL_REPEAT, nReplyChannel, tMessage)
|
||||
sent = true
|
||||
end
|
||||
end
|
||||
@@ -91,12 +149,35 @@ function send( nRecipient, message, sProtocol )
|
||||
return sent
|
||||
end
|
||||
|
||||
function broadcast( message, sProtocol )
|
||||
--- Broadcasts a string message over the predefined @{CHANNEL_BROADCAST}
|
||||
-- channel. The message will be received by every device listening to rednet.
|
||||
--
|
||||
-- @param message The message to send. This should not contain coroutines or
|
||||
-- functions, as they will be converted to @{nil}.
|
||||
-- @tparam[opt] string sProtocol The "protocol" to send this message under. When
|
||||
-- using @{rednet.receive} one can filter to only receive messages sent under a
|
||||
-- particular protocol.
|
||||
-- @see rednet.receive
|
||||
function broadcast(message, sProtocol)
|
||||
expect(2, sProtocol, "string", "nil")
|
||||
send( CHANNEL_BROADCAST, message, sProtocol )
|
||||
send(CHANNEL_BROADCAST, message, sProtocol)
|
||||
end
|
||||
|
||||
function receive( sProtocolFilter, nTimeout )
|
||||
--- Wait for a rednet message to be received, or until `nTimeout` seconds have
|
||||
-- elapsed.
|
||||
--
|
||||
-- @tparam[opt] string sProtocolFilter The protocol the received message must be
|
||||
-- sent with. If specified, any messages not sent under this protocol will be
|
||||
-- discarded.
|
||||
-- @tparam[opt] number nTimeout The number of seconds to wait if no message is
|
||||
-- received.
|
||||
-- @treturn[1] number The computer which sent this message
|
||||
-- @return[1] The received message
|
||||
-- @treturn[1] string|nil The protocol this message was sent under.
|
||||
-- @treturn[2] nil If the timeout elapsed and no message was received.
|
||||
-- @see rednet.broadcast
|
||||
-- @see rednet.send
|
||||
function receive(sProtocolFilter, nTimeout)
|
||||
-- The parameters used to be ( nTimeout ), detect this case for backwards compatibility
|
||||
if type(sProtocolFilter) == "number" and nTimeout == nil then
|
||||
sProtocolFilter, nTimeout = nil, sProtocolFilter
|
||||
@@ -108,7 +189,7 @@ function receive( sProtocolFilter, nTimeout )
|
||||
local timer = nil
|
||||
local sFilter = nil
|
||||
if nTimeout then
|
||||
timer = os.startTimer( nTimeout )
|
||||
timer = os.startTimer(nTimeout)
|
||||
sFilter = nil
|
||||
else
|
||||
sFilter = "rednet_message"
|
||||
@@ -116,7 +197,7 @@ function receive( sProtocolFilter, nTimeout )
|
||||
|
||||
-- Wait for events
|
||||
while true do
|
||||
local sEvent, p1, p2, p3 = os.pullEvent( sFilter )
|
||||
local sEvent, p1, p2, p3 = os.pullEvent(sFilter)
|
||||
if sEvent == "rednet_message" then
|
||||
-- Return the first matching rednet_message
|
||||
local nSenderID, message, sProtocol = p1, p2, p3
|
||||
@@ -132,26 +213,62 @@ function receive( sProtocolFilter, nTimeout )
|
||||
end
|
||||
end
|
||||
|
||||
function host( sProtocol, sHostname )
|
||||
--- Register the system as "hosting" the desired protocol under the specified
|
||||
-- name. If a rednet @{rednet.lookup|lookup} is performed for that protocol (and
|
||||
-- maybe name) on the same network, the registered system will automatically
|
||||
-- respond via a background process, hence providing the system performing the
|
||||
-- lookup with its ID number.
|
||||
--
|
||||
-- Multiple computers may not register themselves on the same network as having
|
||||
-- the same names against the same protocols, and the title `localhost` is
|
||||
-- specifically reserved. They may, however, share names as long as their hosted
|
||||
-- protocols are different, or if they only join a given network after
|
||||
-- "registering" themselves before doing so (eg while offline or part of a
|
||||
-- different network).
|
||||
--
|
||||
-- @tparam string sProtocol The protocol this computer provides.
|
||||
-- @tparam string sHostname The name this protocol exposes for the given protocol.
|
||||
-- @throws If trying to register a hostname which is reserved, or currently in use.
|
||||
-- @see rednet.unhost
|
||||
-- @see rednet.lookup
|
||||
function host(sProtocol, sHostname)
|
||||
expect(1, sProtocol, "string")
|
||||
expect(2, sHostname, "string")
|
||||
if sHostname == "localhost" then
|
||||
error( "Reserved hostname", 2 )
|
||||
error("Reserved hostname", 2)
|
||||
end
|
||||
if tHostnames[ sProtocol ] ~= sHostname then
|
||||
if lookup( sProtocol, sHostname ) ~= nil then
|
||||
error( "Hostname in use", 2 )
|
||||
if tHostnames[sProtocol] ~= sHostname then
|
||||
if lookup(sProtocol, sHostname) ~= nil then
|
||||
error("Hostname in use", 2)
|
||||
end
|
||||
tHostnames[ sProtocol ] = sHostname
|
||||
tHostnames[sProtocol] = sHostname
|
||||
end
|
||||
end
|
||||
|
||||
function unhost( sProtocol )
|
||||
--- Stop @{rednet.host|hosting} a specific protocol, meaning it will no longer
|
||||
--- respond to @{rednet.lookup} requests.
|
||||
--
|
||||
-- @tparam string sProtocol The protocol to unregister your self from.
|
||||
function unhost(sProtocol)
|
||||
expect(1, sProtocol, "string")
|
||||
tHostnames[ sProtocol ] = nil
|
||||
tHostnames[sProtocol] = nil
|
||||
end
|
||||
|
||||
function lookup( sProtocol, sHostname )
|
||||
--- Search the local rednet network for systems @{rednet.host|hosting} the
|
||||
-- desired protocol and returns any computer IDs that respond as "registered"
|
||||
-- against it.
|
||||
--
|
||||
-- If a hostname is specified, only one ID will be returned (assuming an exact
|
||||
-- match is found).
|
||||
--
|
||||
-- @tparam string sProtocol The protocol to search for.
|
||||
-- @tparam[opt] string sHostname The hostname to search for.
|
||||
--
|
||||
-- @treturn[1] { number }|nil A list of computer IDs hosting the given
|
||||
-- protocol, or @{nil} if none exist.
|
||||
-- @treturn[2] number|nil The computer ID with the provided hostname and protocol,
|
||||
-- or @{nil} if none exists.
|
||||
function lookup(sProtocol, sHostname)
|
||||
expect(1, sProtocol, "string")
|
||||
expect(2, sHostname, "string", "nil")
|
||||
|
||||
@@ -162,30 +279,30 @@ function lookup( sProtocol, sHostname )
|
||||
end
|
||||
|
||||
-- Check localhost first
|
||||
if tHostnames[ sProtocol ] then
|
||||
if tHostnames[sProtocol] then
|
||||
if sHostname == nil then
|
||||
table.insert( tResults, os.getComputerID() )
|
||||
elseif sHostname == "localhost" or sHostname == tHostnames[ sProtocol ] then
|
||||
table.insert(tResults, os.getComputerID())
|
||||
elseif sHostname == "localhost" or sHostname == tHostnames[sProtocol] then
|
||||
return os.getComputerID()
|
||||
end
|
||||
end
|
||||
|
||||
if not isOpen() then
|
||||
if tResults then
|
||||
return table.unpack( tResults )
|
||||
return table.unpack(tResults)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Broadcast a lookup packet
|
||||
broadcast( {
|
||||
broadcast({
|
||||
sType = "lookup",
|
||||
sProtocol = sProtocol,
|
||||
sHostname = sHostname,
|
||||
}, "dns" )
|
||||
}, "dns")
|
||||
|
||||
-- Start a timer
|
||||
local timer = os.startTimer( 2 )
|
||||
local timer = os.startTimer(2)
|
||||
|
||||
-- Wait for events
|
||||
while true do
|
||||
@@ -196,7 +313,7 @@ function lookup( sProtocol, sHostname )
|
||||
if sMessageProtocol == "dns" and type(tMessage) == "table" and tMessage.sType == "lookup response" then
|
||||
if tMessage.sProtocol == sProtocol then
|
||||
if sHostname == nil then
|
||||
table.insert( tResults, nSenderID )
|
||||
table.insert(tResults, nSenderID)
|
||||
elseif tMessage.sHostname == sHostname then
|
||||
return nSenderID
|
||||
end
|
||||
@@ -210,15 +327,17 @@ function lookup( sProtocol, sHostname )
|
||||
end
|
||||
end
|
||||
if tResults then
|
||||
return table.unpack( tResults )
|
||||
return table.unpack(tResults)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local bRunning = false
|
||||
|
||||
--- @local
|
||||
function run()
|
||||
if bRunning then
|
||||
error( "rednet is already running", 2 )
|
||||
error("rednet is already running", 2)
|
||||
end
|
||||
bRunning = true
|
||||
|
||||
@@ -227,12 +346,12 @@ function run()
|
||||
if sEvent == "modem_message" then
|
||||
-- Got a modem message, process it and add it to the rednet event queue
|
||||
local sModem, nChannel, nReplyChannel, tMessage = p1, p2, p3, p4
|
||||
if isOpen( sModem ) and ( nChannel == os.getComputerID() or nChannel == CHANNEL_BROADCAST ) then
|
||||
if type( tMessage ) == "table" and tMessage.nMessageID then
|
||||
if not tReceivedMessages[ tMessage.nMessageID ] then
|
||||
tReceivedMessages[ tMessage.nMessageID ] = true
|
||||
tReceivedMessageTimeouts[ os.startTimer( 30 ) ] = tMessage.nMessageID
|
||||
os.queueEvent( "rednet_message", nReplyChannel, tMessage.message, tMessage.sProtocol )
|
||||
if isOpen(sModem) and (nChannel == os.getComputerID() or nChannel == CHANNEL_BROADCAST) then
|
||||
if type(tMessage) == "table" and tMessage.nMessageID then
|
||||
if not tReceivedMessages[tMessage.nMessageID] then
|
||||
tReceivedMessages[tMessage.nMessageID] = true
|
||||
tReceivedMessageTimeouts[os.startTimer(30)] = tMessage.nMessageID
|
||||
os.queueEvent("rednet_message", nReplyChannel, tMessage.message, tMessage.sProtocol)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -241,23 +360,23 @@ function run()
|
||||
-- Got a rednet message (queued from above), respond to dns lookup
|
||||
local nSenderID, tMessage, sProtocol = p1, p2, p3
|
||||
if sProtocol == "dns" and type(tMessage) == "table" and tMessage.sType == "lookup" then
|
||||
local sHostname = tHostnames[ tMessage.sProtocol ]
|
||||
local sHostname = tHostnames[tMessage.sProtocol]
|
||||
if sHostname ~= nil and (tMessage.sHostname == nil or tMessage.sHostname == sHostname) then
|
||||
rednet.send( nSenderID, {
|
||||
rednet.send(nSenderID, {
|
||||
sType = "lookup response",
|
||||
sHostname = sHostname,
|
||||
sProtocol = tMessage.sProtocol,
|
||||
}, "dns" )
|
||||
}, "dns")
|
||||
end
|
||||
end
|
||||
|
||||
elseif sEvent == "timer" then
|
||||
-- Got a timer event, use it to clear the event queue
|
||||
local nTimer = p1
|
||||
local nMessage = tReceivedMessageTimeouts[ nTimer ]
|
||||
local nMessage = tReceivedMessageTimeouts[nTimer]
|
||||
if nMessage then
|
||||
tReceivedMessageTimeouts[ nTimer ] = nil
|
||||
tReceivedMessages[ nMessage ] = nil
|
||||
tReceivedMessageTimeouts[nTimer] = nil
|
||||
tReceivedMessages[nMessage] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,62 +1,197 @@
|
||||
local expect = dofile("rom/modules/main/cc/expect.lua").expect
|
||||
--- The settings API allows to store values and save them to a file for
|
||||
-- persistent configurations for CraftOS and your programs.
|
||||
--
|
||||
-- By default, the settings API will load its configuration from the
|
||||
-- `/.settings` file. One can then use @{settings.save} to update the file.
|
||||
--
|
||||
-- @module settings
|
||||
|
||||
local tSettings = {}
|
||||
local expect = dofile("rom/modules/main/cc/expect.lua")
|
||||
local type, expect, field = type, expect.expect, expect.field
|
||||
|
||||
function set( sName, value )
|
||||
expect(1, sName, "string")
|
||||
local details, values = {}, {}
|
||||
|
||||
local function reserialize(value)
|
||||
if type(value) ~= "table" then return value end
|
||||
return textutils.unserialize(textutils.serialize(value))
|
||||
end
|
||||
|
||||
local function copy(value)
|
||||
if type(value) ~= "table" then return value end
|
||||
local result = {}
|
||||
for k, v in pairs(value) do result[k] = copy(v) end
|
||||
return result
|
||||
end
|
||||
|
||||
local valid_types = { "number", "string", "boolean", "table" }
|
||||
for _, v in ipairs(valid_types) do valid_types[v] = true end
|
||||
|
||||
--- Define a new setting, optional specifying various properties about it.
|
||||
--
|
||||
-- While settings do not have to be added before being used, doing so allows
|
||||
-- you to provide defaults and additional metadata.
|
||||
--
|
||||
-- @tparam string name The name of this option
|
||||
-- @tparam[opt] { description? = string, default? = value, type? = string } options
|
||||
-- Options for this setting. This table accepts the following fields:
|
||||
--
|
||||
-- - `description`: A description which may be printed when running the `set` program.
|
||||
-- - `default`: A default value, which is returned by @{settings.get} if the
|
||||
-- setting has not been changed.
|
||||
-- - `type`: Require values to be of this type. @{set|Setting} the value to another type
|
||||
-- will error.
|
||||
function define(name, options)
|
||||
expect(1, name, "string")
|
||||
expect(2, options, "table", nil)
|
||||
|
||||
if options then
|
||||
options = {
|
||||
description = field(options, "description", "string", "nil"),
|
||||
default = reserialize(field(options, "default", "number", "string", "boolean", "table", "nil")),
|
||||
type = field(options, "type", "string", "nil"),
|
||||
}
|
||||
|
||||
if options.type and not valid_types[options.type] then
|
||||
error(("Unknown type %q. Expected one of %s."):format(options.type, table.concat(valid_types, ", ")), 2)
|
||||
end
|
||||
else
|
||||
options = {}
|
||||
end
|
||||
|
||||
details[name] = options
|
||||
end
|
||||
|
||||
--- Remove a @{define|definition} of a setting.
|
||||
--
|
||||
-- If a setting has been changed, this does not remove its value. Use @{settings.unset}
|
||||
-- for that.
|
||||
--
|
||||
-- @tparam string name The name of this option
|
||||
function undefine(name)
|
||||
expect(1, name, "string")
|
||||
details[name] = nil
|
||||
end
|
||||
|
||||
local function set_value(name, value)
|
||||
local new = reserialize(value)
|
||||
local old = values[name]
|
||||
if old == nil then
|
||||
local opt = details[name]
|
||||
old = opt and opt.default
|
||||
end
|
||||
|
||||
values[name] = new
|
||||
if old ~= new then
|
||||
-- This should be safe, as os.queueEvent copies values anyway.
|
||||
os.queueEvent("setting_changed", name, new, old)
|
||||
end
|
||||
end
|
||||
|
||||
--- Set the value of a setting.
|
||||
--
|
||||
-- @tparam string name The name of the setting to set
|
||||
-- @param value The setting's value. This cannot be `nil`, and must be
|
||||
-- serialisable by @{textutils.serialize}.
|
||||
-- @throws If this value cannot be serialised
|
||||
-- @see settings.unset
|
||||
function set(name, value)
|
||||
expect(1, name, "string")
|
||||
expect(2, value, "number", "string", "boolean", "table")
|
||||
|
||||
if type(value) == "table" then
|
||||
-- Ensure value is serializeable
|
||||
value = textutils.unserialize( textutils.serialize(value) )
|
||||
end
|
||||
tSettings[ sName ] = value
|
||||
local opt = details[name]
|
||||
if opt and opt.type then expect(2, value, opt.type) end
|
||||
|
||||
set_value(name, value)
|
||||
end
|
||||
|
||||
local copy
|
||||
function copy( value )
|
||||
if type(value) == "table" then
|
||||
local result = {}
|
||||
for k, v in pairs(value) do
|
||||
result[k] = copy(v)
|
||||
end
|
||||
return result
|
||||
else
|
||||
return value
|
||||
end
|
||||
end
|
||||
|
||||
function get( sName, default )
|
||||
expect(1, sName, "string")
|
||||
local result = tSettings[ sName ]
|
||||
--- Get the value of a setting.
|
||||
--
|
||||
-- @tparam string name The name of the setting to get.
|
||||
-- @param[opt] default The value to use should there be pre-existing value for
|
||||
-- this setting. If not given, it will use the setting's default value if given,
|
||||
-- or `nil` otherwise.
|
||||
-- @return The setting's, or the default if the setting has not been changed.
|
||||
function get(name, default)
|
||||
expect(1, name, "string")
|
||||
local result = values[name]
|
||||
if result ~= nil then
|
||||
return copy(result)
|
||||
else
|
||||
elseif default ~= nil then
|
||||
return default
|
||||
else
|
||||
local opt = details[name]
|
||||
return opt and copy(opt.default)
|
||||
end
|
||||
end
|
||||
|
||||
function unset( sName )
|
||||
expect(1, sName, "string")
|
||||
tSettings[ sName ] = nil
|
||||
--- Get details about a specific setting.
|
||||
--
|
||||
-- @tparam string name The name of the setting to get.
|
||||
-- @treturn { description? = string, default? = value, type? = string, value? = value }
|
||||
-- Information about this setting. This includes all information from @{settings.define},
|
||||
-- as well as this setting's value.
|
||||
function getDetails(name)
|
||||
expect(1, name, "string")
|
||||
local deets = copy(details[name]) or {}
|
||||
deets.value = values[name]
|
||||
deets.changed = deets.value ~= nil
|
||||
if deets.value == nil then deets.value = deets.default end
|
||||
return deets
|
||||
end
|
||||
|
||||
--- Remove the value of a setting, setting it to the default.
|
||||
--
|
||||
-- @{settings.get} will return the default value until the setting's value is
|
||||
-- @{settings.set|set}, or the computer is rebooted.
|
||||
--
|
||||
-- @tparam string name The name of the setting to unset.
|
||||
-- @see settings.set
|
||||
-- @see settings.clear
|
||||
function unset(name)
|
||||
expect(1, name, "string")
|
||||
set_value(name, nil)
|
||||
end
|
||||
|
||||
--- Resets the value of all settings. Equivalent to calling @{settings.unset}
|
||||
--- on every setting.
|
||||
--
|
||||
-- @see settings.unset
|
||||
function clear()
|
||||
tSettings = {}
|
||||
for name in pairs(values) do
|
||||
set_value(name, nil)
|
||||
end
|
||||
end
|
||||
|
||||
--- Get the names of all currently defined settings.
|
||||
--
|
||||
-- @treturn { string } An alphabetically sorted list of all currently-defined
|
||||
-- settings.
|
||||
function getNames()
|
||||
local result = {}
|
||||
for k in pairs( tSettings ) do
|
||||
result[ #result + 1 ] = k
|
||||
local result, n = {}, 1
|
||||
for k in pairs(details) do
|
||||
result[n], n = k, n + 1
|
||||
end
|
||||
for k in pairs(values) do
|
||||
if not details[k] then result[n], n = k, n + 1 end
|
||||
end
|
||||
table.sort(result)
|
||||
return result
|
||||
end
|
||||
|
||||
function load( sPath )
|
||||
expect(1, sPath, "string")
|
||||
local file = fs.open( sPath, "r" )
|
||||
--- Load settings from the given file.
|
||||
--
|
||||
-- Existing settings will be merged with any pre-existing ones. Conflicting
|
||||
-- entries will be overwritten, but any others will be preserved.
|
||||
--
|
||||
-- @tparam[opt] string sPath The file to load from, defaulting to `.settings`.
|
||||
-- @treturn boolean Whether settings were successfully read from this
|
||||
-- file. Reasons for failure may include the file not existing or being
|
||||
-- corrupted.
|
||||
--
|
||||
-- @see settings.save
|
||||
function load(sPath)
|
||||
expect(1, sPath, "string", "nil")
|
||||
local file = fs.open(sPath or ".settings", "r")
|
||||
if not file then
|
||||
return false
|
||||
end
|
||||
@@ -64,29 +199,41 @@ function load( sPath )
|
||||
local sText = file.readAll()
|
||||
file.close()
|
||||
|
||||
local tFile = textutils.unserialize( sText )
|
||||
local tFile = textutils.unserialize(sText)
|
||||
if type(tFile) ~= "table" then
|
||||
return false
|
||||
end
|
||||
|
||||
for k, v in pairs(tFile) do
|
||||
if type(k) == "string" and
|
||||
(type(v) == "string" or type(v) == "number" or type(v) == "boolean" or type(v) == "table") then
|
||||
set( k, v )
|
||||
local ty_v = type(k)
|
||||
if type(k) == "string" and (ty_v == "string" or ty_v == "number" or ty_v == "boolean" or ty_v == "table") then
|
||||
local opt = details[name]
|
||||
if not opt or not opt.type or ty_v == opt.type then
|
||||
set_value(k, v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function save( sPath )
|
||||
expect(1, sPath, "string")
|
||||
local file = fs.open( sPath, "w" )
|
||||
--- Save settings to the given file.
|
||||
--
|
||||
-- This will entirely overwrite the pre-existing file. Settings defined in the
|
||||
-- file, but not currently loaded will be removed.
|
||||
--
|
||||
-- @tparam[opt] string sPath The path to save settings to, defaulting to `.settings`.
|
||||
-- @treturn boolean If the settings were successfully saved.
|
||||
--
|
||||
-- @see settings.load
|
||||
function save(sPath)
|
||||
expect(1, sPath, "string", "nil")
|
||||
local file = fs.open(sPath or ".settings", "w")
|
||||
if not file then
|
||||
return false
|
||||
end
|
||||
|
||||
file.write( textutils.serialize( tSettings ) )
|
||||
file.write(textutils.serialize(values))
|
||||
file.close()
|
||||
|
||||
return true
|
||||
|
||||
@@ -1,26 +1,49 @@
|
||||
--- The Terminal API provides functions for writing text to the terminal and
|
||||
-- monitors, and drawing ASCII graphics.
|
||||
--
|
||||
-- @module term
|
||||
|
||||
local expect = dofile("rom/modules/main/cc/expect.lua").expect
|
||||
|
||||
local native = term.native and term.native() or term
|
||||
local redirectTarget = native
|
||||
|
||||
local function wrap( _sFunction )
|
||||
return function( ... )
|
||||
return redirectTarget[ _sFunction ]( ... )
|
||||
local function wrap(_sFunction)
|
||||
return function(...)
|
||||
return redirectTarget[_sFunction](...)
|
||||
end
|
||||
end
|
||||
|
||||
local term = {}
|
||||
local term = _ENV
|
||||
|
||||
term.redirect = function( target )
|
||||
--- Redirects terminal output to a monitor, a @{window}, or any other custom
|
||||
-- terminal object. Once the redirect is performed, any calls to a "term"
|
||||
-- function - or to a function that makes use of a term function, as @{print} -
|
||||
-- will instead operate with the new terminal object.
|
||||
--
|
||||
-- A "terminal object" is simply a table that contains functions with the same
|
||||
-- names - and general features - as those found in the term table. For example,
|
||||
-- a wrapped monitor is suitable.
|
||||
--
|
||||
-- The redirect can be undone by pointing back to the previous terminal object
|
||||
-- (which this function returns whenever you switch).
|
||||
--
|
||||
-- @tparam Redirect target The terminal redirect the @{term} API will draw to.
|
||||
-- @treturn Redirect The previous redirect object, as returned by
|
||||
-- @{term.current}.
|
||||
-- @usage
|
||||
-- Redirect to a monitor on the right of the computer.
|
||||
-- term.redirect(peripheral.wrap("right"))
|
||||
term.redirect = function(target)
|
||||
expect(1, target, "table")
|
||||
if target == term or target == _G.term then
|
||||
error( "term is not a recommended redirect target, try term.current() instead", 2 )
|
||||
error("term is not a recommended redirect target, try term.current() instead", 2)
|
||||
end
|
||||
for k, v in pairs( native ) do
|
||||
if type( k ) == "string" and type( v ) == "function" then
|
||||
if type( target[k] ) ~= "function" then
|
||||
for k, v in pairs(native) do
|
||||
if type(k) == "string" and type(v) == "function" then
|
||||
if type(target[k]) ~= "function" then
|
||||
target[k] = function()
|
||||
error( "Redirect object is missing method " .. k .. ".", 2 )
|
||||
error("Redirect object is missing method " .. k .. ".", 2)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -30,31 +53,36 @@ term.redirect = function( target )
|
||||
return oldRedirectTarget
|
||||
end
|
||||
|
||||
--- Returns the current terminal object of the computer.
|
||||
--
|
||||
-- @treturn Redirect The current terminal redirect
|
||||
-- @usage
|
||||
-- Create a new @{window} which draws to the current redirect target
|
||||
-- window.create(term.current(), 1, 1, 10, 10)
|
||||
term.current = function()
|
||||
return redirectTarget
|
||||
end
|
||||
|
||||
--- Get the native terminal object of the current computer.
|
||||
--
|
||||
-- It is recommended you do not use this function unless you absolutely have
|
||||
-- to. In a multitasked environment, @{term.native} will _not_ be the current
|
||||
-- terminal object, and so drawing may interfere with other programs.
|
||||
--
|
||||
-- @treturn Redirect The native terminal redirect.
|
||||
term.native = function()
|
||||
-- NOTE: please don't use this function unless you have to.
|
||||
-- If you're running in a redirected or multitasked enviorment, term.native() will NOT be
|
||||
-- the current terminal when your program starts up. It is far better to use term.current()
|
||||
return native
|
||||
end
|
||||
|
||||
-- Some methods shouldn't go through redirects, so we move them to the main
|
||||
-- term API.
|
||||
for _, method in ipairs { "nativePaletteColor", "nativePaletteColour"} do
|
||||
for _, method in ipairs { "nativePaletteColor", "nativePaletteColour" } do
|
||||
term[method] = native[method]
|
||||
native[method] = nil
|
||||
end
|
||||
|
||||
for k, v in pairs( native ) do
|
||||
if type( k ) == "string" and type( v ) == "function" and term[k] == nil then
|
||||
term[k] = wrap( k )
|
||||
for k, v in pairs(native) do
|
||||
if type(k) == "string" and type(v) == "function" and rawget(term, k) == nil then
|
||||
term[k] = wrap(k)
|
||||
end
|
||||
end
|
||||
|
||||
local env = _ENV
|
||||
for k, v in pairs( term ) do
|
||||
env[k] = v
|
||||
end
|
||||
|
||||
@@ -1,32 +1,65 @@
|
||||
local expect = dofile("rom/modules/main/cc/expect.lua").expect
|
||||
--- The `textutils` API provides helpful utilities for formatting and
|
||||
-- manipulating strings.
|
||||
--
|
||||
-- @module textutils
|
||||
|
||||
function slowWrite( sText, nRate )
|
||||
local expect = dofile("rom/modules/main/cc/expect.lua")
|
||||
local expect, field = expect.expect, expect.field
|
||||
|
||||
--- Slowly writes string text at current cursor position,
|
||||
-- character-by-character.
|
||||
--
|
||||
-- Like @{write}, this does not insert a newline at the end.
|
||||
--
|
||||
-- @tparam string sText The the text to write to the screen
|
||||
-- @tparam[opt] number nRate The number of characters to write each second,
|
||||
-- Defaults to 20.
|
||||
-- @usage textutils.slowWrite("Hello, world!")
|
||||
-- @usage textutils.slowWrite("Hello, world!", 5)
|
||||
function slowWrite(sText, nRate)
|
||||
expect(2, nRate, "number", "nil")
|
||||
nRate = nRate or 20
|
||||
if nRate < 0 then
|
||||
error( "Rate must be positive", 2 )
|
||||
error("Rate must be positive", 2)
|
||||
end
|
||||
local nSleep = 1 / nRate
|
||||
|
||||
sText = tostring( sText )
|
||||
sText = tostring(sText)
|
||||
local x, y = term.getCursorPos()
|
||||
local len = #sText
|
||||
|
||||
for n = 1, len do
|
||||
term.setCursorPos( x, y )
|
||||
sleep( nSleep )
|
||||
local nLines = write( string.sub( sText, 1, n ) )
|
||||
term.setCursorPos(x, y)
|
||||
sleep(nSleep)
|
||||
local nLines = write(string.sub(sText, 1, n))
|
||||
local _, newY = term.getCursorPos()
|
||||
y = newY - nLines
|
||||
end
|
||||
end
|
||||
|
||||
function slowPrint( sText, nRate )
|
||||
slowWrite( sText, nRate )
|
||||
--- Slowly prints string text at current cursor position,
|
||||
-- character-by-character.
|
||||
--
|
||||
-- Like @{print}, this inserts a newline after printing.
|
||||
--
|
||||
-- @tparam string sText The the text to write to the screen
|
||||
-- @tparam[opt] number nRate The number of characters to write each second,
|
||||
-- Defaults to 20.
|
||||
-- @usage textutils.slowPrint("Hello, world!")
|
||||
-- @usage textutils.slowPrint("Hello, world!", 5)
|
||||
function slowPrint(sText, nRate)
|
||||
slowWrite(sText, nRate)
|
||||
print()
|
||||
end
|
||||
|
||||
function formatTime( nTime, bTwentyFourHour )
|
||||
--- Takes input time and formats it in a more readable format such as `6:30 PM`.
|
||||
--
|
||||
-- @tparam number nTime The time to format, as provided by @{os.time}.
|
||||
-- @tparam[opt] boolean bTwentyFourHour Whether to format this as a 24-hour
|
||||
-- clock (`18:30`) rather than a 12-hour one (`6:30 AM`)
|
||||
-- @treturn string The formatted time
|
||||
-- @usage textutils.formatTime(os.time())
|
||||
function formatTime(nTime, bTwentyFourHour)
|
||||
expect(1, nTime, "number")
|
||||
expect(2, bTwentyFourHour, "boolean", "nil")
|
||||
local sTOD = nil
|
||||
@@ -44,26 +77,26 @@ function formatTime( nTime, bTwentyFourHour )
|
||||
local nHour = math.floor(nTime)
|
||||
local nMinute = math.floor((nTime - nHour) * 60)
|
||||
if sTOD then
|
||||
return string.format( "%d:%02d %s", nHour, nMinute, sTOD )
|
||||
return string.format("%d:%02d %s", nHour, nMinute, sTOD)
|
||||
else
|
||||
return string.format( "%d:%02d", nHour, nMinute )
|
||||
return string.format("%d:%02d", nHour, nMinute)
|
||||
end
|
||||
end
|
||||
|
||||
local function makePagedScroll( _term, _nFreeLines )
|
||||
local function makePagedScroll(_term, _nFreeLines)
|
||||
local nativeScroll = _term.scroll
|
||||
local nFreeLines = _nFreeLines or 0
|
||||
return function( _n )
|
||||
return function(_n)
|
||||
for _ = 1, _n do
|
||||
nativeScroll( 1 )
|
||||
nativeScroll(1)
|
||||
|
||||
if nFreeLines <= 0 then
|
||||
local _, h = _term.getSize()
|
||||
_term.setCursorPos( 1, h )
|
||||
_term.write( "Press any key to continue" )
|
||||
os.pullEvent( "key" )
|
||||
_term.setCursorPos(1, h)
|
||||
_term.write("Press any key to continue")
|
||||
os.pullEvent("key")
|
||||
_term.clearLine()
|
||||
_term.setCursorPos( 1, h )
|
||||
_term.setCursorPos(1, h)
|
||||
else
|
||||
nFreeLines = nFreeLines - 1
|
||||
end
|
||||
@@ -71,38 +104,55 @@ local function makePagedScroll( _term, _nFreeLines )
|
||||
end
|
||||
end
|
||||
|
||||
function pagedPrint( _sText, _nFreeLines )
|
||||
--- Prints a given string to the display.
|
||||
--
|
||||
-- If the action can be completed without scrolling, it acts much the same as
|
||||
-- @{print}; otherwise, it will throw up a "Press any key to continue" prompt at
|
||||
-- the bottom of the display. Each press will cause it to scroll down and write
|
||||
-- a single line more before prompting again, if need be.
|
||||
--
|
||||
-- @tparam string _sText The text to print to the screen.
|
||||
-- @tparam[opt] number _nFreeLines The number of lines which will be
|
||||
-- automatically scrolled before the first prompt appears (meaning _nFreeLines +
|
||||
-- 1 lines will be printed). This can be set to the terminal's height - 2 to
|
||||
-- always try to fill the screen. Defaults to 0, meaning only one line is
|
||||
-- displayed before prompting.
|
||||
-- @treturn number The number of lines printed.
|
||||
-- @usage
|
||||
-- local width, height = term.getSize()
|
||||
-- textutils.pagedPrint(("This is a rather verbose dose of repetition.\n"):rep(30), height - 2)
|
||||
function pagedPrint(_sText, _nFreeLines)
|
||||
expect(2, _nFreeLines, "number", "nil")
|
||||
-- Setup a redirector
|
||||
local oldTerm = term.current()
|
||||
local newTerm = {}
|
||||
for k, v in pairs( oldTerm ) do
|
||||
for k, v in pairs(oldTerm) do
|
||||
newTerm[k] = v
|
||||
end
|
||||
newTerm.scroll = makePagedScroll( oldTerm, _nFreeLines )
|
||||
term.redirect( newTerm )
|
||||
newTerm.scroll = makePagedScroll(oldTerm, _nFreeLines)
|
||||
term.redirect(newTerm)
|
||||
|
||||
-- Print the text
|
||||
local result
|
||||
local ok, err = pcall( function()
|
||||
local ok, err = pcall(function()
|
||||
if _sText ~= nil then
|
||||
result = print( _sText )
|
||||
result = print(_sText)
|
||||
else
|
||||
result = print()
|
||||
end
|
||||
end )
|
||||
end)
|
||||
|
||||
-- Removed the redirector
|
||||
term.redirect( oldTerm )
|
||||
term.redirect(oldTerm)
|
||||
|
||||
-- Propogate errors
|
||||
if not ok then
|
||||
error( err, 0 )
|
||||
error(err, 0)
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
local function tabulateCommon( bPaged, ... )
|
||||
local function tabulateCommon(bPaged, ...)
|
||||
local tAll = table.pack(...)
|
||||
for i = 1, tAll.n do
|
||||
expect(i, tAll[i], "number", "table")
|
||||
@@ -110,17 +160,17 @@ local function tabulateCommon( bPaged, ... )
|
||||
|
||||
local w, h = term.getSize()
|
||||
local nMaxLen = w / 8
|
||||
for n, t in ipairs( tAll ) do
|
||||
for n, t in ipairs(tAll) do
|
||||
if type(t) == "table" then
|
||||
for nu, sItem in pairs(t) do
|
||||
if type( sItem ) ~= "string" then
|
||||
error( "bad argument #" .. n .. "." .. nu .. " (expected string, got " .. type( sItem ) .. ")", 3 )
|
||||
if type(sItem) ~= "string" then
|
||||
error("bad argument #" .. n .. "." .. nu .. " (expected string, got " .. type(sItem) .. ")", 3)
|
||||
end
|
||||
nMaxLen = math.max( #sItem + 1, nMaxLen )
|
||||
nMaxLen = math.max(#sItem + 1, nMaxLen)
|
||||
end
|
||||
end
|
||||
end
|
||||
local nCols = math.floor( w / nMaxLen )
|
||||
local nCols = math.floor(w / nMaxLen)
|
||||
local nLines = 0
|
||||
local function newLine()
|
||||
if bPaged and nLines >= h - 3 then
|
||||
@@ -131,9 +181,9 @@ local function tabulateCommon( bPaged, ... )
|
||||
nLines = nLines + 1
|
||||
end
|
||||
|
||||
local function drawCols( _t )
|
||||
local function drawCols(_t)
|
||||
local nCol = 1
|
||||
for _, s in ipairs( _t ) do
|
||||
for _, s in ipairs(_t) do
|
||||
if nCol > nCols then
|
||||
nCol = 1
|
||||
newLine()
|
||||
@@ -141,61 +191,81 @@ local function tabulateCommon( bPaged, ... )
|
||||
|
||||
local cx, cy = term.getCursorPos()
|
||||
cx = 1 + (nCol - 1) * nMaxLen
|
||||
term.setCursorPos( cx, cy )
|
||||
term.write( s )
|
||||
term.setCursorPos(cx, cy)
|
||||
term.write(s)
|
||||
|
||||
nCol = nCol + 1
|
||||
end
|
||||
print()
|
||||
end
|
||||
for _, t in ipairs( tAll ) do
|
||||
for _, t in ipairs(tAll) do
|
||||
if type(t) == "table" then
|
||||
if #t > 0 then
|
||||
drawCols( t )
|
||||
drawCols(t)
|
||||
end
|
||||
elseif type(t) == "number" then
|
||||
term.setTextColor( t )
|
||||
term.setTextColor(t)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function tabulate( ... )
|
||||
return tabulateCommon( false, ... )
|
||||
--- Prints tables in a structured form.
|
||||
--
|
||||
-- This accepts multiple arguments, either a table or a number. When
|
||||
-- encountering a table, this will be treated as a table row, with each column
|
||||
-- width being auto-adjusted.
|
||||
--
|
||||
-- When encountering a number, this sets the text color of the subsequent rows to it.
|
||||
--
|
||||
-- @tparam {string...}|number ... The rows and text colors to display.
|
||||
-- @usage textutils.tabulate(colors.orange, { "1", "2", "3" }, colors.lightBlue, { "A", "B", "C" })
|
||||
function tabulate(...)
|
||||
return tabulateCommon(false, ...)
|
||||
end
|
||||
|
||||
function pagedTabulate( ... )
|
||||
return tabulateCommon( true, ... )
|
||||
--- Prints tables in a structured form, stopping and prompting for input should
|
||||
-- the result not fit on the terminal.
|
||||
--
|
||||
-- This functions identically to @{textutils.tabulate}, but will prompt for user
|
||||
-- input should the whole output not fit on the display.
|
||||
--
|
||||
-- @tparam {string...}|number ... The rows and text colors to display.
|
||||
-- @usage textutils.tabulate(colors.orange, { "1", "2", "3" }, colors.lightBlue, { "A", "B", "C" })
|
||||
-- @see textutils.tabulate
|
||||
-- @see textutils.pagedPrint
|
||||
function pagedTabulate(...)
|
||||
return tabulateCommon(true, ...)
|
||||
end
|
||||
|
||||
local g_tLuaKeywords = {
|
||||
[ "and" ] = true,
|
||||
[ "break" ] = true,
|
||||
[ "do" ] = true,
|
||||
[ "else" ] = true,
|
||||
[ "elseif" ] = true,
|
||||
[ "end" ] = true,
|
||||
[ "false" ] = true,
|
||||
[ "for" ] = true,
|
||||
[ "function" ] = true,
|
||||
[ "if" ] = true,
|
||||
[ "in" ] = true,
|
||||
[ "local" ] = true,
|
||||
[ "nil" ] = true,
|
||||
[ "not" ] = true,
|
||||
[ "or" ] = true,
|
||||
[ "repeat" ] = true,
|
||||
[ "return" ] = true,
|
||||
[ "then" ] = true,
|
||||
[ "true" ] = true,
|
||||
[ "until" ] = true,
|
||||
[ "while" ] = true,
|
||||
["and"] = true,
|
||||
["break"] = true,
|
||||
["do"] = true,
|
||||
["else"] = true,
|
||||
["elseif"] = true,
|
||||
["end"] = true,
|
||||
["false"] = true,
|
||||
["for"] = true,
|
||||
["function"] = true,
|
||||
["if"] = true,
|
||||
["in"] = true,
|
||||
["local"] = true,
|
||||
["nil"] = true,
|
||||
["not"] = true,
|
||||
["or"] = true,
|
||||
["repeat"] = true,
|
||||
["return"] = true,
|
||||
["then"] = true,
|
||||
["true"] = true,
|
||||
["until"] = true,
|
||||
["while"] = true,
|
||||
}
|
||||
|
||||
local function serializeImpl( t, tTracking, sIndent )
|
||||
local function serializeImpl(t, tTracking, sIndent)
|
||||
local sType = type(t)
|
||||
if sType == "table" then
|
||||
if tTracking[t] ~= nil then
|
||||
error( "Cannot serialize table with recursive entries", 0 )
|
||||
error("Cannot serialize table with recursive entries", 0)
|
||||
end
|
||||
tTracking[t] = true
|
||||
|
||||
@@ -209,15 +279,15 @@ local function serializeImpl( t, tTracking, sIndent )
|
||||
local tSeen = {}
|
||||
for k, v in ipairs(t) do
|
||||
tSeen[k] = true
|
||||
sResult = sResult .. sSubIndent .. serializeImpl( v, tTracking, sSubIndent ) .. ",\n"
|
||||
sResult = sResult .. sSubIndent .. serializeImpl(v, tTracking, sSubIndent) .. ",\n"
|
||||
end
|
||||
for k, v in pairs(t) do
|
||||
if not tSeen[k] then
|
||||
local sEntry
|
||||
if type(k) == "string" and not g_tLuaKeywords[k] and string.match( k, "^[%a_][%a%d_]*$" ) then
|
||||
sEntry = k .. " = " .. serializeImpl( v, tTracking, sSubIndent ) .. ",\n"
|
||||
if type(k) == "string" and not g_tLuaKeywords[k] and string.match(k, "^[%a_][%a%d_]*$") then
|
||||
sEntry = k .. " = " .. serializeImpl(v, tTracking, sSubIndent) .. ",\n"
|
||||
else
|
||||
sEntry = "[ " .. serializeImpl( k, tTracking, sSubIndent ) .. " ] = " .. serializeImpl( v, tTracking, sSubIndent ) .. ",\n"
|
||||
sEntry = "[ " .. serializeImpl(k, tTracking, sSubIndent) .. " ] = " .. serializeImpl(v, tTracking, sSubIndent) .. ",\n"
|
||||
end
|
||||
sResult = sResult .. sSubIndent .. sEntry
|
||||
end
|
||||
@@ -227,31 +297,52 @@ local function serializeImpl( t, tTracking, sIndent )
|
||||
end
|
||||
|
||||
elseif sType == "string" then
|
||||
return string.format( "%q", t )
|
||||
return string.format("%q", t)
|
||||
|
||||
elseif sType == "number" or sType == "boolean" or sType == "nil" then
|
||||
return tostring(t)
|
||||
|
||||
else
|
||||
error( "Cannot serialize type " .. sType, 0 )
|
||||
error("Cannot serialize type " .. sType, 0)
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
empty_json_array = setmetatable({}, {
|
||||
__newindex = function()
|
||||
error("attempt to mutate textutils.empty_json_array", 2)
|
||||
end,
|
||||
})
|
||||
local function mk_tbl(str, name)
|
||||
local msg = "attempt to mutate textutils." .. name
|
||||
return setmetatable({}, {
|
||||
__newindex = function() error(msg, 2) end,
|
||||
__tostring = function() return str end,
|
||||
})
|
||||
end
|
||||
|
||||
local function serializeJSONImpl( t, tTracking, bNBTStyle )
|
||||
--- A table representing an empty JSON array, in order to distinguish it from an
|
||||
-- empty JSON object.
|
||||
--
|
||||
-- The contents of this table should not be modified.
|
||||
--
|
||||
-- @usage textutils.serialiseJSON(textutils.empty_json_array)
|
||||
-- @see textutils.serialiseJSON
|
||||
-- @see textutils.unserialiseJSON
|
||||
empty_json_array = mk_tbl("[]", "empty_json_array")
|
||||
|
||||
--- A table representing the JSON null value.
|
||||
--
|
||||
-- The contents of this table should not be modified.
|
||||
--
|
||||
-- @usage textutils.serialiseJSON(textutils.json_null)
|
||||
-- @see textutils.serialiseJSON
|
||||
-- @see textutils.unserialiseJSON
|
||||
json_null = mk_tbl("null", "json_null")
|
||||
|
||||
local function serializeJSONImpl(t, tTracking, bNBTStyle)
|
||||
local sType = type(t)
|
||||
if t == empty_json_array then
|
||||
return "[]"
|
||||
if t == empty_json_array then return "[]"
|
||||
elseif t == json_null then return "null"
|
||||
|
||||
elseif sType == "table" then
|
||||
if tTracking[t] ~= nil then
|
||||
error( "Cannot serialize table with recursive entries", 0 )
|
||||
error("Cannot serialize table with recursive entries", 0)
|
||||
end
|
||||
tTracking[t] = true
|
||||
|
||||
@@ -268,9 +359,9 @@ local function serializeJSONImpl( t, tTracking, bNBTStyle )
|
||||
if type(k) == "string" then
|
||||
local sEntry
|
||||
if bNBTStyle then
|
||||
sEntry = tostring(k) .. ":" .. serializeJSONImpl( v, tTracking, bNBTStyle )
|
||||
sEntry = tostring(k) .. ":" .. serializeJSONImpl(v, tTracking, bNBTStyle)
|
||||
else
|
||||
sEntry = string.format( "%q", k ) .. ":" .. serializeJSONImpl( v, tTracking, bNBTStyle )
|
||||
sEntry = string.format("%q", k) .. ":" .. serializeJSONImpl(v, tTracking, bNBTStyle)
|
||||
end
|
||||
if nObjectSize == 0 then
|
||||
sObjectResult = sObjectResult .. sEntry
|
||||
@@ -281,7 +372,7 @@ local function serializeJSONImpl( t, tTracking, bNBTStyle )
|
||||
end
|
||||
end
|
||||
for _, v in ipairs(t) do
|
||||
local sEntry = serializeJSONImpl( v, tTracking, bNBTStyle )
|
||||
local sEntry = serializeJSONImpl(v, tTracking, bNBTStyle)
|
||||
if nArraySize == 0 then
|
||||
sArrayResult = sArrayResult .. sEntry
|
||||
else
|
||||
@@ -299,27 +390,247 @@ local function serializeJSONImpl( t, tTracking, bNBTStyle )
|
||||
end
|
||||
|
||||
elseif sType == "string" then
|
||||
return string.format( "%q", t )
|
||||
return string.format("%q", t)
|
||||
|
||||
elseif sType == "number" or sType == "boolean" then
|
||||
return tostring(t)
|
||||
|
||||
else
|
||||
error( "Cannot serialize type " .. sType, 0 )
|
||||
error("Cannot serialize type " .. sType, 0)
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
function serialize( t )
|
||||
local tTracking = {}
|
||||
return serializeImpl( t, tTracking, "" )
|
||||
local unserialise_json
|
||||
do
|
||||
local sub, find, match, concat, tonumber = string.sub, string.find, string.match, table.concat, tonumber
|
||||
|
||||
--- Skip any whitespace
|
||||
local function skip(str, pos)
|
||||
local _, last = find(str, "^[ \n\r\v]+", pos)
|
||||
if last then return last + 1 else return pos end
|
||||
end
|
||||
|
||||
local escapes = {
|
||||
["b"] = '\b', ["f"] = '\f', ["n"] = '\n', ["r"] = '\r', ["t"] = '\t',
|
||||
["\""] = "\"", ["/"] = "/", ["\\"] = "\\",
|
||||
}
|
||||
|
||||
local mt = {}
|
||||
|
||||
local function error_at(pos, msg, ...)
|
||||
if select('#', ...) > 0 then msg = msg:format(...) end
|
||||
error(setmetatable({ pos = pos, msg = msg }, mt))
|
||||
end
|
||||
|
||||
local function expected(pos, actual, exp)
|
||||
if actual == "" then actual = "end of input" else actual = ("%q"):format(actual) end
|
||||
error_at(pos, "Unexpected %s, expected %s.", actual, exp)
|
||||
end
|
||||
|
||||
local function parse_string(str, pos)
|
||||
local buf, n = {}, 1
|
||||
|
||||
while true do
|
||||
local c = sub(str, pos, pos)
|
||||
if c == "" then error_at(pos, "Unexpected end of input, expected '\"'.") end
|
||||
if c == '"' then break end
|
||||
|
||||
if c == '\\' then
|
||||
-- Handle the various escapes
|
||||
c = sub(str, pos + 1, pos + 1)
|
||||
if c == "" then error_at(pos, "Unexpected end of input, expected escape sequence.") end
|
||||
|
||||
if c == "u" then
|
||||
local num_str = match(str, "^%x%x%x%x", pos + 2)
|
||||
if not num_str then error_at(pos, "Malformed unicode escape %q.", sub(str, pos + 2, pos + 5)) end
|
||||
buf[n], n, pos = utf8.char(tonumber(num_str, 16)), n + 1, pos + 6
|
||||
else
|
||||
local unesc = escapes[c]
|
||||
if not unesc then error_at(pos + 1, "Unknown escape character %q.", unesc) end
|
||||
buf[n], n, pos = unesc, n + 1, pos + 2
|
||||
end
|
||||
elseif c >= '\x20' then
|
||||
buf[n], n, pos = c, n + 1, pos + 1
|
||||
else
|
||||
error_at(pos + 1, "Unescaped whitespace %q.", c)
|
||||
end
|
||||
end
|
||||
|
||||
return concat(buf, "", 1, n - 1), pos + 1
|
||||
end
|
||||
|
||||
local valid = { b = true, B = true, s = true, S = true, l = true, L = true, f = true, F = true, d = true, D = true }
|
||||
local function parse_number(str, pos, opts)
|
||||
local _, last, num_str = find(str, '^(-?%d+%.?%d*[eE]?[+-]?%d*)', pos)
|
||||
local val = tonumber(num_str)
|
||||
if not val then error_at(pos, "Malformed number %q.", num_str) end
|
||||
|
||||
if opts.nbt_style and valid[sub(str, pos + 1, pos + 1)] then return val, last + 2 end
|
||||
|
||||
return val, last + 1
|
||||
end
|
||||
|
||||
local function parse_ident(str, pos)
|
||||
local _, last, val = find(str, '^([%a][%w_]*)', pos)
|
||||
return val, last + 1
|
||||
end
|
||||
|
||||
local function decode_impl(str, pos, opts)
|
||||
local c = sub(str, pos, pos)
|
||||
if c == '"' then return parse_string(str, pos + 1)
|
||||
elseif c == "-" or c >= "0" and c <= "9" then return parse_number(str, pos, opts)
|
||||
elseif c == "t" then
|
||||
if sub(str, pos + 1, pos + 3) == "rue" then return true, pos + 4 end
|
||||
elseif c == 'f' then
|
||||
if sub(str, pos + 1, pos + 4) == "alse" then return false, pos + 5 end
|
||||
elseif c == 'n' then
|
||||
if sub(str, pos + 1, pos + 3) == "ull" then
|
||||
if opts.parse_null then
|
||||
return json_null, pos + 4
|
||||
else
|
||||
return nil, pos + 4
|
||||
end
|
||||
end
|
||||
elseif c == "{" then
|
||||
local obj = {}
|
||||
|
||||
pos = skip(str, pos + 1)
|
||||
c = sub(str, pos, pos)
|
||||
|
||||
if c == "" then return error_at(pos, "Unexpected end of input, expected '}'.") end
|
||||
if c == "}" then return obj, pos + 1 end
|
||||
|
||||
while true do
|
||||
local key, value
|
||||
if c == "\"" then key, pos = parse_string(str, pos + 1)
|
||||
elseif opts.nbt_style then key, pos = parse_ident(str, pos)
|
||||
else return expected(pos, c, "object key")
|
||||
end
|
||||
|
||||
pos = skip(str, pos)
|
||||
|
||||
c = sub(str, pos, pos)
|
||||
if c ~= ":" then return expected(pos, c, "':'") end
|
||||
|
||||
value, pos = decode_impl(str, skip(str, pos + 1), opts)
|
||||
obj[key] = value
|
||||
|
||||
-- Consume the next delimiter
|
||||
pos = skip(str, pos)
|
||||
c = sub(str, pos, pos)
|
||||
if c == "}" then break
|
||||
elseif c == "," then pos = skip(str, pos + 1)
|
||||
else return expected(pos, c, "',' or '}'")
|
||||
end
|
||||
|
||||
c = sub(str, pos, pos)
|
||||
end
|
||||
|
||||
return obj, pos + 1
|
||||
|
||||
elseif c == "[" then
|
||||
local arr, n = {}, 1
|
||||
|
||||
pos = skip(str, pos + 1)
|
||||
c = sub(str, pos, pos)
|
||||
|
||||
if c == "" then return expected(pos, c, "']'") end
|
||||
if c == "]" then return empty_json_array, pos + 1 end
|
||||
|
||||
while true do
|
||||
n, arr[n], pos = n + 1, decode_impl(str, pos, opts)
|
||||
|
||||
-- Consume the next delimiter
|
||||
pos = skip(str, pos)
|
||||
c = sub(str, pos, pos)
|
||||
if c == "]" then break
|
||||
elseif c == "," then pos = skip(str, pos + 1)
|
||||
else return expected(pos, c, "',' or ']'")
|
||||
end
|
||||
end
|
||||
|
||||
return arr, pos + 1
|
||||
elseif c == "" then error_at(pos, 'Unexpected end of input.')
|
||||
end
|
||||
|
||||
error_at(pos, "Unexpected character %q.", c)
|
||||
end
|
||||
|
||||
--- Converts a serialised JSON string back into a reassembled Lua object.
|
||||
--
|
||||
-- This may be used with @{textutils.serializeJSON}, or when communicating
|
||||
-- with command blocks or web APIs.
|
||||
--
|
||||
-- @tparam string s The serialised string to deserialise.
|
||||
-- @tparam[opt] { nbt_style? = boolean, parse_null? = boolean } options
|
||||
-- Options which control how this JSON object is parsed.
|
||||
--
|
||||
-- - `nbt_style`: When true, this will accept [stringified NBT][nbt] strings,
|
||||
-- as produced by many commands.
|
||||
-- - `parse_null`: When true, `null` will be parsed as @{json_null}, rather
|
||||
-- than `nil`.
|
||||
--
|
||||
-- [nbt]: https://minecraft.gamepedia.com/NBT_format
|
||||
-- @return[1] The deserialised object
|
||||
-- @treturn[2] nil If the object could not be deserialised.
|
||||
-- @treturn string A message describing why the JSON string is invalid.
|
||||
unserialise_json = function(s, options)
|
||||
expect(1, s, "string")
|
||||
expect(2, options, "table", "nil")
|
||||
|
||||
if options then
|
||||
field(options, "nbt_style", "boolean", "nil")
|
||||
field(options, "nbt_style", "boolean", "nil")
|
||||
else
|
||||
options = {}
|
||||
end
|
||||
|
||||
local ok, res, pos = pcall(decode_impl, s, skip(s, 1), options)
|
||||
if not ok then
|
||||
if type(res) == "table" and getmetatable(res) == mt then
|
||||
return nil, ("Malformed JSON at position %d: %s"):format(res.pos, res.msg)
|
||||
end
|
||||
|
||||
error(res, 0)
|
||||
end
|
||||
|
||||
pos = skip(s, pos)
|
||||
if pos <= #s then
|
||||
return nil, ("Malformed JSON at position %d: Unexpected trailing character %q."):format(pos, sub(s, pos, pos))
|
||||
end
|
||||
return res
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
function unserialize( s )
|
||||
--- Convert a Lua object into a textual representation, suitable for
|
||||
-- saving in a file or pretty-printing.
|
||||
--
|
||||
-- @param t The object to serialise
|
||||
-- @treturn string The serialised representation
|
||||
-- @throws If the object contains a value which cannot be
|
||||
-- serialised. This includes functions and tables which appear multiple
|
||||
-- times.
|
||||
function serialize(t)
|
||||
local tTracking = {}
|
||||
return serializeImpl(t, tTracking, "")
|
||||
end
|
||||
|
||||
serialise = serialize -- GB version
|
||||
|
||||
--- Converts a serialised string back into a reassembled Lua object.
|
||||
--
|
||||
-- This is mainly used together with @{textutils.serialize}.
|
||||
--
|
||||
-- @tparam string s The serialised string to deserialise.
|
||||
-- @return[1] The deserialised object
|
||||
-- @treturn[2] nil If the object could not be deserialised.
|
||||
function unserialize(s)
|
||||
expect(1, s, "string")
|
||||
local func = load( "return " .. s, "unserialize", "t", {} )
|
||||
local func = load("return " .. s, "unserialize", "t", {})
|
||||
if func then
|
||||
local ok, result = pcall( func )
|
||||
local ok, result = pcall(func)
|
||||
if ok then
|
||||
return result
|
||||
end
|
||||
@@ -327,14 +638,44 @@ function unserialize( s )
|
||||
return nil
|
||||
end
|
||||
|
||||
function serializeJSON( t, bNBTStyle )
|
||||
unserialise = unserialize -- GB version
|
||||
|
||||
--- Returns a JSON representation of the given data.
|
||||
--
|
||||
-- This function attempts to guess whether a table is a JSON array or
|
||||
-- object. However, empty tables are assumed to be empty objects - use
|
||||
-- @{textutils.empty_json_array} to mark an empty array.
|
||||
--
|
||||
-- This is largely intended for interacting with various functions from the
|
||||
-- @{commands} API, though may also be used in making @{http} requests.
|
||||
--
|
||||
-- @param t The value to serialise. Like @{textutils.serialise}, this should not
|
||||
-- contain recursive tables or functions.
|
||||
-- @tparam[opt] boolean bNBTStyle Whether to produce NBT-style JSON (non-quoted keys)
|
||||
-- instead of standard JSON.
|
||||
-- @treturn string The JSON representation of the input.
|
||||
-- @throws If the object contains a value which cannot be
|
||||
-- serialised. This includes functions and tables which appear multiple
|
||||
-- times.
|
||||
-- @usage textutils.serializeJSON({ values = { 1, "2", true } })
|
||||
function serializeJSON(t, bNBTStyle)
|
||||
expect(1, t, "table", "string", "number", "boolean")
|
||||
expect(2, bNBTStyle, "boolean", "nil")
|
||||
local tTracking = {}
|
||||
return serializeJSONImpl( t, tTracking, bNBTStyle or false )
|
||||
return serializeJSONImpl(t, tTracking, bNBTStyle or false)
|
||||
end
|
||||
|
||||
function urlEncode( str )
|
||||
serialiseJSON = serializeJSON -- GB version
|
||||
|
||||
unserializeJSON = unserialise_json
|
||||
unserialiseJSON = unserialise_json
|
||||
|
||||
--- Replaces certain characters in a string to make it safe for use in URLs or POST data.
|
||||
--
|
||||
-- @tparam string str The string to encode
|
||||
-- @treturn string The encoded string.
|
||||
-- @usage print("https://example.com/?view=" .. textutils.urlEncode(read()))
|
||||
function urlEncode(str)
|
||||
expect(1, str, "string")
|
||||
if str then
|
||||
str = string.gsub(str, "\n", "\r\n")
|
||||
@@ -346,40 +687,57 @@ function urlEncode( str )
|
||||
else
|
||||
-- Non-ASCII (encode as UTF-8)
|
||||
return
|
||||
string.format("%%%02X", 192 + bit32.band( bit32.arshift(n, 6), 31 ) ) ..
|
||||
string.format("%%%02X", 128 + bit32.band( n, 63 ) )
|
||||
string.format("%%%02X", 192 + bit32.band(bit32.arshift(n, 6), 31)) ..
|
||||
string.format("%%%02X", 128 + bit32.band(n, 63))
|
||||
end
|
||||
end )
|
||||
end)
|
||||
str = string.gsub(str, " ", "+")
|
||||
end
|
||||
return str
|
||||
end
|
||||
|
||||
local tEmpty = {}
|
||||
function complete( sSearchText, tSearchTable )
|
||||
|
||||
--- Provides a list of possible completions for a partial Lua expression.
|
||||
--
|
||||
-- If the completed element is a table, suggestions will have `.` appended to
|
||||
-- them. Similarly, functions have `(` appended to them.
|
||||
--
|
||||
-- @tparam string sSearchText The partial expression to complete, such as a
|
||||
-- variable name or table index.
|
||||
--
|
||||
-- @tparam[opt] table tSearchTable The table to find variables in, defaulting to
|
||||
-- the global environment (@{_G}). The function also searches the "parent"
|
||||
-- environment via the `__index` metatable field.
|
||||
--
|
||||
-- @treturn { string... } The (possibly empty) list of completions.
|
||||
-- @see shell.setCompletionFunction
|
||||
-- @see read
|
||||
-- @usage textutils.complete( "pa", getfenv() )
|
||||
function complete(sSearchText, tSearchTable)
|
||||
expect(1, sSearchText, "string")
|
||||
expect(2, tSearchTable, "table", "nil")
|
||||
|
||||
if g_tLuaKeywords[sSearchText] then return tEmpty end
|
||||
local nStart = 1
|
||||
local nDot = string.find( sSearchText, ".", nStart, true )
|
||||
local nDot = string.find(sSearchText, ".", nStart, true)
|
||||
local tTable = tSearchTable or _ENV
|
||||
while nDot do
|
||||
local sPart = string.sub( sSearchText, nStart, nDot - 1 )
|
||||
local value = tTable[ sPart ]
|
||||
if type( value ) == "table" then
|
||||
local sPart = string.sub(sSearchText, nStart, nDot - 1)
|
||||
local value = tTable[sPart]
|
||||
if type(value) == "table" then
|
||||
tTable = value
|
||||
nStart = nDot + 1
|
||||
nDot = string.find( sSearchText, ".", nStart, true )
|
||||
nDot = string.find(sSearchText, ".", nStart, true)
|
||||
else
|
||||
return tEmpty
|
||||
end
|
||||
end
|
||||
local nColon = string.find( sSearchText, ":", nStart, true )
|
||||
local nColon = string.find(sSearchText, ":", nStart, true)
|
||||
if nColon then
|
||||
local sPart = string.sub( sSearchText, nStart, nColon - 1 )
|
||||
local value = tTable[ sPart ]
|
||||
if type( value ) == "table" then
|
||||
local sPart = string.sub(sSearchText, nStart, nColon - 1)
|
||||
local value = tTable[sPart]
|
||||
if type(value) == "table" then
|
||||
tTable = value
|
||||
nStart = nColon + 1
|
||||
else
|
||||
@@ -387,24 +745,24 @@ function complete( sSearchText, tSearchTable )
|
||||
end
|
||||
end
|
||||
|
||||
local sPart = string.sub( sSearchText, nStart )
|
||||
local sPart = string.sub(sSearchText, nStart)
|
||||
local nPartLength = #sPart
|
||||
|
||||
local tResults = {}
|
||||
local tSeen = {}
|
||||
while tTable do
|
||||
for k, v in pairs( tTable ) do
|
||||
for k, v in pairs(tTable) do
|
||||
if not tSeen[k] and type(k) == "string" then
|
||||
if string.find( k, sPart, 1, true ) == 1 then
|
||||
if not g_tLuaKeywords[k] and string.match( k, "^[%a_][%a%d_]*$" ) then
|
||||
local sResult = string.sub( k, nPartLength + 1 )
|
||||
if string.find(k, sPart, 1, true) == 1 then
|
||||
if not g_tLuaKeywords[k] and string.match(k, "^[%a_][%a%d_]*$") then
|
||||
local sResult = string.sub(k, nPartLength + 1)
|
||||
if nColon then
|
||||
if type(v) == "function" then
|
||||
table.insert( tResults, sResult .. "(" )
|
||||
table.insert(tResults, sResult .. "(")
|
||||
elseif type(v) == "table" then
|
||||
local tMetatable = getmetatable( v )
|
||||
if tMetatable and ( type( tMetatable.__call ) == "function" or type( tMetatable.__call ) == "table" ) then
|
||||
table.insert( tResults, sResult .. "(" )
|
||||
local tMetatable = getmetatable(v)
|
||||
if tMetatable and (type(tMetatable.__call) == "function" or type(tMetatable.__call) == "table") then
|
||||
table.insert(tResults, sResult .. "(")
|
||||
end
|
||||
end
|
||||
else
|
||||
@@ -413,26 +771,21 @@ function complete( sSearchText, tSearchTable )
|
||||
elseif type(v) == "table" and next(v) ~= nil then
|
||||
sResult = sResult .. "."
|
||||
end
|
||||
table.insert( tResults, sResult )
|
||||
table.insert(tResults, sResult)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
tSeen[k] = true
|
||||
end
|
||||
local tMetatable = getmetatable( tTable )
|
||||
if tMetatable and type( tMetatable.__index ) == "table" then
|
||||
local tMetatable = getmetatable(tTable)
|
||||
if tMetatable and type(tMetatable.__index) == "table" then
|
||||
tTable = tMetatable.__index
|
||||
else
|
||||
tTable = nil
|
||||
end
|
||||
end
|
||||
|
||||
table.sort( tResults )
|
||||
table.sort(tResults)
|
||||
return tResults
|
||||
end
|
||||
|
||||
-- GB versions
|
||||
serialise = serialize
|
||||
unserialise = unserialize
|
||||
serialiseJSON = serializeJSON
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
--- The turtle API allows you to control your turtle.
|
||||
--
|
||||
-- @module turtle
|
||||
|
||||
if not turtle then
|
||||
error( "Cannot load turtle API on computer", 2 )
|
||||
error("Cannot load turtle API on computer", 2)
|
||||
end
|
||||
native = turtle.native or turtle
|
||||
native = turtle.native or turtle --- @local
|
||||
|
||||
local function addCraftMethod( object )
|
||||
if peripheral.getType( "left" ) == "workbench" then
|
||||
object.craft = function( ... )
|
||||
return peripheral.call( "left", "craft", ... )
|
||||
local function addCraftMethod(object)
|
||||
if peripheral.getType("left") == "workbench" then
|
||||
object.craft = function(...)
|
||||
return peripheral.call("left", "craft", ...)
|
||||
end
|
||||
elseif peripheral.getType( "right" ) == "workbench" then
|
||||
object.craft = function( ... )
|
||||
return peripheral.call( "right", "craft", ... )
|
||||
elseif peripheral.getType("right") == "workbench" then
|
||||
object.craft = function(...)
|
||||
return peripheral.call("right", "craft", ...)
|
||||
end
|
||||
else
|
||||
object.craft = nil
|
||||
@@ -20,15 +23,15 @@ end
|
||||
|
||||
-- Put commands into environment table
|
||||
local env = _ENV
|
||||
for k, v in pairs( native ) do
|
||||
for k, v in pairs(native) do
|
||||
if k == "equipLeft" or k == "equipRight" then
|
||||
env[k] = function( ... )
|
||||
local result, err = v( ... )
|
||||
addCraftMethod( turtle )
|
||||
env[k] = function(...)
|
||||
local result, err = v(...)
|
||||
addCraftMethod(turtle)
|
||||
return result, err
|
||||
end
|
||||
else
|
||||
env[k] = v
|
||||
end
|
||||
end
|
||||
addCraftMethod( env )
|
||||
addCraftMethod(env)
|
||||
|
||||
@@ -1,85 +1,178 @@
|
||||
--- The vector API provides methods to create and manipulate vectors.
|
||||
--
|
||||
-- An introduction to vectors can be found on [Wikipedia][wiki].
|
||||
--
|
||||
-- [wiki]: http://en.wikipedia.org/wiki/Euclidean_vector
|
||||
--
|
||||
-- @module vector
|
||||
|
||||
--- A 3-dimensional vector, with `x`, `y`, and `z` values.
|
||||
--
|
||||
-- This is suitable for representing both position and directional vectors.
|
||||
--
|
||||
-- @type Vector
|
||||
local vector = {
|
||||
add = function( self, o )
|
||||
return vector.new(
|
||||
self.x + o.x,
|
||||
self.y + o.y,
|
||||
self.z + o.z
|
||||
)
|
||||
end,
|
||||
sub = function( self, o )
|
||||
return vector.new(
|
||||
self.x - o.x,
|
||||
self.y - o.y,
|
||||
self.z - o.z
|
||||
)
|
||||
end,
|
||||
mul = function( self, m )
|
||||
return vector.new(
|
||||
self.x * m,
|
||||
self.y * m,
|
||||
self.z * m
|
||||
)
|
||||
end,
|
||||
div = function( self, m )
|
||||
return vector.new(
|
||||
self.x / m,
|
||||
self.y / m,
|
||||
self.z / m
|
||||
)
|
||||
end,
|
||||
unm = function( self )
|
||||
return vector.new(
|
||||
-self.x,
|
||||
-self.y,
|
||||
-self.z
|
||||
)
|
||||
end,
|
||||
dot = function( self, o )
|
||||
return self.x * o.x + self.y * o.y + self.z * o.z
|
||||
end,
|
||||
cross = function( self, o )
|
||||
return vector.new(
|
||||
self.y * o.z - self.z * o.y,
|
||||
self.z * o.x - self.x * o.z,
|
||||
self.x * o.y - self.y * o.x
|
||||
)
|
||||
end,
|
||||
length = function( self )
|
||||
return math.sqrt( self.x * self.x + self.y * self.y + self.z * self.z )
|
||||
end,
|
||||
normalize = function( self )
|
||||
return self:mul( 1 / self:length() )
|
||||
end,
|
||||
round = function( self, nTolerance )
|
||||
nTolerance = nTolerance or 1.0
|
||||
return vector.new(
|
||||
math.floor( (self.x + nTolerance * 0.5) / nTolerance ) * nTolerance,
|
||||
math.floor( (self.y + nTolerance * 0.5) / nTolerance ) * nTolerance,
|
||||
math.floor( (self.z + nTolerance * 0.5) / nTolerance ) * nTolerance
|
||||
)
|
||||
end,
|
||||
tostring = function( self )
|
||||
return self.x .. "," .. self.y .. "," .. self.z
|
||||
end,
|
||||
--- Adds two vectors together.
|
||||
--
|
||||
-- @tparam Vector self The first vector to add.
|
||||
-- @tparam Vector o The second vector to add.
|
||||
-- @treturn Vector The resulting vector
|
||||
-- @usage v1:add(v2)
|
||||
-- @usage v1 + v2
|
||||
add = function(self, o)
|
||||
return vector.new(
|
||||
self.x + o.x,
|
||||
self.y + o.y,
|
||||
self.z + o.z
|
||||
)
|
||||
end,
|
||||
|
||||
--- Subtracts one vector from another.
|
||||
--
|
||||
-- @tparam Vector self The vector to subtract from.
|
||||
-- @tparam Vector o The vector to subtract.
|
||||
-- @treturn Vector The resulting vector
|
||||
-- @usage v1:sub(v2)
|
||||
-- @usage v1 - v2
|
||||
sub = function(self, o)
|
||||
return vector.new(
|
||||
self.x - o.x,
|
||||
self.y - o.y,
|
||||
self.z - o.z
|
||||
)
|
||||
end,
|
||||
|
||||
--- Multiplies a vector by a scalar value.
|
||||
--
|
||||
-- @tparam Vector self The vector to multiply.
|
||||
-- @tparam number m The scalar value to multiply with.
|
||||
-- @treturn Vector A vector with value `(x * m, y * m, z * m)`.
|
||||
-- @usage v:mul(3)
|
||||
-- @usage v * 3
|
||||
mul = function(self, m)
|
||||
return vector.new(
|
||||
self.x * m,
|
||||
self.y * m,
|
||||
self.z * m
|
||||
)
|
||||
end,
|
||||
|
||||
--- Divides a vector by a scalar value.
|
||||
--
|
||||
-- @tparam Vector self The vector to divide.
|
||||
-- @tparam number m The scalar value to divide by.
|
||||
-- @treturn Vector A vector with value `(x / m, y / m, z / m)`.
|
||||
-- @usage v:div(3)
|
||||
-- @usage v / 3
|
||||
div = function(self, m)
|
||||
return vector.new(
|
||||
self.x / m,
|
||||
self.y / m,
|
||||
self.z / m
|
||||
)
|
||||
end,
|
||||
|
||||
--- Negate a vector
|
||||
--
|
||||
-- @tparam Vector self The vector to negate.
|
||||
-- @treturn Vector The negated vector.
|
||||
-- @usage -v
|
||||
unm = function(self)
|
||||
return vector.new(
|
||||
-self.x,
|
||||
-self.y,
|
||||
-self.z
|
||||
)
|
||||
end,
|
||||
|
||||
--- Compute the dot product of two vectors
|
||||
--
|
||||
-- @tparam Vector self The first vector to compute the dot product of.
|
||||
-- @tparam Vector o The second vector to compute the dot product of.
|
||||
-- @treturn Vector The dot product of `self` and `o`.
|
||||
-- @usage v1:dot(v2)
|
||||
dot = function(self, o)
|
||||
return self.x * o.x + self.y * o.y + self.z * o.z
|
||||
end,
|
||||
|
||||
--- Compute the cross product of two vectors
|
||||
--
|
||||
-- @tparam Vector self The first vector to compute the cross product of.
|
||||
-- @tparam Vector o The second vector to compute the cross product of.
|
||||
-- @treturn Vector The cross product of `self` and `o`.
|
||||
-- @usage v1:cross(v2)
|
||||
cross = function(self, o)
|
||||
return vector.new(
|
||||
self.y * o.z - self.z * o.y,
|
||||
self.z * o.x - self.x * o.z,
|
||||
self.x * o.y - self.y * o.x
|
||||
)
|
||||
end,
|
||||
|
||||
--- Get the length (also referred to as magnitude) of this vector.
|
||||
-- @tparam Vector self This vector.
|
||||
-- @treturn number The length of this vector.
|
||||
length = function(self)
|
||||
return math.sqrt(self.x * self.x + self.y * self.y + self.z * self.z)
|
||||
end,
|
||||
|
||||
--- Divide this vector by its length, producing with the same direction, but
|
||||
-- of length 1.
|
||||
--
|
||||
-- @tparam Vector self The vector to normalise
|
||||
-- @treturn Vector The normalised vector
|
||||
-- @usage v:normalize()
|
||||
normalize = function(self)
|
||||
return self:mul(1 / self:length())
|
||||
end,
|
||||
|
||||
--- Construct a vector with each dimension rounded to the nearest value.
|
||||
--
|
||||
-- @tparam Vector self The vector to round
|
||||
-- @tparam[opt] number tolerance The tolerance that we should round to,
|
||||
-- defaulting to 1. For instance, a tolerance of 0.5 will round to the
|
||||
-- nearest 0.5.
|
||||
-- @treturn Vector The rounded vector.
|
||||
round = function(self, tolerance)
|
||||
tolerance = tolerance or 1.0
|
||||
return vector.new(
|
||||
math.floor((self.x + tolerance * 0.5) / tolerance) * tolerance,
|
||||
math.floor((self.y + tolerance * 0.5) / tolerance) * tolerance,
|
||||
math.floor((self.z + tolerance * 0.5) / tolerance) * tolerance
|
||||
)
|
||||
end,
|
||||
|
||||
--- Convert this vector into a string, for pretty printing.
|
||||
--
|
||||
-- @tparam Vector self This vector.
|
||||
-- @treturn string This vector's string representation.
|
||||
-- @usage v:tostring()
|
||||
-- @usage tostring(v)
|
||||
tostring = function(self)
|
||||
return self.x .. "," .. self.y .. "," .. self.z
|
||||
end,
|
||||
}
|
||||
|
||||
local vmetatable = {
|
||||
__index = vector,
|
||||
__add = vector.add,
|
||||
__sub = vector.sub,
|
||||
__mul = vector.mul,
|
||||
__div = vector.div,
|
||||
__unm = vector.unm,
|
||||
__tostring = vector.tostring,
|
||||
__index = vector,
|
||||
__add = vector.add,
|
||||
__sub = vector.sub,
|
||||
__mul = vector.mul,
|
||||
__div = vector.div,
|
||||
__unm = vector.unm,
|
||||
__tostring = vector.tostring,
|
||||
}
|
||||
|
||||
function new( x, y, z )
|
||||
local v = {
|
||||
x = tonumber(x) or 0,
|
||||
y = tonumber(y) or 0,
|
||||
z = tonumber(z) or 0,
|
||||
}
|
||||
setmetatable( v, vmetatable )
|
||||
return v
|
||||
--- Construct a new @{Vector} with the given coordinates.
|
||||
--
|
||||
-- @tparam number x The X coordinate or direction of the vector.
|
||||
-- @tparam number y The Y coordinate or direction of the vector.
|
||||
-- @tparam number z The Z coordinate or direction of the vector.
|
||||
-- @treturn Vector The constructed vector.
|
||||
function new(x, y, z)
|
||||
return setmetatable({
|
||||
x = tonumber(x) or 0,
|
||||
y = tonumber(y) or 0,
|
||||
z = tonumber(z) or 0,
|
||||
}, vmetatable)
|
||||
end
|
||||
|
||||
@@ -1,29 +1,76 @@
|
||||
--- The Window API allows easy definition of spaces within the display that can
|
||||
-- be written/drawn to, then later redrawn/repositioned/etc as need be. The API
|
||||
-- itself contains only one function, @{window.create}, which returns the
|
||||
-- windows themselves.
|
||||
--
|
||||
-- Windows are considered terminal objects - as such, they have access to nearly
|
||||
-- all the commands in the term API (plus a few extras of their own, listed
|
||||
-- within said API) and are valid targets to redirect to.
|
||||
--
|
||||
-- Each window has a "parent" terminal object, which can be the computer's own
|
||||
-- display, a monitor, another window or even other, user-defined terminal
|
||||
-- objects. Whenever a window is rendered to, the actual screen-writing is
|
||||
-- performed via that parent (or, if that has one too, then that parent, and so
|
||||
-- forth). Bear in mind that the cursor of a window's parent will hence be moved
|
||||
-- around etc when writing a given child window.
|
||||
--
|
||||
-- Windows retain a memory of everything rendered "through" them (hence acting
|
||||
-- as display buffers), and if the parent's display is wiped, the window's
|
||||
-- content can be easily redrawn later. A window may also be flagged as
|
||||
-- invisible, preventing any changes to it from being rendered until it's
|
||||
-- flagged as visible once more.
|
||||
--
|
||||
-- A parent terminal object may have multiple children assigned to it, and
|
||||
-- windows may overlap. For example, the Multishell system functions by
|
||||
-- assigning each tab a window covering the screen, each using the starting
|
||||
-- terminal display as its parent, and only one of which is visible at a time.
|
||||
--
|
||||
-- @module window
|
||||
|
||||
local expect = dofile("rom/modules/main/cc/expect.lua").expect
|
||||
|
||||
local tHex = {
|
||||
[ colors.white ] = "0",
|
||||
[ colors.orange ] = "1",
|
||||
[ colors.magenta ] = "2",
|
||||
[ colors.lightBlue ] = "3",
|
||||
[ colors.yellow ] = "4",
|
||||
[ colors.lime ] = "5",
|
||||
[ colors.pink ] = "6",
|
||||
[ colors.gray ] = "7",
|
||||
[ colors.lightGray ] = "8",
|
||||
[ colors.cyan ] = "9",
|
||||
[ colors.purple ] = "a",
|
||||
[ colors.blue ] = "b",
|
||||
[ colors.brown ] = "c",
|
||||
[ colors.green ] = "d",
|
||||
[ colors.red ] = "e",
|
||||
[ colors.black ] = "f",
|
||||
[colors.white] = "0",
|
||||
[colors.orange] = "1",
|
||||
[colors.magenta] = "2",
|
||||
[colors.lightBlue] = "3",
|
||||
[colors.yellow] = "4",
|
||||
[colors.lime] = "5",
|
||||
[colors.pink] = "6",
|
||||
[colors.gray] = "7",
|
||||
[colors.lightGray] = "8",
|
||||
[colors.cyan] = "9",
|
||||
[colors.purple] = "a",
|
||||
[colors.blue] = "b",
|
||||
[colors.brown] = "c",
|
||||
[colors.green] = "d",
|
||||
[colors.red] = "e",
|
||||
[colors.black] = "f",
|
||||
}
|
||||
|
||||
local type = type
|
||||
local string_rep = string.rep
|
||||
local string_sub = string.sub
|
||||
|
||||
function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
||||
--- Returns a terminal object that is a space within the specified parent
|
||||
-- terminal object. This can then be used (or even redirected to) in the same
|
||||
-- manner as eg a wrapped monitor. Refer to @{term|the term API} for a list of
|
||||
-- functions available to it.
|
||||
--
|
||||
-- @{term} itself may not be passed as the parent, though @{term.native} is
|
||||
-- acceptable. Generally, @{term.current} or a wrapped monitor will be most
|
||||
-- suitable, though windows may even have other windows assigned as their
|
||||
-- parents.
|
||||
--
|
||||
-- @tparam term.Redirect parent The parent terminal redirect to draw to.
|
||||
-- @tparam number nX The x coordinate this window is drawn at in the parent terminal
|
||||
-- @tparam number nY The y coordinate this window is drawn at in the parent terminal
|
||||
-- @tparam number nWidth The width of this window
|
||||
-- @tparam number nHeight The height of this window
|
||||
-- @tparam[opt] boolean bStartVisible Whether this window is visible by
|
||||
-- default. Defaults to `true`.
|
||||
-- @treturn Window The constructed window
|
||||
function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
|
||||
expect(1, parent, "table")
|
||||
expect(2, nX, "number")
|
||||
expect(3, nY, "number")
|
||||
@@ -32,21 +79,21 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
||||
expect(6, bStartVisible, "boolean", "nil")
|
||||
|
||||
if parent == term then
|
||||
error( "term is not a recommended window parent, try term.current() instead", 2 )
|
||||
error("term is not a recommended window parent, try term.current() instead", 2)
|
||||
end
|
||||
|
||||
local sEmptySpaceLine
|
||||
local tEmptyColorLines = {}
|
||||
local function createEmptyLines( nWidth )
|
||||
sEmptySpaceLine = string_rep( " ", nWidth )
|
||||
local function createEmptyLines(nWidth)
|
||||
sEmptySpaceLine = string_rep(" ", nWidth)
|
||||
for n = 0, 15 do
|
||||
local nColor = 2 ^ n
|
||||
local sHex = tHex[nColor]
|
||||
tEmptyColorLines[nColor] = string_rep( sHex, nWidth )
|
||||
tEmptyColorLines[nColor] = string_rep(sHex, nWidth)
|
||||
end
|
||||
end
|
||||
|
||||
createEmptyLines( nWidth )
|
||||
createEmptyLines(nWidth)
|
||||
|
||||
-- Setup
|
||||
local bVisible = bStartVisible ~= false
|
||||
@@ -59,8 +106,8 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
||||
local tPalette = {}
|
||||
do
|
||||
local sEmptyText = sEmptySpaceLine
|
||||
local sEmptyTextColor = tEmptyColorLines[ nTextColor ]
|
||||
local sEmptyBackgroundColor = tEmptyColorLines[ nBackgroundColor ]
|
||||
local sEmptyTextColor = tEmptyColorLines[nTextColor]
|
||||
local sEmptyBackgroundColor = tEmptyColorLines[nBackgroundColor]
|
||||
for y = 1, nHeight do
|
||||
tLines[y] = {
|
||||
text = sEmptyText,
|
||||
@@ -71,7 +118,7 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
||||
|
||||
for i = 0, 15 do
|
||||
local c = 2 ^ i
|
||||
tPalette[c] = { parent.getPaletteColour( c ) }
|
||||
tPalette[c] = { parent.getPaletteColour(c) }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -79,45 +126,45 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
||||
local function updateCursorPos()
|
||||
if nCursorX >= 1 and nCursorY >= 1 and
|
||||
nCursorX <= nWidth and nCursorY <= nHeight then
|
||||
parent.setCursorPos( nX + nCursorX - 1, nY + nCursorY - 1 )
|
||||
parent.setCursorPos(nX + nCursorX - 1, nY + nCursorY - 1)
|
||||
else
|
||||
parent.setCursorPos( 0, 0 )
|
||||
parent.setCursorPos(0, 0)
|
||||
end
|
||||
end
|
||||
|
||||
local function updateCursorBlink()
|
||||
parent.setCursorBlink( bCursorBlink )
|
||||
parent.setCursorBlink(bCursorBlink)
|
||||
end
|
||||
|
||||
local function updateCursorColor()
|
||||
parent.setTextColor( nTextColor )
|
||||
parent.setTextColor(nTextColor)
|
||||
end
|
||||
|
||||
local function redrawLine( n )
|
||||
local tLine = tLines[ n ]
|
||||
parent.setCursorPos( nX, nY + n - 1 )
|
||||
parent.blit( tLine.text, tLine.textColor, tLine.backgroundColor )
|
||||
local function redrawLine(n)
|
||||
local tLine = tLines[n]
|
||||
parent.setCursorPos(nX, nY + n - 1)
|
||||
parent.blit(tLine.text, tLine.textColor, tLine.backgroundColor)
|
||||
end
|
||||
|
||||
local function redraw()
|
||||
for n = 1, nHeight do
|
||||
redrawLine( n )
|
||||
redrawLine(n)
|
||||
end
|
||||
end
|
||||
|
||||
local function updatePalette()
|
||||
for k, v in pairs( tPalette ) do
|
||||
parent.setPaletteColour( k, v[1], v[2], v[3] )
|
||||
for k, v in pairs(tPalette) do
|
||||
parent.setPaletteColour(k, v[1], v[2], v[3])
|
||||
end
|
||||
end
|
||||
|
||||
local function internalBlit( sText, sTextColor, sBackgroundColor )
|
||||
local function internalBlit(sText, sTextColor, sBackgroundColor)
|
||||
local nStart = nCursorX
|
||||
local nEnd = nStart + #sText - 1
|
||||
if nCursorY >= 1 and nCursorY <= nHeight then
|
||||
if nStart <= nWidth and nEnd >= 1 then
|
||||
-- Modify line
|
||||
local tLine = tLines[ nCursorY ]
|
||||
local tLine = tLines[nCursorY]
|
||||
if nStart == 1 and nEnd == nWidth then
|
||||
tLine.text = sText
|
||||
tLine.textColor = sTextColor
|
||||
@@ -127,14 +174,14 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
||||
if nStart < 1 then
|
||||
local nClipStart = 1 - nStart + 1
|
||||
local nClipEnd = nWidth - nStart + 1
|
||||
sClippedText = string_sub( sText, nClipStart, nClipEnd )
|
||||
sClippedTextColor = string_sub( sTextColor, nClipStart, nClipEnd )
|
||||
sClippedBackgroundColor = string_sub( sBackgroundColor, nClipStart, nClipEnd )
|
||||
sClippedText = string_sub(sText, nClipStart, nClipEnd)
|
||||
sClippedTextColor = string_sub(sTextColor, nClipStart, nClipEnd)
|
||||
sClippedBackgroundColor = string_sub(sBackgroundColor, nClipStart, nClipEnd)
|
||||
elseif nEnd > nWidth then
|
||||
local nClipEnd = nWidth - nStart + 1
|
||||
sClippedText = string_sub( sText, 1, nClipEnd )
|
||||
sClippedTextColor = string_sub( sTextColor, 1, nClipEnd )
|
||||
sClippedBackgroundColor = string_sub( sBackgroundColor, 1, nClipEnd )
|
||||
sClippedText = string_sub(sText, 1, nClipEnd)
|
||||
sClippedTextColor = string_sub(sTextColor, 1, nClipEnd)
|
||||
sClippedBackgroundColor = string_sub(sBackgroundColor, 1, nClipEnd)
|
||||
else
|
||||
sClippedText = sText
|
||||
sClippedTextColor = sTextColor
|
||||
@@ -147,9 +194,9 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
||||
local sNewText, sNewTextColor, sNewBackgroundColor
|
||||
if nStart > 1 then
|
||||
local nOldEnd = nStart - 1
|
||||
sNewText = string_sub( sOldText, 1, nOldEnd ) .. sClippedText
|
||||
sNewTextColor = string_sub( sOldTextColor, 1, nOldEnd ) .. sClippedTextColor
|
||||
sNewBackgroundColor = string_sub( sOldBackgroundColor, 1, nOldEnd ) .. sClippedBackgroundColor
|
||||
sNewText = string_sub(sOldText, 1, nOldEnd) .. sClippedText
|
||||
sNewTextColor = string_sub(sOldTextColor, 1, nOldEnd) .. sClippedTextColor
|
||||
sNewBackgroundColor = string_sub(sOldBackgroundColor, 1, nOldEnd) .. sClippedBackgroundColor
|
||||
else
|
||||
sNewText = sClippedText
|
||||
sNewTextColor = sClippedTextColor
|
||||
@@ -157,9 +204,9 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
||||
end
|
||||
if nEnd < nWidth then
|
||||
local nOldStart = nEnd + 1
|
||||
sNewText = sNewText .. string_sub( sOldText, nOldStart, nWidth )
|
||||
sNewTextColor = sNewTextColor .. string_sub( sOldTextColor, nOldStart, nWidth )
|
||||
sNewBackgroundColor = sNewBackgroundColor .. string_sub( sOldBackgroundColor, nOldStart, nWidth )
|
||||
sNewText = sNewText .. string_sub(sOldText, nOldStart, nWidth)
|
||||
sNewTextColor = sNewTextColor .. string_sub(sOldTextColor, nOldStart, nWidth)
|
||||
sNewBackgroundColor = sNewBackgroundColor .. string_sub(sOldBackgroundColor, nOldStart, nWidth)
|
||||
end
|
||||
|
||||
tLine.text = sNewText
|
||||
@@ -169,7 +216,7 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
||||
|
||||
-- Redraw line
|
||||
if bVisible then
|
||||
redrawLine( nCursorY )
|
||||
redrawLine(nCursorY)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -182,28 +229,32 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
||||
end
|
||||
end
|
||||
|
||||
-- Terminal implementation
|
||||
--- The window object. Refer to the @{window|module's documentation} for
|
||||
-- a full description.
|
||||
--
|
||||
-- @type Window
|
||||
-- @see term.Redirect
|
||||
local window = {}
|
||||
|
||||
function window.write( sText )
|
||||
sText = tostring( sText )
|
||||
internalBlit( sText, string_rep( tHex[ nTextColor ], #sText ), string_rep( tHex[ nBackgroundColor ], #sText ) )
|
||||
function window.write(sText)
|
||||
sText = tostring(sText)
|
||||
internalBlit(sText, string_rep(tHex[nTextColor], #sText), string_rep(tHex[nBackgroundColor], #sText))
|
||||
end
|
||||
|
||||
function window.blit( sText, sTextColor, sBackgroundColor )
|
||||
function window.blit(sText, sTextColor, sBackgroundColor)
|
||||
if type(sText) ~= "string" then expect(1, sText, "string") end
|
||||
if type(sTextColor) ~= "string" then expect(2, sTextColor, "string") end
|
||||
if type(sBackgroundColor) ~= "string" then expect(3, sBackgroundColor, "string") end
|
||||
if #sTextColor ~= #sText or #sBackgroundColor ~= #sText then
|
||||
error( "Arguments must be the same length", 2 )
|
||||
error("Arguments must be the same length", 2)
|
||||
end
|
||||
internalBlit( sText, sTextColor, sBackgroundColor )
|
||||
internalBlit(sText, sTextColor, sBackgroundColor)
|
||||
end
|
||||
|
||||
function window.clear()
|
||||
local sEmptyText = sEmptySpaceLine
|
||||
local sEmptyTextColor = tEmptyColorLines[ nTextColor ]
|
||||
local sEmptyBackgroundColor = tEmptyColorLines[ nBackgroundColor ]
|
||||
local sEmptyTextColor = tEmptyColorLines[nTextColor]
|
||||
local sEmptyBackgroundColor = tEmptyColorLines[nBackgroundColor]
|
||||
for y = 1, nHeight do
|
||||
tLines[y] = {
|
||||
text = sEmptyText,
|
||||
@@ -221,15 +272,15 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
||||
function window.clearLine()
|
||||
if nCursorY >= 1 and nCursorY <= nHeight then
|
||||
local sEmptyText = sEmptySpaceLine
|
||||
local sEmptyTextColor = tEmptyColorLines[ nTextColor ]
|
||||
local sEmptyBackgroundColor = tEmptyColorLines[ nBackgroundColor ]
|
||||
tLines[ nCursorY ] = {
|
||||
local sEmptyTextColor = tEmptyColorLines[nTextColor]
|
||||
local sEmptyBackgroundColor = tEmptyColorLines[nBackgroundColor]
|
||||
tLines[nCursorY] = {
|
||||
text = sEmptyText,
|
||||
textColor = sEmptyTextColor,
|
||||
backgroundColor = sEmptyBackgroundColor,
|
||||
}
|
||||
if bVisible then
|
||||
redrawLine( nCursorY )
|
||||
redrawLine(nCursorY)
|
||||
updateCursorColor()
|
||||
updateCursorPos()
|
||||
end
|
||||
@@ -240,17 +291,17 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
||||
return nCursorX, nCursorY
|
||||
end
|
||||
|
||||
function window.setCursorPos( x, y )
|
||||
function window.setCursorPos(x, y)
|
||||
if type(x) ~= "number" then expect(1, x, "number") end
|
||||
if type(y) ~= "number" then expect(2, y, "number") end
|
||||
nCursorX = math.floor( x )
|
||||
nCursorY = math.floor( y )
|
||||
nCursorX = math.floor(x)
|
||||
nCursorY = math.floor(y)
|
||||
if bVisible then
|
||||
updateCursorPos()
|
||||
end
|
||||
end
|
||||
|
||||
function window.setCursorBlink( blink )
|
||||
function window.setCursorBlink(blink)
|
||||
if type(blink) ~= "boolean" then expect(1, blink, "boolean") end
|
||||
bCursorBlink = blink
|
||||
if bVisible then
|
||||
@@ -274,10 +325,10 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
||||
return isColor()
|
||||
end
|
||||
|
||||
local function setTextColor( color )
|
||||
local function setTextColor(color)
|
||||
if type(color) ~= "number" then expect(1, color, "number") end
|
||||
if tHex[color] == nil then
|
||||
error( "Invalid color (got " .. color .. ")" , 2 )
|
||||
error("Invalid color (got " .. color .. ")" , 2)
|
||||
end
|
||||
|
||||
nTextColor = color
|
||||
@@ -289,50 +340,50 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
||||
window.setTextColor = setTextColor
|
||||
window.setTextColour = setTextColor
|
||||
|
||||
function window.setPaletteColour( colour, r, g, b )
|
||||
function window.setPaletteColour(colour, r, g, b)
|
||||
if type(colour) ~= "number" then expect(1, colour, "number") end
|
||||
|
||||
if tHex[colour] == nil then
|
||||
error( "Invalid color (got " .. colour .. ")" , 2 )
|
||||
error("Invalid color (got " .. colour .. ")" , 2)
|
||||
end
|
||||
|
||||
local tCol
|
||||
if type(r) == "number" and g == nil and b == nil then
|
||||
tCol = { colours.unpackRGB( r ) }
|
||||
tPalette[ colour ] = tCol
|
||||
tCol = { colours.unpackRGB(r) }
|
||||
tPalette[colour] = tCol
|
||||
else
|
||||
if type(r) ~= "number" then expect(2, r, "number") end
|
||||
if type(g) ~= "number" then expect(3, g, "number") end
|
||||
if type(b) ~= "number" then expect(4, b, "number") end
|
||||
|
||||
tCol = tPalette[ colour ]
|
||||
tCol = tPalette[colour]
|
||||
tCol[1] = r
|
||||
tCol[2] = g
|
||||
tCol[3] = b
|
||||
end
|
||||
|
||||
if bVisible then
|
||||
return parent.setPaletteColour( colour, tCol[1], tCol[2], tCol[3] )
|
||||
return parent.setPaletteColour(colour, tCol[1], tCol[2], tCol[3])
|
||||
end
|
||||
end
|
||||
|
||||
window.setPaletteColor = window.setPaletteColour
|
||||
|
||||
function window.getPaletteColour( colour )
|
||||
function window.getPaletteColour(colour)
|
||||
if type(colour) ~= "number" then expect(1, colour, "number") end
|
||||
if tHex[colour] == nil then
|
||||
error( "Invalid color (got " .. colour .. ")" , 2 )
|
||||
error("Invalid color (got " .. colour .. ")" , 2)
|
||||
end
|
||||
local tCol = tPalette[ colour ]
|
||||
local tCol = tPalette[colour]
|
||||
return tCol[1], tCol[2], tCol[3]
|
||||
end
|
||||
|
||||
window.getPaletteColor = window.getPaletteColour
|
||||
|
||||
local function setBackgroundColor( color )
|
||||
local function setBackgroundColor(color)
|
||||
if type(color) ~= "number" then expect(1, color, "number") end
|
||||
if tHex[color] == nil then
|
||||
error( "Invalid color (got " .. color .. ")", 2 )
|
||||
error("Invalid color (got " .. color .. ")", 2)
|
||||
end
|
||||
nBackgroundColor = color
|
||||
end
|
||||
@@ -344,13 +395,13 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
||||
return nWidth, nHeight
|
||||
end
|
||||
|
||||
function window.scroll( n )
|
||||
function window.scroll(n)
|
||||
if type(n) ~= "number" then expect(1, n, "number") end
|
||||
if n ~= 0 then
|
||||
local tNewLines = {}
|
||||
local sEmptyText = sEmptySpaceLine
|
||||
local sEmptyTextColor = tEmptyColorLines[ nTextColor ]
|
||||
local sEmptyBackgroundColor = tEmptyColorLines[ nBackgroundColor ]
|
||||
local sEmptyTextColor = tEmptyColorLines[nTextColor]
|
||||
local sEmptyBackgroundColor = tEmptyColorLines[nBackgroundColor]
|
||||
for newY = 1, nHeight do
|
||||
local y = newY + n
|
||||
if y >= 1 and y <= nHeight then
|
||||
@@ -388,6 +439,13 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
||||
return nBackgroundColor
|
||||
end
|
||||
|
||||
--- Get the buffered contents of a line in this window.
|
||||
---
|
||||
-- @tparam number y The y position of the line to get.
|
||||
-- @treturn string The textual content of this line.
|
||||
-- @treturn string The text colours of this line, suitable for use with @{term.blit}.
|
||||
-- @treturn string The background colours of this line, suitable for use with @{term.blit}.
|
||||
-- @throws If `y` is not between 1 and this window's height.
|
||||
function window.getLine(y)
|
||||
if type(y) ~= "number" then expect(1, y, "number") end
|
||||
|
||||
@@ -399,16 +457,26 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
||||
end
|
||||
|
||||
-- Other functions
|
||||
function window.setVisible( bVis )
|
||||
if type(bVis) ~= "boolean" then expect(1, bVis, "boolean") end
|
||||
if bVisible ~= bVis then
|
||||
bVisible = bVis
|
||||
|
||||
--- Set whether this window is visible. Invisible windows will not be drawn
|
||||
-- to the screen until they are made visible again.
|
||||
--
|
||||
-- Making an invisible window visible will immediately draw it.
|
||||
--
|
||||
-- @tparam boolean visible Whether this window is visible.
|
||||
function window.setVisible(visible)
|
||||
if type(visible) ~= "boolean" then expect(1, visible, "boolean") end
|
||||
if bVisible ~= visible then
|
||||
bVisible = visible
|
||||
if bVisible then
|
||||
window.redraw()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Draw this window. This does nothing if the window is not visible.
|
||||
--
|
||||
-- @see Window:setVisible
|
||||
function window.redraw()
|
||||
if bVisible then
|
||||
redraw()
|
||||
@@ -419,6 +487,8 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
||||
end
|
||||
end
|
||||
|
||||
--- Set the current terminal's cursor to where this window's cursor is. This
|
||||
-- does nothing if the window is not visible.
|
||||
function window.restoreCursor()
|
||||
if bVisible then
|
||||
updateCursorBlink()
|
||||
@@ -427,31 +497,47 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
||||
end
|
||||
end
|
||||
|
||||
--- Get the position of the top left corner of this window.
|
||||
--
|
||||
-- @treturn number The x position of this window.
|
||||
-- @treturn number The y position of this window.
|
||||
function window.getPosition()
|
||||
return nX, nY
|
||||
end
|
||||
|
||||
function window.reposition( nNewX, nNewY, nNewWidth, nNewHeight, newParent )
|
||||
if type(nNewX) ~= "number" then expect(1, nNewX, "number") end
|
||||
if type(nNewY) ~= "number" then expect(2, nNewY, "number") end
|
||||
if nNewWidth ~= nil or nNewHeight ~= nil then
|
||||
expect(3, nNewWidth, "number")
|
||||
expect(4, nNewHeight, "number")
|
||||
--- Reposition or resize the given window.
|
||||
--
|
||||
-- This function also accepts arguments to change the size of this window.
|
||||
-- It is recommended that you fire a `term_resize` event after changing a
|
||||
-- window's, to allow programs to adjust their sizing.
|
||||
--
|
||||
-- @tparam number new_x The new x position of this window.
|
||||
-- @tparam number new_y The new y position of this window.
|
||||
-- @tparam[opt] number new_width The new width of this window.
|
||||
-- @tparam number new_height The new height of this window.
|
||||
-- @tparam[opt] term.Redirect new_parent The new redirect object this
|
||||
-- window should draw to.
|
||||
function window.reposition(new_x, new_y, new_width, new_height, new_parent)
|
||||
if type(new_x) ~= "number" then expect(1, new_x, "number") end
|
||||
if type(new_y) ~= "number" then expect(2, new_y, "number") end
|
||||
if new_width ~= nil or new_height ~= nil then
|
||||
expect(3, new_width, "number")
|
||||
expect(4, new_height, "number")
|
||||
end
|
||||
if newParent ~= nil and type(newParent) ~= "table" then expect(5, newParent, "table") end
|
||||
if new_parent ~= nil and type(new_parent) ~= "table" then expect(5, new_parent, "table") end
|
||||
|
||||
nX = nNewX
|
||||
nY = nNewY
|
||||
nX = new_x
|
||||
nY = new_y
|
||||
|
||||
if newParent then parent = newParent end
|
||||
if new_parent then parent = new_parent end
|
||||
|
||||
if nNewWidth and nNewHeight then
|
||||
if new_width and new_height then
|
||||
local tNewLines = {}
|
||||
createEmptyLines( nNewWidth )
|
||||
createEmptyLines(new_width)
|
||||
local sEmptyText = sEmptySpaceLine
|
||||
local sEmptyTextColor = tEmptyColorLines[ nTextColor ]
|
||||
local sEmptyBackgroundColor = tEmptyColorLines[ nBackgroundColor ]
|
||||
for y = 1, nNewHeight do
|
||||
local sEmptyTextColor = tEmptyColorLines[nTextColor]
|
||||
local sEmptyBackgroundColor = tEmptyColorLines[nBackgroundColor]
|
||||
for y = 1, new_height do
|
||||
if y > nHeight then
|
||||
tNewLines[y] = {
|
||||
text = sEmptyText,
|
||||
@@ -460,25 +546,25 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
||||
}
|
||||
else
|
||||
local tOldLine = tLines[y]
|
||||
if nNewWidth == nWidth then
|
||||
if new_width == nWidth then
|
||||
tNewLines[y] = tOldLine
|
||||
elseif nNewWidth < nWidth then
|
||||
elseif new_width < nWidth then
|
||||
tNewLines[y] = {
|
||||
text = string_sub( tOldLine.text, 1, nNewWidth ),
|
||||
textColor = string_sub( tOldLine.textColor, 1, nNewWidth ),
|
||||
backgroundColor = string_sub( tOldLine.backgroundColor, 1, nNewWidth ),
|
||||
text = string_sub(tOldLine.text, 1, new_width),
|
||||
textColor = string_sub(tOldLine.textColor, 1, new_width),
|
||||
backgroundColor = string_sub(tOldLine.backgroundColor, 1, new_width),
|
||||
}
|
||||
else
|
||||
tNewLines[y] = {
|
||||
text = tOldLine.text .. string_sub( sEmptyText, nWidth + 1, nNewWidth ),
|
||||
textColor = tOldLine.textColor .. string_sub( sEmptyTextColor, nWidth + 1, nNewWidth ),
|
||||
backgroundColor = tOldLine.backgroundColor .. string_sub( sEmptyBackgroundColor, nWidth + 1, nNewWidth ),
|
||||
text = tOldLine.text .. string_sub(sEmptyText, nWidth + 1, new_width),
|
||||
textColor = tOldLine.textColor .. string_sub(sEmptyTextColor, nWidth + 1, new_width),
|
||||
backgroundColor = tOldLine.backgroundColor .. string_sub(sEmptyBackgroundColor, nWidth + 1, new_width),
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
nWidth = nNewWidth
|
||||
nHeight = nNewHeight
|
||||
nWidth = new_width
|
||||
nHeight = new_height
|
||||
tLines = tNewLines
|
||||
end
|
||||
if bVisible then
|
||||
|
||||
@@ -1 +1 @@
|
||||
adventure is a text adventure game for CraftOS. To navigate around the world of adventure, type simple instructions to the interpreter, for example: "go north", "punch tree", "craft planks", "mine coal with pickaxe", "hit creeper with sword"
|
||||
adventure is a text adventure game for CraftOS. To navigate around the world of adventure, type simple instructions to the interpreter, for example: "go north", "punch tree", "craft planks", "mine coal with pickaxe", "hit creeper with sword"
|
||||
|
||||
@@ -3,4 +3,4 @@ alias assigns shell commands to run other programs.
|
||||
ex:
|
||||
"alias dir ls" will make the "dir" command run the "ls" program
|
||||
"alias dir" will remove the alias set on "dir"
|
||||
"alias" will list all current aliases.
|
||||
"alias" will list all current aliases.
|
||||
|
||||
@@ -2,4 +2,4 @@ Surf the rednet superhighway with "chat", the networked chat program for CraftOS
|
||||
|
||||
ex:
|
||||
"chat host forgecraft" will create a chatroom with the name "forgecraft"
|
||||
"chat join forgecraft direwolf20" will connect to the chatroom with the name "forgecraft", using the nickname "direwolf20"
|
||||
"chat join forgecraft direwolf20" will connect to the chatroom with the name "forgecraft", using the nickname "direwolf20"
|
||||
|
||||
@@ -1 +1 @@
|
||||
clear clears the screen.
|
||||
clear clears the screen.
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
coroutine is a standard Lua5.1 API.
|
||||
Refer to http://www.lua.org/manual/5.1/ for more information.
|
||||
Refer to http://www.lua.org/manual/5.1/ for more information.
|
||||
|
||||
@@ -1 +1 @@
|
||||
dance is a program for Turtles. Turtles love to get funky.
|
||||
dance is a program for Turtles. Turtles love to get funky.
|
||||
|
||||
@@ -14,4 +14,4 @@ disk.getID( drive )
|
||||
Events fired by the disk API:
|
||||
"disk" when a disk or other item is inserted into a disk drive. Argument is the name of the drive
|
||||
"disk_eject" when a disk is removed from a disk drive. Argument is the name of the drive
|
||||
Type "help events" to learn about the event system.
|
||||
Type "help events" to learn about the event system.
|
||||
|
||||
@@ -3,4 +3,4 @@ dj plays Music Discs from disk drives attached to the computer.
|
||||
ex:
|
||||
"dj" or "dj play" plays a random disc.
|
||||
"dj play left" plays the disc in the drive on the left of the computer.
|
||||
"dj stop" stops the current disc.
|
||||
"dj stop" stops the current disc.
|
||||
|
||||
@@ -2,4 +2,4 @@ drive tells you which disk drive the current or specified directory is located i
|
||||
|
||||
ex:
|
||||
"drive" tell you the disk drive of the current directory.
|
||||
"drive foo" tells you the disk drive of the subdirectory "foo"
|
||||
"drive foo" tells you the disk drive of the subdirectory "foo"
|
||||
|
||||
@@ -16,4 +16,4 @@ getDiskID()
|
||||
Events fired by the Disk Drive:
|
||||
"disk" when a disk or other item is inserted into the drive. Argument is the name of the drive.
|
||||
"disk_eject" when a disk is removed from a drive. Argument is the name of the drive.
|
||||
Type "help events" to learn about the event system.
|
||||
Type "help events" to learn about the event system.
|
||||
|
||||
@@ -1 +1 @@
|
||||
Mostly harmless.
|
||||
Mostly harmless.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
edit is a text editor for creating or modifying programs or text files. After creating a program with edit, type its filename in the shell to run it. You can open any of the builtin programs with edit to learn how to program.
|
||||
|
||||
ex:
|
||||
"edit hello" opens a file called "hello" for editing.
|
||||
"edit hello" opens a file called "hello" for editing.
|
||||
|
||||
@@ -1 +1 @@
|
||||
exit will exit the current shell.
|
||||
exit will exit the current shell.
|
||||
|
||||
@@ -4,7 +4,7 @@ Type "help gpsapi" for help using GPS functions in lua programs.
|
||||
ex:
|
||||
"gps locate" will connect to nearby GPS servers, and try to determine the position of the computer or turtle.
|
||||
"gps host" will try to determine the position, and host a GPS server if successful.
|
||||
"gps host 10 20 30" will host a GPS server, using the manually entered position 10,20,30.
|
||||
"gps host 10 20 30" will host a GPS server, using the manually entered position 10,20,30.
|
||||
|
||||
Take care when manually entering host positions. If the positions entered into multiple GPS hosts
|
||||
are not consistent, the results of locate calls will be incorrect.
|
||||
are not consistent, the results of locate calls will be incorrect.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Functions in the GPS API:
|
||||
gps.locate( timeout )
|
||||
|
||||
The locate function will send a signal to nearby gps servers, and wait for responses before the timeout. If it receives enough responses to determine this computers position then x, y and z co-ordinates will be returned, otherwise it will return nil. If GPS hosts do not have their positions configured correctly, results will be inaccurate.
|
||||
The locate function will send a signal to nearby gps servers, and wait for responses before the timeout. If it receives enough responses to determine this computers position then x, y and z co-ordinates will be returned, otherwise it will return nil. If GPS hosts do not have their positions configured correctly, results will be inaccurate.
|
||||
|
||||
@@ -1 +1 @@
|
||||
hello prints the text "Hello World!" to the screen.
|
||||
hello prints the text "Hello World!" to the screen.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
help is the help tool you're currently using.
|
||||
Type "help index" to see all help topics.
|
||||
Type "help" to see the help intro.
|
||||
Type "help helpapi" for information on the help Lua API.
|
||||
Type "help helpapi" for information on the help Lua API.
|
||||
|
||||
@@ -2,4 +2,4 @@ id prints the unique identifier of this computer, or a Disk in an attached Disk
|
||||
|
||||
ex:
|
||||
"id" will print this Computers ID and label
|
||||
"id left" will print the ID and label of the disk in the Disk Drive on the left
|
||||
"id left" will print the ID and label of the disk in the Disk Drive on the left
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
io is a standard Lua5.1 API, reimplemented for CraftOS. Not all the features are availiable.
|
||||
Refer to http://www.lua.org/manual/5.1/ for more information.
|
||||
Refer to http://www.lua.org/manual/5.1/ for more information.
|
||||
|
||||
@@ -3,7 +3,7 @@ The keys API contains constants for all the key codes that can be returned by th
|
||||
Example usage:
|
||||
local sEvent, nKey = os.pullEvent()
|
||||
if sEvent == "key" and nKey == keys.enter then
|
||||
-- Do something
|
||||
-- Do something
|
||||
end
|
||||
|
||||
See http://www.minecraftwiki.net/wiki/Key_codes, or the source code, for a complete reference.
|
||||
See http://www.minecraftwiki.net/wiki/Key_codes, or the source code, for a complete reference.
|
||||
|
||||
@@ -1 +1 @@
|
||||
ls will list all the directories and files in the current location. Use "type" to find out if an item is a file or a directory.
|
||||
ls will list all the directories and files in the current location. Use "type" to find out if an item is a file or a directory.
|
||||
|
||||
@@ -1 +1 @@
|
||||
lua is an interactive prompt for the lua programming language. It's a useful tool for learning the language.
|
||||
lua is an interactive prompt for the lua programming language. It's a useful tool for learning the language.
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
math is a standard Lua5.1 API.
|
||||
Refer to http://www.lua.org/manual/5.1/ for more information.
|
||||
Refer to http://www.lua.org/manual/5.1/ for more information.
|
||||
|
||||
@@ -2,4 +2,4 @@ mkdir creates a directory in the current location.
|
||||
|
||||
ex:
|
||||
"mkdir foo" creates a directory named "foo".
|
||||
"mkdir ../foo" creates a directory named "foo" in the directory above the current directory.
|
||||
"mkdir ../foo" creates a directory named "foo" in the directory above the current directory.
|
||||
|
||||
@@ -9,4 +9,4 @@ transmit( channel, replyChannel, message )
|
||||
isWireless()
|
||||
|
||||
Events fired by Modems:
|
||||
"modem_message" when a message is received on an open channel. Arguments are name, channel, replyChannel, message, distance
|
||||
"modem_message" when a message is received on an open channel. Arguments are name, channel, replyChannel, message, distance
|
||||
|
||||
@@ -3,4 +3,4 @@ Type "help monitors" for help using monitors as peripherals in lua programs.
|
||||
|
||||
ex:
|
||||
"monitor left hello" will run the "hello" program on the monitor to the left of the computer.
|
||||
"monitor top edit foo" will run the edit program on the top monitor, editing the file "foo".
|
||||
"monitor top edit foo" will run the edit program on the top monitor, editing the file "foo".
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
multishell is the toplevel program on Advanced Computers which manages background tabs.
|
||||
Type "help shellapi" for information about the shell lua api.
|
||||
Type "help shellapi" for information about the shell lua api.
|
||||
|
||||
@@ -23,4 +23,4 @@ os.reboot()
|
||||
Events emitted by the os API:
|
||||
"timer" when a timeout started by os.startTimer() completes. Argument is the token returned by os.startTimer().
|
||||
"alarm" when a time passed to os.setAlarm() is reached. Argument is the token returned by os.setAlarm().
|
||||
Type "help events" to learn about the event system.
|
||||
Type "help events" to learn about the event system.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Functions in the Parallel API:
|
||||
parallel.waitForAny( function1, function2, ... )
|
||||
parallel.waitForAll( function1, function2, ... )
|
||||
These methods provide an easy way to run multiple lua functions simultaneously.
|
||||
These methods provide an easy way to run multiple lua functions simultaneously.
|
||||
|
||||
@@ -3,4 +3,4 @@ Functions in the pocket API:
|
||||
pocket.equipBack()
|
||||
pocket.unequipBack()
|
||||
|
||||
When equipping upgrades, it will search your inventory for a suitable upgrade, starting in the selected slot. If one cannot be found then it will check your offhand.
|
||||
When equipping upgrades, it will search your inventory for a suitable upgrade, starting in the selected slot. If one cannot be found then it will check your offhand.
|
||||
|
||||
@@ -9,4 +9,4 @@ getPageSize()
|
||||
setCursorPos( x, y )
|
||||
getCursorPos()
|
||||
write( text )
|
||||
endPage()
|
||||
endPage()
|
||||
|
||||
@@ -8,4 +8,4 @@ To quickly shutdown a computer, hold Ctrl+S for 1 second.
|
||||
To quickly reboot a computer, hold Ctrl+R for 1 second.
|
||||
|
||||
To learn about the programming APIs availiable, type "apis" or "help apis".
|
||||
If you get stuck, visit the forums at http://www.computercraft.info/ for advice and tutorials.
|
||||
If you get stuck, visit the forums at http://www.computercraft.info/ for advice and tutorials.
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
reboot will turn the computer off and on again.
|
||||
You can also hold Ctrl+R at any time to quickly reboot.
|
||||
You can also hold Ctrl+R at any time to quickly reboot.
|
||||
|
||||
@@ -6,4 +6,4 @@ ex:
|
||||
"redstone set right blue false" turns off the blue wire in the bundled cable on the right redstone output.
|
||||
"redstone pulse front 10 1" emits 10 one second redstone pulses on the front redstone output.
|
||||
|
||||
Type "help redstoneapi" or "help rs" for information on the redstone Lua API.
|
||||
Type "help redstoneapi" or "help rs" for information on the redstone Lua API.
|
||||
|
||||
@@ -3,4 +3,4 @@ refuel is a program for Turtles. Refuel will consume items from the inventory as
|
||||
ex:
|
||||
"refuel" will refuel with at most one fuel item
|
||||
"refuel 10" will refuel with at most 10 fuel items
|
||||
"refuel all" will refuel with as many fuel items as possible
|
||||
"refuel all" will refuel with as many fuel items as possible
|
||||
|
||||
@@ -1 +1 @@
|
||||
repeat is a program for repeating rednet messages across long distances. To use, connect 2 or more modems to a computer and run the "repeat" program; from then on, any rednet message sent from any computer in wireless range or connected by networking cable to either of the modems will be repeated to those on the other side.
|
||||
repeat is a program for repeating rednet messages across long distances. To use, connect 2 or more modems to a computer and run the "repeat" program; from then on, any rednet message sent from any computer in wireless range or connected by networking cable to either of the modems will be repeated to those on the other side.
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
shell is the toplevel program which interprets commands and runs program.
|
||||
Type "help shellapi" for information about the shell lua api.
|
||||
Type "help shellapi" for information about the shell lua api.
|
||||
|
||||
@@ -1 +1 @@
|
||||
shutdown will turn off the computer.
|
||||
shutdown will turn off the computer.
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
string is a standard Lua5.1 API.
|
||||
Refer to http://www.lua.org/manual/5.1/ for more information.
|
||||
Refer to http://www.lua.org/manual/5.1/ for more information.
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
table is a standard Lua5.1 API.
|
||||
Refer to http://www.lua.org/manual/5.1/ for more information.
|
||||
Refer to http://www.lua.org/manual/5.1/ for more information.
|
||||
|
||||
@@ -1 +1 @@
|
||||
time prints the current time of day.
|
||||
time prints the current time of day.
|
||||
|
||||
@@ -1 +1 @@
|
||||
type determines the type of a file or directory. Prints "file", "directory" or "does not exist".
|
||||
type determines the type of a file or directory. Prints "file", "directory" or "does not exist".
|
||||
|
||||
@@ -5,28 +5,29 @@
|
||||
|
||||
local native_select, native_type = select, type
|
||||
|
||||
--- Expect an argument to have a specific type.
|
||||
--
|
||||
-- @tparam int index The 1-based argument index.
|
||||
-- @param value The argument's value.
|
||||
-- @tparam string ... The allowed types of the argument.
|
||||
-- @throws If the value is not one of the allowed types.
|
||||
local function expect(index, value, ...)
|
||||
local t = native_type(value)
|
||||
for i = 1, native_select("#", ...) do
|
||||
if t == native_select(i, ...) then return true end
|
||||
end
|
||||
|
||||
local function get_type_names(...)
|
||||
local types = table.pack(...)
|
||||
for i = types.n, 1, -1 do
|
||||
if types[i] == "nil" then table.remove(types, i) end
|
||||
end
|
||||
|
||||
local type_names
|
||||
if #types <= 1 then
|
||||
type_names = tostring(...)
|
||||
return tostring(...)
|
||||
else
|
||||
type_names = table.concat(types, ", ", 1, #types - 1) .. " or " .. types[#types]
|
||||
return table.concat(types, ", ", 1, #types - 1) .. " or " .. types[#types]
|
||||
end
|
||||
end
|
||||
--- Expect an argument to have a specific type.
|
||||
--
|
||||
-- @tparam number index The 1-based argument index.
|
||||
-- @param value The argument's value.
|
||||
-- @tparam string ... The allowed types of the argument.
|
||||
-- @return The given `value`.
|
||||
-- @throws If the value is not one of the allowed types.
|
||||
local function expect(index, value, ...)
|
||||
local t = native_type(value)
|
||||
for i = 1, native_select("#", ...) do
|
||||
if t == native_select(i, ...) then return value end
|
||||
end
|
||||
|
||||
-- If we can determine the function name with a high level of confidence, try to include it.
|
||||
@@ -36,11 +37,39 @@ local function expect(index, value, ...)
|
||||
if ok and info.name and #info.name ~= "" and info.what ~= "C" then name = info.name end
|
||||
end
|
||||
|
||||
local type_names = get_type_names(...)
|
||||
if name then
|
||||
error( ("bad argument #%d to '%s' (expected %s, got %s)"):format(index, name, type_names, t), 3 )
|
||||
error(("bad argument #%d to '%s' (expected %s, got %s)"):format(index, name, type_names, t), 3)
|
||||
else
|
||||
error( ("bad argument #%d (expected %s, got %s)"):format(index, type_names, t), 3 )
|
||||
error(("bad argument #%d (expected %s, got %s)"):format(index, type_names, t), 3)
|
||||
end
|
||||
end
|
||||
|
||||
return { expect = expect }
|
||||
--- Expect an field to have a specific type.
|
||||
--
|
||||
-- @tparam table tbl The table to index.
|
||||
-- @tparam string index The field name to check.
|
||||
-- @tparam string ... The allowed types of the argument.
|
||||
-- @return The contents of the given field.
|
||||
-- @throws If the field is not one of the allowed types.
|
||||
local function field(tbl, index, ...)
|
||||
expect(1, tbl, "table")
|
||||
expect(2, index, "string")
|
||||
|
||||
local value = tbl[index]
|
||||
local t = native_type(value)
|
||||
for i = 1, native_select("#", ...) do
|
||||
if t == native_select(i, ...) then return value end
|
||||
end
|
||||
|
||||
if value == nil then
|
||||
error(("field '%s' missing from table"):format(index), 3)
|
||||
else
|
||||
error(("bad field '%s' (expected %s, got %s)"):format(index, get_type_names(...), t), 3)
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
expect = expect,
|
||||
field = field,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,419 @@
|
||||
--- Provides a "pretty printer", for rendering data structures in an
|
||||
-- aesthetically pleasing manner.
|
||||
--
|
||||
-- In order to display something using @{cc.pretty}, you build up a series of
|
||||
-- @{Doc|documents}. These behave a little bit like strings; you can concatenate
|
||||
-- them together and then print them to the screen.
|
||||
--
|
||||
-- However, documents also allow you to control how they should be printed. There
|
||||
-- are several functions (such as @{nest} and @{group}) which allow you to control
|
||||
-- the "layout" of the document. When you come to display the document, the 'best'
|
||||
-- (most compact) layout is used.
|
||||
--
|
||||
-- @module cc.pretty
|
||||
-- @usage Print a table to the terminal
|
||||
-- local pretty = require "cc.pretty"
|
||||
-- pretty.write(pretty.dump({ 1, 2, 3 }))
|
||||
--
|
||||
-- @usage Build a custom document and display it
|
||||
-- local pretty = require "cc.pretty"
|
||||
-- pretty.write(pretty.group(pretty.text("hello") .. pretty.space_line .. pretty.text("world")))
|
||||
|
||||
local expect = require "cc.expect".expect
|
||||
local type, getmetatable, setmetatable, colours, str_write = type, getmetatable, setmetatable, colours, write
|
||||
|
||||
--- @{table.insert} alternative, but with the length stored inline.
|
||||
local function append(out, value)
|
||||
local n = out.n + 1
|
||||
out[n], out.n = value, n
|
||||
end
|
||||
|
||||
--- A document containing formatted text, with multiple possible layouts.
|
||||
--
|
||||
-- Documents effectively represent a sequence of strings in alternative layouts,
|
||||
-- which we will try to print in the most compact form necessary.
|
||||
--
|
||||
-- @type Doc
|
||||
local Doc = { }
|
||||
|
||||
--- An empty document.
|
||||
local empty = setmetatable({ tag = "nil" }, Doc)
|
||||
|
||||
--- A document with a single space in it.
|
||||
local space = setmetatable({ tag = "text", text = " " }, Doc)
|
||||
|
||||
--- A line break. When collapsed with @{group}, this will be replaced with @{empty}.
|
||||
local line = setmetatable({ tag = "line", flat = empty }, Doc)
|
||||
|
||||
--- A line break. When collapsed with @{group}, this will be replaced with @{space}.
|
||||
local space_line = setmetatable({ tag = "line", flat = space }, Doc)
|
||||
|
||||
local text_cache = { [""] = empty, [" "] = space, ["\n"] = space_line }
|
||||
|
||||
local function mk_text(text, colour)
|
||||
return text_cache[text] or setmetatable({ tag = "text", text = text, colour = colour }, Doc)
|
||||
end
|
||||
|
||||
--- Create a new document from a string.
|
||||
--
|
||||
-- If your string contains multiple lines, @{group} will flatten the string
|
||||
-- into a single line, with spaces between each line.
|
||||
--
|
||||
-- @tparam string text The string to construct a new document with.
|
||||
-- @tparam[opt] number colour The colour this text should be printed with. If not given, we default to the current
|
||||
-- colour.
|
||||
-- @treturn Doc The document with the provided text.
|
||||
local function text(text, colour)
|
||||
expect(1, text, "string")
|
||||
expect(2, colour, "number", "nil")
|
||||
|
||||
local cached = text_cache[text]
|
||||
if cached then return cached end
|
||||
|
||||
local new_line = text:find("\n", 1)
|
||||
if not new_line then return mk_text(text, colour) end
|
||||
|
||||
-- Split the string by "\n". With a micro-optimisation to skip empty strings.
|
||||
local doc = setmetatable({ tag = "concat", n = 0 }, Doc)
|
||||
if new_line ~= 1 then append(doc, mk_text(text:sub(1, new_line - 1), colour)) end
|
||||
|
||||
new_line = new_line + 1
|
||||
while true do
|
||||
local next_line = text:find("\n", new_line)
|
||||
append(doc, space_line)
|
||||
if not next_line then
|
||||
if new_line <= #text then append(doc, mk_text(text:sub(new_line), colour)) end
|
||||
return doc
|
||||
else
|
||||
if new_line <= next_line - 1 then
|
||||
append(doc, mk_text(text:sub(new_line, next_line - 1), colour))
|
||||
end
|
||||
new_line = next_line + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Concatenate several documents together. This behaves very similar to string concatenation.
|
||||
--
|
||||
-- @tparam Doc|string ... The documents to concatenate.
|
||||
-- @treturn Doc The concatenated documents.
|
||||
-- @usage pretty.concat(doc1, " - ", doc2)
|
||||
-- @usage doc1 .. " - " .. doc2
|
||||
local function concat(...)
|
||||
local args = table.pack(...)
|
||||
for i = 1, args.n do
|
||||
if type(args[i]) == "string" then args[i] = text(args[i]) end
|
||||
if getmetatable(args[i]) ~= Doc then expect(i, args[i], "document") end
|
||||
end
|
||||
|
||||
if args.n == 0 then return empty end
|
||||
if args.n == 1 then return args[1] end
|
||||
|
||||
args.tag = "concat"
|
||||
return setmetatable(args, Doc)
|
||||
end
|
||||
|
||||
Doc.__concat = concat --- @local
|
||||
|
||||
--- Indent later lines of the given document with the given number of spaces.
|
||||
--
|
||||
-- For instance, nesting the document
|
||||
-- ```txt
|
||||
-- foo
|
||||
-- bar
|
||||
-- ```
|
||||
-- by two spaces will produce
|
||||
-- ```txt
|
||||
-- foo
|
||||
-- bar
|
||||
-- ```
|
||||
--
|
||||
-- @tparam number depth The number of spaces with which the document should be indented.
|
||||
-- @tparam Doc doc The document to indent.
|
||||
-- @treturn Doc The nested document.
|
||||
-- @usage pretty.nest(2, pretty.text("foo\nbar"))
|
||||
local function nest(depth, doc)
|
||||
expect(1, depth, "number")
|
||||
if getmetatable(doc) ~= Doc then expect(2, doc, "document") end
|
||||
if depth <= 0 then error("depth must be a positive number", 2) end
|
||||
|
||||
return setmetatable({ tag = "nest", depth = depth, doc }, Doc)
|
||||
end
|
||||
|
||||
local function flatten(doc)
|
||||
if doc.flat then return doc.flat end
|
||||
|
||||
local kind = doc.tag
|
||||
if kind == "nil" or kind == "text" then
|
||||
return doc
|
||||
elseif kind == "concat" then
|
||||
local out = setmetatable({ tag = "concat", n = doc.n }, Doc)
|
||||
for i = 1, doc.n do out[i] = flatten(doc[i]) end
|
||||
doc.flat, out.flat = out, out -- cache the flattened node
|
||||
return out
|
||||
elseif kind == "nest" then
|
||||
return flatten(doc[1])
|
||||
elseif kind == "group" then
|
||||
return doc[1]
|
||||
else
|
||||
error("Unknown doc " .. kind)
|
||||
end
|
||||
end
|
||||
|
||||
--- Builds a document which is displayed on a single line if there is enough
|
||||
-- room, or as normal if not.
|
||||
--
|
||||
-- @tparam Doc doc The document to group.
|
||||
-- @treturn Doc The grouped document.
|
||||
local function group(doc)
|
||||
if getmetatable(doc) ~= Doc then expect(1, doc, "document") end
|
||||
|
||||
if doc.tag == "group" then return doc end -- Skip if already grouped.
|
||||
|
||||
local flattened = flatten(doc)
|
||||
if flattened == doc then return doc end -- Also skip if flattening does nothing.
|
||||
return setmetatable({ tag = "group", flattened, doc }, Doc)
|
||||
end
|
||||
|
||||
local function get_remaining(doc, width)
|
||||
local kind = doc.tag
|
||||
if kind == "nil" or kind == "line" then
|
||||
return width
|
||||
elseif kind == "text" then
|
||||
return width - #doc.text
|
||||
elseif kind == "concat" then
|
||||
for i = 1, doc.n do
|
||||
width = get_remaining(doc[i], width)
|
||||
if width < 0 then break end
|
||||
end
|
||||
return width
|
||||
elseif kind == "group" or kind == "nest" then
|
||||
return get_remaining(kind[1])
|
||||
else
|
||||
error("Unknown doc " .. kind)
|
||||
end
|
||||
end
|
||||
|
||||
--- Display a document on the terminal.
|
||||
--
|
||||
-- @tparam Doc doc The document to render
|
||||
-- @tparam[opt] number ribbon_frac The maximum fraction of the width that we should write in.
|
||||
local function write(doc, ribbon_frac)
|
||||
if getmetatable(doc) ~= Doc then expect(1, doc, "document") end
|
||||
expect(2, ribbon_frac, "number", "nil")
|
||||
|
||||
local term = term
|
||||
local width, height = term.getSize()
|
||||
local ribbon_width = (ribbon_frac or 0.6) * width
|
||||
if ribbon_width < 0 then ribbon_width = 0 end
|
||||
if ribbon_width > width then ribbon_width = width end
|
||||
|
||||
local def_colour = term.getTextColour()
|
||||
local current_colour = def_colour
|
||||
|
||||
local function go(doc, indent, col)
|
||||
local kind = doc.tag
|
||||
if kind == "nil" then
|
||||
return col
|
||||
elseif kind == "text" then
|
||||
local doc_colour = doc.colour or def_colour
|
||||
if doc_colour ~= current_colour then
|
||||
term.setTextColour(doc_colour)
|
||||
current_colour = doc_colour
|
||||
end
|
||||
|
||||
str_write(doc.text)
|
||||
|
||||
return col + #doc.text
|
||||
elseif kind == "line" then
|
||||
local _, y = term.getCursorPos()
|
||||
if y < height then
|
||||
term.setCursorPos(indent + 1, y + 1)
|
||||
else
|
||||
term.scroll(1)
|
||||
term.setCursorPos(indent + 1, height)
|
||||
end
|
||||
|
||||
return indent
|
||||
elseif kind == "concat" then
|
||||
for i = 1, doc.n do col = go(doc[i], indent, col) end
|
||||
return col
|
||||
elseif kind == "nest" then
|
||||
return go(doc[1], indent + doc.depth, col)
|
||||
elseif kind == "group" then
|
||||
if get_remaining(doc[1], math.min(width, ribbon_width + indent) - col) >= 0 then
|
||||
return go(doc[1], indent, col)
|
||||
else
|
||||
return go(doc[2], indent, col)
|
||||
end
|
||||
else
|
||||
error("Unknown doc " .. kind)
|
||||
end
|
||||
end
|
||||
|
||||
local col = math.max(term.getCursorPos() - 1, 0)
|
||||
go(doc, 0, col)
|
||||
if current_colour ~= def_colour then term.setTextColour(def_colour) end
|
||||
end
|
||||
|
||||
--- Display a document on the terminal with a trailing new line.
|
||||
--
|
||||
-- @tparam Doc doc The document to render.
|
||||
-- @tparam[opt] number ribbon_frac The maximum fraction of the width that we should write in.
|
||||
local function print(doc, ribbon_frac)
|
||||
if getmetatable(doc) ~= Doc then expect(1, doc, "document") end
|
||||
expect(2, ribbon_frac, "number", "nil")
|
||||
write(doc, ribbon_frac)
|
||||
str_write("\n")
|
||||
end
|
||||
|
||||
--- Render a document, converting it into a string.
|
||||
--
|
||||
-- @tparam Doc doc The document to render.
|
||||
-- @tparam[opt] number width The maximum width of this document. Note that long strings will not be wrapped to
|
||||
-- fit this width - it is only used for finding the best layout.
|
||||
-- @tparam[opt] number ribbon_frac The maximum fraction of the width that we should write in.
|
||||
-- @treturn string The rendered document as a string.
|
||||
local function render(doc, width, ribbon_frac)
|
||||
if getmetatable(doc) ~= Doc then expect(1, doc, "document") end
|
||||
expect(2, width, "number", "nil")
|
||||
expect(3, ribbon_frac, "number", "nil")
|
||||
|
||||
local ribbon_width
|
||||
if width then
|
||||
ribbon_width = (ribbon_frac or 0.6) * width
|
||||
if ribbon_width < 0 then ribbon_width = 0 end
|
||||
if ribbon_width > width then ribbon_width = width end
|
||||
end
|
||||
|
||||
local out = { n = 0 }
|
||||
local function go(doc, indent, col)
|
||||
local kind = doc.tag
|
||||
if kind == "nil" then
|
||||
return col
|
||||
elseif kind == "text" then
|
||||
append(out, doc.text)
|
||||
return col + #doc.text
|
||||
elseif kind == "line" then
|
||||
append(out, "\n" .. (" "):rep(indent))
|
||||
return indent
|
||||
elseif kind == "concat" then
|
||||
for i = 1, doc.n do col = go(doc[i], indent, col) end
|
||||
return col
|
||||
elseif kind == "nest" then
|
||||
return go(doc[1], indent + doc.depth, col)
|
||||
elseif kind == "group" then
|
||||
if not width or get_remaining(doc[1], math.min(width, ribbon_width + indent) - col) >= 0 then
|
||||
return go(doc[1], indent, col)
|
||||
else
|
||||
return go(doc[2], indent, col)
|
||||
end
|
||||
else
|
||||
error("Unknown doc " .. kind)
|
||||
end
|
||||
end
|
||||
|
||||
go(doc, 0, 0)
|
||||
return table.concat(out, "", 1, out.n)
|
||||
end
|
||||
|
||||
Doc.__tostring = render --- @local
|
||||
|
||||
local keywords = {
|
||||
["and"] = true, ["break"] = true, ["do"] = true, ["else"] = true,
|
||||
["elseif"] = true, ["end"] = true, ["false"] = true, ["for"] = true,
|
||||
["function"] = true, ["if"] = true, ["in"] = true, ["local"] = true,
|
||||
["nil"] = true, ["not"] = true, ["or"] = true, ["repeat"] = true, ["return"] = true,
|
||||
["then"] = true, ["true"] = true, ["until"] = true, ["while"] = true,
|
||||
}
|
||||
|
||||
local comma = text(",")
|
||||
local braces = text("{}")
|
||||
local obrace, cbrace = text("{"), text("}")
|
||||
local obracket, cbracket = text("["), text("] = ")
|
||||
|
||||
local function key_compare(a, b)
|
||||
local ta, tb = type(a), type(b)
|
||||
|
||||
if ta == "string" then return tb ~= "string" or a < b
|
||||
elseif tb == "string" then return false
|
||||
end
|
||||
|
||||
if ta == "number" then return tb ~= "number" or a < b end
|
||||
return false
|
||||
end
|
||||
|
||||
local function pretty_impl(obj, tracking)
|
||||
local obj_type = type(obj)
|
||||
if obj_type == "string" then
|
||||
local formatted = ("%q"):format(obj):gsub("\\\n", "\\n")
|
||||
return text(formatted, colours.red)
|
||||
elseif obj_type == "number" then
|
||||
return text(tostring(obj), colours.magenta)
|
||||
elseif obj_type ~= "table" or tracking[obj] then
|
||||
return text(tostring(obj), colours.lightGrey)
|
||||
elseif getmetatable(obj) ~= nil and getmetatable(obj).__tostring then
|
||||
return text(tostring(obj))
|
||||
elseif next(obj) == nil then
|
||||
return braces
|
||||
else
|
||||
tracking[obj] = true
|
||||
local doc = setmetatable({ tag = "concat", n = 1, space_line }, Doc)
|
||||
|
||||
local length, keys, keysn = #obj, {}, 1
|
||||
for k in pairs(obj) do keys[keysn], keysn = k, keysn + 1 end
|
||||
table.sort(keys, key_compare)
|
||||
|
||||
for i = 1, keysn - 1 do
|
||||
if i > 1 then append(doc, comma) append(doc, space_line) end
|
||||
|
||||
local k = keys[i]
|
||||
local v = obj[k]
|
||||
local ty = type(k)
|
||||
if ty == "number" and k % 1 == 0 and k >= 1 and k <= length then
|
||||
append(doc, pretty_impl(v, tracking))
|
||||
elseif ty == "string" and not keywords[k] and k:match("^[%a_][%a%d_]*$") then
|
||||
append(doc, text(k .. " = "))
|
||||
append(doc, pretty_impl(v, tracking))
|
||||
else
|
||||
append(doc, obracket)
|
||||
append(doc, pretty_impl(k, tracking))
|
||||
append(doc, cbracket)
|
||||
append(doc, pretty_impl(v, tracking))
|
||||
end
|
||||
end
|
||||
|
||||
tracking[obj] = nil
|
||||
return group(concat(obrace, nest(2, concat(table.unpack(doc, 1, n))), space_line, cbrace))
|
||||
end
|
||||
end
|
||||
|
||||
--- Pretty-print an arbitrary object, converting it into a document.
|
||||
--
|
||||
-- This can then be rendered with @{write} or @{print}.
|
||||
--
|
||||
-- @param obj The object to pretty-print.
|
||||
-- @treturn Doc The object formatted as a document.
|
||||
-- @usage Display a table on the screen
|
||||
-- local pretty = require "cc.pretty"
|
||||
-- pretty.print(pretty.pretty({ 1, 2, 3 }))
|
||||
local function pretty(obj)
|
||||
return pretty_impl(obj, {})
|
||||
end
|
||||
|
||||
return {
|
||||
empty = empty,
|
||||
space = space,
|
||||
line = line,
|
||||
space_line = space_line,
|
||||
text = text,
|
||||
concat = concat,
|
||||
nest = nest,
|
||||
group = group,
|
||||
|
||||
write = write,
|
||||
print = print,
|
||||
render = render,
|
||||
|
||||
pretty = pretty,
|
||||
}
|
||||
@@ -97,7 +97,7 @@ end
|
||||
-- complete.build(
|
||||
-- { complete.choice, { "get", "put" } },
|
||||
-- complete.dir,
|
||||
-- } complete.file, many = true }
|
||||
-- { complete.file, many = true }
|
||||
-- )
|
||||
local function build(...)
|
||||
local arguments = table.pack(...)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
|
||||
if not shell.openTab then
|
||||
printError( "Requires multishell" )
|
||||
printError("Requires multishell")
|
||||
return
|
||||
end
|
||||
|
||||
local tArgs = { ... }
|
||||
if #tArgs > 0 then
|
||||
shell.openTab( table.unpack( tArgs ) )
|
||||
shell.openTab(table.unpack(tArgs))
|
||||
else
|
||||
shell.openTab( "shell" )
|
||||
shell.openTab("shell")
|
||||
end
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
|
||||
if not shell.openTab then
|
||||
printError( "Requires multishell" )
|
||||
printError("Requires multishell")
|
||||
return
|
||||
end
|
||||
|
||||
local tArgs = { ... }
|
||||
if #tArgs > 0 then
|
||||
local nTask = shell.openTab( table.unpack( tArgs ) )
|
||||
local nTask = shell.openTab(table.unpack(tArgs))
|
||||
if nTask then
|
||||
shell.switchTab( nTask )
|
||||
shell.switchTab(nTask)
|
||||
end
|
||||
else
|
||||
local nTask = shell.openTab( "shell" )
|
||||
local nTask = shell.openTab("shell")
|
||||
if nTask then
|
||||
shell.switchTab( nTask )
|
||||
shell.switchTab(nTask)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -12,84 +12,84 @@ local bWindowsResized = false
|
||||
local nScrollPos = 1
|
||||
local bScrollRight = false
|
||||
|
||||
local function selectProcess( n )
|
||||
local function selectProcess(n)
|
||||
if nCurrentProcess ~= n then
|
||||
if nCurrentProcess then
|
||||
local tOldProcess = tProcesses[ nCurrentProcess ]
|
||||
tOldProcess.window.setVisible( false )
|
||||
local tOldProcess = tProcesses[nCurrentProcess]
|
||||
tOldProcess.window.setVisible(false)
|
||||
end
|
||||
nCurrentProcess = n
|
||||
if nCurrentProcess then
|
||||
local tNewProcess = tProcesses[ nCurrentProcess ]
|
||||
tNewProcess.window.setVisible( true )
|
||||
local tNewProcess = tProcesses[nCurrentProcess]
|
||||
tNewProcess.window.setVisible(true)
|
||||
tNewProcess.bInteracted = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function setProcessTitle( n, sTitle )
|
||||
tProcesses[ n ].sTitle = sTitle
|
||||
local function setProcessTitle(n, sTitle)
|
||||
tProcesses[n].sTitle = sTitle
|
||||
end
|
||||
|
||||
local function resumeProcess( nProcess, sEvent, ... )
|
||||
local tProcess = tProcesses[ nProcess ]
|
||||
local function resumeProcess(nProcess, sEvent, ...)
|
||||
local tProcess = tProcesses[nProcess]
|
||||
local sFilter = tProcess.sFilter
|
||||
if sFilter == nil or sFilter == sEvent or sEvent == "terminate" then
|
||||
local nPreviousProcess = nRunningProcess
|
||||
nRunningProcess = nProcess
|
||||
term.redirect( tProcess.terminal )
|
||||
local ok, result = coroutine.resume( tProcess.co, sEvent, ... )
|
||||
term.redirect(tProcess.terminal)
|
||||
local ok, result = coroutine.resume(tProcess.co, sEvent, ...)
|
||||
tProcess.terminal = term.current()
|
||||
if ok then
|
||||
tProcess.sFilter = result
|
||||
else
|
||||
printError( result )
|
||||
printError(result)
|
||||
end
|
||||
nRunningProcess = nPreviousProcess
|
||||
end
|
||||
end
|
||||
|
||||
local function launchProcess( bFocus, tProgramEnv, sProgramPath, ... )
|
||||
local tProgramArgs = table.pack( ... )
|
||||
local function launchProcess(bFocus, tProgramEnv, sProgramPath, ...)
|
||||
local tProgramArgs = table.pack(...)
|
||||
local nProcess = #tProcesses + 1
|
||||
local tProcess = {}
|
||||
tProcess.sTitle = fs.getName( sProgramPath )
|
||||
tProcess.sTitle = fs.getName(sProgramPath)
|
||||
if bShowMenu then
|
||||
tProcess.window = window.create( parentTerm, 1, 2, w, h - 1, false )
|
||||
tProcess.window = window.create(parentTerm, 1, 2, w, h - 1, false)
|
||||
else
|
||||
tProcess.window = window.create( parentTerm, 1, 1, w, h, false )
|
||||
tProcess.window = window.create(parentTerm, 1, 1, w, h, false)
|
||||
end
|
||||
tProcess.co = coroutine.create( function()
|
||||
os.run( tProgramEnv, sProgramPath, table.unpack( tProgramArgs, 1, tProgramArgs.n ) )
|
||||
tProcess.co = coroutine.create(function()
|
||||
os.run(tProgramEnv, sProgramPath, table.unpack(tProgramArgs, 1, tProgramArgs.n))
|
||||
if not tProcess.bInteracted then
|
||||
term.setCursorBlink( false )
|
||||
print( "Press any key to continue" )
|
||||
os.pullEvent( "char" )
|
||||
term.setCursorBlink(false)
|
||||
print("Press any key to continue")
|
||||
os.pullEvent("char")
|
||||
end
|
||||
end )
|
||||
end)
|
||||
tProcess.sFilter = nil
|
||||
tProcess.terminal = tProcess.window
|
||||
tProcess.bInteracted = false
|
||||
tProcesses[ nProcess ] = tProcess
|
||||
tProcesses[nProcess] = tProcess
|
||||
if bFocus then
|
||||
selectProcess( nProcess )
|
||||
selectProcess(nProcess)
|
||||
end
|
||||
resumeProcess( nProcess )
|
||||
resumeProcess(nProcess)
|
||||
return nProcess
|
||||
end
|
||||
|
||||
local function cullProcess( nProcess )
|
||||
local tProcess = tProcesses[ nProcess ]
|
||||
if coroutine.status( tProcess.co ) == "dead" then
|
||||
local function cullProcess(nProcess)
|
||||
local tProcess = tProcesses[nProcess]
|
||||
if coroutine.status(tProcess.co) == "dead" then
|
||||
if nCurrentProcess == nProcess then
|
||||
selectProcess( nil )
|
||||
selectProcess(nil)
|
||||
end
|
||||
table.remove( tProcesses, nProcess )
|
||||
table.remove(tProcesses, nProcess)
|
||||
if nCurrentProcess == nil then
|
||||
if nProcess > 1 then
|
||||
selectProcess( nProcess - 1 )
|
||||
selectProcess(nProcess - 1)
|
||||
elseif #tProcesses > 0 then
|
||||
selectProcess( 1 )
|
||||
selectProcess(1)
|
||||
end
|
||||
end
|
||||
if nScrollPos ~= 1 then
|
||||
@@ -103,7 +103,7 @@ end
|
||||
local function cullProcesses()
|
||||
local culled = false
|
||||
for n = #tProcesses, 1, -1 do
|
||||
culled = culled or cullProcess( n )
|
||||
culled = culled or cullProcess(n)
|
||||
end
|
||||
return culled
|
||||
end
|
||||
@@ -121,40 +121,40 @@ end
|
||||
local function redrawMenu()
|
||||
if bShowMenu then
|
||||
-- Draw menu
|
||||
parentTerm.setCursorPos( 1, 1 )
|
||||
parentTerm.setBackgroundColor( menuOtherBgColor )
|
||||
parentTerm.setCursorPos(1, 1)
|
||||
parentTerm.setBackgroundColor(menuOtherBgColor)
|
||||
parentTerm.clearLine()
|
||||
local nCharCount = 0
|
||||
local nSize = parentTerm.getSize()
|
||||
if nScrollPos ~= 1 then
|
||||
parentTerm.setTextColor( menuOtherTextColor )
|
||||
parentTerm.setBackgroundColor( menuOtherBgColor )
|
||||
parentTerm.write( "<" )
|
||||
parentTerm.setTextColor(menuOtherTextColor)
|
||||
parentTerm.setBackgroundColor(menuOtherBgColor)
|
||||
parentTerm.write("<")
|
||||
nCharCount = 1
|
||||
end
|
||||
for n = nScrollPos, #tProcesses do
|
||||
if n == nCurrentProcess then
|
||||
parentTerm.setTextColor( menuMainTextColor )
|
||||
parentTerm.setBackgroundColor( menuMainBgColor )
|
||||
parentTerm.setTextColor(menuMainTextColor)
|
||||
parentTerm.setBackgroundColor(menuMainBgColor)
|
||||
else
|
||||
parentTerm.setTextColor( menuOtherTextColor )
|
||||
parentTerm.setBackgroundColor( menuOtherBgColor )
|
||||
parentTerm.setTextColor(menuOtherTextColor)
|
||||
parentTerm.setBackgroundColor(menuOtherBgColor)
|
||||
end
|
||||
parentTerm.write( " " .. tProcesses[n].sTitle .. " " )
|
||||
parentTerm.write(" " .. tProcesses[n].sTitle .. " ")
|
||||
nCharCount = nCharCount + #tProcesses[n].sTitle + 2
|
||||
end
|
||||
if nCharCount > nSize then
|
||||
parentTerm.setTextColor( menuOtherTextColor )
|
||||
parentTerm.setBackgroundColor( menuOtherBgColor )
|
||||
parentTerm.setCursorPos( nSize, 1 )
|
||||
parentTerm.write( ">" )
|
||||
parentTerm.setTextColor(menuOtherTextColor)
|
||||
parentTerm.setBackgroundColor(menuOtherBgColor)
|
||||
parentTerm.setCursorPos(nSize, 1)
|
||||
parentTerm.write(">")
|
||||
bScrollRight = true
|
||||
else
|
||||
bScrollRight = false
|
||||
end
|
||||
|
||||
-- Put the cursor back where it should be
|
||||
local tProcess = tProcesses[ nCurrentProcess ]
|
||||
local tProcess = tProcesses[nCurrentProcess]
|
||||
if tProcess then
|
||||
tProcess.window.restoreCursor()
|
||||
end
|
||||
@@ -174,15 +174,15 @@ local function resizeWindows()
|
||||
local tProcess = tProcesses[n]
|
||||
local x, y = tProcess.window.getCursorPos()
|
||||
if y > windowHeight then
|
||||
tProcess.window.scroll( y - windowHeight )
|
||||
tProcess.window.setCursorPos( x, windowHeight )
|
||||
tProcess.window.scroll(y - windowHeight)
|
||||
tProcess.window.setCursorPos(x, windowHeight)
|
||||
end
|
||||
tProcess.window.reposition( 1, windowY, w, windowHeight )
|
||||
tProcess.window.reposition(1, windowY, w, windowHeight)
|
||||
end
|
||||
bWindowsResized = true
|
||||
end
|
||||
|
||||
local function setMenuVisible( bVis )
|
||||
local function setMenuVisible(bVis)
|
||||
if bShowMenu ~= bVis then
|
||||
bShowMenu = bVis
|
||||
resizeWindows()
|
||||
@@ -196,17 +196,17 @@ function multishell.getFocus()
|
||||
return nCurrentProcess
|
||||
end
|
||||
|
||||
function multishell.setFocus( n )
|
||||
function multishell.setFocus(n)
|
||||
expect(1, n, "number")
|
||||
if n >= 1 and n <= #tProcesses then
|
||||
selectProcess( n )
|
||||
selectProcess(n)
|
||||
redrawMenu()
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function multishell.getTitle( n )
|
||||
function multishell.getTitle(n)
|
||||
expect(1, n, "number")
|
||||
if n >= 1 and n <= #tProcesses then
|
||||
return tProcesses[n].sTitle
|
||||
@@ -214,11 +214,11 @@ function multishell.getTitle( n )
|
||||
return nil
|
||||
end
|
||||
|
||||
function multishell.setTitle( n, sTitle )
|
||||
function multishell.setTitle(n, sTitle)
|
||||
expect(1, n, "number")
|
||||
expect(2, sTitle, "string")
|
||||
if n >= 1 and n <= #tProcesses then
|
||||
setProcessTitle( n, sTitle )
|
||||
setProcessTitle(n, sTitle)
|
||||
redrawMenu()
|
||||
end
|
||||
end
|
||||
@@ -227,14 +227,14 @@ function multishell.getCurrent()
|
||||
return nRunningProcess
|
||||
end
|
||||
|
||||
function multishell.launch( tProgramEnv, sProgramPath, ... )
|
||||
function multishell.launch(tProgramEnv, sProgramPath, ...)
|
||||
expect(1, tProgramEnv, "table")
|
||||
expect(2, sProgramPath, "string")
|
||||
local previousTerm = term.current()
|
||||
setMenuVisible( #tProcesses + 1 >= 2 )
|
||||
local nResult = launchProcess( false, tProgramEnv, sProgramPath, ... )
|
||||
setMenuVisible(#tProcesses + 1 >= 2)
|
||||
local nResult = launchProcess(false, tProgramEnv, sProgramPath, ...)
|
||||
redrawMenu()
|
||||
term.redirect( previousTerm )
|
||||
term.redirect(previousTerm)
|
||||
return nResult
|
||||
end
|
||||
|
||||
@@ -244,16 +244,16 @@ end
|
||||
|
||||
-- Begin
|
||||
parentTerm.clear()
|
||||
setMenuVisible( false )
|
||||
launchProcess( true, {
|
||||
setMenuVisible(false)
|
||||
launchProcess(true, {
|
||||
["shell"] = shell,
|
||||
["multishell"] = multishell,
|
||||
}, "/rom/programs/shell.lua" )
|
||||
}, "/rom/programs/shell.lua")
|
||||
|
||||
-- Run processes
|
||||
while #tProcesses > 0 do
|
||||
-- Get the event
|
||||
local tEventData = table.pack( os.pullEventRaw() )
|
||||
local tEventData = table.pack(os.pullEventRaw())
|
||||
local sEvent = tEventData[1]
|
||||
if sEvent == "term_resize" then
|
||||
-- Resize event
|
||||
@@ -264,9 +264,9 @@ while #tProcesses > 0 do
|
||||
elseif sEvent == "char" or sEvent == "key" or sEvent == "key_up" or sEvent == "paste" or sEvent == "terminate" then
|
||||
-- Keyboard event
|
||||
-- Passthrough to current process
|
||||
resumeProcess( nCurrentProcess, table.unpack( tEventData, 1, tEventData.n ) )
|
||||
if cullProcess( nCurrentProcess ) then
|
||||
setMenuVisible( #tProcesses >= 2 )
|
||||
resumeProcess(nCurrentProcess, table.unpack(tEventData, 1, tEventData.n))
|
||||
if cullProcess(nCurrentProcess) then
|
||||
setMenuVisible(#tProcesses >= 2)
|
||||
redrawMenu()
|
||||
end
|
||||
|
||||
@@ -289,7 +289,7 @@ while #tProcesses > 0 do
|
||||
for n = nScrollPos, #tProcesses do
|
||||
local tabEnd = tabStart + #tProcesses[n].sTitle + 1
|
||||
if x >= tabStart and x <= tabEnd then
|
||||
selectProcess( n )
|
||||
selectProcess(n)
|
||||
redrawMenu()
|
||||
break
|
||||
end
|
||||
@@ -298,9 +298,9 @@ while #tProcesses > 0 do
|
||||
end
|
||||
else
|
||||
-- Passthrough to current process
|
||||
resumeProcess( nCurrentProcess, sEvent, button, x, bShowMenu and y - 1 or y )
|
||||
if cullProcess( nCurrentProcess ) then
|
||||
setMenuVisible( #tProcesses >= 2 )
|
||||
resumeProcess(nCurrentProcess, sEvent, button, x, bShowMenu and y - 1 or y)
|
||||
if cullProcess(nCurrentProcess) then
|
||||
setMenuVisible(#tProcesses >= 2)
|
||||
redrawMenu()
|
||||
end
|
||||
end
|
||||
@@ -318,9 +318,9 @@ while #tProcesses > 0 do
|
||||
end
|
||||
elseif not (bShowMenu and y == 1) then
|
||||
-- Passthrough to current process
|
||||
resumeProcess( nCurrentProcess, sEvent, p1, x, bShowMenu and y - 1 or y )
|
||||
if cullProcess( nCurrentProcess ) then
|
||||
setMenuVisible( #tProcesses >= 2 )
|
||||
resumeProcess(nCurrentProcess, sEvent, p1, x, bShowMenu and y - 1 or y)
|
||||
if cullProcess(nCurrentProcess) then
|
||||
setMenuVisible(#tProcesses >= 2)
|
||||
redrawMenu()
|
||||
end
|
||||
end
|
||||
@@ -330,10 +330,10 @@ while #tProcesses > 0 do
|
||||
-- Passthrough to all processes
|
||||
local nLimit = #tProcesses -- Storing this ensures any new things spawned don't get the event
|
||||
for n = 1, nLimit do
|
||||
resumeProcess( n, table.unpack( tEventData, 1, tEventData.n ) )
|
||||
resumeProcess(n, table.unpack(tEventData, 1, tEventData.n))
|
||||
end
|
||||
if cullProcesses() then
|
||||
setMenuVisible( #tProcesses >= 2 )
|
||||
setMenuVisible(#tProcesses >= 2)
|
||||
redrawMenu()
|
||||
end
|
||||
end
|
||||
@@ -342,15 +342,15 @@ while #tProcesses > 0 do
|
||||
-- Pass term_resize to all processes
|
||||
local nLimit = #tProcesses -- Storing this ensures any new things spawned don't get the event
|
||||
for n = 1, nLimit do
|
||||
resumeProcess( n, "term_resize" )
|
||||
resumeProcess(n, "term_resize")
|
||||
end
|
||||
bWindowsResized = false
|
||||
if cullProcesses() then
|
||||
setMenuVisible( #tProcesses >= 2 )
|
||||
setMenuVisible(#tProcesses >= 2)
|
||||
redrawMenu()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Shutdown
|
||||
term.redirect( parentTerm )
|
||||
term.redirect(parentTerm)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
local tArgs = { ... }
|
||||
if #tArgs > 2 then
|
||||
print( "Usage: alias <alias> <program>" )
|
||||
print("Usage: alias <alias> <program>")
|
||||
return
|
||||
end
|
||||
|
||||
@@ -10,17 +10,17 @@ local sProgram = tArgs[2]
|
||||
|
||||
if sAlias and sProgram then
|
||||
-- Set alias
|
||||
shell.setAlias( sAlias, sProgram )
|
||||
shell.setAlias(sAlias, sProgram)
|
||||
elseif sAlias then
|
||||
-- Clear alias
|
||||
shell.clearAlias( sAlias )
|
||||
shell.clearAlias(sAlias)
|
||||
else
|
||||
-- List aliases
|
||||
local tAliases = shell.aliases()
|
||||
local tList = {}
|
||||
for sAlias, sCommand in pairs( tAliases ) do
|
||||
table.insert( tList, sAlias .. ":" .. sCommand )
|
||||
for sAlias, sCommand in pairs(tAliases) do
|
||||
table.insert(tList, sAlias .. ":" .. sCommand)
|
||||
end
|
||||
table.sort( tList )
|
||||
textutils.pagedTabulate( tList )
|
||||
table.sort(tList)
|
||||
textutils.pagedTabulate(tList)
|
||||
end
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
|
||||
local tApis = {}
|
||||
for k, v in pairs( _G ) do
|
||||
for k, v in pairs(_G) do
|
||||
if type(k) == "string" and type(v) == "table" and k ~= "_G" then
|
||||
table.insert( tApis, k )
|
||||
table.insert(tApis, k)
|
||||
end
|
||||
end
|
||||
table.insert( tApis, "shell" )
|
||||
table.insert( tApis, "package" )
|
||||
table.insert(tApis, "shell")
|
||||
table.insert(tApis, "package")
|
||||
if multishell then
|
||||
table.insert( tApis, "multishell" )
|
||||
table.insert(tApis, "multishell")
|
||||
end
|
||||
table.sort( tApis )
|
||||
table.sort(tApis)
|
||||
|
||||
textutils.pagedTabulate( tApis )
|
||||
textutils.pagedTabulate(tApis)
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
|
||||
local tArgs = { ... }
|
||||
if #tArgs < 1 then
|
||||
print( "Usage: cd <path>" )
|
||||
print("Usage: cd <path>")
|
||||
return
|
||||
end
|
||||
|
||||
local sNewDir = shell.resolve( tArgs[1] )
|
||||
if fs.isDir( sNewDir ) then
|
||||
shell.setDir( sNewDir )
|
||||
local sNewDir = shell.resolve(tArgs[1])
|
||||
if fs.isDir(sNewDir) then
|
||||
shell.setDir(sNewDir)
|
||||
else
|
||||
print( "Not a directory" )
|
||||
print("Not a directory")
|
||||
return
|
||||
end
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
term.clear()
|
||||
term.setCursorPos( 1, 1 )
|
||||
term.setCursorPos(1, 1)
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
|
||||
if not commands then
|
||||
printError( "Requires a Command Computer." )
|
||||
printError("Requires a Command Computer.")
|
||||
return
|
||||
end
|
||||
|
||||
local tCommands = commands.list()
|
||||
table.sort( tCommands )
|
||||
table.sort(tCommands)
|
||||
|
||||
if term.isColor() then
|
||||
term.setTextColor( colors.green )
|
||||
term.setTextColor(colors.green)
|
||||
end
|
||||
print( "Available commands:" )
|
||||
term.setTextColor( colors.white )
|
||||
print("Available commands:")
|
||||
term.setTextColor(colors.white)
|
||||
|
||||
textutils.pagedTabulate( tCommands )
|
||||
textutils.pagedTabulate(tCommands)
|
||||
|
||||
@@ -1,40 +1,40 @@
|
||||
|
||||
local tArgs = { ... }
|
||||
if not commands then
|
||||
printError( "Requires a Command Computer." )
|
||||
printError("Requires a Command Computer.")
|
||||
return
|
||||
end
|
||||
if #tArgs == 0 then
|
||||
printError( "Usage: exec <command>" )
|
||||
printError("Usage: exec <command>")
|
||||
return
|
||||
end
|
||||
|
||||
local function printSuccess( text )
|
||||
local function printSuccess(text)
|
||||
if term.isColor() then
|
||||
term.setTextColor( colors.green )
|
||||
term.setTextColor(colors.green)
|
||||
end
|
||||
print( text )
|
||||
term.setTextColor( colors.white )
|
||||
print(text)
|
||||
term.setTextColor(colors.white)
|
||||
end
|
||||
|
||||
local sCommand = string.lower( tArgs[1] )
|
||||
local sCommand = string.lower(tArgs[1])
|
||||
for n = 2, #tArgs do
|
||||
sCommand = sCommand .. " " .. tArgs[n]
|
||||
end
|
||||
|
||||
local bResult, tOutput = commands.exec( sCommand )
|
||||
local bResult, tOutput = commands.exec(sCommand)
|
||||
if bResult then
|
||||
printSuccess( "Success" )
|
||||
printSuccess("Success")
|
||||
if #tOutput > 0 then
|
||||
for n = 1, #tOutput do
|
||||
print( tOutput[n] )
|
||||
print(tOutput[n])
|
||||
end
|
||||
end
|
||||
else
|
||||
printError( "Failed" )
|
||||
printError("Failed")
|
||||
if #tOutput > 0 then
|
||||
for n = 1, #tOutput do
|
||||
print( tOutput[n] )
|
||||
print(tOutput[n])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
|
||||
local tArgs = { ... }
|
||||
if #tArgs < 2 then
|
||||
print( "Usage: cp <source> <destination>" )
|
||||
print("Usage: cp <source> <destination>")
|
||||
return
|
||||
end
|
||||
|
||||
local sSource = shell.resolve( tArgs[1] )
|
||||
local sDest = shell.resolve( tArgs[2] )
|
||||
local tFiles = fs.find( sSource )
|
||||
local sSource = shell.resolve(tArgs[1])
|
||||
local sDest = shell.resolve(tArgs[2])
|
||||
local tFiles = fs.find(sSource)
|
||||
if #tFiles > 0 then
|
||||
for _, sFile in ipairs( tFiles ) do
|
||||
if fs.isDir( sDest ) then
|
||||
fs.copy( sFile, fs.combine( sDest, fs.getName(sFile) ) )
|
||||
for _, sFile in ipairs(tFiles) do
|
||||
if fs.isDir(sDest) then
|
||||
fs.copy(sFile, fs.combine(sDest, fs.getName(sFile)))
|
||||
elseif #tFiles == 1 then
|
||||
if fs.exists( sDest ) then
|
||||
printError( "Destination exists" )
|
||||
elseif fs.isReadOnly( sDest ) then
|
||||
printError( "Destination is read-only" )
|
||||
elseif fs.getFreeSpace( sDest ) < fs.getSize( sFile ) then
|
||||
printError( "Not enough space" )
|
||||
if fs.exists(sDest) then
|
||||
printError("Destination exists")
|
||||
elseif fs.isReadOnly(sDest) then
|
||||
printError("Destination is read-only")
|
||||
elseif fs.getFreeSpace(sDest) < fs.getSize(sFile) then
|
||||
printError("Not enough space")
|
||||
else
|
||||
fs.copy( sFile, sDest )
|
||||
fs.copy(sFile, sDest)
|
||||
end
|
||||
else
|
||||
printError( "Cannot overwrite file multiple times" )
|
||||
printError("Cannot overwrite file multiple times")
|
||||
return
|
||||
end
|
||||
end
|
||||
else
|
||||
printError( "No matching files" )
|
||||
printError("No matching files")
|
||||
end
|
||||
|
||||
@@ -3,19 +3,19 @@ local tArgs = { ... }
|
||||
-- Get where a directory is mounted
|
||||
local sPath = shell.dir()
|
||||
if tArgs[1] ~= nil then
|
||||
sPath = shell.resolve( tArgs[1] )
|
||||
sPath = shell.resolve(tArgs[1])
|
||||
end
|
||||
|
||||
if fs.exists( sPath ) then
|
||||
write( fs.getDrive( sPath ) .. " (" )
|
||||
local nSpace = fs.getFreeSpace( sPath )
|
||||
if fs.exists(sPath) then
|
||||
write(fs.getDrive(sPath) .. " (")
|
||||
local nSpace = fs.getFreeSpace(sPath)
|
||||
if nSpace >= 1000 * 1000 then
|
||||
print( math.floor( nSpace / (100 * 1000) ) / 10 .. "MB remaining)" )
|
||||
print(math.floor(nSpace / (100 * 1000)) / 10 .. "MB remaining)")
|
||||
elseif nSpace >= 1000 then
|
||||
print( math.floor( nSpace / 100 ) / 10 .. "KB remaining)" )
|
||||
print(math.floor(nSpace / 100) / 10 .. "KB remaining)")
|
||||
else
|
||||
print( nSpace .. "B remaining)" )
|
||||
print(nSpace .. "B remaining)")
|
||||
end
|
||||
else
|
||||
print( "No such path" )
|
||||
print("No such path")
|
||||
end
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
-- Get file to edit
|
||||
local tArgs = { ... }
|
||||
if #tArgs == 0 then
|
||||
print( "Usage: edit <path>" )
|
||||
print("Usage: edit <path>")
|
||||
return
|
||||
end
|
||||
|
||||
-- Error checking
|
||||
local sPath = shell.resolve( tArgs[1] )
|
||||
local bReadOnly = fs.isReadOnly( sPath )
|
||||
if fs.exists( sPath ) and fs.isDir( sPath ) then
|
||||
print( "Cannot edit a directory." )
|
||||
local sPath = shell.resolve(tArgs[1])
|
||||
local bReadOnly = fs.isReadOnly(sPath)
|
||||
if fs.exists(sPath) and fs.isDir(sPath) then
|
||||
print("Cannot edit a directory.")
|
||||
return
|
||||
end
|
||||
|
||||
-- Create .lua files by default
|
||||
if not fs.exists( sPath ) and not string.find( sPath, "%." ) then
|
||||
local sExtension = settings.get("edit.default_extension", "" )
|
||||
if sExtension ~= "" and type( sExtension ) == "string" then
|
||||
if not fs.exists(sPath) and not string.find(sPath, "%.") then
|
||||
local sExtension = settings.get("edit.default_extension", "")
|
||||
if sExtension ~= "" and type(sExtension) == "string" then
|
||||
sPath = sPath .. "." .. sExtension
|
||||
end
|
||||
end
|
||||
@@ -51,59 +51,59 @@ local bMenu = false
|
||||
local nMenuItem = 1
|
||||
local tMenuItems = {}
|
||||
if not bReadOnly then
|
||||
table.insert( tMenuItems, "Save" )
|
||||
table.insert(tMenuItems, "Save")
|
||||
end
|
||||
if shell.openTab then
|
||||
table.insert( tMenuItems, "Run" )
|
||||
table.insert(tMenuItems, "Run")
|
||||
end
|
||||
if peripheral.find( "printer" ) then
|
||||
table.insert( tMenuItems, "Print" )
|
||||
if peripheral.find("printer") then
|
||||
table.insert(tMenuItems, "Print")
|
||||
end
|
||||
table.insert( tMenuItems, "Exit" )
|
||||
table.insert(tMenuItems, "Exit")
|
||||
|
||||
local sStatus = "Press Ctrl to access menu"
|
||||
if #sStatus > w - 5 then
|
||||
sStatus = "Press Ctrl for menu"
|
||||
end
|
||||
|
||||
local function load( _sPath )
|
||||
local function load(_sPath)
|
||||
tLines = {}
|
||||
if fs.exists( _sPath ) then
|
||||
local file = io.open( _sPath, "r" )
|
||||
if fs.exists(_sPath) then
|
||||
local file = io.open(_sPath, "r")
|
||||
local sLine = file:read()
|
||||
while sLine do
|
||||
table.insert( tLines, sLine )
|
||||
table.insert(tLines, sLine)
|
||||
sLine = file:read()
|
||||
end
|
||||
file:close()
|
||||
end
|
||||
|
||||
if #tLines == 0 then
|
||||
table.insert( tLines, "" )
|
||||
table.insert(tLines, "")
|
||||
end
|
||||
end
|
||||
|
||||
local function save( _sPath )
|
||||
local function save(_sPath)
|
||||
-- Create intervening folder
|
||||
local sDir = _sPath:sub(1, _sPath:len() - fs.getName(_sPath):len() )
|
||||
if not fs.exists( sDir ) then
|
||||
fs.makeDir( sDir )
|
||||
local sDir = _sPath:sub(1, _sPath:len() - fs.getName(_sPath):len())
|
||||
if not fs.exists(sDir) then
|
||||
fs.makeDir(sDir)
|
||||
end
|
||||
|
||||
-- Save
|
||||
local file, fileerr
|
||||
local function innerSave()
|
||||
file, fileerr = fs.open( _sPath, "w" )
|
||||
file, fileerr = fs.open(_sPath, "w")
|
||||
if file then
|
||||
for _, sLine in ipairs( tLines ) do
|
||||
file.write( sLine .. "\n" )
|
||||
for _, sLine in ipairs(tLines) do
|
||||
file.write(sLine .. "\n")
|
||||
end
|
||||
else
|
||||
error( "Failed to open " .. _sPath )
|
||||
error("Failed to open " .. _sPath)
|
||||
end
|
||||
end
|
||||
|
||||
local ok, err = pcall( innerSave )
|
||||
local ok, err = pcall(innerSave)
|
||||
if file then
|
||||
file.close()
|
||||
end
|
||||
@@ -134,38 +134,38 @@ local tKeywords = {
|
||||
["while"] = true,
|
||||
}
|
||||
|
||||
local function tryWrite( sLine, regex, colour )
|
||||
local match = string.match( sLine, regex )
|
||||
local function tryWrite(sLine, regex, colour)
|
||||
local match = string.match(sLine, regex)
|
||||
if match then
|
||||
if type(colour) == "number" then
|
||||
term.setTextColour( colour )
|
||||
term.setTextColour(colour)
|
||||
else
|
||||
term.setTextColour( colour(match) )
|
||||
term.setTextColour(colour(match))
|
||||
end
|
||||
term.write( match )
|
||||
term.setTextColour( textColour )
|
||||
return string.sub( sLine, #match + 1 )
|
||||
term.write(match)
|
||||
term.setTextColour(textColour)
|
||||
return string.sub(sLine, #match + 1)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function writeHighlighted( sLine )
|
||||
local function writeHighlighted(sLine)
|
||||
while #sLine > 0 do
|
||||
sLine =
|
||||
tryWrite( sLine, "^%-%-%[%[.-%]%]", commentColour ) or
|
||||
tryWrite( sLine, "^%-%-.*", commentColour ) or
|
||||
tryWrite( sLine, "^\"\"", stringColour ) or
|
||||
tryWrite( sLine, "^\".-[^\\]\"", stringColour ) or
|
||||
tryWrite( sLine, "^\'\'", stringColour ) or
|
||||
tryWrite( sLine, "^\'.-[^\\]\'", stringColour ) or
|
||||
tryWrite( sLine, "^%[%[.-%]%]", stringColour ) or
|
||||
tryWrite( sLine, "^[%w_]+", function( match )
|
||||
if tKeywords[ match ] then
|
||||
tryWrite(sLine, "^%-%-%[%[.-%]%]", commentColour) or
|
||||
tryWrite(sLine, "^%-%-.*", commentColour) or
|
||||
tryWrite(sLine, "^\"\"", stringColour) or
|
||||
tryWrite(sLine, "^\".-[^\\]\"", stringColour) or
|
||||
tryWrite(sLine, "^\'\'", stringColour) or
|
||||
tryWrite(sLine, "^\'.-[^\\]\'", stringColour) or
|
||||
tryWrite(sLine, "^%[%[.-%]%]", stringColour) or
|
||||
tryWrite(sLine, "^[%w_]+", function(match)
|
||||
if tKeywords[match] then
|
||||
return keywordColour
|
||||
end
|
||||
return textColour
|
||||
end ) or
|
||||
tryWrite( sLine, "^[^%w_]", textColour )
|
||||
end) or
|
||||
tryWrite(sLine, "^[^%w_]", textColour)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -173,14 +173,14 @@ local tCompletions
|
||||
local nCompletion
|
||||
|
||||
local tCompleteEnv = _ENV
|
||||
local function complete( sLine )
|
||||
if settings.get( "edit.autocomplete" ) then
|
||||
local nStartPos = string.find( sLine, "[a-zA-Z0-9_%.:]+$" )
|
||||
local function complete(sLine)
|
||||
if settings.get("edit.autocomplete") then
|
||||
local nStartPos = string.find(sLine, "[a-zA-Z0-9_%.:]+$")
|
||||
if nStartPos then
|
||||
sLine = string.sub( sLine, nStartPos )
|
||||
sLine = string.sub(sLine, nStartPos)
|
||||
end
|
||||
if #sLine > 0 then
|
||||
return textutils.complete( sLine, tCompleteEnv )
|
||||
return textutils.complete(sLine, tCompleteEnv)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
@@ -189,7 +189,7 @@ end
|
||||
local function recomplete()
|
||||
local sLine = tLines[y]
|
||||
if not bMenu and not bReadOnly and x == #sLine + 1 then
|
||||
tCompletions = complete( sLine )
|
||||
tCompletions = complete(sLine)
|
||||
if tCompletions and #tCompletions > 0 then
|
||||
nCompletion = 1
|
||||
else
|
||||
@@ -201,85 +201,85 @@ local function recomplete()
|
||||
end
|
||||
end
|
||||
|
||||
local function writeCompletion( sLine )
|
||||
local function writeCompletion(sLine)
|
||||
if nCompletion then
|
||||
local sCompletion = tCompletions[ nCompletion ]
|
||||
term.setTextColor( colours.white )
|
||||
term.setBackgroundColor( colours.grey )
|
||||
term.write( sCompletion )
|
||||
term.setTextColor( textColour )
|
||||
term.setBackgroundColor( bgColour )
|
||||
local sCompletion = tCompletions[nCompletion]
|
||||
term.setTextColor(colours.white)
|
||||
term.setBackgroundColor(colours.grey)
|
||||
term.write(sCompletion)
|
||||
term.setTextColor(textColour)
|
||||
term.setBackgroundColor(bgColour)
|
||||
end
|
||||
end
|
||||
|
||||
local function redrawText()
|
||||
local cursorX, cursorY = x, y
|
||||
for y = 1, h - 1 do
|
||||
term.setCursorPos( 1 - scrollX, y )
|
||||
term.setCursorPos(1 - scrollX, y)
|
||||
term.clearLine()
|
||||
|
||||
local sLine = tLines[ y + scrollY ]
|
||||
local sLine = tLines[y + scrollY]
|
||||
if sLine ~= nil then
|
||||
writeHighlighted( sLine )
|
||||
writeHighlighted(sLine)
|
||||
if cursorY == y and cursorX == #sLine + 1 then
|
||||
writeCompletion()
|
||||
end
|
||||
end
|
||||
end
|
||||
term.setCursorPos( x - scrollX, y - scrollY )
|
||||
term.setCursorPos(x - scrollX, y - scrollY)
|
||||
end
|
||||
|
||||
local function redrawLine(_nY)
|
||||
local sLine = tLines[_nY]
|
||||
if sLine then
|
||||
term.setCursorPos( 1 - scrollX, _nY - scrollY )
|
||||
term.setCursorPos(1 - scrollX, _nY - scrollY)
|
||||
term.clearLine()
|
||||
writeHighlighted( sLine )
|
||||
writeHighlighted(sLine)
|
||||
if _nY == y and x == #sLine + 1 then
|
||||
writeCompletion()
|
||||
end
|
||||
term.setCursorPos( x - scrollX, _nY - scrollY )
|
||||
term.setCursorPos(x - scrollX, _nY - scrollY)
|
||||
end
|
||||
end
|
||||
|
||||
local function redrawMenu()
|
||||
-- Clear line
|
||||
term.setCursorPos( 1, h )
|
||||
term.setCursorPos(1, h)
|
||||
term.clearLine()
|
||||
|
||||
-- Draw line numbers
|
||||
term.setCursorPos( w - #( "Ln " .. y ) + 1, h )
|
||||
term.setTextColour( highlightColour )
|
||||
term.write( "Ln " )
|
||||
term.setTextColour( textColour )
|
||||
term.write( y )
|
||||
term.setCursorPos(w - #("Ln " .. y) + 1, h)
|
||||
term.setTextColour(highlightColour)
|
||||
term.write("Ln ")
|
||||
term.setTextColour(textColour)
|
||||
term.write(y)
|
||||
|
||||
term.setCursorPos( 1, h )
|
||||
term.setCursorPos(1, h)
|
||||
if bMenu then
|
||||
-- Draw menu
|
||||
term.setTextColour( textColour )
|
||||
for nItem, sItem in pairs( tMenuItems ) do
|
||||
term.setTextColour(textColour)
|
||||
for nItem, sItem in pairs(tMenuItems) do
|
||||
if nItem == nMenuItem then
|
||||
term.setTextColour( highlightColour )
|
||||
term.write( "[" )
|
||||
term.setTextColour( textColour )
|
||||
term.write( sItem )
|
||||
term.setTextColour( highlightColour )
|
||||
term.write( "]" )
|
||||
term.setTextColour( textColour )
|
||||
term.setTextColour(highlightColour)
|
||||
term.write("[")
|
||||
term.setTextColour(textColour)
|
||||
term.write(sItem)
|
||||
term.setTextColour(highlightColour)
|
||||
term.write("]")
|
||||
term.setTextColour(textColour)
|
||||
else
|
||||
term.write( " " .. sItem .. " " )
|
||||
term.write(" " .. sItem .. " ")
|
||||
end
|
||||
end
|
||||
else
|
||||
-- Draw status
|
||||
term.setTextColour( highlightColour )
|
||||
term.write( sStatus )
|
||||
term.setTextColour( textColour )
|
||||
term.setTextColour(highlightColour)
|
||||
term.write(sStatus)
|
||||
term.setTextColour(textColour)
|
||||
end
|
||||
|
||||
-- Reset cursor
|
||||
term.setCursorPos( x - scrollX, y - scrollY )
|
||||
term.setCursorPos(x - scrollX, y - scrollY)
|
||||
end
|
||||
|
||||
local tMenuFuncs = {
|
||||
@@ -287,7 +287,7 @@ local tMenuFuncs = {
|
||||
if bReadOnly then
|
||||
sStatus = "Access denied"
|
||||
else
|
||||
local ok, _, fileerr = save( sPath )
|
||||
local ok, _, fileerr = save(sPath)
|
||||
if ok then
|
||||
sStatus = "Saved to " .. sPath
|
||||
else
|
||||
@@ -301,14 +301,14 @@ local tMenuFuncs = {
|
||||
redrawMenu()
|
||||
end,
|
||||
Print = function()
|
||||
local printer = peripheral.find( "printer" )
|
||||
local printer = peripheral.find("printer")
|
||||
if not printer then
|
||||
sStatus = "No printer attached"
|
||||
return
|
||||
end
|
||||
|
||||
local nPage = 0
|
||||
local sName = fs.getName( sPath )
|
||||
local sName = fs.getName(sPath)
|
||||
if printer.getInkLevel() < 1 then
|
||||
sStatus = "Printer out of ink"
|
||||
return
|
||||
@@ -326,7 +326,7 @@ local tMenuFuncs = {
|
||||
}
|
||||
printerTerminal.scroll = function()
|
||||
if nPage == 1 then
|
||||
printer.setPageTitle( sName .. " (page " .. nPage .. ")" )
|
||||
printer.setPageTitle(sName .. " (page " .. nPage .. ")")
|
||||
end
|
||||
|
||||
while not printer.newPage() do
|
||||
@@ -338,38 +338,38 @@ local tMenuFuncs = {
|
||||
sStatus = "Printer output tray full, please empty"
|
||||
end
|
||||
|
||||
term.redirect( screenTerminal )
|
||||
term.redirect(screenTerminal)
|
||||
redrawMenu()
|
||||
term.redirect( printerTerminal )
|
||||
term.redirect(printerTerminal)
|
||||
|
||||
sleep(0.5)
|
||||
end
|
||||
|
||||
nPage = nPage + 1
|
||||
if nPage == 1 then
|
||||
printer.setPageTitle( sName )
|
||||
printer.setPageTitle(sName)
|
||||
else
|
||||
printer.setPageTitle( sName .. " (page " .. nPage .. ")" )
|
||||
printer.setPageTitle(sName .. " (page " .. nPage .. ")")
|
||||
end
|
||||
end
|
||||
|
||||
bMenu = false
|
||||
term.redirect( printerTerminal )
|
||||
local ok, error = pcall( function()
|
||||
term.redirect(printerTerminal)
|
||||
local ok, error = pcall(function()
|
||||
term.scroll()
|
||||
for _, sLine in ipairs( tLines ) do
|
||||
print( sLine )
|
||||
for _, sLine in ipairs(tLines) do
|
||||
print(sLine)
|
||||
end
|
||||
end )
|
||||
term.redirect( screenTerminal )
|
||||
end)
|
||||
term.redirect(screenTerminal)
|
||||
if not ok then
|
||||
print( error )
|
||||
print(error)
|
||||
end
|
||||
|
||||
while not printer.endPage() do
|
||||
sStatus = "Printer output tray full, please empty"
|
||||
redrawMenu()
|
||||
sleep( 0.5 )
|
||||
sleep(0.5)
|
||||
end
|
||||
bMenu = true
|
||||
|
||||
@@ -385,15 +385,15 @@ local tMenuFuncs = {
|
||||
end,
|
||||
Run = function()
|
||||
local sTempPath = "/.temp"
|
||||
local ok = save( sTempPath )
|
||||
local ok = save(sTempPath)
|
||||
if ok then
|
||||
local nTask = shell.openTab( sTempPath )
|
||||
local nTask = shell.openTab(sTempPath)
|
||||
if nTask then
|
||||
shell.switchTab( nTask )
|
||||
shell.switchTab(nTask)
|
||||
else
|
||||
sStatus = "Error starting Task"
|
||||
end
|
||||
fs.delete( sTempPath )
|
||||
fs.delete(sTempPath)
|
||||
else
|
||||
sStatus = "Error saving to " .. sTempPath
|
||||
end
|
||||
@@ -401,16 +401,16 @@ local tMenuFuncs = {
|
||||
end,
|
||||
}
|
||||
|
||||
local function doMenuItem( _n )
|
||||
local function doMenuItem(_n)
|
||||
tMenuFuncs[tMenuItems[_n]]()
|
||||
if bMenu then
|
||||
bMenu = false
|
||||
term.setCursorBlink( true )
|
||||
term.setCursorBlink(true)
|
||||
end
|
||||
redrawMenu()
|
||||
end
|
||||
|
||||
local function setCursor( newX, newY )
|
||||
local function setCursor(newX, newY)
|
||||
local _, oldY = x, y
|
||||
x, y = newX, newY
|
||||
local screenX = x - scrollX
|
||||
@@ -441,12 +441,12 @@ local function setCursor( newX, newY )
|
||||
if bRedraw then
|
||||
redrawText()
|
||||
elseif y ~= oldY then
|
||||
redrawLine( oldY )
|
||||
redrawLine( y )
|
||||
redrawLine(oldY)
|
||||
redrawLine(y)
|
||||
else
|
||||
redrawLine( y )
|
||||
redrawLine(y)
|
||||
end
|
||||
term.setCursorPos( screenX, screenY )
|
||||
term.setCursorPos(screenX, screenY)
|
||||
|
||||
redrawMenu()
|
||||
end
|
||||
@@ -454,10 +454,10 @@ end
|
||||
-- Actual program functionality begins
|
||||
load(sPath)
|
||||
|
||||
term.setBackgroundColour( bgColour )
|
||||
term.setBackgroundColour(bgColour)
|
||||
term.clear()
|
||||
term.setCursorPos(x, y)
|
||||
term.setCursorBlink( true )
|
||||
term.setCursorBlink(true)
|
||||
|
||||
recomplete()
|
||||
redrawText()
|
||||
@@ -466,9 +466,9 @@ redrawMenu()
|
||||
local function acceptCompletion()
|
||||
if nCompletion then
|
||||
-- Append the completion
|
||||
local sCompletion = tCompletions[ nCompletion ]
|
||||
local sCompletion = tCompletions[nCompletion]
|
||||
tLines[y] = tLines[y] .. sCompletion
|
||||
setCursor( x + #sCompletion , y )
|
||||
setCursor(x + #sCompletion , y)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -490,7 +490,7 @@ while bRunning do
|
||||
elseif y > 1 then
|
||||
-- Move cursor up
|
||||
setCursor(
|
||||
math.min( x, #tLines[y - 1] + 1 ),
|
||||
math.min(x, #tLines[y - 1] + 1),
|
||||
y - 1
|
||||
)
|
||||
end
|
||||
@@ -511,7 +511,7 @@ while bRunning do
|
||||
elseif y < #tLines then
|
||||
-- Move cursor down
|
||||
setCursor(
|
||||
math.min( x, #tLines[y + 1] + 1 ),
|
||||
math.min(x, #tLines[y + 1] + 1),
|
||||
y + 1
|
||||
)
|
||||
end
|
||||
@@ -527,7 +527,7 @@ while bRunning do
|
||||
-- Indent line
|
||||
local sLine = tLines[y]
|
||||
tLines[y] = string.sub(sLine, 1, x - 1) .. " " .. string.sub(sLine, x)
|
||||
setCursor( x + 4, y )
|
||||
setCursor(x + 4, y)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -542,7 +542,7 @@ while bRunning do
|
||||
newY = 1
|
||||
end
|
||||
setCursor(
|
||||
math.min( x, #tLines[newY] + 1 ),
|
||||
math.min(x, #tLines[newY] + 1),
|
||||
newY
|
||||
)
|
||||
end
|
||||
@@ -557,8 +557,8 @@ while bRunning do
|
||||
else
|
||||
newY = #tLines
|
||||
end
|
||||
local newX = math.min( x, #tLines[newY] + 1 )
|
||||
setCursor( newX, newY )
|
||||
local newX = math.min(x, #tLines[newY] + 1)
|
||||
setCursor(newX, newY)
|
||||
end
|
||||
|
||||
elseif param == keys.home then
|
||||
@@ -576,7 +576,7 @@ while bRunning do
|
||||
-- Move cursor to the end
|
||||
local nLimit = #tLines[y] + 1
|
||||
if x < nLimit then
|
||||
setCursor( nLimit, y )
|
||||
setCursor(nLimit, y)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -585,9 +585,9 @@ while bRunning do
|
||||
if not bMenu then
|
||||
if x > 1 then
|
||||
-- Move cursor left
|
||||
setCursor( x - 1, y )
|
||||
setCursor(x - 1, y)
|
||||
elseif x == 1 and y > 1 then
|
||||
setCursor( #tLines[y - 1] + 1, y - 1 )
|
||||
setCursor(#tLines[y - 1] + 1, y - 1)
|
||||
end
|
||||
else
|
||||
-- Move menu left
|
||||
@@ -604,13 +604,13 @@ while bRunning do
|
||||
local nLimit = #tLines[y] + 1
|
||||
if x < nLimit then
|
||||
-- Move cursor right
|
||||
setCursor( x + 1, y )
|
||||
setCursor(x + 1, y)
|
||||
elseif nCompletion and x == #tLines[y] + 1 then
|
||||
-- Accept autocomplete
|
||||
acceptCompletion()
|
||||
elseif x == nLimit and y < #tLines then
|
||||
-- Go to next line
|
||||
setCursor( 1, y + 1 )
|
||||
setCursor(1, y + 1)
|
||||
end
|
||||
else
|
||||
-- Move menu right
|
||||
@@ -632,7 +632,7 @@ while bRunning do
|
||||
redrawLine(y)
|
||||
elseif y < #tLines then
|
||||
tLines[y] = tLines[y] .. tLines[y + 1]
|
||||
table.remove( tLines, y + 1 )
|
||||
table.remove(tLines, y + 1)
|
||||
recomplete()
|
||||
redrawText()
|
||||
end
|
||||
@@ -646,17 +646,17 @@ while bRunning do
|
||||
local sLine = tLines[y]
|
||||
if x > 4 and string.sub(sLine, x - 4, x - 1) == " " and not string.sub(sLine, 1, x - 1):find("%S") then
|
||||
tLines[y] = string.sub(sLine, 1, x - 5) .. string.sub(sLine, x)
|
||||
setCursor( x - 4, y )
|
||||
setCursor(x - 4, y)
|
||||
else
|
||||
tLines[y] = string.sub(sLine, 1, x - 2) .. string.sub(sLine, x)
|
||||
setCursor( x - 1, y )
|
||||
setCursor(x - 1, y)
|
||||
end
|
||||
elseif y > 1 then
|
||||
-- Remove newline
|
||||
local sPrevLen = #tLines[y - 1]
|
||||
tLines[y - 1] = tLines[y - 1] .. tLines[y]
|
||||
table.remove( tLines, y )
|
||||
setCursor( sPrevLen + 1, y - 1 )
|
||||
table.remove(tLines, y)
|
||||
setCursor(sPrevLen + 1, y - 1)
|
||||
redrawText()
|
||||
end
|
||||
end
|
||||
@@ -671,13 +671,13 @@ while bRunning do
|
||||
spaces = 0
|
||||
end
|
||||
tLines[y] = string.sub(sLine, 1, x - 1)
|
||||
table.insert( tLines, y + 1, string.rep(' ', spaces) .. string.sub(sLine, x) )
|
||||
setCursor( spaces + 1, y + 1 )
|
||||
table.insert(tLines, y + 1, string.rep(' ', spaces) .. string.sub(sLine, x))
|
||||
setCursor(spaces + 1, y + 1)
|
||||
redrawText()
|
||||
|
||||
elseif bMenu then
|
||||
-- Menu selection
|
||||
doMenuItem( nMenuItem )
|
||||
doMenuItem(nMenuItem)
|
||||
|
||||
end
|
||||
|
||||
@@ -685,9 +685,9 @@ while bRunning do
|
||||
-- Menu toggle
|
||||
bMenu = not bMenu
|
||||
if bMenu then
|
||||
term.setCursorBlink( false )
|
||||
term.setCursorBlink(false)
|
||||
else
|
||||
term.setCursorBlink( true )
|
||||
term.setCursorBlink(true)
|
||||
end
|
||||
redrawMenu()
|
||||
|
||||
@@ -698,13 +698,13 @@ while bRunning do
|
||||
-- Input text
|
||||
local sLine = tLines[y]
|
||||
tLines[y] = string.sub(sLine, 1, x - 1) .. param .. string.sub(sLine, x)
|
||||
setCursor( x + 1, y )
|
||||
setCursor(x + 1, y)
|
||||
|
||||
elseif bMenu then
|
||||
-- Select menu items
|
||||
for n, sMenuItem in ipairs( tMenuItems ) do
|
||||
for n, sMenuItem in ipairs(tMenuItems) do
|
||||
if string.lower(string.sub(sMenuItem, 1, 1)) == string.lower(param) then
|
||||
doMenuItem( n )
|
||||
doMenuItem(n)
|
||||
break
|
||||
end
|
||||
end
|
||||
@@ -715,13 +715,13 @@ while bRunning do
|
||||
-- Close menu if open
|
||||
if bMenu then
|
||||
bMenu = false
|
||||
term.setCursorBlink( true )
|
||||
term.setCursorBlink(true)
|
||||
redrawMenu()
|
||||
end
|
||||
-- Input text
|
||||
local sLine = tLines[y]
|
||||
tLines[y] = string.sub(sLine, 1, x - 1) .. param .. string.sub(sLine, x)
|
||||
setCursor( x + #param , y )
|
||||
setCursor(x + #param , y)
|
||||
end
|
||||
|
||||
elseif sEvent == "mouse_click" then
|
||||
@@ -730,9 +730,9 @@ while bRunning do
|
||||
-- Left click
|
||||
local cx, cy = param2, param3
|
||||
if cy < h then
|
||||
local newY = math.min( math.max( scrollY + cy, 1 ), #tLines )
|
||||
local newX = math.min( math.max( scrollX + cx, 1 ), #tLines[newY] + 1 )
|
||||
setCursor( newX, newY )
|
||||
local newY = math.min(math.max(scrollY + cy, 1), #tLines)
|
||||
local newX = math.min(math.max(scrollX + cx, 1), #tLines[newY] + 1)
|
||||
setCursor(newX, newY)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -761,7 +761,7 @@ while bRunning do
|
||||
|
||||
elseif sEvent == "term_resize" then
|
||||
w, h = term.getSize()
|
||||
setCursor( x, y )
|
||||
setCursor(x, y)
|
||||
redrawMenu()
|
||||
redrawText()
|
||||
|
||||
@@ -770,5 +770,5 @@ end
|
||||
|
||||
-- Cleanup
|
||||
term.clear()
|
||||
term.setCursorBlink( false )
|
||||
term.setCursorPos( 1, 1 )
|
||||
term.setCursorBlink(false)
|
||||
term.setCursorPos(1, 1)
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
-- Get arguments
|
||||
local tArgs = { ... }
|
||||
if #tArgs == 0 then
|
||||
print( "Usage: eject <drive>" )
|
||||
print("Usage: eject <drive>")
|
||||
return
|
||||
end
|
||||
|
||||
local sDrive = tArgs[1]
|
||||
|
||||
-- Check the disk exists
|
||||
local bPresent = disk.isPresent( sDrive )
|
||||
local bPresent = disk.isPresent(sDrive)
|
||||
if not bPresent then
|
||||
print( "Nothing in " .. sDrive .. " drive" )
|
||||
print("Nothing in " .. sDrive .. " drive")
|
||||
return
|
||||
end
|
||||
|
||||
disk.eject( sDrive )
|
||||
disk.eject(sDrive)
|
||||
|
||||
@@ -32,7 +32,7 @@ if not term.isColour() then
|
||||
end
|
||||
|
||||
-- Determines if the file exists, and can be edited on this computer
|
||||
local tArgs = {...}
|
||||
local tArgs = { ... }
|
||||
if #tArgs == 0 then
|
||||
print("Usage: paint <path>")
|
||||
return
|
||||
@@ -45,9 +45,9 @@ if fs.exists(sPath) and fs.isDir(sPath) then
|
||||
end
|
||||
|
||||
-- Create .nfp files by default
|
||||
if not fs.exists( sPath ) and not string.find( sPath, "%." ) then
|
||||
local sExtension = settings.get("paint.default_extension", "" )
|
||||
if sExtension ~= "" and type( sExtension ) == "string" then
|
||||
if not fs.exists(sPath) and not string.find(sPath, "%.") then
|
||||
local sExtension = settings.get("paint.default_extension", "")
|
||||
if sExtension ~= "" and type(sExtension) == "string" then
|
||||
sPath = sPath .. "." .. sExtension
|
||||
end
|
||||
end
|
||||
@@ -57,7 +57,7 @@ end
|
||||
-- Functions --
|
||||
---------------
|
||||
|
||||
local function getCanvasPixel( x, y )
|
||||
local function getCanvasPixel(x, y)
|
||||
if canvas[y] then
|
||||
return canvas[y][x]
|
||||
end
|
||||
@@ -69,12 +69,12 @@ end
|
||||
params: colour = the number to convert to a hex value
|
||||
returns: a string representing the chosen colour
|
||||
]]
|
||||
local function getCharOf( colour )
|
||||
local function getCharOf(colour)
|
||||
-- Incorrect values always convert to nil
|
||||
if type(colour) == "number" then
|
||||
local value = math.floor( math.log(colour) / math.log(2) ) + 1
|
||||
local value = math.floor(math.log(colour) / math.log(2)) + 1
|
||||
if value >= 1 and value <= 16 then
|
||||
return string.sub( "0123456789abcdef", value, value )
|
||||
return string.sub("0123456789abcdef", value, value)
|
||||
end
|
||||
end
|
||||
return " "
|
||||
@@ -87,9 +87,9 @@ end
|
||||
]]
|
||||
local tColourLookup = {}
|
||||
for n = 1, 16 do
|
||||
tColourLookup[ string.byte( "0123456789abcdef", n, n ) ] = 2 ^ (n - 1)
|
||||
tColourLookup[string.byte("0123456789abcdef", n, n)] = 2 ^ (n - 1)
|
||||
end
|
||||
local function getColourOf( char )
|
||||
local function getColourOf(char)
|
||||
-- Values not in the hex table are transparent (canvas coloured)
|
||||
return tColourLookup[char]
|
||||
end
|
||||
@@ -107,9 +107,9 @@ local function load(path)
|
||||
while sLine do
|
||||
local line = {}
|
||||
for x = 1, w - 2 do
|
||||
line[x] = getColourOf( string.byte(sLine, x, x) )
|
||||
line[x] = getColourOf(string.byte(sLine, x, x))
|
||||
end
|
||||
table.insert( canvas, line )
|
||||
table.insert(canvas, line)
|
||||
sLine = file.readLine()
|
||||
end
|
||||
file.close()
|
||||
@@ -128,7 +128,7 @@ local function save(path)
|
||||
fs.makeDir(sDir)
|
||||
end
|
||||
|
||||
local file, err = fs.open( path, "w" )
|
||||
local file, err = fs.open(path, "w")
|
||||
if not file then
|
||||
return false, err
|
||||
end
|
||||
@@ -140,13 +140,13 @@ local function save(path)
|
||||
local sLine = ""
|
||||
local nLastChar = 0
|
||||
for x = 1, w - 2 do
|
||||
local c = getCharOf( getCanvasPixel( x, y ) )
|
||||
local c = getCharOf(getCanvasPixel(x, y))
|
||||
sLine = sLine .. c
|
||||
if c ~= " " then
|
||||
nLastChar = x
|
||||
end
|
||||
end
|
||||
sLine = string.sub( sLine, 1, nLastChar )
|
||||
sLine = string.sub(sLine, 1, nLastChar)
|
||||
tLines[y] = sLine
|
||||
if #sLine > 0 then
|
||||
nLastLine = y
|
||||
@@ -155,7 +155,7 @@ local function save(path)
|
||||
|
||||
-- Save out
|
||||
for n = 1, nLastLine do
|
||||
file.writeLine( tLines[ n ] )
|
||||
file.writeLine(tLines[n])
|
||||
end
|
||||
file.close()
|
||||
return true
|
||||
@@ -176,38 +176,38 @@ local function drawInterface()
|
||||
-- Colour Picker
|
||||
for i = 1, 16 do
|
||||
term.setCursorPos(w - 1, i)
|
||||
term.setBackgroundColour( 2 ^ (i - 1) )
|
||||
term.setBackgroundColour(2 ^ (i - 1))
|
||||
term.write(" ")
|
||||
end
|
||||
|
||||
term.setCursorPos(w - 1, 17)
|
||||
term.setBackgroundColour( canvasColour )
|
||||
term.setTextColour( colours.grey )
|
||||
term.setBackgroundColour(canvasColour)
|
||||
term.setTextColour(colours.grey)
|
||||
term.write("\127\127")
|
||||
|
||||
-- Left and Right Selected Colours
|
||||
do
|
||||
term.setCursorPos(w - 1, 18)
|
||||
if leftColour ~= nil then
|
||||
term.setBackgroundColour( leftColour )
|
||||
term.setBackgroundColour(leftColour)
|
||||
term.write(" ")
|
||||
else
|
||||
term.setBackgroundColour( canvasColour )
|
||||
term.setTextColour( colours.grey )
|
||||
term.setBackgroundColour(canvasColour)
|
||||
term.setTextColour(colours.grey)
|
||||
term.write("\127")
|
||||
end
|
||||
if rightColour ~= nil then
|
||||
term.setBackgroundColour( rightColour )
|
||||
term.setBackgroundColour(rightColour)
|
||||
term.write(" ")
|
||||
else
|
||||
term.setBackgroundColour( canvasColour )
|
||||
term.setTextColour( colours.grey )
|
||||
term.setBackgroundColour(canvasColour)
|
||||
term.setTextColour(colours.grey)
|
||||
term.write("\127")
|
||||
end
|
||||
end
|
||||
|
||||
-- Padding
|
||||
term.setBackgroundColour( canvasColour )
|
||||
term.setBackgroundColour(canvasColour)
|
||||
for i = 20, h - 1 do
|
||||
term.setCursorPos(w - 1, i)
|
||||
term.write(" ")
|
||||
@@ -218,15 +218,15 @@ end
|
||||
Converts a single pixel of a single line of the canvas and draws it
|
||||
returns: nil
|
||||
]]
|
||||
local function drawCanvasPixel( x, y )
|
||||
local pixel = getCanvasPixel( x, y )
|
||||
local function drawCanvasPixel(x, y)
|
||||
local pixel = getCanvasPixel(x, y)
|
||||
if pixel then
|
||||
term.setBackgroundColour( pixel or canvasColour )
|
||||
term.setBackgroundColour(pixel or canvasColour)
|
||||
term.setCursorPos(x, y)
|
||||
term.write(" ")
|
||||
else
|
||||
term.setBackgroundColour( canvasColour )
|
||||
term.setTextColour( colours.grey )
|
||||
term.setBackgroundColour(canvasColour)
|
||||
term.setTextColour(colours.grey)
|
||||
term.setCursorPos(x, y)
|
||||
term.write("\127")
|
||||
end
|
||||
@@ -236,9 +236,9 @@ end
|
||||
Converts each colour in a single line of the canvas and draws it
|
||||
returns: nil
|
||||
]]
|
||||
local function drawCanvasLine( y )
|
||||
local function drawCanvasLine(y)
|
||||
for x = 1, w - 2 do
|
||||
drawCanvasPixel( x, y )
|
||||
drawCanvasPixel(x, y)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -248,7 +248,7 @@ end
|
||||
]]
|
||||
local function drawCanvas()
|
||||
for y = 1, h - 1 do
|
||||
drawCanvasLine( y )
|
||||
drawCanvasLine(y)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -377,7 +377,7 @@ local function handleEvents()
|
||||
end
|
||||
canvas[p3][p2] = paintColour
|
||||
|
||||
drawCanvasPixel( p2, p3 )
|
||||
drawCanvasPixel(p2, p3)
|
||||
end
|
||||
elseif id == "key" then
|
||||
if p1 == keys.leftCtrl or p1 == keys.rightCtrl then
|
||||
|
||||
@@ -59,10 +59,10 @@ local cR4 = colors.yellow
|
||||
local tArgs = { ... }
|
||||
|
||||
--Functions--
|
||||
local function printCentred( yc, stg )
|
||||
local function printCentred(yc, stg)
|
||||
local xc = math.floor((TermW - #stg) / 2) + 1
|
||||
term.setCursorPos(xc, yc)
|
||||
term.write( stg )
|
||||
term.write(stg)
|
||||
end
|
||||
|
||||
local function centerOrgin()
|
||||
@@ -173,9 +173,9 @@ end
|
||||
local function loadLevel(nNum)
|
||||
sLevelTitle = "Level " .. nNum
|
||||
if nNum == nil then return error("nNum == nil") end
|
||||
local sDir = fs.getDir( shell.getRunningProgram() )
|
||||
local sDir = fs.getDir(shell.getRunningProgram())
|
||||
local sLevelD = sDir .. "/levels/" .. tostring(nNum) .. ".dat"
|
||||
if not ( fs.exists(sLevelD) or fs.isDir(sLevelD) ) then return error("Level Not Exists : " .. sLevelD) end
|
||||
if not (fs.exists(sLevelD) or fs.isDir(sLevelD)) then return error("Level Not Exists : " .. sLevelD) end
|
||||
fLevel = fs.open(sLevelD, "r")
|
||||
local wl = true
|
||||
Blocks = tonumber(string.sub(fLevel.readLine(), 1, 1))
|
||||
@@ -512,20 +512,20 @@ local function gRender(sContext)
|
||||
end
|
||||
|
||||
function InterFace.drawBar()
|
||||
term.setBackgroundColor( colors.black )
|
||||
term.setTextColor( InterFace.cTitle )
|
||||
printCentred( 1, " " .. sLevelTitle .. " " )
|
||||
term.setBackgroundColor(colors.black)
|
||||
term.setTextColor(InterFace.cTitle)
|
||||
printCentred(1, " " .. sLevelTitle .. " ")
|
||||
|
||||
term.setCursorPos(1, 1)
|
||||
term.setBackgroundColor( cW )
|
||||
write( " " )
|
||||
term.setBackgroundColor( colors.black )
|
||||
write( " x " .. tostring(Blocks) .. " " )
|
||||
term.setBackgroundColor(cW)
|
||||
write(" ")
|
||||
term.setBackgroundColor(colors.black)
|
||||
write(" x " .. tostring(Blocks) .. " ")
|
||||
|
||||
term.setCursorPos( TermW - 8, TermH )
|
||||
term.setBackgroundColor( colors.black )
|
||||
term.setCursorPos(TermW - 8, TermH)
|
||||
term.setBackgroundColor(colors.black)
|
||||
term.setTextColour(InterFace.cSpeedD)
|
||||
write(" <<" )
|
||||
write(" <<")
|
||||
if bPaused then
|
||||
term.setTextColour(InterFace.cSpeedA)
|
||||
else
|
||||
@@ -539,9 +539,9 @@ function InterFace.drawBar()
|
||||
end
|
||||
write(" >>")
|
||||
|
||||
term.setCursorPos( TermW - 1, 1 )
|
||||
term.setBackgroundColor( colors.black )
|
||||
term.setTextColour( InterFace.cExit )
|
||||
term.setCursorPos(TermW - 1, 1)
|
||||
term.setBackgroundColor(colors.black)
|
||||
term.setTextColour(InterFace.cExit)
|
||||
write(" X")
|
||||
term.setBackgroundColor(colors.black)
|
||||
end
|
||||
@@ -612,7 +612,7 @@ local function startG(LevelN)
|
||||
elseif isExit == "retry" then
|
||||
return LevelN
|
||||
elseif fExit == "yes" then
|
||||
if fs.exists( fs.getDir( shell.getRunningProgram() ) .. "/levels/" .. tostring(LevelN + 1) .. ".dat" ) then
|
||||
if fs.exists(fs.getDir(shell.getRunningProgram()) .. "/levels/" .. tostring(LevelN + 1) .. ".dat") then
|
||||
return LevelN + 1
|
||||
else
|
||||
return nil
|
||||
@@ -629,26 +629,26 @@ local ok, err = true, nil
|
||||
--Menu--
|
||||
local sStartLevel = tArgs[1]
|
||||
if ok and not sStartLevel then
|
||||
ok, err = pcall( function()
|
||||
ok, err = pcall(function()
|
||||
term.setTextColor(colors.white)
|
||||
term.setBackgroundColor( colors.black )
|
||||
term.setBackgroundColor(colors.black)
|
||||
term.clear()
|
||||
drawStars()
|
||||
term.setTextColor( colors.red )
|
||||
printCentred( TermH / 2 - 1, " REDIRECTION " )
|
||||
printCentred( TermH / 2 - 0, " ComputerCraft Edition " )
|
||||
term.setTextColor( colors.yellow )
|
||||
printCentred( TermH / 2 + 2, " Click to Begin " )
|
||||
os.pullEvent( "mouse_click" )
|
||||
end )
|
||||
term.setTextColor(colors.red)
|
||||
printCentred(TermH / 2 - 1, " REDIRECTION ")
|
||||
printCentred(TermH / 2 - 0, " ComputerCraft Edition ")
|
||||
term.setTextColor(colors.yellow)
|
||||
printCentred(TermH / 2 + 2, " Click to Begin ")
|
||||
os.pullEvent("mouse_click")
|
||||
end)
|
||||
end
|
||||
|
||||
--Game--
|
||||
if ok then
|
||||
ok, err = pcall( function()
|
||||
ok, err = pcall(function()
|
||||
local nLevel
|
||||
if sStartLevel then
|
||||
nLevel = tonumber( sStartLevel )
|
||||
nLevel = tonumber(sStartLevel)
|
||||
else
|
||||
nLevel = 1
|
||||
end
|
||||
@@ -656,36 +656,36 @@ if ok then
|
||||
reset()
|
||||
nLevel = startG(nLevel)
|
||||
end
|
||||
end )
|
||||
end)
|
||||
end
|
||||
|
||||
--Upsell screen--
|
||||
if ok then
|
||||
ok, err = pcall( function()
|
||||
ok, err = pcall(function()
|
||||
term.setTextColor(colors.white)
|
||||
term.setBackgroundColor( colors.black )
|
||||
term.setBackgroundColor(colors.black)
|
||||
term.clear()
|
||||
drawStars()
|
||||
term.setTextColor( colors.red )
|
||||
term.setTextColor(colors.red)
|
||||
if TermW >= 40 then
|
||||
printCentred( TermH / 2 - 1, " Thank you for playing Redirection " )
|
||||
printCentred( TermH / 2 - 0, " ComputerCraft Edition " )
|
||||
printCentred( TermH / 2 + 2, " Check out the full game: " )
|
||||
term.setTextColor( colors.yellow )
|
||||
printCentred( TermH / 2 + 3, " http://www.redirectiongame.com " )
|
||||
printCentred(TermH / 2 - 1, " Thank you for playing Redirection ")
|
||||
printCentred(TermH / 2 - 0, " ComputerCraft Edition ")
|
||||
printCentred(TermH / 2 + 2, " Check out the full game: ")
|
||||
term.setTextColor(colors.yellow)
|
||||
printCentred(TermH / 2 + 3, " http://www.redirectiongame.com ")
|
||||
else
|
||||
printCentred( TermH / 2 - 2, " Thank you for " )
|
||||
printCentred( TermH / 2 - 1, " playing Redirection " )
|
||||
printCentred( TermH / 2 - 0, " ComputerCraft Edition " )
|
||||
printCentred( TermH / 2 + 2, " Check out the full game: " )
|
||||
term.setTextColor( colors.yellow )
|
||||
printCentred( TermH / 2 + 3, " www.redirectiongame.com " )
|
||||
printCentred(TermH / 2 - 2, " Thank you for ")
|
||||
printCentred(TermH / 2 - 1, " playing Redirection ")
|
||||
printCentred(TermH / 2 - 0, " ComputerCraft Edition ")
|
||||
printCentred(TermH / 2 + 2, " Check out the full game: ")
|
||||
term.setTextColor(colors.yellow)
|
||||
printCentred(TermH / 2 + 3, " www.redirectiongame.com ")
|
||||
end
|
||||
parallel.waitForAll(
|
||||
function() sleep(2) end,
|
||||
function() os.pullEvent( "mouse_click" ) end
|
||||
function() os.pullEvent("mouse_click") end
|
||||
)
|
||||
end )
|
||||
end)
|
||||
end
|
||||
|
||||
--Clear and exit--
|
||||
@@ -695,9 +695,9 @@ term.setBackgroundColor(colors.black)
|
||||
term.clear()
|
||||
if not ok then
|
||||
if err == "Terminated" then
|
||||
print( "Check out the full version of Redirection:" )
|
||||
print( "http://www.redirectiongame.com" )
|
||||
print("Check out the full version of Redirection:")
|
||||
print("http://www.redirectiongame.com")
|
||||
else
|
||||
printError( err )
|
||||
printError(err)
|
||||
end
|
||||
end
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,10 @@
|
||||
local tArgs = { ... }
|
||||
|
||||
local function printUsage()
|
||||
print( "Usages:")
|
||||
print( "dj play" )
|
||||
print( "dj play <drive>" )
|
||||
print( "dj stop" )
|
||||
print("Usages:")
|
||||
print("dj play")
|
||||
print("dj play <drive>")
|
||||
print("dj stop")
|
||||
end
|
||||
|
||||
if #tArgs > 2 then
|
||||
@@ -23,24 +23,24 @@ elseif sCommand == "play" or sCommand == nil then
|
||||
if sName == nil then
|
||||
-- No disc specified, pick one at random
|
||||
local tNames = {}
|
||||
for _, sName in ipairs( peripheral.getNames() ) do
|
||||
if disk.isPresent( sName ) and disk.hasAudio( sName ) then
|
||||
table.insert( tNames, sName )
|
||||
for _, sName in ipairs(peripheral.getNames()) do
|
||||
if disk.isPresent(sName) and disk.hasAudio(sName) then
|
||||
table.insert(tNames, sName)
|
||||
end
|
||||
end
|
||||
if #tNames == 0 then
|
||||
print( "No Music Discs in attached disk drives" )
|
||||
print("No Music Discs in attached disk drives")
|
||||
return
|
||||
end
|
||||
sName = tNames[ math.random(1, #tNames) ]
|
||||
sName = tNames[math.random(1, #tNames)]
|
||||
end
|
||||
|
||||
-- Play the disc
|
||||
if disk.isPresent( sName ) and disk.hasAudio( sName ) then
|
||||
print( "Playing " .. disk.getAudioTitle( sName ) )
|
||||
disk.playAudio( sName )
|
||||
if disk.isPresent(sName) and disk.hasAudio(sName) then
|
||||
print("Playing " .. disk.getAudioTitle(sName))
|
||||
disk.playAudio(sName)
|
||||
else
|
||||
print( "No Music Disc in disk drive: " .. sName )
|
||||
print("No Music Disc in disk drive: " .. sName)
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
if term.isColour() then
|
||||
term.setTextColour( 2 ^ math.random(0, 15) )
|
||||
term.setTextColour(2 ^ math.random(0, 15))
|
||||
end
|
||||
textutils.slowPrint( "Hello World!" )
|
||||
term.setTextColour( colours.white )
|
||||
textutils.slowPrint("Hello World!")
|
||||
term.setTextColour(colours.white)
|
||||
|
||||
@@ -15,11 +15,11 @@ else
|
||||
fruitColour = colours.white
|
||||
end
|
||||
|
||||
local function printCentred( y, s )
|
||||
local function printCentred(y, s)
|
||||
local x = math.floor((w - #s) / 2)
|
||||
term.setCursorPos(x, y)
|
||||
--term.clearLine()
|
||||
term.write( s )
|
||||
term.write(s)
|
||||
end
|
||||
|
||||
local xVel, yVel = 1, 0
|
||||
@@ -65,9 +65,9 @@ local function addFruit()
|
||||
if fruit.snake == nil and fruit.wall == nil and fruit.fruit == nil then
|
||||
screen[x][y] = { fruit = true }
|
||||
term.setCursorPos(x, y)
|
||||
term.setBackgroundColour( fruitColour )
|
||||
term.setBackgroundColour(fruitColour)
|
||||
term.write(" ")
|
||||
term.setBackgroundColour( colours.black )
|
||||
term.setBackgroundColour(colours.black)
|
||||
break
|
||||
end
|
||||
end
|
||||
@@ -79,23 +79,23 @@ local function addFruit()
|
||||
end
|
||||
|
||||
local function drawMenu()
|
||||
term.setTextColour( headingColour )
|
||||
term.setTextColour(headingColour)
|
||||
term.setCursorPos(1, 1)
|
||||
term.write( "SCORE " )
|
||||
term.write("SCORE ")
|
||||
|
||||
term.setTextColour( textColour )
|
||||
term.setTextColour(textColour)
|
||||
term.setCursorPos(7, 1)
|
||||
term.write( tostring(nScore) )
|
||||
term.write(tostring(nScore))
|
||||
|
||||
term.setTextColour( headingColour )
|
||||
term.setTextColour(headingColour)
|
||||
term.setCursorPos(w - 11, 1)
|
||||
term.write( "DIFFICULTY ")
|
||||
term.write("DIFFICULTY ")
|
||||
|
||||
term.setTextColour( textColour )
|
||||
term.setTextColour(textColour)
|
||||
term.setCursorPos(w, 1)
|
||||
term.write( tostring(nDifficulty or "?") )
|
||||
term.write(tostring(nDifficulty or "?"))
|
||||
|
||||
term.setTextColour( colours.white )
|
||||
term.setTextColour(colours.white)
|
||||
end
|
||||
|
||||
local function update( )
|
||||
@@ -150,9 +150,9 @@ local function update( )
|
||||
end
|
||||
|
||||
term.setCursorPos(xPos, yPos)
|
||||
term.setBackgroundColour( wormColour )
|
||||
term.setBackgroundColour(wormColour)
|
||||
term.write(" ")
|
||||
term.setBackgroundColour( colours.black )
|
||||
term.setBackgroundColour(colours.black)
|
||||
|
||||
drawMenu()
|
||||
end
|
||||
@@ -163,29 +163,29 @@ local function drawFrontend()
|
||||
--term.setTextColour( titleColour )
|
||||
--printCentred( math.floor(h/2) - 4, " W O R M " )
|
||||
|
||||
term.setTextColour( headingColour )
|
||||
printCentred( math.floor(h / 2) - 3, "" )
|
||||
printCentred( math.floor(h / 2) - 2, " SELECT DIFFICULTY " )
|
||||
printCentred( math.floor(h / 2) - 1, "" )
|
||||
term.setTextColour(headingColour)
|
||||
printCentred(math.floor(h / 2) - 3, "")
|
||||
printCentred(math.floor(h / 2) - 2, " SELECT DIFFICULTY ")
|
||||
printCentred(math.floor(h / 2) - 1, "")
|
||||
|
||||
printCentred( math.floor(h / 2) + 0, " " )
|
||||
printCentred( math.floor(h / 2) + 1, " " )
|
||||
printCentred( math.floor(h / 2) + 2, " " )
|
||||
printCentred( math.floor(h / 2) - 1 + nDifficulty, " [ ] " )
|
||||
printCentred(math.floor(h / 2) + 0, " ")
|
||||
printCentred(math.floor(h / 2) + 1, " ")
|
||||
printCentred(math.floor(h / 2) + 2, " ")
|
||||
printCentred(math.floor(h / 2) - 1 + nDifficulty, " [ ] ")
|
||||
|
||||
term.setTextColour( textColour )
|
||||
printCentred( math.floor(h / 2) + 0, "EASY" )
|
||||
printCentred( math.floor(h / 2) + 1, "MEDIUM" )
|
||||
printCentred( math.floor(h / 2) + 2, "HARD" )
|
||||
printCentred( math.floor(h / 2) + 3, "" )
|
||||
term.setTextColour(textColour)
|
||||
printCentred(math.floor(h / 2) + 0, "EASY")
|
||||
printCentred(math.floor(h / 2) + 1, "MEDIUM")
|
||||
printCentred(math.floor(h / 2) + 2, "HARD")
|
||||
printCentred(math.floor(h / 2) + 3, "")
|
||||
|
||||
term.setTextColour( colours.white )
|
||||
term.setTextColour(colours.white)
|
||||
end
|
||||
|
||||
drawMenu()
|
||||
drawFrontend()
|
||||
while true do
|
||||
local _, key = os.pullEvent( "key" )
|
||||
local _, key = os.pullEvent("key")
|
||||
if key == keys.up or key == keys.w then
|
||||
-- Up
|
||||
if nDifficulty > 1 then
|
||||
@@ -226,7 +226,7 @@ while bRunning do
|
||||
local event, p1 = os.pullEvent()
|
||||
if event == "timer" and p1 == timer then
|
||||
timer = os.startTimer(nInterval)
|
||||
update( false )
|
||||
update(false)
|
||||
|
||||
elseif event == "key" then
|
||||
local key = p1
|
||||
@@ -257,24 +257,24 @@ while bRunning do
|
||||
end
|
||||
|
||||
-- Display the gameover screen
|
||||
term.setTextColour( headingColour )
|
||||
printCentred( math.floor(h / 2) - 2, " " )
|
||||
printCentred( math.floor(h / 2) - 1, " G A M E O V E R " )
|
||||
term.setTextColour(headingColour)
|
||||
printCentred(math.floor(h / 2) - 2, " ")
|
||||
printCentred(math.floor(h / 2) - 1, " G A M E O V E R ")
|
||||
|
||||
term.setTextColour( textColour )
|
||||
printCentred( math.floor(h / 2) + 0, " " )
|
||||
printCentred( math.floor(h / 2) + 1, " FINAL SCORE " .. nScore .. " " )
|
||||
printCentred( math.floor(h / 2) + 2, " " )
|
||||
term.setTextColour( colours.white )
|
||||
term.setTextColour(textColour)
|
||||
printCentred(math.floor(h / 2) + 0, " ")
|
||||
printCentred(math.floor(h / 2) + 1, " FINAL SCORE " .. nScore .. " ")
|
||||
printCentred(math.floor(h / 2) + 2, " ")
|
||||
term.setTextColour(colours.white)
|
||||
|
||||
local timer = os.startTimer(2.5)
|
||||
repeat
|
||||
local e, p = os.pullEvent()
|
||||
if e == "timer" and p == timer then
|
||||
term.setTextColour( textColour )
|
||||
printCentred( math.floor(h / 2) + 2, " PRESS ANY KEY " )
|
||||
printCentred( math.floor(h / 2) + 3, " " )
|
||||
term.setTextColour( colours.white )
|
||||
term.setTextColour(textColour)
|
||||
printCentred(math.floor(h / 2) + 2, " PRESS ANY KEY ")
|
||||
printCentred(math.floor(h / 2) + 3, " ")
|
||||
term.setTextColour(colours.white)
|
||||
end
|
||||
until e == "char"
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
|
||||
local function printUsage()
|
||||
print( "Usages:" )
|
||||
print( "gps host" )
|
||||
print( "gps host <x> <y> <z>" )
|
||||
print( "gps locate" )
|
||||
print("Usages:")
|
||||
print("gps host")
|
||||
print("gps host <x> <y> <z>")
|
||||
print("gps locate")
|
||||
end
|
||||
|
||||
local tArgs = { ... }
|
||||
@@ -16,27 +16,27 @@ end
|
||||
if sCommand == "locate" then
|
||||
-- "gps locate"
|
||||
-- Just locate this computer (this will print the results)
|
||||
gps.locate( 2, true )
|
||||
gps.locate(2, true)
|
||||
|
||||
elseif sCommand == "host" then
|
||||
-- "gps host"
|
||||
-- Act as a GPS host
|
||||
if pocket then
|
||||
print( "GPS Hosts must be stationary" )
|
||||
print("GPS Hosts must be stationary")
|
||||
return
|
||||
end
|
||||
|
||||
-- Find a modem
|
||||
local sModemSide = nil
|
||||
for _, sSide in ipairs( rs.getSides() ) do
|
||||
if peripheral.getType( sSide ) == "modem" and peripheral.call( sSide, "isWireless" ) then
|
||||
for _, sSide in ipairs(rs.getSides()) do
|
||||
if peripheral.getType(sSide) == "modem" and peripheral.call(sSide, "isWireless") then
|
||||
sModemSide = sSide
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if sModemSide == nil then
|
||||
print( "No wireless modems found. 1 required." )
|
||||
print("No wireless modems found. 1 required.")
|
||||
return
|
||||
end
|
||||
|
||||
@@ -51,31 +51,31 @@ elseif sCommand == "host" then
|
||||
printUsage()
|
||||
return
|
||||
end
|
||||
print( "Position is " .. x .. "," .. y .. "," .. z )
|
||||
print("Position is " .. x .. "," .. y .. "," .. z)
|
||||
else
|
||||
-- Position is to be determined using locate
|
||||
x, y, z = gps.locate( 2, true )
|
||||
x, y, z = gps.locate(2, true)
|
||||
if x == nil then
|
||||
print( "Run \"gps host <x> <y> <z>\" to set position manually" )
|
||||
print("Run \"gps host <x> <y> <z>\" to set position manually")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- Open a channel
|
||||
local modem = peripheral.wrap( sModemSide )
|
||||
print( "Opening channel on modem " .. sModemSide )
|
||||
modem.open( gps.CHANNEL_GPS )
|
||||
local modem = peripheral.wrap(sModemSide)
|
||||
print("Opening channel on modem " .. sModemSide)
|
||||
modem.open(gps.CHANNEL_GPS)
|
||||
|
||||
-- Serve requests indefinately
|
||||
local nServed = 0
|
||||
while true do
|
||||
local e, p1, p2, p3, p4, p5 = os.pullEvent( "modem_message" )
|
||||
local e, p1, p2, p3, p4, p5 = os.pullEvent("modem_message")
|
||||
if e == "modem_message" then
|
||||
-- We received a message from a modem
|
||||
local sSide, sChannel, sReplyChannel, sMessage, nDistance = p1, p2, p3, p4, p5
|
||||
if sSide == sModemSide and sChannel == gps.CHANNEL_GPS and sMessage == "PING" and nDistance then
|
||||
-- We received a ping message on the GPS channel, send a response
|
||||
modem.transmit( sReplyChannel, gps.CHANNEL_GPS, { x, y, z } )
|
||||
modem.transmit(sReplyChannel, gps.CHANNEL_GPS, { x, y, z })
|
||||
|
||||
-- Print the number of requests handled
|
||||
nServed = nServed + 1
|
||||
@@ -83,7 +83,7 @@ elseif sCommand == "host" then
|
||||
local _, y = term.getCursorPos()
|
||||
term.setCursorPos(1, y - 1)
|
||||
end
|
||||
print( nServed .. " GPS requests served" )
|
||||
print(nServed .. " GPS requests served")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -7,20 +7,20 @@ else
|
||||
end
|
||||
|
||||
if sTopic == "index" then
|
||||
print( "Help topics available:" )
|
||||
print("Help topics available:")
|
||||
local tTopics = help.topics()
|
||||
textutils.pagedTabulate( tTopics )
|
||||
textutils.pagedTabulate(tTopics)
|
||||
return
|
||||
end
|
||||
|
||||
local sFile = help.lookup( sTopic )
|
||||
local file = sFile ~= nil and io.open( sFile ) or nil
|
||||
local sFile = help.lookup(sTopic)
|
||||
local file = sFile ~= nil and io.open(sFile) or nil
|
||||
if file then
|
||||
local sContents = file:read("*a")
|
||||
file:close()
|
||||
|
||||
local _, nHeight = term.getSize()
|
||||
textutils.pagedPrint( sContents, nHeight - 3 )
|
||||
textutils.pagedPrint(sContents, nHeight - 3)
|
||||
else
|
||||
print( "No help available" )
|
||||
print("No help available")
|
||||
end
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
|
||||
local function printUsage()
|
||||
print( "Usages:" )
|
||||
print( "pastebin put <filename>" )
|
||||
print( "pastebin get <code> <filename>" )
|
||||
print( "pastebin run <code> <arguments>" )
|
||||
print("Usages:")
|
||||
print("pastebin put <filename>")
|
||||
print("pastebin get <code> <filename>")
|
||||
print("pastebin run <code> <arguments>")
|
||||
end
|
||||
|
||||
local tArgs = { ... }
|
||||
@@ -13,8 +13,8 @@ if #tArgs < 2 then
|
||||
end
|
||||
|
||||
if not http then
|
||||
printError( "Pastebin requires http API" )
|
||||
printError( "Set http_enable to true in ComputerCraft.cfg" )
|
||||
printError("Pastebin requires the http API")
|
||||
printError("Set http.enabled to true in CC: Tweaked's config")
|
||||
return
|
||||
end
|
||||
|
||||
@@ -29,7 +29,7 @@ local function extractId(paste)
|
||||
}
|
||||
|
||||
for i = 1, #patterns do
|
||||
local code = paste:match( patterns[i] )
|
||||
local code = paste:match(patterns[i])
|
||||
if code then return code end
|
||||
end
|
||||
|
||||
@@ -37,36 +37,36 @@ local function extractId(paste)
|
||||
end
|
||||
|
||||
local function get(url)
|
||||
local paste = extractId( url )
|
||||
local paste = extractId(url)
|
||||
if not paste then
|
||||
io.stderr:write( "Invalid pastebin code.\n" )
|
||||
io.write( "The code is the ID at the end of the pastebin.com URL.\n" )
|
||||
io.stderr:write("Invalid pastebin code.\n")
|
||||
io.write("The code is the ID at the end of the pastebin.com URL.\n")
|
||||
return
|
||||
end
|
||||
|
||||
write( "Connecting to pastebin.com... " )
|
||||
write("Connecting to pastebin.com... ")
|
||||
-- Add a cache buster so that spam protection is re-checked
|
||||
local cacheBuster = ("%x"):format(math.random(0, 2 ^ 30))
|
||||
local response, err = http.get(
|
||||
"https://pastebin.com/raw/" .. textutils.urlEncode( paste ) .. "?cb=" .. cacheBuster
|
||||
"https://pastebin.com/raw/" .. textutils.urlEncode(paste) .. "?cb=" .. cacheBuster
|
||||
)
|
||||
|
||||
if response then
|
||||
-- If spam protection is activated, we get redirected to /paste with Content-Type: text/html
|
||||
local headers = response.getResponseHeaders()
|
||||
if not headers["Content-Type"] or not headers["Content-Type"]:find( "^text/plain" ) then
|
||||
io.stderr:write( "Failed.\n" )
|
||||
print( "Pastebin blocked the download due to spam protection. Please complete the captcha in a web browser: https://pastebin.com/" .. textutils.urlEncode( paste ) )
|
||||
if not headers["Content-Type"] or not headers["Content-Type"]:find("^text/plain") then
|
||||
io.stderr:write("Failed.\n")
|
||||
print("Pastebin blocked the download due to spam protection. Please complete the captcha in a web browser: https://pastebin.com/" .. textutils.urlEncode(paste))
|
||||
return
|
||||
end
|
||||
|
||||
print( "Success." )
|
||||
print("Success.")
|
||||
|
||||
local sResponse = response.readAll()
|
||||
response.close()
|
||||
return sResponse
|
||||
else
|
||||
io.stderr:write( "Failed.\n" )
|
||||
io.stderr:write("Failed.\n")
|
||||
print(err)
|
||||
end
|
||||
end
|
||||
@@ -76,20 +76,20 @@ if sCommand == "put" then
|
||||
-- Upload a file to pastebin.com
|
||||
-- Determine file to upload
|
||||
local sFile = tArgs[2]
|
||||
local sPath = shell.resolve( sFile )
|
||||
if not fs.exists( sPath ) or fs.isDir( sPath ) then
|
||||
print( "No such file" )
|
||||
local sPath = shell.resolve(sFile)
|
||||
if not fs.exists(sPath) or fs.isDir(sPath) then
|
||||
print("No such file")
|
||||
return
|
||||
end
|
||||
|
||||
-- Read in the file
|
||||
local sName = fs.getName( sPath )
|
||||
local file = fs.open( sPath, "r" )
|
||||
local sName = fs.getName(sPath)
|
||||
local file = fs.open(sPath, "r")
|
||||
local sText = file.readAll()
|
||||
file.close()
|
||||
|
||||
-- POST the contents to pastebin
|
||||
write( "Connecting to pastebin.com... " )
|
||||
write("Connecting to pastebin.com... ")
|
||||
local key = "0ec2eb25b6166c0c27a394ae118ad829"
|
||||
local response = http.post(
|
||||
"https://pastebin.com/api/api_post.php",
|
||||
@@ -101,17 +101,17 @@ if sCommand == "put" then
|
||||
)
|
||||
|
||||
if response then
|
||||
print( "Success." )
|
||||
print("Success.")
|
||||
|
||||
local sResponse = response.readAll()
|
||||
response.close()
|
||||
|
||||
local sCode = string.match( sResponse, "[^/]+$" )
|
||||
print( "Uploaded as " .. sResponse )
|
||||
print( "Run \"pastebin get " .. sCode .. "\" to download anywhere" )
|
||||
local sCode = string.match(sResponse, "[^/]+$")
|
||||
print("Uploaded as " .. sResponse)
|
||||
print("Run \"pastebin get " .. sCode .. "\" to download anywhere")
|
||||
|
||||
else
|
||||
print( "Failed." )
|
||||
print("Failed.")
|
||||
end
|
||||
|
||||
elseif sCommand == "get" then
|
||||
@@ -124,20 +124,20 @@ elseif sCommand == "get" then
|
||||
-- Determine file to download
|
||||
local sCode = tArgs[2]
|
||||
local sFile = tArgs[3]
|
||||
local sPath = shell.resolve( sFile )
|
||||
if fs.exists( sPath ) then
|
||||
print( "File already exists" )
|
||||
local sPath = shell.resolve(sFile)
|
||||
if fs.exists(sPath) then
|
||||
print("File already exists")
|
||||
return
|
||||
end
|
||||
|
||||
-- GET the contents from pastebin
|
||||
local res = get(sCode)
|
||||
if res then
|
||||
local file = fs.open( sPath, "w" )
|
||||
file.write( res )
|
||||
local file = fs.open(sPath, "w")
|
||||
file.write(res)
|
||||
file.close()
|
||||
|
||||
print( "Downloaded as " .. sFile )
|
||||
print("Downloaded as " .. sFile)
|
||||
end
|
||||
elseif sCommand == "run" then
|
||||
local sCode = tArgs[2]
|
||||
@@ -146,12 +146,12 @@ elseif sCommand == "run" then
|
||||
if res then
|
||||
local func, err = load(res, sCode, "t", _ENV)
|
||||
if not func then
|
||||
printError( err )
|
||||
printError(err)
|
||||
return
|
||||
end
|
||||
local success, msg = pcall(func, select(3, ...))
|
||||
if not success then
|
||||
printError( msg )
|
||||
printError(msg)
|
||||
end
|
||||
end
|
||||
else
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
|
||||
local function printUsage()
|
||||
print( "Usage:" )
|
||||
print( "wget <url> [filename]" )
|
||||
print( "wget run <url>" )
|
||||
print("Usage:")
|
||||
print("wget <url> [filename]")
|
||||
print("wget run <url>")
|
||||
end
|
||||
|
||||
local tArgs = { ... }
|
||||
|
||||
local run = false
|
||||
if tArgs[1] == "run" then
|
||||
table.remove( tArgs, 1 )
|
||||
table.remove(tArgs, 1)
|
||||
run = true
|
||||
end
|
||||
|
||||
@@ -18,36 +18,36 @@ if #tArgs < 1 then
|
||||
return
|
||||
end
|
||||
|
||||
local url = table.remove( tArgs, 1 )
|
||||
local url = table.remove(tArgs, 1)
|
||||
|
||||
if not http then
|
||||
printError( "wget requires http API" )
|
||||
printError( "Set http_enable to true in ComputerCraft.cfg" )
|
||||
printError("wget requires the http API")
|
||||
printError("Set http.enabled to true in CC: Tweaked's config")
|
||||
return
|
||||
end
|
||||
|
||||
local function getFilename( sUrl )
|
||||
sUrl = sUrl:gsub( "[#?].*" , "" ):gsub( "/+$" , "" )
|
||||
return sUrl:match( "/([^/]+)$" )
|
||||
local function getFilename(sUrl)
|
||||
sUrl = sUrl:gsub("[#?].*" , ""):gsub("/+$" , "")
|
||||
return sUrl:match("/([^/]+)$")
|
||||
end
|
||||
|
||||
local function get( sUrl )
|
||||
local function get(sUrl)
|
||||
-- Check if the URL is valid
|
||||
local ok, err = http.checkURL( url )
|
||||
local ok, err = http.checkURL(url)
|
||||
if not ok then
|
||||
printError( err or "Invalid URL." )
|
||||
printError(err or "Invalid URL.")
|
||||
return
|
||||
end
|
||||
|
||||
write( "Connecting to " .. sUrl .. "... " )
|
||||
write("Connecting to " .. sUrl .. "... ")
|
||||
|
||||
local response = http.get( sUrl , nil , true )
|
||||
local response = http.get(sUrl , nil , true)
|
||||
if not response then
|
||||
print( "Failed." )
|
||||
print("Failed.")
|
||||
return nil
|
||||
end
|
||||
|
||||
print( "Success." )
|
||||
print("Success.")
|
||||
|
||||
local sResponse = response.readAll()
|
||||
response.close()
|
||||
@@ -66,22 +66,22 @@ if run then
|
||||
|
||||
local ok, err = pcall(func, table.unpack(tArgs))
|
||||
if not ok then
|
||||
printError( err )
|
||||
printError(err)
|
||||
end
|
||||
else
|
||||
local sFile = tArgs[1] or getFilename( url )
|
||||
local sPath = shell.resolve( sFile )
|
||||
if fs.exists( sPath ) then
|
||||
print( "File already exists" )
|
||||
local sFile = tArgs[1] or getFilename(url)
|
||||
local sPath = shell.resolve(sFile)
|
||||
if fs.exists(sPath) then
|
||||
print("File already exists")
|
||||
return
|
||||
end
|
||||
|
||||
local res = get(url)
|
||||
if not res then return end
|
||||
|
||||
local file = fs.open( sPath, "wb" )
|
||||
file.write( res )
|
||||
local file = fs.open(sPath, "wb")
|
||||
file.write(res)
|
||||
file.close()
|
||||
|
||||
print( "Downloaded as " .. sFile )
|
||||
print("Downloaded as " .. sFile)
|
||||
end
|
||||
|
||||
@@ -2,28 +2,28 @@
|
||||
local sDrive = nil
|
||||
local tArgs = { ... }
|
||||
if #tArgs > 0 then
|
||||
sDrive = tostring( tArgs[1] )
|
||||
sDrive = tostring(tArgs[1])
|
||||
end
|
||||
|
||||
if sDrive == nil then
|
||||
print( "This is computer #" .. os.getComputerID() )
|
||||
print("This is computer #" .. os.getComputerID())
|
||||
|
||||
local label = os.getComputerLabel()
|
||||
if label then
|
||||
print( "This computer is labelled \"" .. label .. "\"" )
|
||||
print("This computer is labelled \"" .. label .. "\"")
|
||||
end
|
||||
|
||||
else
|
||||
local bData = disk.hasData( sDrive )
|
||||
local bData = disk.hasData(sDrive)
|
||||
if not bData then
|
||||
print( "No disk in drive " .. sDrive )
|
||||
print("No disk in drive " .. sDrive)
|
||||
return
|
||||
end
|
||||
|
||||
print( "The disk is #" .. disk.getID( sDrive ) )
|
||||
print("The disk is #" .. disk.getID(sDrive))
|
||||
|
||||
local label = disk.getLabel( sDrive )
|
||||
local label = disk.getLabel(sDrive)
|
||||
if label then
|
||||
print( "The disk is labelled \"" .. label .. "\"" )
|
||||
print("The disk is labelled \"" .. label .. "\"")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,67 +1,67 @@
|
||||
|
||||
local function printUsage()
|
||||
print( "Usages:" )
|
||||
print( "label get" )
|
||||
print( "label get <drive>" )
|
||||
print( "label set <text>" )
|
||||
print( "label set <drive> <text>" )
|
||||
print( "label clear" )
|
||||
print( "label clear <drive>" )
|
||||
print("Usages:")
|
||||
print("label get")
|
||||
print("label get <drive>")
|
||||
print("label set <text>")
|
||||
print("label set <drive> <text>")
|
||||
print("label clear")
|
||||
print("label clear <drive>")
|
||||
end
|
||||
|
||||
local function checkDrive( sDrive )
|
||||
if peripheral.getType( sDrive ) == "drive" then
|
||||
local function checkDrive(sDrive)
|
||||
if peripheral.getType(sDrive) == "drive" then
|
||||
-- Check the disk exists
|
||||
local bData = disk.hasData( sDrive )
|
||||
local bData = disk.hasData(sDrive)
|
||||
if not bData then
|
||||
print( "No disk in " .. sDrive .. " drive" )
|
||||
print("No disk in " .. sDrive .. " drive")
|
||||
return false
|
||||
end
|
||||
else
|
||||
print( "No disk drive named " .. sDrive )
|
||||
print("No disk drive named " .. sDrive)
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function get( sDrive )
|
||||
local function get(sDrive)
|
||||
if sDrive ~= nil then
|
||||
if checkDrive( sDrive ) then
|
||||
local sLabel = disk.getLabel( sDrive )
|
||||
if checkDrive(sDrive) then
|
||||
local sLabel = disk.getLabel(sDrive)
|
||||
if sLabel then
|
||||
print( "Disk label is \"" .. sLabel .. "\"" )
|
||||
print("Disk label is \"" .. sLabel .. "\"")
|
||||
else
|
||||
print( "No Disk label" )
|
||||
print("No Disk label")
|
||||
end
|
||||
end
|
||||
else
|
||||
local sLabel = os.getComputerLabel()
|
||||
if sLabel then
|
||||
print( "Computer label is \"" .. sLabel .. "\"" )
|
||||
print("Computer label is \"" .. sLabel .. "\"")
|
||||
else
|
||||
print( "No Computer label" )
|
||||
print("No Computer label")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function set( sDrive, sText )
|
||||
local function set(sDrive, sText)
|
||||
if sDrive ~= nil then
|
||||
if checkDrive( sDrive ) then
|
||||
disk.setLabel( sDrive, sText )
|
||||
local sLabel = disk.getLabel( sDrive )
|
||||
if checkDrive(sDrive) then
|
||||
disk.setLabel(sDrive, sText)
|
||||
local sLabel = disk.getLabel(sDrive)
|
||||
if sLabel then
|
||||
print( "Disk label set to \"" .. sLabel .. "\"" )
|
||||
print("Disk label set to \"" .. sLabel .. "\"")
|
||||
else
|
||||
print( "Disk label cleared" )
|
||||
print("Disk label cleared")
|
||||
end
|
||||
end
|
||||
else
|
||||
os.setComputerLabel( sText )
|
||||
os.setComputerLabel(sText)
|
||||
local sLabel = os.getComputerLabel()
|
||||
if sLabel then
|
||||
print( "Computer label set to \"" .. sLabel .. "\"" )
|
||||
print("Computer label set to \"" .. sLabel .. "\"")
|
||||
else
|
||||
print( "Computer label cleared" )
|
||||
print("Computer label cleared")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -71,27 +71,27 @@ local sCommand = tArgs[1]
|
||||
if sCommand == "get" then
|
||||
-- Get a label
|
||||
if #tArgs == 1 then
|
||||
get( nil )
|
||||
get(nil)
|
||||
elseif #tArgs == 2 then
|
||||
get( tArgs[2] )
|
||||
get(tArgs[2])
|
||||
else
|
||||
printUsage()
|
||||
end
|
||||
elseif sCommand == "set" then
|
||||
-- Set a label
|
||||
if #tArgs == 2 then
|
||||
set( nil, tArgs[2] )
|
||||
set(nil, tArgs[2])
|
||||
elseif #tArgs == 3 then
|
||||
set( tArgs[2], tArgs[3] )
|
||||
set(tArgs[2], tArgs[3])
|
||||
else
|
||||
printUsage()
|
||||
end
|
||||
elseif sCommand == "clear" then
|
||||
-- Clear a label
|
||||
if #tArgs == 1 then
|
||||
set( nil, nil )
|
||||
set(nil, nil)
|
||||
elseif #tArgs == 2 then
|
||||
set( tArgs[2], nil )
|
||||
set(tArgs[2], nil)
|
||||
else
|
||||
printUsage()
|
||||
end
|
||||
|
||||
@@ -4,35 +4,35 @@ local tArgs = { ... }
|
||||
-- Get all the files in the directory
|
||||
local sDir = shell.dir()
|
||||
if tArgs[1] ~= nil then
|
||||
sDir = shell.resolve( tArgs[1] )
|
||||
sDir = shell.resolve(tArgs[1])
|
||||
end
|
||||
|
||||
if not fs.isDir( sDir ) then
|
||||
printError( "Not a directory" )
|
||||
if not fs.isDir(sDir) then
|
||||
printError("Not a directory")
|
||||
return
|
||||
end
|
||||
|
||||
-- Sort into dirs/files, and calculate column count
|
||||
local tAll = fs.list( sDir )
|
||||
local tAll = fs.list(sDir)
|
||||
local tFiles = {}
|
||||
local tDirs = {}
|
||||
|
||||
local bShowHidden = settings.get( "list.show_hidden" )
|
||||
for _, sItem in pairs( tAll ) do
|
||||
if bShowHidden or string.sub( sItem, 1, 1 ) ~= "." then
|
||||
local sPath = fs.combine( sDir, sItem )
|
||||
if fs.isDir( sPath ) then
|
||||
table.insert( tDirs, sItem )
|
||||
local bShowHidden = settings.get("list.show_hidden")
|
||||
for _, sItem in pairs(tAll) do
|
||||
if bShowHidden or string.sub(sItem, 1, 1) ~= "." then
|
||||
local sPath = fs.combine(sDir, sItem)
|
||||
if fs.isDir(sPath) then
|
||||
table.insert(tDirs, sItem)
|
||||
else
|
||||
table.insert( tFiles, sItem )
|
||||
table.insert(tFiles, sItem)
|
||||
end
|
||||
end
|
||||
end
|
||||
table.sort( tDirs )
|
||||
table.sort( tFiles )
|
||||
table.sort(tDirs)
|
||||
table.sort(tFiles)
|
||||
|
||||
if term.isColour() then
|
||||
textutils.pagedTabulate( colors.green, tDirs, colors.white, tFiles )
|
||||
textutils.pagedTabulate(colors.green, tDirs, colors.white, tFiles)
|
||||
else
|
||||
textutils.pagedTabulate( tDirs, tFiles )
|
||||
textutils.pagedTabulate(tDirs, tFiles)
|
||||
end
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
|
||||
local tArgs = { ... }
|
||||
if #tArgs > 0 then
|
||||
print( "This is an interactive Lua prompt." )
|
||||
print( "To run a lua program, just type its name." )
|
||||
print("This is an interactive Lua prompt.")
|
||||
print("To run a lua program, just type its name.")
|
||||
return
|
||||
end
|
||||
|
||||
local pretty = require "cc.pretty"
|
||||
|
||||
local bRunning = true
|
||||
local tCommandHistory = {}
|
||||
local tEnv = {
|
||||
["exit"] = setmetatable({}, {
|
||||
__tostring = function() return "Call exit() to exit" end,
|
||||
__tostring = function() return "Call exit() to exit." end,
|
||||
__call = function() bRunning = false end,
|
||||
}),
|
||||
["_echo"] = function( ... )
|
||||
["_echo"] = function(...)
|
||||
return ...
|
||||
end,
|
||||
}
|
||||
setmetatable( tEnv, { __index = _ENV } )
|
||||
setmetatable(tEnv, { __index = _ENV })
|
||||
|
||||
-- Replace our package.path, so that it loads from the current directory, rather
|
||||
-- than from /rom/programs. This makes it a little more friendly to use and
|
||||
@@ -37,38 +39,38 @@ do
|
||||
end
|
||||
|
||||
if term.isColour() then
|
||||
term.setTextColour( colours.yellow )
|
||||
term.setTextColour(colours.yellow)
|
||||
end
|
||||
print( "Interactive Lua prompt." )
|
||||
print( "Call exit() to exit." )
|
||||
term.setTextColour( colours.white )
|
||||
print("Interactive Lua prompt.")
|
||||
print("Call exit() to exit.")
|
||||
term.setTextColour(colours.white)
|
||||
|
||||
while bRunning do
|
||||
--if term.isColour() then
|
||||
-- term.setTextColour( colours.yellow )
|
||||
--end
|
||||
write( "lua> " )
|
||||
write("lua> ")
|
||||
--term.setTextColour( colours.white )
|
||||
|
||||
local s = read( nil, tCommandHistory, function( sLine )
|
||||
if settings.get( "lua.autocomplete" ) then
|
||||
local nStartPos = string.find( sLine, "[a-zA-Z0-9_%.:]+$" )
|
||||
local s = read(nil, tCommandHistory, function(sLine)
|
||||
if settings.get("lua.autocomplete") then
|
||||
local nStartPos = string.find(sLine, "[a-zA-Z0-9_%.:]+$")
|
||||
if nStartPos then
|
||||
sLine = string.sub( sLine, nStartPos )
|
||||
sLine = string.sub(sLine, nStartPos)
|
||||
end
|
||||
if #sLine > 0 then
|
||||
return textutils.complete( sLine, tEnv )
|
||||
return textutils.complete(sLine, tEnv)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end )
|
||||
end)
|
||||
if s:match("%S") and tCommandHistory[#tCommandHistory] ~= s then
|
||||
table.insert( tCommandHistory, s )
|
||||
table.insert(tCommandHistory, s)
|
||||
end
|
||||
|
||||
local nForcePrint = 0
|
||||
local func, e = load( s, "=lua", "t", tEnv )
|
||||
local func2 = load( "return _echo(" .. s .. ");", "=lua", "t", tEnv )
|
||||
local func, e = load(s, "=lua", "t", tEnv)
|
||||
local func2 = load("return _echo(" .. s .. ");", "=lua", "t", tEnv)
|
||||
if not func then
|
||||
if func2 then
|
||||
func = func2
|
||||
@@ -82,33 +84,24 @@ while bRunning do
|
||||
end
|
||||
|
||||
if func then
|
||||
local tResults = table.pack( pcall( func ) )
|
||||
local tResults = table.pack(pcall(func))
|
||||
if tResults[1] then
|
||||
local n = 1
|
||||
while n < tResults.n or n <= nForcePrint do
|
||||
local value = tResults[ n + 1 ]
|
||||
if type( value ) == "table" then
|
||||
local metatable = getmetatable( value )
|
||||
if type(metatable) == "table" and type(metatable.__tostring) == "function" then
|
||||
print( tostring( value ) )
|
||||
else
|
||||
local ok, serialised = pcall( textutils.serialise, value )
|
||||
if ok then
|
||||
print( serialised )
|
||||
else
|
||||
print( tostring( value ) )
|
||||
end
|
||||
end
|
||||
local value = tResults[n + 1]
|
||||
local ok, serialised = pcall(pretty.pretty, value)
|
||||
if ok then
|
||||
pretty.print(serialised)
|
||||
else
|
||||
print( tostring( value ) )
|
||||
print(tostring(value))
|
||||
end
|
||||
n = n + 1
|
||||
end
|
||||
else
|
||||
printError( tResults[2] )
|
||||
printError(tResults[2])
|
||||
end
|
||||
else
|
||||
printError( e )
|
||||
printError(e)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
local tArgs = { ... }
|
||||
|
||||
if #tArgs < 1 then
|
||||
print( "Usage: mkdir <paths>" )
|
||||
print("Usage: mkdir <paths>")
|
||||
return
|
||||
end
|
||||
|
||||
for _, v in ipairs( tArgs ) do
|
||||
local sNewDir = shell.resolve( v )
|
||||
if fs.exists( sNewDir ) and not fs.isDir( sNewDir ) then
|
||||
printError( v .. ": Destination exists" )
|
||||
elseif fs.isReadOnly( sNewDir ) then
|
||||
printError( v .. ": Access denied" )
|
||||
for _, v in ipairs(tArgs) do
|
||||
local sNewDir = shell.resolve(v)
|
||||
if fs.exists(sNewDir) and not fs.isDir(sNewDir) then
|
||||
printError(v .. ": Destination exists")
|
||||
elseif fs.isReadOnly(sNewDir) then
|
||||
printError(v .. ": Access denied")
|
||||
else
|
||||
fs.makeDir( sNewDir )
|
||||
fs.makeDir(sNewDir)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
local function printUsage()
|
||||
print( "Usage: monitor <name> <program> <arguments>" )
|
||||
print("Usage: monitor <name> <program> <arguments>")
|
||||
return
|
||||
end
|
||||
|
||||
@@ -10,56 +10,65 @@ if #tArgs < 2 then
|
||||
end
|
||||
|
||||
local sName = tArgs[1]
|
||||
if peripheral.getType( sName ) ~= "monitor" then
|
||||
print( "No monitor named " .. sName )
|
||||
if peripheral.getType(sName) ~= "monitor" then
|
||||
print("No monitor named " .. sName)
|
||||
return
|
||||
end
|
||||
|
||||
local sProgram = tArgs[2]
|
||||
local sPath = shell.resolveProgram( sProgram )
|
||||
local sPath = shell.resolveProgram(sProgram)
|
||||
if sPath == nil then
|
||||
print( "No such program: " .. sProgram )
|
||||
print("No such program: " .. sProgram)
|
||||
return
|
||||
end
|
||||
|
||||
print( "Running " .. sProgram .. " on monitor " .. sName )
|
||||
print("Running " .. sProgram .. " on monitor " .. sName)
|
||||
|
||||
local monitor = peripheral.wrap( sName )
|
||||
local previousTerm = term.redirect( monitor )
|
||||
local monitor = peripheral.wrap(sName)
|
||||
local previousTerm = term.redirect(monitor)
|
||||
|
||||
local co = coroutine.create( function()
|
||||
shell.run( sProgram, table.unpack( tArgs, 3 ) )
|
||||
end )
|
||||
local co = coroutine.create(function()
|
||||
shell.run(sProgram, table.unpack(tArgs, 3))
|
||||
end)
|
||||
|
||||
local function resume( ... )
|
||||
local ok, param = coroutine.resume( co, ... )
|
||||
local function resume(...)
|
||||
local ok, param = coroutine.resume(co, ...)
|
||||
if not ok then
|
||||
printError( param )
|
||||
printError(param)
|
||||
end
|
||||
return param
|
||||
end
|
||||
|
||||
local ok, param = pcall( function()
|
||||
local timers = {}
|
||||
|
||||
local ok, param = pcall(function()
|
||||
local sFilter = resume()
|
||||
while coroutine.status( co ) ~= "dead" do
|
||||
local tEvent = table.pack( os.pullEventRaw() )
|
||||
while coroutine.status(co) ~= "dead" do
|
||||
local tEvent = table.pack(os.pullEventRaw())
|
||||
if sFilter == nil or tEvent[1] == sFilter or tEvent[1] == "terminate" then
|
||||
sFilter = resume( table.unpack( tEvent, 1, tEvent.n ) )
|
||||
sFilter = resume(table.unpack(tEvent, 1, tEvent.n))
|
||||
end
|
||||
if coroutine.status( co ) ~= "dead" and (sFilter == nil or sFilter == "mouse_click") then
|
||||
if coroutine.status(co) ~= "dead" and (sFilter == nil or sFilter == "mouse_click") then
|
||||
if tEvent[1] == "monitor_touch" and tEvent[2] == sName then
|
||||
sFilter = resume( "mouse_click", 1, table.unpack( tEvent, 3, tEvent.n ) )
|
||||
timers[os.startTimer(0.1)] = { tEvent[3], tEvent[4] }
|
||||
sFilter = resume("mouse_click", 1, table.unpack(tEvent, 3, tEvent.n))
|
||||
end
|
||||
end
|
||||
if coroutine.status( co ) ~= "dead" and (sFilter == nil or sFilter == "term_resize") then
|
||||
if coroutine.status(co) ~= "dead" and (sFilter == nil or sFilter == "term_resize") then
|
||||
if tEvent[1] == "monitor_resize" and tEvent[2] == sName then
|
||||
sFilter = resume( "term_resize" )
|
||||
sFilter = resume("term_resize")
|
||||
end
|
||||
end
|
||||
if coroutine.status(co) ~= "dead" and (sFilter == nil or sFilter == "mouse_up") then
|
||||
if tEvent[1] == "timer" and timers[tEvent[2]] then
|
||||
sFilter = resume("mouse_up", 1, table.unpack(timers[tEvent[2]], 1, 2))
|
||||
timers[tEvent[2]] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end )
|
||||
end)
|
||||
|
||||
term.redirect( previousTerm )
|
||||
term.redirect(previousTerm)
|
||||
if not ok then
|
||||
printError( param )
|
||||
printError(param)
|
||||
end
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
local tMotd = {}
|
||||
|
||||
for sPath in string.gmatch(settings.get( "motd.path" ), "[^:]+") do
|
||||
for sPath in string.gmatch(settings.get("motd.path"), "[^:]+") do
|
||||
if fs.exists(sPath) then
|
||||
for sLine in io.lines(sPath) do
|
||||
table.insert(tMotd, sLine)
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
|
||||
local tArgs = { ... }
|
||||
if #tArgs < 2 then
|
||||
print( "Usage: mv <source> <destination>" )
|
||||
print("Usage: mv <source> <destination>")
|
||||
return
|
||||
end
|
||||
|
||||
local sSource = shell.resolve( tArgs[1] )
|
||||
local sDest = shell.resolve( tArgs[2] )
|
||||
local tFiles = fs.find( sSource )
|
||||
local sSource = shell.resolve(tArgs[1])
|
||||
local sDest = shell.resolve(tArgs[2])
|
||||
local tFiles = fs.find(sSource)
|
||||
if #tFiles > 0 then
|
||||
for _, sFile in ipairs( tFiles ) do
|
||||
if fs.isDir( sDest ) then
|
||||
fs.move( sFile, fs.combine( sDest, fs.getName(sFile) ) )
|
||||
for _, sFile in ipairs(tFiles) do
|
||||
if fs.isDir(sDest) then
|
||||
fs.move(sFile, fs.combine(sDest, fs.getName(sFile)))
|
||||
elseif #tFiles == 1 then
|
||||
fs.move( sFile, sDest )
|
||||
fs.move(sFile, sDest)
|
||||
else
|
||||
printError( "Cannot overwrite file multiple times" )
|
||||
printError("Cannot overwrite file multiple times")
|
||||
return
|
||||
end
|
||||
end
|
||||
else
|
||||
printError( "No matching files" )
|
||||
printError("No matching files")
|
||||
end
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
local tPeripherals = peripheral.getNames()
|
||||
print( "Attached Peripherals:" )
|
||||
print("Attached Peripherals:")
|
||||
if #tPeripherals > 0 then
|
||||
for n = 1, #tPeripherals do
|
||||
local sPeripheral = tPeripherals[n]
|
||||
print( sPeripheral .. " (" .. peripheral.getType( sPeripheral ) .. ")" )
|
||||
print(sPeripheral .. " (" .. peripheral.getType(sPeripheral) .. ")")
|
||||
end
|
||||
else
|
||||
print( "None" )
|
||||
print("None")
|
||||
end
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
if not pocket then
|
||||
printError( "Requires a Pocket Computer" )
|
||||
printError("Requires a Pocket Computer")
|
||||
return
|
||||
end
|
||||
|
||||
local ok, err = pocket.equipBack()
|
||||
if not ok then
|
||||
printError( err )
|
||||
printError(err)
|
||||
else
|
||||
print( "Item equipped" )
|
||||
print("Item equipped")
|
||||
end
|
||||
|
||||
@@ -158,9 +158,9 @@ local block_T = {
|
||||
bg = colorass(colors.purple, colors.white),
|
||||
}
|
||||
|
||||
local blocks = { block_line, block_square, block_s1, block_s2, block_L1, block_L2, block_T}
|
||||
local blocks = { block_line, block_square, block_s1, block_s2, block_L1, block_L2, block_T }
|
||||
|
||||
local points = {4, 10, 30, 120}
|
||||
local points = { 4, 10, 30, 120 }
|
||||
|
||||
local function lpad(text, amt)
|
||||
text = tostring(text)
|
||||
@@ -456,7 +456,7 @@ local function playGame()
|
||||
sleep(.25)
|
||||
for r = 1, #rows do
|
||||
table.remove(pit, rows[r])
|
||||
table.insert(pit, 1, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0})
|
||||
table.insert(pit, 1, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 })
|
||||
end
|
||||
redrawPit()
|
||||
lines = lines + #rows
|
||||
@@ -496,7 +496,7 @@ local function playGame()
|
||||
|
||||
|
||||
while not halt do
|
||||
local e = {os.pullEvent()}
|
||||
local e = { os.pullEvent() }
|
||||
if e[1] == "timer" then
|
||||
if e[2] == dropTimer then
|
||||
blockFall()
|
||||
@@ -517,7 +517,7 @@ local function playGame()
|
||||
elseif key == keys.space then
|
||||
hidePit()
|
||||
msgBox("Paused")
|
||||
while ({os.pullEvent("key")})[2] ~= keys.space do end
|
||||
while ({ os.pullEvent("key") })[2] ~= keys.space do end
|
||||
redrawPit()
|
||||
drawBlockAt(curBlock, curX, curY, curRot)
|
||||
dropTimer = os.startTimer(dropSpeed)
|
||||
@@ -606,7 +606,7 @@ local function runMenu()
|
||||
drawMenu()
|
||||
|
||||
while true do
|
||||
local event = {os.pullEvent()}
|
||||
local event = { os.pullEvent() }
|
||||
if event[1] == "key" then
|
||||
local key = event[2]
|
||||
if key == keys.right or key == keys.d and selected == 1 then
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
if not pocket then
|
||||
printError( "Requires a Pocket Computer" )
|
||||
printError("Requires a Pocket Computer")
|
||||
return
|
||||
end
|
||||
|
||||
local ok, err = pocket.unequipBack()
|
||||
if not ok then
|
||||
printError( err )
|
||||
printError(err)
|
||||
else
|
||||
print( "Item unequipped" )
|
||||
print("Item unequipped")
|
||||
end
|
||||
|
||||
@@ -5,5 +5,5 @@ if #tArgs > 0 and tArgs[1] == "all" then
|
||||
bAll = true
|
||||
end
|
||||
|
||||
local tPrograms = shell.programs( bAll )
|
||||
textutils.pagedTabulate( tPrograms )
|
||||
local tPrograms = shell.programs(bAll)
|
||||
textutils.pagedTabulate(tPrograms)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user