SHOW:
|
|
- or go back to the newest paste.
1 | - | local args = { ... } |
1 | + | --I DID NOT MAKE THIS PROGRAM IM MAKING SLIGHT EDITS TO MY NEEDS |
2 | local tArgs = { ... } | |
3 | ||
4 | local connections = {} | |
5 | ||
6 | local nshAPI = { | |
7 | connList = connections | |
8 | } | |
9 | - | local bufferDirs = {"/","/LyqydOS/","/usr/apis/","/disk/"} |
9 | + | |
10 | nshAPI.getRemoteID = function() | |
11 | - | if not framebuffer then |
11 | + | |
12 | - | for i = 1, #bufferDirs do |
12 | + | |
13 | - | if fs.exists(bufferDirs[i].."framebuffer") and os.loadAPI(bufferDirs[i].."framebuffer") then |
13 | + | if cInfo.thread == coroutine.running() then |
14 | if cNum == "localShell" then | |
15 | --if we are a client running on the server, return the remote server ID. | |
16 | if nshAPI.serverNum then | |
17 | return nshAPI.serverNum | |
18 | - | if not framebuffer then |
18 | + | |
19 | - | print("Couldn't find framebuffer API, using fallback") |
19 | + | |
20 | end | |
21 | end | |
22 | - | local function rawSend(id, msg) |
22 | + | |
23 | - | if term.current then |
23 | + | |
24 | - | return rednet.send(id, msg, "tror") |
24 | + | |
25 | --client running without local server, return remote server ID. | |
26 | if nshAPI.serverNum then return nshAPI.serverNum end | |
27 | return nil | |
28 | end | |
29 | ||
30 | - | local function rawRecv(id, timeout) |
30 | + | |
31 | - | if type(timeout) == "number" then timeout = os.startTimer(timeout) end |
31 | + | |
32 | if id then | |
33 | return rednet.send(id, msg) | |
34 | - | if event[1] == "rednet_message" and (id == nil and true or event[2] == id) and (not term.current and true or event[4] == "tror") then |
34 | + | |
35 | return nil | |
36 | end | |
37 | ||
38 | nshAPI.receive = function(timeout) | |
39 | if type(timeout) == number then timeout = os.startTimer(timeout) end | |
40 | while true do | |
41 | event = {os.pullEvent()} | |
42 | if event[1] == "rednet_message" and event[2] == nshAPI.getRemoteID() then | |
43 | return event[3] | |
44 | elseif event[1] == "timer" and event[2] == timeout then | |
45 | return nil | |
46 | - | if cInfo and type(cInfo) == "table" and cInfo.thread == coroutine.running() then |
46 | + | |
47 | end | |
48 | end | |
49 | ||
50 | nshAPI.getClientCapabilities = function() | |
51 | if nshAPI.clientCapabilities then return nshAPI.clientCapabilities end | |
52 | nshAPI.send("SP:;clientCapabilities") | |
53 | return nshAPI.receive(1) | |
54 | end | |
55 | ||
56 | nshAPI.getRemoteConnections = function() | |
57 | local remotes = {} | |
58 | for cNum, cInfo in pairs(nshAPI.connList) do | |
59 | table.insert(remotes, cNum) | |
60 | if cInfo.outbound then | |
61 | table.insert(remotes, cInfo.outbound) | |
62 | end | |
63 | end | |
64 | return remotes | |
65 | end | |
66 | - | return rawSend(id, msg) |
66 | + | |
67 | local packetConversion = { | |
68 | query = "SQ", | |
69 | response = "SR", | |
70 | data = "SP", | |
71 | close = "SC", | |
72 | - | return rawRecv(nshAPI.getRemoteID(), timeout) |
72 | + | |
73 | fileSend = "FS", | |
74 | fileResponse = "FR", | |
75 | fileHeader = "FH", | |
76 | fileData = "FD", | |
77 | fileEnd = "FE", | |
78 | textWrite = "TW", | |
79 | textCursorPos = "TC", | |
80 | textGetCursorPos = "TG", | |
81 | textGetSize = "TD", | |
82 | textInfo = "TI", | |
83 | textClear = "TE", | |
84 | textClearLine = "TL", | |
85 | textScroll = "TS", | |
86 | textBlink = "TB", | |
87 | textColor = "TF", | |
88 | textBackground = "TK", | |
89 | textIsColor = "TA", | |
90 | event = "EV", | |
91 | SQ = "query", | |
92 | - | nshAPI.packFile = function(path) |
92 | + | |
93 | - | local data = {} |
93 | + | |
94 | - | local count = 0 |
94 | + | |
95 | - | local handle = io.open(path, "rb") |
95 | + | |
96 | - | if handle then |
96 | + | |
97 | - | local byte = handle:read() |
97 | + | |
98 | - | repeat |
98 | + | |
99 | - | data[#data + 1] = byte |
99 | + | |
100 | - | count = count + 1 |
100 | + | |
101 | - | if count % 1000 == 0 then |
101 | + | |
102 | - | os.queueEvent("yield") |
102 | + | |
103 | - | os.pullEvent("yield") |
103 | + | |
104 | TD = "textGetSize", | |
105 | - | byte = handle:read() |
105 | + | |
106 | - | until not byte |
106 | + | |
107 | - | handle:close() |
107 | + | |
108 | TS = "textScroll", | |
109 | TB = "textBlink", | |
110 | TF = "textColor", | |
111 | - | local outputTable = {} |
111 | + | |
112 | - | for i = 1, #data, 3 do |
112 | + | |
113 | - | local num1, num2, num3 = data[i], data[i + 1] or 0, data[i + 2] or 0 |
113 | + | |
114 | - | table.insert(outputTable, string.char(bit.band(bit.brshift(num1, 2), 63))) |
114 | + | |
115 | - | table.insert(outputTable, string.char(bit.bor(bit.band(bit.blshift(num1, 4), 48), bit.band(bit.brshift(num2, 4), 15)))) |
115 | + | |
116 | - | table.insert(outputTable, string.char(bit.bor(bit.band(bit.blshift(num2, 2), 60), bit.band(bit.brshift(num3, 6), 3)))) |
116 | + | |
117 | - | table.insert(outputTable, string.char(bit.band(num3, 63))) |
117 | + | |
118 | for _, side in ipairs(rs.getSides()) do | |
119 | - | --mark non-data (invalid) bytes |
119 | + | |
120 | - | if #data % 3 == 1 then |
120 | + | |
121 | - | outputTable[#outputTable] = "=" |
121 | + | |
122 | - | outputTable[#outputTable - 1] = "=" |
122 | + | |
123 | - | elseif #data % 3 == 2 then |
123 | + | |
124 | - | outputTable[#outputTable] = "=" |
124 | + | |
125 | return modemFound | |
126 | - | return table.concat(outputTable, "") |
126 | + | |
127 | ||
128 | local function send(id, type, message) | |
129 | - | nshAPI.unpackAndSaveFile = function(path, data) |
129 | + | return rednet.send(id, packetConversion[type]..":;"..message) |
130 | - | local outputTable = {} |
130 | + | |
131 | - | for i=1, #data, 4 do |
131 | + | |
132 | - | local char1, char2, char3, char4 = string.byte(string.sub(data, i, i)), string.byte(string.sub(data, i + 1, i + 1)), string.byte(string.sub(data, i + 2, i + 2)), string.byte(string.sub(data, i + 3, i + 3)) |
132 | + | |
133 | - | table.insert(outputTable, bit.band(bit.bor(bit.blshift(char1, 2), bit.brshift(char2, 4)), 255)) |
133 | + | |
134 | - | table.insert(outputTable, bit.band(bit.bor(bit.blshift(char2, 4), bit.brshift(char3, 2)), 255)) |
134 | + | |
135 | - | table.insert(outputTable, bit.band(bit.bor(bit.blshift(char3, 6), char4), 255)) |
135 | + | |
136 | if time then listenTimeOut = os.startTimer(time) end | |
137 | - | --clean invalid bytes if marked |
137 | + | |
138 | - | if string.sub(data, #data, #data) == "=" then |
138 | + | |
139 | - | table.remove(outputTable) |
139 | + | |
140 | - | if string.sub(data, #data - 1, #data - 1) == "=" then |
140 | + | |
141 | - | table.remove(outputTable) |
141 | + | |
142 | sender, message = p1, p2 | |
143 | if id == sender and message then | |
144 | - | local handle = io.open(path, "wb") |
144 | + | |
145 | - | if handle then |
145 | + | |
146 | - | for i = 1, #outputTable do |
146 | + | |
147 | - | handle:write(outputTable[i]) |
147 | + | |
148 | - | if i % 10 == 0 then |
148 | + | |
149 | - | os.startTimer(0.1) |
149 | + | |
150 | - | os.pullEvent("timer") |
150 | + | |
151 | end | |
152 | ||
153 | - | handle:close() |
153 | + | |
154 | if not pType then return false end | |
155 | if pType == "textWrite" and value then | |
156 | term.write(value) | |
157 | elseif pType == "textClear" then | |
158 | term.clear() | |
159 | elseif pType == "textClearLine" then | |
160 | term.clearLine() | |
161 | elseif pType == "textGetCursorPos" then | |
162 | local x, y = term.getCursorPos() | |
163 | send(conn, "textInfo", math.floor(x)..","..math.floor(y)) | |
164 | elseif pType == "textCursorPos" then | |
165 | local x, y = string.match(value, "(%d+),(%d+)") | |
166 | term.setCursorPos(tonumber(x), tonumber(y)) | |
167 | elseif pType == "textBlink" then | |
168 | if value == "true" then | |
169 | term.setCursorBlink(true) | |
170 | else | |
171 | term.setCursorBlink(false) | |
172 | end | |
173 | elseif pType == "textGetSize" then | |
174 | x, y = term.getSize() | |
175 | send(conn, "textInfo", x..","..y) | |
176 | elseif pType == "textScroll" and value then | |
177 | term.scroll(tonumber(value)) | |
178 | elseif pType == "textIsColor" then | |
179 | send(conn, "textInfo", tostring(term.isColor())) | |
180 | - | textTable = "TT", |
180 | + | |
181 | value = tonumber(value) | |
182 | if (value == 1 or value == 32768) or term.isColor() then | |
183 | term.setTextColor(value) | |
184 | end | |
185 | elseif pType == "textBackground" and value then | |
186 | value = tonumber(value) | |
187 | if (value == 1 or value == 32768) or term.isColor() then | |
188 | term.setBackgroundColor(value) | |
189 | end | |
190 | end | |
191 | return | |
192 | end | |
193 | ||
194 | local function textRedirect (id) | |
195 | local textTable = {} | |
196 | textTable.id = id | |
197 | textTable.write = function(text) | |
198 | return send(textTable.id, "textWrite", text) | |
199 | end | |
200 | textTable.clear = function() | |
201 | return send(textTable.id, "textClear", "nil") | |
202 | end | |
203 | textTable.clearLine = function() | |
204 | - | TT = "textTable", |
204 | + | |
205 | end | |
206 | textTable.getCursorPos = function() | |
207 | send(textTable.id, "textGetCursorPos", "nil") | |
208 | local pType, message = awaitResponse(textTable.id, 2) | |
209 | if pType and pType == "textInfo" then | |
210 | local x, y = string.match(message, "(%d+),(%d+)") | |
211 | return tonumber(x), tonumber(y) | |
212 | end | |
213 | end | |
214 | textTable.setCursorPos = function(x, y) | |
215 | return send(textTable.id, "textCursorPos", math.floor(x)..","..math.floor(y)) | |
216 | end | |
217 | textTable.setCursorBlink = function(b) | |
218 | if b then | |
219 | return send(textTable.id, "textBlink", "true") | |
220 | - | local function send(id, pType, message) |
220 | + | |
221 | - | if pType and message then |
221 | + | |
222 | - | return rawSend(id, packetConversion[pType]..":;"..message) |
222 | + | |
223 | end | |
224 | textTable.getSize = function() | |
225 | send(textTable.id, "textGetSize", "nil") | |
226 | local pType, message = awaitResponse(textTable.id, 2) | |
227 | if pType and pType == "textInfo" then | |
228 | local x, y = string.match(message, "(%d+),(%d+)") | |
229 | return tonumber(x), tonumber(y) | |
230 | end | |
231 | end | |
232 | textTable.scroll = function(lines) | |
233 | return send(textTable.id, "textScroll", lines) | |
234 | end | |
235 | textTable.isColor = function() | |
236 | send(textTable.id, "textIsColor", "nil") | |
237 | local pType, message = awaitResponse(textTable.id, 2) | |
238 | if pType and pType == "textInfo" then | |
239 | if message == "true" then | |
240 | return true | |
241 | end | |
242 | end | |
243 | return false | |
244 | end | |
245 | textTable.isColour = textTable.isColor | |
246 | textTable.setTextColor = function(color) | |
247 | return send(textTable.id, "textColor", tostring(color)) | |
248 | end | |
249 | textTable.setTextColour = textTable.setTextColor | |
250 | textTable.setBackgroundColor = function(color) | |
251 | return send(textTable.id, "textBackground", tostring(color)) | |
252 | end | |
253 | textTable.setBackgroundColour = textTable.setBackgroundColor | |
254 | return textTable | |
255 | end | |
256 | ||
257 | local eventFilter = { | |
258 | key = true, | |
259 | - | local x, y = string.match(value, "(%-?%d+),(%-?%d+)") |
259 | + | |
260 | mouse_click = true, | |
261 | mouse_drag = true, | |
262 | mouse_scroll = true, | |
263 | } | |
264 | ||
265 | local function newSession() | |
266 | local path = "/shell" | |
267 | if #tArgs >= 2 and shell.resolveProgram(tArgs[2]) then path = shell.resolveProgram(tArgs[2]) end | |
268 | local sessionThread = coroutine.create(function() shell.run(path) end) | |
269 | return sessionThread | |
270 | end | |
271 | ||
272 | if #tArgs >= 1 and tArgs[1] == "host" then | |
273 | _G.nsh = nshAPI | |
274 | _G.nsh = nshAPI | |
275 | if not openModem() then return end | |
276 | local connInfo = {} | |
277 | connInfo.target = term.native | |
278 | local path = "/shell" | |
279 | if #tArgs >= 3 and shell.resolveProgram(tArgs[3]) then path = shell.resolveProgram(tArgs[3]) end | |
280 | connInfo.thread = coroutine.create(function() shell.run(path) end) | |
281 | connections.localShell = connInfo | |
282 | term.clear() | |
283 | term.setCursorPos(1,1) | |
284 | - | elseif pType == "textTable" then |
284 | + | |
285 | - | local linesTable = textutils.unserialize(value) |
285 | + | |
286 | - | for i=1, linesTable.sizeY do |
286 | + | |
287 | - | term.setCursorPos(1,i) |
287 | + | |
288 | - | local lineEnd = false |
288 | + | |
289 | - | local offset = 1 |
289 | + | |
290 | - | while not lineEnd do |
290 | + | |
291 | - | local textColorString = string.match(string.sub(linesTable.textColor[i], offset), string.sub(linesTable.textColor[i], offset, offset).."*") |
291 | + | |
292 | - | local backColorString = string.match(string.sub(linesTable.backColor[i], offset), string.sub(linesTable.backColor[i], offset, offset).."*") |
292 | + | |
293 | - | term.setTextColor(2 ^ tonumber(string.sub(textColorString, 1, 1), 16)) |
293 | + | |
294 | - | term.setBackgroundColor(2 ^ tonumber(string.sub(backColorString, 1, 1), 16)) |
294 | + | |
295 | - | term.write(string.sub(linesTable.text[i], offset, offset + math.min(#textColorString, #backColorString) - 1)) |
295 | + | |
296 | - | offset = offset + math.min(#textColorString, #backColorString) |
296 | + | |
297 | - | if offset > linesTable.sizeX then lineEnd = true end |
297 | + | |
298 | eventTable = textutils.unserialize(message) | |
299 | else | |
300 | - | term.setCursorPos(linesTable.cursorX, linesTable.cursorY) |
300 | + | |
301 | - | term.setCursorBlink(linesTable.cursorBlink) |
301 | + | |
302 | end | |
303 | if not connections[conn].filter or eventTable[1] == connections[conn].filter then | |
304 | connections[conn].filter = nil | |
305 | term.redirect(connections[conn].target) | |
306 | - | local function textRedirect(id) |
306 | + | passback = {coroutine.resume(connections[conn].thread, unpack(eventTable))} |
307 | if passback[1] and passback[2] then | |
308 | connections[conn].filter = passback[2] | |
309 | end | |
310 | if coroutine.status(connections[conn].thread) == "dead" then | |
311 | send(conn, "close", "disconnect") | |
312 | table.remove(connections, conn) | |
313 | end | |
314 | term.restore() | |
315 | end | |
316 | elseif packetType == "query" then | |
317 | --reset connection | |
318 | connections[conn].status = "open" | |
319 | connections[conn].target = textRedirect(conn) | |
320 | connections[conn].thread = newSession() | |
321 | send(conn, "response", "OK") | |
322 | - | local x, y = string.match(message, "(%-?%d+),(%-?%d+)") |
322 | + | term.redirect(connections[conn].target) |
323 | coroutine.resume(connections[conn].thread) | |
324 | term.restore() | |
325 | elseif packetType == "close" then | |
326 | table.remove(connections, conn) | |
327 | send(conn, "close", "disconnect") | |
328 | --close connection | |
329 | else | |
330 | --we got a packet, have an open connection, but despite it being in the conversion table, don't handle it ourselves. Send it onward. | |
331 | if not connections[conn].filter or eventTable[1] == connections[conn].filter then | |
332 | connections[conn].filter = nil | |
333 | term.redirect(connections[conn].target) | |
334 | passback = {coroutine.resume(connections[conn].thread, unpack(event))} | |
335 | if passback[2] then | |
336 | connections[conn].filter = passback[2] | |
337 | end | |
338 | if coroutine.status(connections[conn].thread) == "dead" then | |
339 | send(conn, "close", "disconnect") | |
340 | table.remove(connections, conn) | |
341 | end | |
342 | term.restore() | |
343 | end | |
344 | end | |
345 | elseif packetType ~= "query" then | |
346 | --usually, we would send a disconnect here, but this prevents one from hosting nsh and connecting to other computers. Pass these to all shells as well. | |
347 | for cNum, cInfo in pairs(connections) do | |
348 | if not cInfo.filter or event[1] == cInfo.filter then | |
349 | cInfo.filter = nil | |
350 | term.redirect(cInfo.target) | |
351 | passback = {coroutine.resume(cInfo.thread, unpack(event))} | |
352 | if passback[2] then | |
353 | cInfo.filter = passback[2] | |
354 | end | |
355 | term.restore() | |
356 | end | |
357 | end | |
358 | else | |
359 | --open new connection | |
360 | local connInfo = {} | |
361 | connInfo.status = "open" | |
362 | connInfo.target = textRedirect(conn) | |
363 | connInfo.thread = newSession() | |
364 | send(conn, "response", "OK") | |
365 | connections[conn] = connInfo | |
366 | term.redirect(connInfo.target) | |
367 | coroutine.resume(connInfo.thread) | |
368 | term.restore() | |
369 | - | local function getServerID(server) |
369 | + | |
370 | - | if tonumber(server) then |
370 | + | |
371 | - | return tonumber(server) |
371 | + | |
372 | - | elseif term.current then |
372 | + | |
373 | - | return rednet.lookup("tror", args[1]) |
373 | + | if not cInfo.filter or event[1] == cInfo.filter then |
374 | cInfo.filter = nil | |
375 | term.redirect(cInfo.target) | |
376 | passback = {coroutine.resume(cInfo.thread, unpack(event))} | |
377 | - | local function resumeThread(conn, event) |
377 | + | if passback[2] then |
378 | - | local cInfo = connections[conn] |
378 | + | cInfo.filter = passback[2] |
379 | - | if connections[conn] and (not connections[conn].filter or event[1] == connections[conn].filter) then |
379 | + | |
380 | - | connections[conn].filter = nil |
380 | + | term.restore() |
381 | - | local _oldTerm = term.redirect(connections[conn].target) |
381 | + | |
382 | - | local passback = {coroutine.resume(connections[conn].thread, unpack(event))} |
382 | + | |
383 | - | if passback[1] and passback[2] then |
383 | + | |
384 | - | connections[conn].filter = passback[2] |
384 | + | |
385 | --user interaction. | |
386 | - | if coroutine.status(connections[conn].thread) == "dead" then |
386 | + | |
387 | - | send(conn, "close", "disconnect") |
387 | + | |
388 | - | connections[conn] = false |
388 | + | |
389 | if cNum ~= "localShell" then | |
390 | - | if _oldTerm then |
390 | + | |
391 | - | term.redirect(_oldTerm) |
391 | + | |
392 | end | |
393 | - | term.restore() |
393 | + | |
394 | end | |
395 | - | if connections[conn] and conn ~= "localShell" and framebuffer and connections[conn].target.changed then |
395 | + | |
396 | - | send(conn, "textTable", textutils.serialize(connections[conn].target.buffer)) |
396 | + | _G.nsh = nil |
397 | - | connections[conn].target.changed = false |
397 | + | |
398 | else | |
399 | --dispatch all other events to all shells | |
400 | for cNum, cInfo in pairs(connections) do | |
401 | if not cInfo.filter or event[1] == cInfo.filter then | |
402 | cInfo.filter = nil | |
403 | term.redirect(cInfo.target) | |
404 | passback = {coroutine.resume(cInfo.thread, unpack(event))} | |
405 | if passback[2] then | |
406 | cInfo.filter = passback[2] | |
407 | end | |
408 | term.restore() | |
409 | end | |
410 | - | local function newSession(conn, x, y, color) |
410 | + | |
411 | - | local session = {} |
411 | + | |
412 | - | local path = "/rom/programs/shell" |
412 | + | |
413 | - | if #args >= 2 and shell.resolveProgram(args[2]) then path = shell.resolveProgram(args[2]) end |
413 | + | |
414 | - | session.thread = coroutine.create(function() shell.run(path) end) |
414 | + | elseif #tArgs == 1 and nsh and nsh.getRemoteID() then |
415 | - | if framebuffer then |
415 | + | |
416 | - | local target = {} |
416 | + | |
417 | - | local _target = framebuffer.new(x, y, color) |
417 | + | |
418 | - | for k, v in pairs(_target) do |
418 | + | |
419 | - | if type(k) == "string" and type(v) == "function" then |
419 | + | |
420 | - | target[k] = function(...) |
420 | + | |
421 | - | target.changed = true |
421 | + | |
422 | - | return _target[k](...) |
422 | + | |
423 | end | |
424 | local fileTransferState = nil | |
425 | - | target[k] = _target[k] |
425 | + | |
426 | local serverNum = tonumber(tArgs[1]) | |
427 | send(serverNum, "query", "connect") | |
428 | - | session.target = target |
428 | + | |
429 | if pType ~= "response" then | |
430 | - | session.target = textRedirect(conn) |
430 | + | |
431 | return | |
432 | - | session.status = "open" |
432 | + | |
433 | - | _oldTerm = term.redirect(session.target) |
433 | + | |
434 | - | coroutine.resume(session.thread) |
434 | + | |
435 | - | if _oldTerm then |
435 | + | |
436 | - | term.redirect(_oldTerm) |
436 | + | |
437 | local clientID = nsh.getRemoteID() | |
438 | - | term.restore() |
438 | + | local serverID = tonumber(tArgs[1]) |
439 | while true do | |
440 | - | if framebuffer then |
440 | + | |
441 | - | send(conn, "textTable", textutils.serialize(session.target.buffer)) |
441 | + | |
442 | - | session.target.changed = false |
442 | + | |
443 | if event[2] == serverID and string.sub(event[3], 1, 2) == "SC" then break end | |
444 | - | return session |
444 | + | |
445 | end | |
446 | elseif eventFilter[event[1]] then | |
447 | - | if #args >= 1 and args[1] == "host" then |
447 | + | |
448 | end | |
449 | end | |
450 | - | if term.current then |
450 | + | |
451 | - | if args[4] then |
451 | + | |
452 | - | rednet.host("tror", args[4]) |
452 | + | |
453 | - | elseif os.getComputerLabel() then |
453 | + | |
454 | - | rednet.host("tror", os.getComputerLabel()) |
454 | + | |
455 | elseif #tArgs == 1 then --either no server running or we are the local shell on the server. | |
456 | - | print("No label or hostname provided!") |
456 | + | local serverNum = tonumber(tArgs[1]) |
457 | if nsh then | |
458 | local conns = nsh.getRemoteConnections() | |
459 | for i = 1, #conns do | |
460 | if conns[i] == serverNum then | |
461 | - | connInfo.target = term.current and term.current() or term.native |
461 | + | |
462 | - | local path = "/rom/programs/shell" |
462 | + | |
463 | - | if #args >= 3 and shell.resolveProgram(args[3]) then path = shell.resolveProgram(args[3]) end |
463 | + | |
464 | end | |
465 | end | |
466 | local fileTransferState = nil | |
467 | local fileData = nil | |
468 | if not openModem() then return end | |
469 | send(serverNum, "query", "connect") | |
470 | local pType, message = awaitResponse(serverNum, 2) | |
471 | if pType ~= "response" then | |
472 | print("Connection failed.") | |
473 | - | if type(event[3]) == "string" and packetConversion[string.sub(event[3], 1, 2)] then |
473 | + | |
474 | else | |
475 | if nsh then nshAPI = nsh end | |
476 | if nshAPI.connList and nshAPI.connList.localShell then nshAPI.connList.localShell.outbound = serverNum end | |
477 | nshAPI.serverNum = serverNum | |
478 | nshAPI.clientCapabilities = "-fileTransfer-extensions-" | |
479 | term.clear() | |
480 | term.setCursorPos(1,1) | |
481 | end | |
482 | ||
483 | while true do | |
484 | event = {os.pullEventRaw()} | |
485 | if event[1] == "rednet_message" and event[2] == serverNum then | |
486 | if packetConversion[string.sub(event[3], 1, 2)] then | |
487 | - | resumeThread(conn, eventTable) |
487 | + | |
488 | message = string.match(event[3], ";(.*)") | |
489 | - | local connType, color, x, y = string.match(message, "(%a+):(%a+);(%d+),(%d+)") |
489 | + | |
490 | - | if connType == "connect" or (connType == "resume" and (not framebuffer)) then |
490 | + | |
491 | - | --reset connection |
491 | + | |
492 | - | send(conn, "response", "OK") |
492 | + | |
493 | - | connections[conn] = newSession(conn, tonumber(x), tonumber(y), color == "true") |
493 | + | |
494 | - | elseif connType == "resume" and connections[conn] and tonumber(x) == connections[conn].target.buffer.sizeX and tonumber(y) == connections[conn].target.buffer.sizeY then |
494 | + | |
495 | - | --restore connection |
495 | + | |
496 | - | send(conn, "response", "OK") |
496 | + | |
497 | - | send(conn, "textTable", textutils.serialize(connections[conn].target.buffer)) |
497 | + | if fs.exists(message) then |
498 | send(serverNum, "fileHeader", message) | |
499 | local file = io.open(message, "r") | |
500 | - | connections[conn] = nil |
500 | + | if file then |
501 | send(serverNum, "fileData", file:read("*a")) | |
502 | file:close() | |
503 | end | |
504 | else | |
505 | - | resumeThread(conn, event) |
505 | + | |
506 | end | |
507 | send(serverNum, "fileEnd", "end") | |
508 | elseif packetType == "fileSend" then | |
509 | --receive a file from the server, but don't overwrite existing files. | |
510 | - | resumeThread(cNum, event) |
510 | + | if not fs.exists(message) then |
511 | fileTransferState = "receive_wait:"..message | |
512 | send(serverNum, "fileResponse", "ok") | |
513 | fileData = "" | |
514 | else | |
515 | - | local color, x, y = string.match(message, "connect:(%a+);(%d+),(%d+)") |
515 | + | |
516 | - | local connInfo = newSession(conn, tonumber(x), tonumber(y), color == "true") |
516 | + | |
517 | elseif packetType == "fileHeader" then | |
518 | if message == "fileNotFound" then | |
519 | fileTransferState = nil | |
520 | end | |
521 | elseif packetType == "fileData" then | |
522 | - | resumeThread(cNum, event) |
522 | + | |
523 | fileData = fileData..message | |
524 | end | |
525 | elseif packetType == "fileEnd" then | |
526 | if fileTransferState and string.match(fileTransferState, "(.-):") == "receive_wait" then | |
527 | local file = io.open(string.match(fileTransferState, ":(.*)"), "w") | |
528 | if file then | |
529 | file:write(fileData) | |
530 | file:close() | |
531 | end | |
532 | fileTransferState = nil | |
533 | end | |
534 | elseif packetType == "close" then | |
535 | if term.isColor() then | |
536 | term.setBackgroundColor(colors.black) | |
537 | term.setTextColor(colors.white) | |
538 | end | |
539 | - | resumeThread(cNum, event) |
539 | + | |
540 | term.setCursorPos(1, 1) | |
541 | print("Connection closed by server.") | |
542 | nshAPI.serverNum = nil | |
543 | if nshAPI.connList and nshAPI.connList.localShell then nshAPI.connList.localShell.outbound = nil end | |
544 | - | elseif #args <= 2 and nsh and nsh.getRemoteID() then |
544 | + | |
545 | end | |
546 | end | |
547 | elseif event[1] == "mouse_click" or event[1] == "mouse_drag" or event[1] == "mouse_scroll" or event[1] == "key" or event[1] == "char" then | |
548 | --pack up event | |
549 | send(serverNum, "event", textutils.serialize(event)) | |
550 | elseif event[1] == "terminate" then | |
551 | nshAPI.serverNum = nil | |
552 | if nshAPI.localShell then nshAPI.localShell.outbound = nil end | |
553 | return | |
554 | end | |
555 | end | |
556 | - | local serverNum = getServerID(args[1]) |
556 | + | |
557 | - | if not serverNum then |
557 | + | print("Usage: nsh <serverID>") |
558 | - | print("Server Not Found") |
558 | + | print(" nsh host [remote [local]]") |
559 | end |