Skip to content

Commit

Permalink
Redesign the protocol to use a capability system
Browse files Browse the repository at this point in the history
 - The protocol now uses JSON instead of a quasi-binary format. While
   the former is more efficient to produce, the difference is pretty
   marginal. This helps simplify packet encoding/decoding.

 - All clients connect through the /connect endpoint, providing a
   session id (aka token) and list of capabilities.

 - Specific packets can only be sent to clients with a given capability.
   For instance, terminal packets are only sent to clients with the
   terminal:view capability, while file action packets are sent to both
   file:edit and file:host clients.

This is mostly intended to allow greater flexibility in the future, such
as a desktop-based file syncing program.
  • Loading branch information
SquidDev committed May 23, 2018
1 parent 72d5238 commit b8cbe12
Show file tree
Hide file tree
Showing 15 changed files with 1,154 additions and 624 deletions.
92 changes: 92 additions & 0 deletions .luacheckrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
local function mk_fields(fields)
local out = {}
for i = 1, #fields do out[fields[i]] = {} end
return { fields = out }
end

std = {
read_globals = {
fs = mk_fields { "makeDir", "complete", "copy", "isDir", "list", "getSize", "delete", "isReadOnly", "getFreeSpace", "find", "getDrive", "exists", "move", "combine", "getName", "getDir", "open" },
colours = mk_fields { "cyan", "purple", "brown", "lightGrey", "orange", "red", "magenta", "blue", "white", "grey", "rgb8", "pink", "test", "subtract", "black", "green", "combine", "yellow", "lime", "lightBlue" },
tostring = {},
_CC_DEFAULT_SETTINGS = {},
getfenv = {},
debug = mk_fields { "getupvalue", "debug", "getfenv", "getmetatable", "gethook", "setmetatable", "setfenv", "traceback", "getinfo", "setlocal", "setupvalue", "sethook", "getregistry", "getlocal" },
disk = mk_fields { "getID", "isPresent", "eject", "getMountPath", "stopAudio", "getLabel", "hasAudio", "playAudio", "getAudioTitle", "setLabel", "hasData" },
assert = {},
tonumber = {},
io = mk_fields { "lines", "write", "close", "flush", "open", "output", "read", "input", "type" },
redstone = mk_fields { "setAnalogOutput", "getBundledOutput", "getBundledInput", "setOutput", "setAnalogueOutput", "getInput", "getOutput", "setBundledOutput", "getAnalogueOutput", "testBundledInput", "getSides", "getAnalogInput", "getAnalogOutput", "getAnalogueInput" },
load = {},
http = mk_fields { "checkURLAsync", "websocketAsync", "checkURL", "request", "websocket", "post", "get" },
_G = {
read_only = false,
other_fields = true,
},
peripheral = mk_fields { "call", "isPresent", "getType", "getMethods", "wrap", "find", "getNames" },
vector = mk_fields { "new" },
term = mk_fields { "getCursorPos", "isColour", "getBackgroundColor", "getTextColour", "scroll", "redirect", "getPaletteColor", "setTextColor", "native", "setPaletteColour", "getTextColor", "clear", "write", "setPaletteColor", "setCursorPos", "getBackgroundColour", "isColor", "setTextColour", "blit", "current", "getSize", "getPaletteColour", "setCursorBlink", "setBackgroundColor", "setBackgroundColour", "clearLine" },
coroutine = mk_fields { "running", "yield", "status", "wrap", "create", "resume" },
textutils = mk_fields { "serialize", "unserialize", "empty_json_array", "serializeJSON", "urlEncode", "serialiseJSON", "complete", "serialise", "formatTime", "pagedTabulate", "slowWrite", "slowPrint", "pagedPrint", "tabulate", "unserialise" },
loadstring = {},
rednet = mk_fields { "CHANNEL_REPEAT", "isOpen", "unhost", "close", "send", "open", "host", "CHANNEL_BROADCAST", "broadcast", "run", "receive", "lookup" },
_HOST = {},
string = mk_fields { "sub", "find", "len", "gfind", "reverse", "rep", "match", "gmatch", "dump", "byte", "upper", "gsub", "format", "char", "lower" },
xpcall = {},
package = {
fields = {
preload = {},
config = {},
loaders = {},
loaded = {
read_only = false,
other_fields = true,
},
path = {},
},
},
parallel = mk_fields { "waitForAll", "waitForAny" },
print = {},
unpack = {},
__inext = {},
printError = {},
require = {},
_ENV = {
read_only = false,
other_fields = true,
},
write = {},
next = {},
ipairs = {},
rawequal = {},
getmetatable = {},
sleep = {},
loadfile = {},
settings = mk_fields { "clear", "unset", "set", "getNames", "save", "load", "get" },
rawset = {},
dofile = {},
bit32 = mk_fields { "band", "rshift", "bor", "bnot", "bxor", "arshift", "btest", "lrotate", "lshift", "replace", "rrotate", "extract" },
pairs = {},
rs = mk_fields { "setAnalogOutput", "getBundledOutput", "getBundledInput", "setOutput", "setAnalogueOutput", "getInput", "getOutput", "setBundledOutput", "getAnalogueOutput", "testBundledInput", "getSides", "getAnalogInput", "getAnalogOutput", "getAnalogueInput" },
help = mk_fields { "path", "topics", "completeTopic", "setPath", "lookup" },
window = mk_fields { "create" },
math = mk_fields { "log", "ceil", "atan", "acos", "ldexp", "rad", "pow", "asin", "pi", "deg", "tan", "cos", "tanh", "random", "abs", "frexp", "log10", "floor", "sinh", "max", "sqrt", "modf", "huge", "min", "mod", "fmod", "randomseed", "atan2", "exp", "sin", "cosh" },
_VERSION = {},
pcall = {},
keys = mk_fields { "a", "c", "b", "e", "pageUp", "g", "f", "i", "h", "k", "j", "space", "l", "o", "n", "q", "f1", "s", "insert", "u", "t", "eight", "numPadEnter", "six", "x", "numPad6", "z", "backslash", "rightBracket", "f9", "yen", "left", "numPadSubtract", "noconvert", "grave", "rightCtrl", "numPad2", "rightAlt", "delete", "f4", "home", "leftAlt", "numPad8", "getName", "numLock", "f8", "r", "pageDown", "y", "numPadEquals", "at", "pause", "w", "f11", "slash", "f5", "rightShift", "period", "multiply", "numPad0", "nine", "f13", "capsLock", "two", "leftBracket", "minus", "scollLock", "colon", "f14", "v", "equals", "three", "up", "d", "convert", "f7", "apostrophe", "f15", "f10", "stop", "f2", "numPad3", "comma", "numPad1", "numPadAdd", "tab", "f3", "numPad4", "kana", "four", "right", "numPadDecimal", "numPad7", "leftShift", "backspace", "numPad9", "end", "one", "kanji", "cimcumflex", "m", "semiColon", "underscore", "zero", "p", "return", "ax", "seven", "f6", "enter", "numPadDivide", "numPad5", "f12", "leftCtrl", "down", "five", "numPadComma" },
gps = mk_fields { "CHANNEL_GPS", "locate" },
bit = mk_fields { "band", "blogic_rshift", "bxor", "bor", "bnot", "blshift", "brshift" },
select = {},
paintutils = mk_fields { "drawPixel", "drawLine", "loadImage", "drawImage", "drawFilledBox", "parseImage", "drawBox" },
os = mk_fields { "sleep", "unloadAPI", "getComputerID", "startTimer", "queueEvent", "cancelTimer", "time", "clock", "setAlarm", "computerID", "reboot", "getComputerLabel", "setComputerLabel", "run", "pullEventRaw", "cancelAlarm", "computerLabel", "version", "shutdown", "loadAPI", "pullEvent", "epoch", "day" },
rawget = {},
table = mk_fields { "pack", "insert", "getn", "foreachi", "maxn", "foreach", "sort", "unpack", "concat", "remove" },
read = {},
colors = mk_fields { "cyan", "purple", "brown", "orange", "red", "magenta", "lime", "white", "blue", "rgb8", "lightGray", "pink", "test", "subtract", "black", "green", "combine", "yellow", "lightBlue", "gray" },
setfenv = {},
setmetatable = {},
type = {},
error = {},
shell = mk_fields { "exit", "path", "setDir", "clearAlias", "programs", "dir", "getCompletionInfo", "resolveProgram", "setPath", "getRunningProgram", "setCompletionFunction", "history", "run", "complete", "completeProgram", "resolve", "setAlias", "aliases" },
}
}
1 change: 1 addition & 0 deletions src/codes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ export const enum WebsocketCodes {
GoingAway = 1001,
UnsupportedData = 1003,
PolicyViolation = 1008,
TryAgainLater = 1013,
}
2 changes: 1 addition & 1 deletion src/host/_make.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ local function has_content(line)
not (line:match("^%s*%-%-[^%[]") or line:match("^%s*%-%-$"))
end

