diff 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
line wrap: on
line diff
--- a/rlgwebd.js	Thu Jul 12 07:58:23 2012 -0700
+++ b/rlgwebd.js	Thu Jul 12 22:16:15 2012 -0700
@@ -11,6 +11,7 @@
 var child_process = require('child_process');
 var daemon = require(path.join(localModules, "daemon"));
 var pty = require(path.join(localModules, "pty.js"));
+var WebSocketServer = require(path.join(localModules, "websocket")).server;
 
 /* Configuration variables */
 // These first two files are NOT in the chroot.
@@ -130,6 +131,8 @@
    * with a complete screen. */
   this.framebuf = new Buffer(1024);
   this.frameoff = 0;
+  /* List of WebSockets watching the game. */
+  this.watchsocks = [];
   logins[lkey].sessions.push(this.sessid);
   /* END setup */
   function ttyrec_chunk(datastr) {
@@ -168,6 +171,22 @@
   this.write = function(data) {
     this.term.write(data);
   };
+  // WebSocket watchers.
+  this.addWS = function (conn) {
+    this.watchsocks.push(conn);
+  };
+  this.removeWS = function (conn) {
+    var i = this.watchsocks.indexOf(conn);
+    if (i >= 0) {
+      if (conn.connected)
+        conn.close();
+      this.watchsocks.splice(i, 1);
+      return true;
+    }
+    else
+      return false;
+  };
+  // Teardown.
   this.term.on("exit", function () {
     fs.unlink(ss.lock);
     ss.record.end();
@@ -354,6 +373,27 @@
   this.session = new TermSession(gamename, lkey, dims, handlers);
 }
 
+// Also known as WebSocketAndTermSessionClosureGlueFactory
+function wsWatcher(conn, session) {
+  var ss = this; // is this even needed?
+  var dataH = function(buf) {
+    conn.sendUTF(JSON.stringify({"t": "d", "d": buf.toString("hex")}));
+  };
+  var exitH = function() {
+    if (conn.connected)
+      conn.close();
+  }
+  session.on('data', dataH);
+  session.on('exit', exitH);
+  conn.on('close', function(code, desc) {
+    session.removeListener('data', dataH);
+    session.removeListener('exit', exitH);
+    tslog("A WebSocket watcher has left game %d", session.sessid);
+  });
+  conn.sendUTF(JSON.stringify({"t": "d",
+        "d": session.framebuf.toString("hex", 0, session.frameoff)}));
+}
+
 /* Some functions which check whether a player is currently playing or 
  * has a saved game.  Maybe someday they will provide information on 
  * the game. */
@@ -1029,6 +1069,18 @@
 
 }
 
+function wsHandler(wsRequest) {
+  var urlmatch = wsRequest.resource.match(/^\/watch\/([0-9]*)$/);
+  if (urlmatch !== null && urlmatch[1] && Number(urlmatch[1]) in sessions) {
+    var tsession = sessions[Number(urlmatch[1])];
+    var conn = wsRequest.accept(null, wsRequest.origin);
+    new wsWatcher(conn, tsession);
+    tslog("Game %d is being watched via WebSockets", tsession.sessid);
+  }
+  else
+    wsRequest.reject(404, errorcodes[7]);
+}
+
 function shutdown () {
   httpServer.close();
   httpServer.removeAllListeners('request');
@@ -1066,6 +1118,7 @@
 }
 
 var httpServer; // declare here so shutdown() can find it
+var wsServer;
 
 /* This could be nonblocking, but nothing else can start yet anyway. */
 if (fs.existsSync(ctlsocket)) {
@@ -1106,5 +1159,8 @@
   httpServer.listen(httpPort);
   tslog('rlgwebd running on port %d', httpPort); 
   setInterval(reaper, playtimeout / 4);
+  wsServer = new WebSocketServer({"httpServer": httpServer});
+  wsServer.on("request", wsHandler);
+  tslog('WebSockets are online'); 
 });