diff rlgwebd.js @ 183:db2f5ab112e9

Move all TermSession methods into the prototype.
author John "Elwin" Edwards
date Mon, 19 Jan 2015 08:32:29 -0500
parents 3c0e7697bb30
children ecedc6f7e4ac
line wrap: on
line diff
--- a/rlgwebd.js	Sat Jan 17 19:57:40 2015 -0500
+++ b/rlgwebd.js	Mon Jan 19 08:32:29 2015 -0500
@@ -78,12 +78,12 @@
  *   "exit": Child terminated. Parameters: none
  */
 function TermSession(gname, pname, wsReq) {
-  var ss = this;
   /* Subclass EventEmitter to do the hard work. */
   events.EventEmitter.call(this);
   /* Don't launch anything that's not a real game. */
   if (gname in games) {
     this.game = games[gname];
+    this.gname = gname;
   }
   else {
     this.failed = true;
@@ -108,49 +108,33 @@
   var spawnopts = {"env": childenv, "cwd": "/", "rows": this.h, "cols": this.w,
                    "name": "xterm-256color"};
   this.term = pty.spawn(this.game.path, args, spawnopts);
-  tslog("%s playing %s (pid %d)", this.pname, this.game.uname, this.term.pid);
+  tslog("%s playing %s (pid %d)", this.pname, this.gname, this.term.pid);
   this.failed = false;
-  sessions[this.game.uname + "/" + this.pname] = this;
-  gamemux.emit('begin', this.game.uname, this.pname, 'rlg');
+  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.game.uname);
+  var progressdir = path.join("/dgldir/inprogress", this.gname);
   this.lock = path.join(progressdir, this.pname + ":node:" + ts + ".ttyrec");
   var lmsg = this.term.pid.toString() + '\n' + this.h + '\n' + this.w + '\n'; 
   fs.writeFile(this.lock, lmsg, "utf8"); 
-  var ttyrec = path.join("/dgldir/ttyrec", this.pname, this.game.uname, 
+  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 = [];
-  /* END setup */
-  function ttyrec_chunk(datastr) {
-    ss.lasttime = new Date();
-    var buf = new Buffer(datastr);
-    var chunk = new Buffer(buf.length + 12);
-    /* TTYREC headers */
-    chunk.writeUInt32LE(Math.floor(ss.lasttime.getTime() / 1000), 0);
-    chunk.writeUInt32LE(1000 * (ss.lasttime.getTime() % 1000), 4);
-    chunk.writeUInt32LE(buf.length, 8);
-    buf.copy(chunk, 12);
-    ss.record.write(chunk);
-    ss.framepush(buf);
-    /* Send to the player. */
-    var msg = JSON.stringify({"t": "d", "d": buf.toString("hex")});
-    ss.playerconn.sendUTF(msg);
-    /* Send to any watchers. */
-    for (var i = 0; i < ss.watchers.length; i++) {
-      if (ss.watchers[i].connected)
-        ss.watchers[i].sendUTF(msg);
-    }
-    ss.emit('data', buf);
-  }
+  /* Send initial data. */
+  this.playerconn.sendUTF(JSON.stringify({"t": "s", "w": this.w, "h": this.h, 
+               "p": this.pname, "g": this.gname}));
   /* Begin the ttyrec with some metadata, like dgamelaunch does. */
   var descstr = "\x1b[2J\x1b[1;1H\r\n";
   descstr += "Player: " + this.pname + "\r\nGame: " + this.game.name + "\r\n";
@@ -159,81 +143,18 @@
   descstr += "Time: (" + Math.floor(this.lasttime.getTime() / 1000) + ") ";
   descstr += this.lasttime.toUTCString().slice(0, -4) + "\r\n";
   descstr += "Size: " + this.w + "x" + this.h + "\r\n\x1b[2J";
-  ttyrec_chunk(descstr);
-  this.term.on("data", ttyrec_chunk);
-  this.write = function(data) {
-    this.term.write(data);
-  };
-  // Teardown.
-  this.term.on("exit", function () {
-    var tag = ss.tag();
-    fs.unlink(ss.lock);
-    ss.record.end();
-    var watchsocks = ss.watchers;
-    ss.watchers = [];
-    for (var i = 0; i < watchsocks.length; i++) {
-      if (watchsocks[i].connected)
-        watchsocks[i].close();
-    }
-    if (ss.playerconn.connected) {
-      ss.playerconn.sendUTF(JSON.stringify({"t": "q"}));
-      ss.playerconn.close();
-    }
-    ss.emit('exit');
-    gamemux.emit('end', ss.game.uname, ss.pname);
-    delete sessions[tag];
-    tslog("Game %s ended.", tag);
-  });
-  this.close = function () {
-    if (ss.tag() in sessions)
-      ss.term.kill('SIGHUP');
-  };
-  /* Send initial data. */
-  this.playerconn.sendUTF(JSON.stringify({"t": "s", "w": this.w, "h": this.h, 
-               "p": this.pname, "g": this.game.uname}));
-  /* Attach handlers. */
-  function messageH(message) {
-    var parsedMsg = getMsgWS(message);
-    if (parsedMsg.t == 'q') {
-      ss.close();
-    }
-    else if (parsedMsg.t == 'd') {
-      var hexstr = parsedMsg.d.replace(/[^0-9a-f]/gi, "");
-      if (hexstr.length % 2 != 0) {
-        hexstr = hexstr.slice(0, -1);
-      }
-      var keybuf = new Buffer(hexstr, "hex");
-      ss.write(keybuf);
-    }
-  }
-  this.playerconn.on('message', messageH);
-  this.playerconn.on('close', this.close);
-  /* To 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.game.uname
-    }));
-    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 game %s", ss.tag());
-      }
-    });
-    this.watchers.push(conn);
-  };
+  this.write_ttyrec(descstr);
+  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.game === undefined)
+  if (this.pname === undefined || this.gname === undefined)
     return "";
-  return this.game.uname + "/" + this.pname;
+  return this.gname + "/" + this.pname;
 };
+
 TermSession.prototype.framepush = function(chunk) {
   /* If this chunk resets the screen, discard what preceded it. */
   if (isclear(chunk)) {
@@ -254,6 +175,95 @@
   this.frameoff += chunk.length;
 };
 
+/* Currently this also sends to the player and any watchers. */
+TermSession.prototype.write_ttyrec = function (datastr) {
+  this.lasttime = new Date();
+  var buf = new Buffer(datastr);
+  var chunk = new Buffer(buf.length + 12);
+  /* TTYREC headers */
+  chunk.writeUInt32LE(Math.floor(this.lasttime.getTime() / 1000), 0);
+  chunk.writeUInt32LE(1000 * (this.lasttime.getTime() % 1000), 4);
+  chunk.writeUInt32LE(buf.length, 8);
+  buf.copy(chunk, 12);
+  this.record.write(chunk);
+  this.framepush(buf);
+  /* Send to the player. */
+  var msg = JSON.stringify({"t": "d", "d": buf.toString("hex")});
+  this.playerconn.sendUTF(msg);
+  /* Send to any watchers. */
+  for (var i = 0; i < this.watchers.length; i++) {
+    if (this.watchers[i].connected)
+      this.watchers[i].sendUTF(msg);
+  }
+  this.emit('data', buf);
+};
+
+/* For writing to the subprocess's stdin. */
+TermSession.prototype.write = function (data) {
+  this.term.write(data);
+};
+
+TermSession.prototype.input_msg = function (message) {
+  var parsedMsg = getMsgWS(message);
+  if (parsedMsg.t == 'q') {
+    this.close();
+  }
+  else if (parsedMsg.t == 'd') {
+    var hexstr = parsedMsg.d.replace(/[^0-9a-f]/gi, "");
+    if (hexstr.length % 2 != 0) {
+      hexstr = hexstr.slice(0, -1);
+    }
+    var keybuf = new Buffer(hexstr, "hex");
+    this.write(keybuf);
+  }
+};
+
+/* Teardown. */
+TermSession.prototype.close = function () {
+  if (this.tag() in sessions)
+    this.term.kill('SIGHUP');
+};
+
+TermSession.prototype.destroy = function () {
+  var tag = this.tag();
+  fs.unlink(this.lock);
+  this.record.end();
+  var watchsocks = this.watchers;
+  this.watchers = [];
+  for (var i = 0; i < watchsocks.length; i++) {
+    if (watchsocks[i].connected)
+      watchsocks[i].close();
+  }
+  if (this.playerconn.connected) {
+    this.playerconn.sendUTF(JSON.stringify({"t": "q"}));
+    this.playerconn.close();
+  }
+  this.emit('exit');
+  gamemux.emit('end', this.gname, this.pname);
+  delete sessions[tag];
+  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);