Mercurial > hg > rlgwebd
comparison rlgwebd.js @ 100:3dbfdaf62623
RLG-Web: begin converting to WebSockets.
Use WebSockets for watching, if the browser supports it. Functionality
is not complete yet.
author | John "Elwin" Edwards <elwin@sdf.org> |
---|---|
date | Thu, 12 Jul 2012 22:16:15 -0700 |
parents | 597e9477b8ae |
children | e59d68082664 |
comparison
equal
deleted
inserted
replaced
99:2880cc4372bd | 100:3dbfdaf62623 |
---|---|
9 var fs = require('fs'); | 9 var fs = require('fs'); |
10 var events = require('events'); | 10 var events = require('events'); |
11 var child_process = require('child_process'); | 11 var child_process = require('child_process'); |
12 var daemon = require(path.join(localModules, "daemon")); | 12 var daemon = require(path.join(localModules, "daemon")); |
13 var pty = require(path.join(localModules, "pty.js")); | 13 var pty = require(path.join(localModules, "pty.js")); |
14 var WebSocketServer = require(path.join(localModules, "websocket")).server; | |
14 | 15 |
15 /* Configuration variables */ | 16 /* Configuration variables */ |
16 // These first two files are NOT in the chroot. | 17 // These first two files are NOT in the chroot. |
17 var ctlsocket = "/var/local/rlgwebd/ctl"; | 18 var ctlsocket = "/var/local/rlgwebd/ctl"; |
18 var logfile = "/var/local/rlgwebd/log"; | 19 var logfile = "/var/local/rlgwebd/log"; |
128 this.record = fs.createWriteStream(ttyrec, { mode: 0664 }); | 129 this.record = fs.createWriteStream(ttyrec, { mode: 0664 }); |
129 /* Holds the output since the last screen clear, so watchers can begin | 130 /* Holds the output since the last screen clear, so watchers can begin |
130 * with a complete screen. */ | 131 * with a complete screen. */ |
131 this.framebuf = new Buffer(1024); | 132 this.framebuf = new Buffer(1024); |
132 this.frameoff = 0; | 133 this.frameoff = 0; |
134 /* List of WebSockets watching the game. */ | |
135 this.watchsocks = []; | |
133 logins[lkey].sessions.push(this.sessid); | 136 logins[lkey].sessions.push(this.sessid); |
134 /* END setup */ | 137 /* END setup */ |
135 function ttyrec_chunk(datastr) { | 138 function ttyrec_chunk(datastr) { |
136 var ts = new Date(); | 139 var ts = new Date(); |
137 var buf = new Buffer(datastr); | 140 var buf = new Buffer(datastr); |
166 this.frameoff += chunk.length; | 169 this.frameoff += chunk.length; |
167 }; | 170 }; |
168 this.write = function(data) { | 171 this.write = function(data) { |
169 this.term.write(data); | 172 this.term.write(data); |
170 }; | 173 }; |
174 // WebSocket watchers. | |
175 this.addWS = function (conn) { | |
176 this.watchsocks.push(conn); | |
177 }; | |
178 this.removeWS = function (conn) { | |
179 var i = this.watchsocks.indexOf(conn); | |
180 if (i >= 0) { | |
181 if (conn.connected) | |
182 conn.close(); | |
183 this.watchsocks.splice(i, 1); | |
184 return true; | |
185 } | |
186 else | |
187 return false; | |
188 }; | |
189 // Teardown. | |
171 this.term.on("exit", function () { | 190 this.term.on("exit", function () { |
172 fs.unlink(ss.lock); | 191 fs.unlink(ss.lock); |
173 ss.record.end(); | 192 ss.record.end(); |
174 ss.emit('exit'); | 193 ss.emit('exit'); |
175 var id = ss.sessid; | 194 var id = ss.sessid; |
350 ss.alive = false; | 369 ss.alive = false; |
351 ss.sendQ.push({"t": "q"}); | 370 ss.sendQ.push({"t": "q"}); |
352 } | 371 } |
353 var handlers = {'open': openH, 'data': dataH, 'exit': exitH}; | 372 var handlers = {'open': openH, 'data': dataH, 'exit': exitH}; |
354 this.session = new TermSession(gamename, lkey, dims, handlers); | 373 this.session = new TermSession(gamename, lkey, dims, handlers); |
374 } | |
375 | |
376 // Also known as WebSocketAndTermSessionClosureGlueFactory | |
377 function wsWatcher(conn, session) { | |
378 var ss = this; // is this even needed? | |
379 var dataH = function(buf) { | |
380 conn.sendUTF(JSON.stringify({"t": "d", "d": buf.toString("hex")})); | |
381 }; | |
382 var exitH = function() { | |
383 if (conn.connected) | |
384 conn.close(); | |
385 } | |
386 session.on('data', dataH); | |
387 session.on('exit', exitH); | |
388 conn.on('close', function(code, desc) { | |
389 session.removeListener('data', dataH); | |
390 session.removeListener('exit', exitH); | |
391 tslog("A WebSocket watcher has left game %d", session.sessid); | |
392 }); | |
393 conn.sendUTF(JSON.stringify({"t": "d", | |
394 "d": session.framebuf.toString("hex", 0, session.frameoff)})); | |
355 } | 395 } |
356 | 396 |
357 /* Some functions which check whether a player is currently playing or | 397 /* Some functions which check whether a player is currently playing or |
358 * has a saved game. Maybe someday they will provide information on | 398 * has a saved game. Maybe someday they will provide information on |
359 * the game. */ | 399 * the game. */ |
1027 } | 1067 } |
1028 req.on('end', respond); | 1068 req.on('end', respond); |
1029 | 1069 |
1030 } | 1070 } |
1031 | 1071 |
1072 function wsHandler(wsRequest) { | |
1073 var urlmatch = wsRequest.resource.match(/^\/watch\/([0-9]*)$/); | |
1074 if (urlmatch !== null && urlmatch[1] && Number(urlmatch[1]) in sessions) { | |
1075 var tsession = sessions[Number(urlmatch[1])]; | |
1076 var conn = wsRequest.accept(null, wsRequest.origin); | |
1077 new wsWatcher(conn, tsession); | |
1078 tslog("Game %d is being watched via WebSockets", tsession.sessid); | |
1079 } | |
1080 else | |
1081 wsRequest.reject(404, errorcodes[7]); | |
1082 } | |
1083 | |
1032 function shutdown () { | 1084 function shutdown () { |
1033 httpServer.close(); | 1085 httpServer.close(); |
1034 httpServer.removeAllListeners('request'); | 1086 httpServer.removeAllListeners('request'); |
1035 ctlServer.close(); | 1087 ctlServer.close(); |
1036 tslog("Shutting down..."); | 1088 tslog("Shutting down..."); |
1064 tslog("Not running as root, cannot chroot."); | 1116 tslog("Not running as root, cannot chroot."); |
1065 process.exit(1); | 1117 process.exit(1); |
1066 } | 1118 } |
1067 | 1119 |
1068 var httpServer; // declare here so shutdown() can find it | 1120 var httpServer; // declare here so shutdown() can find it |
1121 var wsServer; | |
1069 | 1122 |
1070 /* This could be nonblocking, but nothing else can start yet anyway. */ | 1123 /* This could be nonblocking, but nothing else can start yet anyway. */ |
1071 if (fs.existsSync(ctlsocket)) { | 1124 if (fs.existsSync(ctlsocket)) { |
1072 fs.unlinkSync(ctlsocket); | 1125 fs.unlinkSync(ctlsocket); |
1073 } | 1126 } |
1104 } | 1157 } |
1105 httpServer = http.createServer(webHandler); | 1158 httpServer = http.createServer(webHandler); |
1106 httpServer.listen(httpPort); | 1159 httpServer.listen(httpPort); |
1107 tslog('rlgwebd running on port %d', httpPort); | 1160 tslog('rlgwebd running on port %d', httpPort); |
1108 setInterval(reaper, playtimeout / 4); | 1161 setInterval(reaper, playtimeout / 4); |
1162 wsServer = new WebSocketServer({"httpServer": httpServer}); | |
1163 wsServer.on("request", wsHandler); | |
1164 tslog('WebSockets are online'); | |
1109 }); | 1165 }); |
1110 | 1166 |