diff rlgwebd.js @ 184:ecedc6f7e4ac

Move TermSession and DglSession common code into a base class.
author John "Elwin" Edwards
date Mon, 19 Jan 2015 14:44:27 -0500
parents db2f5ab112e9
children bbfda4a4eb7f
line wrap: on
line diff
--- a/rlgwebd.js	Mon Jan 19 08:32:29 2015 -0500
+++ b/rlgwebd.js	Mon Jan 19 14:44:27 2015 -0500
@@ -66,7 +66,66 @@
 var allowlogin = true;
 var gamemux = new events.EventEmitter();
 
-/* TODO move TermSession and DglSession methods into the prototypes. */
+/* A base class.  TermSession and DglSession inherit from it. */
+function BaseGame() {
+  /* Games subclass EventEmitter, though there are few listeners. */
+  events.EventEmitter.call(this);
+  /* Array of watching WebSockets. */
+  this.watchers = [];
+  /* replaybuf holds the output since the last screen clear, so watchers can
+   * begin with a complete screen. replaylen is the number of bytes stored. */
+  this.replaybuf = new Buffer(1024);
+  this.replaylen = 0;
+  /* Time of last activity. */
+  this.lasttime = new Date();
+}
+BaseGame.prototype = new events.EventEmitter();
+
+BaseGame.prototype.tag = function () {
+  if (this.pname === undefined || this.gname === undefined)
+    return "";
+  return this.gname + "/" + this.pname;
+};
+
+BaseGame.prototype.framepush = function(chunk) {
+  /* If this chunk resets the screen, discard what preceded it. */
+  if (isclear(chunk)) {
+    this.replaybuf = new Buffer(1024);
+    this.replaylen = 0;
+  }
+  /* Make sure there's space. */
+  while (this.replaybuf.length < chunk.length + this.replaylen) {
+    var nbuf = new Buffer(this.replaybuf.length * 2);
+    this.replaybuf.copy(nbuf, 0, 0, this.replaylen);
+    this.replaybuf = nbuf;
+    if (this.replaybuf.length > 65536) {
+      tslog("Warning: %s frame buffer at %d bytes", this.tag(), 
+              this.replaybuf.length);
+    }
+  }
+  chunk.copy(this.replaybuf, this.replaylen);
+  this.replaylen += chunk.length;
+};
+
+/* Adds a watcher. */
+BaseGame.prototype.attach = function (wsReq) {
+  var conn = wsReq.accept(null, wsReq.origin);
+  conn.sendUTF(JSON.stringify({
+          "t": "w", "w": this.w, "h": this.h, "p": this.pname, "g": this.gname
+  }));
+  conn.sendUTF(JSON.stringify({"t": "d",
+      "d": this.replaybuf.toString("hex", 0, this.replaylen)}));
+  conn.on('close', this.detach.bind(this, conn));
+  this.watchers.push(conn);
+};
+
+BaseGame.prototype.detach = function (socket) {
+  var n = this.watchers.indexOf(socket);
+  if (n >= 0) {
+    this.watchers.splice(n, 1);
+    tslog("A WebSocket watcher has left game %s", this.tag());
+  }
+};
 
 /* Constructor.  A TermSession handles a pty and the game running on it.
  *   gname: (String) Name of the game to launch.
@@ -78,8 +137,7 @@
  *   "exit": Child terminated. Parameters: none
  */
 function TermSession(gname, pname, wsReq) {
-  /* Subclass EventEmitter to do the hard work. */
-  events.EventEmitter.call(this);
+  BaseGame.call(this);
   /* Don't launch anything that's not a real game. */
   if (gname in games) {
     this.game = games[gname];
@@ -113,7 +171,6 @@
   sessions[this.gname + "/" + this.pname] = this;
   gamemux.emit('begin', this.gname, this.pname, 'rlg');
   /* Set up the lockfile and ttyrec */
-  this.lasttime = new Date();
   var ts = timestamp(this.lasttime);
   var progressdir = path.join("/dgldir/inprogress", this.gname);
   this.lock = path.join(progressdir, this.pname + ":node:" + ts + ".ttyrec");
@@ -122,16 +179,10 @@
   var ttyrec = path.join("/dgldir/ttyrec", this.pname, this.gname, 
                ts + ".ttyrec");
   this.record = fs.createWriteStream(ttyrec, { mode: 0664 });
-  /* Holds the output since the last screen clear, so watchers can begin
-   * with a complete screen. */
-  this.framebuf = new Buffer(1024);
-  this.frameoff = 0;
   /* The player's WebSocket and its handlers. */
   this.playerconn = wsReq.accept(null, wsReq.origin);
   this.playerconn.on('message', this.input_msg.bind(this));
   this.playerconn.on('close', this.close.bind(this));
-  /* Array for watcher connections. */
-  this.watchers = [];
   /* Send initial data. */
   this.playerconn.sendUTF(JSON.stringify({"t": "s", "w": this.w, "h": this.h, 
                "p": this.pname, "g": this.gname}));
@@ -147,33 +198,7 @@
   this.term.on("data", this.write_ttyrec.bind(this));
   this.term.on("exit", this.destroy.bind(this));
 }
-TermSession.prototype = new events.EventEmitter();
-
-TermSession.prototype.tag = function () {
-  if (this.pname === undefined || this.gname === undefined)
-    return "";
-  return this.gname + "/" + this.pname;
-};
-
-TermSession.prototype.framepush = function(chunk) {
-  /* If this chunk resets the screen, discard what preceded it. */
-  if (isclear(chunk)) {
-    this.framebuf = new Buffer(1024);
-    this.frameoff = 0;
-  }
-  /* Make sure there's space. */
-  while (this.framebuf.length < chunk.length + this.frameoff) {
-    var nbuf = new Buffer(this.framebuf.length * 2);
-    this.framebuf.copy(nbuf, 0, 0, this.frameoff);
-    this.framebuf = nbuf;
-    if (this.framebuf.length > 65536) {
-      tslog("Warning: Game %s frame buffer at %d bytes", this.tag(), 
-              this.framebuf.length);
-    }
-  }
-  chunk.copy(this.framebuf, this.frameoff);
-  this.frameoff += chunk.length;
-};
+TermSession.prototype = new BaseGame();
 
 /* Currently this also sends to the player and any watchers. */
 TermSession.prototype.write_ttyrec = function (datastr) {
@@ -244,29 +269,9 @@
   tslog("Game %s ended.", tag);
 };
 
-/* Adds a watcher. */
-TermSession.prototype.attach = function (wsReq) {
-  var conn = wsReq.accept(null, wsReq.origin);
-  conn.sendUTF(JSON.stringify({
-          "t": "w", "w": this.w, "h": this.h, "p": this.pname, "g": this.gname
-  }));
-  conn.sendUTF(JSON.stringify({"t": "d",
-      "d": this.framebuf.toString("hex", 0, this.frameoff)}));
-  conn.on('close', this.detach.bind(this, conn));
-  this.watchers.push(conn);
-};
-
-TermSession.prototype.detach = function (socket) {
-  var n = this.watchers.indexOf(socket);
-  if (n >= 0) {
-    this.watchers.splice(n, 1);
-    tslog("A WebSocket watcher has left game %s", this.tag());
-  }
-};
-
 function DglSession(filename) {
   var ss = this;
-  events.EventEmitter.call(this);
+  BaseGame.call(this);
   var pathcoms = filename.split('/');
   this.gname = pathcoms[pathcoms.length - 2];
   if (!(this.gname in games)) {
@@ -282,27 +287,6 @@
    * getting into a race. */
   this.reading = false;
   this.rpos = 0;
-  this.framebuf = new Buffer(1024);
-  this.frameoff = 0;
-  this.framepush = function(chunk) {
-    /* If this chunk resets the screen, discard what preceded it. */
-    if (isclear(chunk)) {
-      this.framebuf = new Buffer(1024);
-      this.frameoff = 0;
-    }
-    /* Make sure there's space. */
-    while (this.framebuf.length < chunk.length + this.frameoff) {
-      var nbuf = new Buffer(this.framebuf.length * 2);
-      this.framebuf.copy(nbuf, 0, 0, this.frameoff);
-      this.framebuf = nbuf;
-      if (this.framebuf.length > 65536) {
-        tslog("Warning: DGL %s frame buffer at %d bytes", this.tag(), 
-                this.framebuf.length);
-      }
-    }
-    chunk.copy(this.framebuf, this.frameoff);
-    this.frameoff += chunk.length;
-  };
   this.readchunk = function () {
     if (this.reading)
       return;
@@ -375,9 +359,6 @@
       }
     });
   });