for _, dep in pairs { "argparse", "framebuffer", "encode" } do
for _, dep in pairs { "argparse", "framebuffer", "encode", "json" } do
out:write(("package.preload[%q] = function(...)\n"):format(dep))

for line in io.lines(dep .. ".lua") do
Expand Down
18 changes: 13 additions & 5 deletions src/host/argparse.lua
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,14 @@ function parser:parse(...)
end

local function get_usage(arg)
if arg.argument then return arg.mvar
elseif arg.value then return arg.names[1] .. "=" .. arg.mvar
else return arg.names[1]
local name
if arg.argument then name = arg.mvar
elseif arg.value then name = arg.names[1] .. "=" .. arg.mvar
else name = arg.names[1]
end

if #arg.names > 1 then name = name .. "," .. table.concat(arg.names, ",", 2) end
return name
end

local function create(prefix)
Expand All @@ -144,7 +148,7 @@ local function create(prefix)
print("USAGE")
local max = 0
for i = 1, #parser.list do max = math.max(max, #get_usage(parser.list[i])) end
local format = "%" .. (max +1 ) .. "s %s"
local format = " %-" .. max .. "s %s"

for i = 1, #parser.list do
local arg = parser.list[i]
Expand All @@ -158,4 +162,8 @@ local function create(prefix)
return parser
end

