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 |