-  this.tag = function () {
-    return this.gname + "/" + this.pname;
-  };
   this.close = function () {
     this.recwatcher.close()
     /* Ensure all data is handled before quitting. */
@@ -393,29 +374,8 @@
     gamemux.emit('end', this.gname, this.pname);
     tslog("DGL %s: closed", ss.tag());
   };
-  this.watchers = [];
-  /* Attach a watcher. */
-  this.attach = function (wsReq) {
-    var conn = wsReq.accept(null, wsReq.origin);
-    conn.sendUTF(JSON.stringify({
-            "t": "w", "w": this.w, "h": this.h, "p": this.pname, 
-            "g": this.gname
-    }));
-    conn.sendUTF(JSON.stringify({"t": "d",
-        "d": this.framebuf.toString("hex", 0, this.frameoff)}));
-    conn.on('close', function () {
-      /* 'this' is the connection when triggered */
-      var n = ss.watchers.indexOf(this);
-      if (n >= 0) {
-        ss.watchers.splice(n, 1);
-        tslog("A WebSocket watcher has left DGL game %s", ss.tag());
-      }
-    });
-    this.watchers.push(conn);
-  };
-  this.lasttime = new Date();
 }
-DglSession.prototype = new events.EventEmitter();
+DglSession.prototype = new BaseGame();
 
 function wsStartGame(wsReq) {
   var playmatch = wsReq.resourceURL.pathname.match(/^\/play\/([^\/]*)$/);