return create
local function is_help(cmd)
return cmd == "help" or cmd == "--help" or cmd == "-h" or cmd == "-?"
end

return { create = create, is_help = is_help }
2 changes: 1 addition & 1 deletion src/host/encode.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
-- a limit of how much impact any of it actually makes. Kinda pointless though
-- as this does 5Mb/s on my machine.
local function fletcher_32(str)
local s1, s2, len, byte = 0, 0, #str, string.byte
local s1, s2, byte = 0, 0, string.byte

if #str % 2 ~= 0 then str = str .. "\0" end
for i = 1, #str, 2 do
Expand Down
41 changes: 21 additions & 20 deletions src/host/framebuffer.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
--- Just another frame buffer, but this one is serialisable!

local stringify = require("json").stringify

local colour_lookup = {}
for i = 0, 16 do
colour_lookup[string.format("%x", i)] = 2 ^ i
Expand Down Expand Up @@ -32,6 +34,7 @@ local function buffer(original)
local text_colour = {}
local back_colour = {}
local palette = {}
local palette_24 = {}

local cursor_x, cursor_y = 1, 1

Expand All @@ -50,6 +53,7 @@ local function buffer(original)
for i = 0, 15 do
local c = 2 ^ i
palette[c] = { original.getPaletteColour( c ) }
palette_24[c] = colours.rgb8(original.getPaletteColour( c ))
end
end

Expand Down Expand Up @@ -116,7 +120,7 @@ local function buffer(original)
local preStart = math.min(1, preStop)
local postStart = cursor_x + #writeText
local postStop = sizeX
local sub, rep = string.sub, string.rep
local sub = string.sub

text[cursor_y] = sub(lineText, preStart, preStop)..writeText..sub(lineText, postStart, postStop)
text_colour[cursor_y] = sub(lineColor, preStart, preStop)..writeFore..sub(lineColor, postStart, postStop)
Expand Down Expand Up @@ -230,12 +234,14 @@ local function buffer(original)
if not palcol then error("Invalid colour (got " .. tostring(colour) .. ")", 2) end
if type(r) == "number" and g == nil and b == nil then
palcol[1], palcol[2], palcol[3] = colours.rgb8(r)
palette_24[colour] = r
else
if type(r) ~= "number" then error("bad argument #2 (expected number, got " .. type(r) .. ")", 2) end
if type(g) ~= "number" then error("bad argument #3 (expected number, got " .. type(g) .. ")", 2) end
if type(b) ~= "number" then error("bad argument #4 (expected number, got " .. type(b ) .. ")", 2 ) end

palcol[1], palcol[2], palcol[3] = r, g, b
palette_24[colour] = colours.rgb8(r, g, b)
end

dirty = true
Expand All @@ -254,30 +260,25 @@ local function buffer(original)
function redirect.is_dirty() return dirty end
function redirect.clear_dirty() dirty = false end

local function char(x)
if x <= 0 then return "00"
elseif x >= 255 then return "ff"
else return ("%02x"):format(x) end
end

function redirect.serialise()
local palettes = {}
for i = 0, 15 do
local c = palette[2^i]
palettes[i * 3 + 1] = char(math.floor(c[1] * 255))
palettes[i * 3 + 2] = char(math.floor(c[2] * 255))
palettes[i * 3 + 3] = char(math.floor(c[3] * 255))
end
return stringify {
packet = 0x10,

return char(sizeX) .. char(sizeY) .. char(cursor_x) .. char(cursor_y)
.. (cursor_blink and "1" or "0") .. cur_text_colour .. cur_back_colour
.. table.concat(palettes)
.. table.concat(text)
.. table.concat(text_colour)
.. table.concat(back_colour)
width = sizeX, height = sizeY,
cursorX = cursor_x, cursorY = cursor_y, cursorB = cursor_blink,
curFore = cur_text_colour, curBack = cur_back_colour,

palette = palette_24,
text = text, fore = text_colour, back = back_colour
}
end

-- Ensure we're in sync with the parent terminal
redirect.setCursorPos(1, 1)
redirect.setBackgroundColor(colours.black)
redirect.setTextColor(colours.white)
redirect.clear()

return redirect
end

Expand Down
Loading

0 comments on commit b8cbe12

Please sign in to comment.