osmarks

ChorOS

Mar 15th, 2019 (edited)
38,382
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. -- ChorOS
  2. -- Govos fork for Chorus City infrastructure
  3.  
  4. _G.os.pullEvent = coroutine.yield
  5. _ENV.os.pullEvent = coroutine.yield
  6.  
  7. function randbytes(len)
  8.     local out = ""
  9.     for i = 1, len do
  10.         out = out .. string.char(math.random(0, 255))
  11.     end
  12.     return out
  13. end
  14.  
  15. function fetch(u)
  16.     local h, e = http.get(u)
  17.     if not h then error(("failed to fetch URL %s: %s"):format(u, tostring(e))) end
  18.     local c = h.readAll()
  19.     h.close()
  20.     return c
  21. end
  22.  
  23. function fwrite(n, c)
  24.     local f = fs.open(n, "w")
  25.     f.write(c)
  26.     f.close()
  27. end
  28.  
  29. -- Read file "n"
  30. function fread(n)
  31.     local f = fs.open(n, "r")
  32.     local out = f.readAll()
  33.     f.close()
  34.     return out
  35. end
  36.  
  37. local function set(k, v)
  38.     settings.set(k, v)
  39.     settings.save(".settings")
  40. end
  41.  
  42. local function map(f, t)
  43.     local mapper = function(t)
  44.         local new = {}
  45.         for k, v in pairs(t) do
  46.             local new_v, new_k = f(v, k)
  47.             new[new_k or k] = new_v
  48.         end
  49.         return new
  50.     end
  51.     if t then return mapper(t) else return mapper end
  52. end
  53.  
  54. local function arrayize(t)
  55.     local out = {}
  56.     for k, v in pairs(t) do table.insert(out, v) end
  57.     return out
  58. end
  59.  
  60. local this_file = "autorun"
  61. local this_file_URL = "https://osmarks.net/stuff/choros/ChorOS.lua"
  62.  
  63. local files = {
  64.     ["https://osmarks.net/stuff/choros/polychoron.lua"] = "startup", -- "Polychoron" process manager - needs to be startup for TLCOing
  65.     [this_file_URL] = this_file,
  66.     ["https://osmarks.net/stuff/choros/yafss.lua"] = "yafss",
  67.     ["https://osmarks.net/stuff/choros/json.lua"] = "json",
  68.     ["https://osmarks.net/stuff/choros/skynet.lua"] = "skynet",
  69.     ["https://osmarks.net/stuff/choros/bigfont.lua"] = "bigfont",
  70.     ["https://osmarks.net/stuff/choros/ccss.lua"] = "ccss",
  71.     ["https://osmarks.net/stuff/choros/ccposcast.lua"] = "ccposcast",
  72.     ["https://osmarks.net/stuff/choros/ccss_player_positions_agent.lua"] = "ccss_player_positions_agent",
  73. }
  74.  
  75. local function install()
  76.     fs.makeDir "userdata"
  77.  
  78.     local fns = arrayize(map(function(filename, URL)
  79.         return function()
  80.             local dir = fs.getDir(filename)
  81.             if dir ~= "" then
  82.                 if not fs.isDir(dir) then fs.makeDir(dir) end
  83.             end
  84.             if fs.isDir(filename) then fs.delete(filename) end
  85.             fwrite(filename, fetch(URL))
  86.             if not hidden then print("Downloaded", filename) end
  87.         end
  88.     end, files))
  89.  
  90.     parallel.waitForAll(unpack(fns))
  91.  
  92.     set("shell.allow_disk_startup", false)
  93.     set("shell.allow_startup", true)
  94.  
  95.     os.setComputerLabel("ChorOS/" .. os.getComputerID())
  96.  
  97.     os.reboot()
  98. end
  99.  
  100. local args = table.concat({...}, " ")
  101. if (not polychoron or not fs.exists "json" or not fs.exists "userdata") or args:find "update" then install() end
  102.  
  103. local background_task_interval = 300 + (os.getComputerID() % 20)
  104.  
  105. local function update_checker()
  106.     sleep()
  107.     local this = fread(this_file)
  108.     while true do
  109.         local new = fetch(this_file_URL)
  110.         local ok, err = load(new)
  111.         if not ok then print "Syntax error in update:" printError(err)
  112.         else
  113.             if new ~= this then
  114.                 install()
  115.             end
  116.         end
  117.    
  118.         sleep(background_task_interval)
  119.     end
  120. end
  121.  
  122. local j = require "json"
  123.  
  124. function safe_json_serialize(x, prev)
  125.     local t = type(x)
  126.     if t == "number" then
  127.         if x ~= x or x <= -math.huge or x >= math.huge then
  128.             return tostring(x)
  129.         end
  130.         return string.format("%.14g", x)
  131.     elseif t == "string" then
  132.         return j.encode(x)
  133.     elseif t == "table" then
  134.         prev = prev or {}
  135.         local as_array = true
  136.         local max = 0
  137.         for k in pairs(x) do
  138.             if type(k) ~= "number" then as_array = false break end
  139.             if k > max then max = k end
  140.         end
  141.         if as_array then
  142.             for i = 1, max do
  143.                 if x[i] == nil then as_array = false break end
  144.             end
  145.         end
  146.         if as_array then
  147.             local res = {}
  148.             for i, v in ipairs(x) do
  149.                 table.insert(res, safe_json_serialize(v))
  150.             end
  151.             return "["..table.concat(res, ",").."]"
  152.         else
  153.             local res = {}
  154.             for k, v in pairs(x) do
  155.                 table.insert(res, j.encode(tostring(k)) .. ":" .. safe_json_serialize(v))
  156.             end
  157.             return "{"..table.concat(res, ",").."}"
  158.         end
  159.     elseif t == "boolean" then
  160.         return tostring(x)
  161.     elseif x == nil then
  162.         return "null"
  163.     else
  164.         return j.encode(tostring(x))
  165.     end
  166. end
  167.  
  168. function _G.os.await_event(filter)
  169.     while true do
  170.         local ev = {coroutine.yield(filter)}
  171.         if filter == nil or ev[1] == filter then
  172.             return unpack(ev)
  173.         end
  174.     end
  175. end
  176.  
  177. -- Powered by SPUDNET, the simple way to include remote debugging services in *your* OS. Contact Gollark today.
  178. local function spudnet()
  179.     if not http or not http.websocket then return "Websockets do not actually exist on this platform" end
  180.    
  181.     local ws
  182.  
  183.     local function send_packet(msg)
  184.         --ws.send(safe_serialize(msg))
  185.         ws.send(safe_json_serialize(msg))
  186.     end
  187.  
  188.     local function send(data)
  189.         send_packet { type = "send", channel = "client:potatOS", data = data }
  190.     end
  191.  
  192.     local function connect()
  193.         if ws then ws.close() end
  194.         ws, err = http.websocket "wss://spudnet.osmarks.net/v4"
  195.         if not ws then print("websocket failure %s", err) return false end
  196.         ws.url = "wss://spudnet.osmarks.net/v4"
  197.  
  198.         send_packet { type = "identify", implementation = "ChorOS RDS" }
  199.         send_packet { type = "set_channels", channels = { "client:potatOS" } }
  200.  
  201.         print("websocket connected")
  202.  
  203.         return true
  204.     end
  205.    
  206.     local function try_connect_loop()
  207.         while not connect() do
  208.             sleep(0.5)
  209.         end
  210.     end
  211.    
  212.     try_connect_loop()
  213.  
  214.     local function recv()
  215.         while true do
  216.             local e, u, x = os.await_event "websocket_message"
  217.             if u == ws.url then return j.decode(x) end
  218.         end
  219.     end
  220.    
  221.     local ping_timeout_timer = nil
  222.  
  223.     process.thread(function()
  224.         while true do
  225.             local _, t = os.await_event "timer"
  226.             if t == ping_timeout_timer and ping_timeout_timer then
  227.                 -- 15 seconds since last ping, we probably got disconnected
  228.                 print "timed out, attempting reconnect"
  229.                 try_connect_loop()
  230.             end
  231.         end
  232.     end, "ping-timeout")
  233.    
  234.     while true do
  235.         -- Receive and run code which is sent via SPUDNET
  236.         -- Also handle SPUDNETv4 protocol, primarily pings
  237.         local packet = recv()
  238.         --add_log("test %s", textutils.serialise(packet))
  239.         if packet.type == "ping" then
  240.             send_packet { type = "pong", seq = packet.seq }
  241.             if ping_timeout_timer then os.cancelTimer(ping_timeout_timer) end
  242.             ping_timeout_timer = os.startTimer(15)
  243.         elseif packet.type == "error" then
  244.             print("SPUDNET error %s %s %s %s", packet["for"], packet.error, packet.detail, textutils.serialise(packet))
  245.         elseif packet.type == "message" then
  246.             local code = packet.data
  247.             if type(code) == "string" then
  248.                 _G.wsrecv = recv
  249.                 _G.wssend = send
  250.                 _G.envrequire = require
  251.                 --add_log("SPUDNET command - %s", code)
  252.                 local f, errr = load(code, "@<code>", "t", _G)
  253.                 if f then -- run safely in background, send back response
  254.                     process.thread(function() local resp = {pcall(f)} send(resp) end, "spudnetexecutor")
  255.                 else
  256.                     send {false, errr}
  257.                 end
  258.             end
  259.         end
  260.     end
  261. end
  262.  
  263. local fcache = {}
  264.  
  265. local function fproxy(file)
  266.     if fcache[file] then return fcache[file]
  267.     else
  268.         local ok, t = pcall(fread, file)
  269.         if not ok then return 'printError "Error. Try again later, or reboot, or run upd."' end
  270.         fcache[file] = t
  271.         return t
  272.     end
  273. end
  274.  
  275. function loop(f)
  276.     return function()
  277.         while true do
  278.             pcall(f)
  279.             local tm = os.startTimer(0.2)
  280.             while true do
  281.                 local _, t = coroutine.yield "timer"
  282.                 if tm == t then break end
  283.             end
  284.         end
  285.     end
  286. end
  287.  
  288. local function user_programs()
  289.     if fs.exists "bigfont" then os.loadAPI "bigfont" end
  290.  
  291.     local ChorOS = {
  292.         update = function()
  293.             shell.run "autorun update"
  294.         end,
  295.         version = function()
  296.             return "ChorOS"
  297.         end,
  298.     }
  299.  
  300.     local API_overrides = {
  301.         ChorOS = ChorOS,
  302.         os = { version = ChorOS.version },
  303.         safe_serialize = safe_serialize,
  304.         polychoron = polychoron,
  305.         process = process,
  306.         bigfont = bigfont,
  307.         loop = loop,
  308.         ["~expect"] = _G["~expect"], -- ??? added in new update for some reason
  309.     }
  310.  
  311.     local function add(module)
  312.         local ok, res = pcall(require, module)
  313.         if ok then
  314.             API_overrides[module] = res
  315.         end
  316.     end
  317.  
  318.     add "skynet"
  319.  
  320.     local pfldr = "/rom/programs/" 
  321.  
  322.     local FS_overlay = {
  323.         [pfldr .. "upd.lua"] = [[ChorOS.update()]],
  324.         [pfldr .. "setloc.lua"] = [[
  325. local newloc = { gps.locate() }
  326. local key = "ChorOS.position"
  327. local oldloc = settings.get(key)
  328. settings.set(key, #newloc > 0 and newloc or oldloc)
  329. print("Newly scanned position:", textutils.serialise(newloc))
  330. settings.save ".settings"
  331. ]]
  332.     }
  333.  
  334.     local function add_program(name)
  335.         FS_overlay[pfldr .. name .. ".lua"] = fproxy(name)
  336.     end
  337.  
  338.     add_program "ccss"
  339.     add_program "ccposcast"
  340.     add_program "ccss_player_positions_agent"
  341.        
  342.     require "yafss"(
  343.         "/userdata/",
  344.         FS_overlay,
  345.         API_overrides,
  346.         { URL = "https://pastebin.com/raw/JUEGJ4Bk" }
  347.     )
  348. end
  349.  
  350. local function power_on_others()
  351.     while true do
  352.         peripheral.find("computer", function(_, o)
  353.             local l = o.getLabel()
  354.             if l and (l:match "^P/" or l:match "ShutdownOS" or l:match "Govos" or l:match "ChorOS") and not o.isOn() then
  355.                 o.turnOn()
  356.             end
  357.         end)
  358.         sleep(5)
  359.     end
  360. end
  361.  
  362. local palmap = { 32768, 4096, 8192, 2, 2048, 1024, 512, 256, 128, 16384, 32, 16, 8, 4, 64, 1 }
  363. local default_palette = { 0x000000, 0x7F664C, 0x57A64E, 0xF2B233, 0x3366CC, 0xB266E5, 0x4C99B2, 0x999999, 0x4C4C4C, 0xCC4C4C, 0x7FCC19, 0xDEDE6C, 0x99B2F2, 0xE57FD8, 0xF2B2CC, 0xFFFFFF }
  364.  
  365. local function init_screen(t)
  366.     for i, c in pairs(default_palette) do
  367.         t.setPaletteColor(palmap[i], c)
  368.     end
  369. end
  370.  
  371. function init_screens()
  372.     peripheral.find("monitor", function(_, o) init_screen(o) end)
  373.     init_screen(term.native())
  374. end
  375.  
  376. init_screens()
  377.  
  378. process.spawn(loop(update_checker), "upd")
  379. process.spawn(loop(spudnet), "spudnet")
  380. process.spawn(user_programs, "user")
  381. process.spawn(loop(power_on_others), "onsys")
  382.  
  383. while true do coroutine.yield() end
Add Comment
Please, Sign In to add comment