Advertisement
osmarks

Rift

Jun 23rd, 2019
756
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 6.54 KB | None | 0 0
  1. local channel = 31415
  2.  
  3. local m = peripheral.find("modem", function(_, o) return o.isWireless() end)
  4. if not m then error "Modem required!" end
  5.  
  6. local tele = peripheral.find "Teleporter"
  7. if not tele then error "Teleporter (see Mekanism wiki) required!" end
  8.  
  9. local sign = peripheral.find "minecraft:sign"
  10.  
  11. local rift_ID = os.getComputerLabel():match "Rift:([A-Za-z0-9_-]+)"
  12.  
  13. if not rift_ID then error "Please relabel this computer `Rift:[unique rift identifier]`." end
  14.  
  15. m.open(channel)
  16.  
  17. local function display_status(...)
  18.     if sign then sign.setSignText(rift_ID, ...) end
  19. end
  20.  
  21. local function split_at_spaces(s)
  22.     local t = {}
  23.     for i in string.gmatch(s, "%S+") do
  24.        table.insert(t, i)
  25.     end
  26.     return t
  27. end
  28.  
  29. local function first_letter(s)
  30.     return string.sub(s, 1, 1)
  31. end
  32.  
  33. local function update_self()
  34.     print "Downloading update."
  35.     local h = http.get "https://pastebin.com/raw/jta5puZL"
  36.     local t = h.readAll()
  37.     h.close()
  38.  
  39.     local fn, err = load(t, "@rift")
  40.     if not fn then printError("Not updating: syntax error:\n" .. err) end
  41.  
  42.     local f = fs.open("startup", "w")
  43.     f.write(t)
  44.     f.close()
  45.     os.reboot()
  46. end
  47.  
  48. local teleporter_errors = {
  49.     [2] = "Frame invalid.",
  50.     [3] = "Pairing error.",
  51.     [4] = "Insufficient energy."
  52. }
  53.  
  54. local function send_message(...)
  55.     m.transmit(channel, channel, { ... })
  56. end
  57.  
  58. local function disconnect(remote)
  59.     display_status "Idle"
  60.     if not remote then send_message("command", "handle_disconnect", rift_ID) end
  61.     tele.setFrequency(("rift-%s-null"):format(rift_ID), true)
  62. end
  63.  
  64. local connected_to = nil
  65.  
  66. local remote_commands = {
  67.     ping = function()
  68.         return { rift_ID }
  69.     end,
  70.     update = function()
  71.         update_self()
  72.     end,
  73.     dial = function(from, to)
  74.         if to ~= rift_ID then return end
  75.         if type(from) ~= "string" then error("Invalid or no originator specified!") end
  76.         local freq = ("rift-%s-%s"):format(from, to)
  77.         tele.setFrequency(freq, true)
  78.         local status = tele.canTeleport()
  79.         if status == 1 then
  80.             print(("Connection established from %s."):format(from))
  81.             display_status("Dialed from:", from)
  82.             connected_to = from
  83.             return { true }
  84.         else
  85.             local e = teleporter_errors[status] or "Unknown error."
  86.             display_status("Error:", e)
  87.             printError(("Error receiving connection from %s: %s"):format(from, e))
  88.             return { false, e }
  89.         end
  90.     end,
  91.     handle_disconnect = function(other_end)
  92.         if other_end == connected_to then disconnect(true) end
  93.     end
  94. }
  95.  
  96. local function timeout(fn, time)
  97.     local timer = os.startTimer(time)
  98.     local co = coroutine.create(fn)
  99.     local filter
  100.     while true do
  101.         local ev = { os.pullEvent() }
  102.         if coroutine.status(co) == "dead" or ev[1] == "timer" and ev[2] == timer then return end
  103.         if not filter or filter == ev[1] then
  104.             local ok, res = coroutine.resume(co, unpack(ev))
  105.             if not ok then error(res)
  106.             else filter = res end
  107.         end
  108.     end
  109. end
  110.  
  111. local usage =
  112. [[Welcome to Rift. All listed commands are accessible by single-letter shortcuts.
  113. dial [destination] - Open a connection to rift [destination].
  114. scan - Scan for available rifts.
  115. scan [filter] - Scan for available rifts whose names contain [filter].
  116. help - Print this.
  117. update - Update this.
  118. disconnect - Disconnect from current paired rift.
  119. id - Get the ID of the current rift.]]
  120.  
  121. local CLI_commands = {
  122.     help = function() textutils.pagedPrint(usage) end,
  123.     dial = function(dest)
  124.         if not dest then error "No destination rift specified." end
  125.         local freq = ("rift-%s-%s"):format(rift_ID, dest)
  126.         print(("Setting frequency to %s."):format(freq))
  127.         tele.setFrequency(freq, true)
  128.         print(("Sending dial request to %s."):format(dest))
  129.         send_message("command", "dial", rift_ID, dest)
  130.  
  131.         local result
  132.         timeout(function()
  133.             while true do
  134.                 local _, response_to, success, error_type = os.pullEvent "remote_result"
  135.                 if response_to == "dial" then
  136.                     if not success then
  137.                         print(dest, "reports error:", error_type)
  138.                         result = "remote_error"
  139.                     else
  140.                         print(dest, "reports success.")
  141.                         local status = tele.canTeleport()
  142.                         if status == 1 then
  143.                             result = "success"
  144.                             print "Link established."
  145.                             display_status("Dialed to:", dest)
  146.                             connected_to = dest
  147.                         else
  148.                             result = "local_error"
  149.                             local e = teleporter_errors[status] or "Unknown error."
  150.                             display_status("Error:", e)
  151.                             print(e)
  152.                         end
  153.                     end
  154.                     break
  155.                 end
  156.             end
  157.         end, 1)
  158.  
  159.         if result == nil then error(dest .. " is unreachable or nonexistent.", 0) end
  160.     end,
  161.     update = function(what)
  162.         if what == "all" then
  163.             send_message("command", "update")
  164.             print "Update command broadcast"
  165.         end
  166.         update_self()
  167.     end,
  168.     scan = function(filter)
  169.         send_message("command", "ping")
  170.         print "Scanning..."
  171.         timeout(function()
  172.             while true do
  173.                 local _, resp_to, from = os.pullEvent "remote_result"
  174.                 if resp_to == "ping" and from and (not filter or from:match(filter)) then
  175.                     print("Found:", from)
  176.                 end
  177.             end
  178.         end, 1)
  179.     end,
  180.     disconnect = disconnect,
  181.     id = function()
  182.         print("Rift ID:", rift_ID)
  183.     end
  184. }
  185.  
  186. local function command_prompt()
  187.     print "Welcome to Rift!"
  188.     local history = {}
  189.     while true do
  190.         write "|> "
  191.         local text = read(nil, history)
  192.  
  193.         if text ~= "" then table.insert(history, text) end
  194.  
  195.         local tokens = split_at_spaces(text)
  196.         local command = table.remove(tokens, 1)
  197.         local args = tokens
  198.         local fn = CLI_commands[command]
  199.  
  200.         if not fn then
  201.             for command_name, func in pairs(CLI_commands) do
  202.                 if command and first_letter(command_name) == first_letter(command) then fn = func end
  203.             end
  204.         end
  205.         if not fn then
  206.             print("Command", command, "not found.")
  207.         else
  208.             local ok, err = pcall(fn, table.unpack(args))
  209.             if not ok then printError(err) end
  210.         end
  211.     end
  212. end
  213.  
  214. local function message_handler()
  215.     while true do
  216.         local _, _, _, _, message = os.pullEvent "modem_message"
  217.         if type(message) == "table" then
  218.             local mtype = table.remove(message, 1)
  219.             if mtype == "command" then
  220.                 local cmd = table.remove(message, 1)
  221.                 local cmd_fn = remote_commands[cmd]
  222.                 if cmd_fn then
  223.                     local ok, res = pcall(cmd_fn, unpack(message))
  224.                     if ok then
  225.                         if type(res) == "table" then
  226.                             send_message("result", cmd, unpack(res))
  227.                         end
  228.                     else
  229.                         printError(res)
  230.                         send_message("error", res)
  231.                     end
  232.                 end
  233.             elseif mtype == "result" then os.queueEvent("remote_result", unpack(message))
  234.             elseif mtype == "error" then os.queueEvent("remote_error", unpack(message)) end
  235.         end
  236.     end
  237. end
  238.  
  239. if ... == "update" then update_self() end
  240.  
  241. disconnect()
  242.  
  243. parallel.waitForAny(message_handler, command_prompt)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement