changeset 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 2880cc4372bd
children e59d68082664
files rlgterm.js rlgwebd.js
diffstat 2 files changed, 88 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/rlgterm.js	Thu Jul 12 07:58:23 2012 -0700
+++ b/rlgterm.js	Thu Jul 12 22:16:15 2012 -0700
@@ -82,7 +82,9 @@
   lname: null,
   lcred: null,
   /* Whether the game is being played or just watched. */
-  playing: false
+  playing: false,
+  /* WebSocket for communication */
+  sock: null
 };
 
 /* The interval ID for checking the status of current games. */
@@ -655,6 +657,10 @@
 function startwatching(gamenumber) {
   if (session.id != null)
     return;
+  if (WebSocket) {
+    wsWatch(gamenumber);
+    return;
+  }
   var wmsg = {"n": Number(gamenumber)};
   var req = new XMLHttpRequest();
   req.onerror = errHandler;
@@ -692,6 +698,27 @@
   return watcher;
 }
 
+function wsWatch(gamenumber) {
+  var sockurl = "ws://localhost:8080/watch/" + String(gamenumber);
+  var ws = new WebSocket(sockurl);
+  ws.onopen = function (event) {
+    session.id = true;
+    session.sock = ws;
+    message("You are now watching game #" + gamenumber + ".");
+    setmode("watch");
+  };
+  ws.onmessage = function (event) {
+    var msgObject = JSON.parse(event.data);
+    if (msgObject.t == 'd') {
+      writeData(msgObject.d);
+    }
+  };
+  ws.onclose = function (event) {
+    session.sock = null;
+    gameover();
+  };
+}
+
 function formreg(ev) {
   ev.preventDefault();
   if (session.id != null)
@@ -775,6 +802,10 @@
 function stop() {
   if (!session.id)
     return;
+  if (session.sock) {
+    session.sock.close();
+    return;
+  }
   var req = new XMLHttpRequest();
   req.onerror = errHandler;
   req.onreadystatechange = function () {
--- 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'); 
 });