Implement computer registration

This commit is contained in:
2022-01-02 15:18:59 +01:00
parent 227b9ba5dc
commit 63d6fe4b26
8 changed files with 550 additions and 11 deletions

294
backend/lua/framebuffer.lua Normal file
View File

@@ -0,0 +1,294 @@
--- https://github.com/SquidDev-CC/cloud-catcher/blob/master/src/host/framebuffer.lua
--- but slightly modified because this isn't cloud catcher
--- Just another frame buffer, but this one is serialisable!
local stringify = require("json").stringify
local colour_lookup = {}
for i = 0, 15 do
colour_lookup[2 ^ i] = string.format("%x", i)
end
--- Create a buffer which can be converted to a string and transmitted.
local function buffer(original)
local text = {}
local text_colour = {}
local back_colour = {}
local palette = {}
local palette_24 = {}
local cursor_x, cursor_y = 1, 1
local cursor_blink = false
local cur_text_colour = "0"
local cur_back_colour = "f"
local sizeX, sizeY = original.getSize()
local color = original.isColor()
local dirty = false
local redirect = {}
if original.getPaletteColour then
for i = 0, 15 do
local c = 2 ^ i
palette[c] = { original.getPaletteColour( c ) }
palette_24[colour_lookup[c]] = colours.rgb8(original.getPaletteColour( c ))
end
end
function redirect.write(writeText)
writeText = tostring(writeText)
original.write(writeText)
dirty = true
-- If we're off the screen then just emulate a write
if cursor_y > sizeY or cursor_y < 1 or cursor_x + #writeText <= 1 or cursor_x > sizeX then
cursor_x = cursor_x + #writeText
return
end
-- Adjust text to fit on screen
if cursor_x < 1 then
writeText = writeText:sub(-cursor_x + 2)
cursor_x = 1
elseif cursor_x + #writeText > sizeX then
writeText = writeText:sub(1, sizeX - cursor_x + 1)
end
local lineText = text[cursor_y]
local lineColor = text_colour[cursor_y]
local lineBack = back_colour[cursor_y]
local preStop = cursor_x - 1
local preStart = math.min(1, preStop)
local postStart = cursor_x + #writeText
local postStop = sizeX
local sub, rep = string.sub, string.rep
text[cursor_y] = sub(lineText, preStart, preStop)..writeText..sub(lineText, postStart, postStop)
text_colour[cursor_y] = sub(lineColor, preStart, preStop)..rep(cur_text_colour, #writeText)..sub(lineColor, postStart, postStop)
back_colour[cursor_y] = sub(lineBack, preStart, preStop)..rep(cur_back_colour, #writeText)..sub(lineBack, postStart, postStop)
cursor_x = cursor_x + #writeText
end
function redirect.blit(writeText, writeFore, writeBack)
original.blit(writeText, writeFore, writeBack)
dirty = true
-- If we're off the screen then just emulate a write
if cursor_y > sizeY or cursor_y < 1 or cursor_x + #writeText <= 1 or cursor_x > sizeX then
cursor_x = cursor_x + #writeText
return
end
if cursor_x < 1 then
--adjust text to fit on screen starting at one.
writeText = writeText:sub(-cursor_x + 2)
writeFore = writeFore:sub(-cursor_x + 2)
writeBack = writeBack:sub(-cursor_x + 2)
cursor_x = 1
elseif cursor_x + #writeText > sizeX then
writeText = writeText:sub(1, sizeX - cursor_x + 1)
writeFore = writeFore:sub(1, sizeX - cursor_x + 1)
writeBack = writeBack:sub(1, sizeX - cursor_x + 1)
end
local lineText = text[cursor_y]
local lineColor = text_colour[cursor_y]
local lineBack = back_colour[cursor_y]
local preStop = cursor_x - 1
local preStart = math.min(1, preStop)
local postStart = cursor_x + #writeText
local postStop = sizeX
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)
back_colour[cursor_y] = sub(lineBack, preStart, preStop)..writeBack..sub(lineBack, postStart, postStop)
cursor_x = cursor_x + #writeText
end
function redirect.clear()
for i = 1, sizeY do
text[i] = string.rep(" ", sizeX)
text_colour[i] = string.rep(cur_text_colour, sizeX)
back_colour[i] = string.rep(cur_back_colour, sizeX)
end
dirty = true
return original.clear()
end
function redirect.clearLine()
-- If we're off the screen then just emulate a clearLine
if cursor_y > sizeY or cursor_y < 1 then
return
end
text[cursor_y] = string.rep(" ", sizeX)
text_colour[cursor_y] = string.rep(cur_text_colour, sizeX)
back_colour[cursor_y] = string.rep(cur_back_colour, sizeX)
dirty = true
return original.clearLine()
end
function redirect.getCursorPos()
return cursor_x, cursor_y
end
function redirect.setCursorPos(x, y)
if type(x) ~= "number" then error("bad argument #1 (expected number, got " .. type(x) .. ")", 2) end
if type(y) ~= "number" then error("bad argument #2 (expected number, got " .. type(y) .. ")", 2) end
if x ~= cursor_x or y ~= cursor_y then
cursor_x = math.floor(x)
cursor_y = math.floor(y)
dirty = true
end
return original.setCursorPos(x, y)
end
function redirect.setCursorBlink(b)
if type(b) ~= "boolean" then error("bad argument #1 (expected boolean, got " .. type(b) .. ")", 2) end
if cursor_blink ~= b then
cursor_blink = b
dirty = true
end
return original.setCursorBlink(b)
end
function redirect.getSize()
return sizeX, sizeY
end
function redirect.scroll(n)
if type(n) ~= "number" then error("bad argument #1 (expected number, got " .. type(n) .. ")", 2) end
local empty_text = string.rep(" ", sizeX)
local empty_text_colour = string.rep(cur_text_colour, sizeX)
local empty_back_colour = string.rep(cur_back_colour, sizeX)
if n > 0 then
for i = 1, sizeY do
text[i] = text[i + n] or empty_text
text_colour[i] = text_colour[i + n] or empty_text_colour
back_colour[i] = back_colour[i + n] or empty_back_colour
end
elseif n < 0 then
for i = sizeY, 1, -1 do
text[i] = text[i + n] or empty_text
text_colour[i] = text_colour[i + n] or empty_text_colour
back_colour[i] = back_colour[i + n] or empty_back_colour
end
end
dirty = true
return original.scroll(n)
end
function redirect.setTextColour(clr)
if type(clr) ~= "number" then error("bad argument #1 (expected number, got " .. type(clr) .. ")", 2) end
local new_colour = colour_lookup[clr] or error("Invalid colour (got " .. clr .. ")" , 2)
if new_colour ~= cur_text_colour then
dirty = true
cur_text_colour = new_colour
end
return original.setTextColour(clr)
end
redirect.setTextColor = redirect.setTextColour
function redirect.setBackgroundColour(clr)
if type(clr) ~= "number" then error("bad argument #1 (expected number, got " .. type(clr) .. ")", 2) end
local new_colour = colour_lookup[clr] or error("Invalid colour (got " .. clr .. ")" , 2)
if new_colour ~= cur_back_colour then
dirty = true
cur_back_colour = new_colour
end
return original.setBackgroundColour(clr)
end
redirect.setBackgroundColor = redirect.setBackgroundColour
function redirect.isColour()
return color == true
end
redirect.isColor = redirect.isColour
function redirect.getTextColour()
return 2 ^ tonumber(cur_text_colour, 16)
end
redirect.getTextColor = redirect.getTextColour
function redirect.getBackgroundColour()
return 2 ^ tonumber(cur_back_colour, 16)
end
redirect.getBackgroundColor = redirect.getBackgroundColour
if original.getPaletteColour then
function redirect.setPaletteColour(colour, r, g, b)
local palcol = palette[colour]
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_lookup[colour]] = colours.rgb8(r, g, b)
end
dirty = true
return original.setPaletteColour(colour, r, g, b)
end
redirect.setPaletteColor = redirect.setPaletteColour
function redirect.getPaletteColour(colour)
local palcol = palette[colour]
if not palcol then error("Invalid colour (got " .. tostring(colour) .. ")", 2) end
return palcol[1], palcol[2], palcol[3]
end
redirect.getPaletteColor = redirect.getPaletteColour
end
function redirect.is_dirty() return dirty end
function redirect.clear_dirty() dirty = false end
function redirect.serialize()
local palette = {}
for i = 0, 15 do
palette[i+1] = string.format("#%06x", palette_24[string.format("%x", i)])
end
return stringify { framebuffer = {
width = sizeX, height = sizeY,
cursorX = cursor_x, cursorY = cursor_y, cursorBlink = cursor_blink,
-- curFg = cur_text_colour, curBg = cur_back_colour,
palette = palette,
textBuf = text, fgBuf = text_colour, bgBuf = 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
return { buffer = buffer }

169
backend/lua/json.lua Normal file
View File

@@ -0,0 +1,169 @@
local tonumber = tonumber
local function skip_delim(str, pos, delim, err_if_missing)
pos = pos + #str:match('^%s*', pos)
if str:sub(pos, pos) ~= delim then
if err_if_missing then error('Expected ' .. delim) end
return pos, false
end
return pos + 1, true
end
-- A table of JSON->Lua escape characters
local esc_map = { b = '\b', f = '\f', n = '\n', r = '\r', t = '\t' }
local function parse_str_val(str, pos)
local out, n = {}, 0
if pos > #str then error("Malformed JSON (in string)") end
while true do
local c = str:sub(pos, pos)
if c == '"' then return table.concat(out, "", 1, n), pos + 1 end
n = n + 1
if c == '\\' then
local nextc = str:sub(pos + 1, pos + 1)
if not nextc then error("Malformed JSON (in string)") end
if nextc == "u" then
local num = tonumber(str:sub(pos + 2, pos + 5), 16)
if not num then error("Malformed JSON (in unicode string) ") end
if num <= 255 then
pos, out[n] = pos + 6, string.char(num)
else
pos, out[n] = pos + 6, "?"
end
else
pos, out[n] = pos + 2, esc_map[nextc] or nextc
end
else
pos, out[n] = pos + 1, c
end
end
end
local function parse_num_val(str, pos)
local num_str = str:match('^-?%d+%.?%d*[eE]?[+-]?%d*', pos)
local val = tonumber(num_str)
if not val then error('Error parsing number at position ' .. pos .. '.') end
return val, pos + #num_str
end
local null = {}
local literals = {['true'] = true, ['false'] = false, ['null'] = null }
-- Build a table of Lua->JSON escape characters
local escapes = {}
for i = 0, 255 do
local c = string.char(i)
if i >= 32 and i <= 126
then escapes[c] = c
else escapes[c] = ("\\u00%02x"):format(i)
end
end
escapes["\t"], escapes["\n"], escapes["\r"], escapes["\""], escapes["\\"] = "\\t", "\\n", "\\r", "\\\"", "\\\\"
local function parse(str, pos, end_delim)
pos = pos or 1
if pos > #str then error('Reached unexpected end of input.') end
local pos = pos + #str:match('^%s*', pos)
local first = str:sub(pos, pos)
if first == '{' then
local obj, key, delim_found = {}, true, true
pos = pos + 1
while true do
key, pos = parse(str, pos, '}')
if key == nil then return obj, pos end
if not delim_found then error('Comma missing between object items.') end
pos = skip_delim(str, pos, ':', true)
obj[key], pos = parse(str, pos)
pos, delim_found = skip_delim(str, pos, ',')
end
elseif first == '[' then
local arr, val, delim_found = {}, true, true
pos = pos + 1
while true do
val, pos = parse(str, pos, ']')
if val == nil then return arr, pos end
if not delim_found then error('Comma missing between array items.') end
arr[#arr + 1] = val
pos, delim_found = skip_delim(str, pos, ',')
end
elseif first == '"' then
return parse_str_val(str, pos + 1)
elseif first == '-' or first:match('%d') then
return parse_num_val(str, pos)
elseif first == end_delim then
return nil, pos + 1
else
for lit_str, lit_val in pairs(literals) do
local lit_end = pos + #lit_str - 1
if str:sub(pos, lit_end) == lit_str then return lit_val, lit_end + 1 end
end
local pos_info_str = 'position ' .. pos .. ': ' .. str:sub(pos, pos + 10)
error('Invalid json syntax starting at ' .. pos_info_str)
end
end
local format, gsub, tostring, pairs, next, type, concat
= string.format, string.gsub, tostring, pairs, next, type, table.concat
local function stringify_impl(t, out, n)
local ty = type(t)
if ty == "table" then
local first_ty = type(next(t))
if first_ty == "nil" then
-- Assume empty tables are arrays
out[n], n = "{}", n + 1
return n
elseif first_ty == "string" then
out[n], n = "{", n + 1
local first = true
for k, v in pairs(t) do
if first then first = false else out[n], n = ",", n + 1 end
out[n] = format("\"%s\":", k)
n = stringify_impl(v, out, n + 1)
end
out[n], n = "}", n + 1
return n
elseif first_ty == "number" then
out[n], n = "[", n + 1
for i = 1, #t do
if i > 1 then out[n], n = ",", n + 1 end
n = stringify_impl(t[i], out, n)
end
out[n], n = "]", n + 1
return n
else
error("Cannot serialize key " .. first_ty)
end
elseif ty == "string" then
if t:match("^[ -~]*$") then
out[n], n = gsub(format("%q", t), "\n", "n"), n + 1
else
out[n], n = "\"" .. gsub(t, ".", escapes) .. "\"", n + 1
end
return n
elseif ty == "number" or ty == "boolean" then
out[n],n = tostring(t), n + 1
return n
else error("Cannot serialize type " .. ty)
end
end
local function stringify(object)
local buffer = {}
local n = stringify_impl(object, buffer, 1)
return concat(buffer, "", 1, n - 1)
end
local function try_parse(msg)
local ok, res = pcall(parse, msg)
if ok then return res else return nil, res end
end
return {
stringify = stringify,
try_parse = try_parse,
parse = parse,
null = null
}