diff rlgwebd.js @ 158:9961a538c00e

rlgwebd.js: get rid of numerical game identifiers. Games will be indentified by gamename/username pairs. This will allow better interoperability with dgamelaunch. Polling clients are no longer supported; the code remnants need to be removed. The reaper() function will likely crash. Unexpectedly, the WebSocket client still works well enough to play. Watching and listing current games are probably broken.
author John "Elwin" Edwards
date Thu, 01 Jan 2015 15:56:22 -0500
parents e7f809f06c5c
children a613380ffdc2
line wrap: on
line diff
--- a/rlgwebd.js	Mon May 12 08:59:47 2014 -0700
+++ b/rlgwebd.js	Thu Jan 01 15:56:22 2015 -0500
@@ -66,7 +66,6 @@
 var clients = {};
 var dglgames = {};
 var allowlogin = true;
-var nextsession = 0;
 var gamemux = new events.EventEmitter();
 
 /* Constructor.  A TermSession handles a pty and the game running on it.
@@ -103,8 +102,7 @@
     return;
   }
   /* Grab a spot in the sessions table. */
-  this.sessid = nextsession++;
-  sessions[this.sessid] = this;
+  sessions[this.game.uname + "/" + this.pname] = this;
   /* Set up the sizes. */
   this.w = Math.floor(Number(dims[1]));
   if (!(this.w > 0 && this.w < 256))
@@ -121,10 +119,9 @@
   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 (index %d, pid %d)", this.pname, this.game.uname, 
-            this.sessid, this.term.pid);
-  this.emit('open', true, this.sessid);
-  gamemux.emit('begin', this.sessid, this.pname, this.game.uname);
+  tslog("%s playing %s (pid %d)", this.pname, this.game.uname, this.term.pid);
+  this.emit('open', true, this.game.uname, this.pname);
+  gamemux.emit('begin', this.game.uname, this.pname);
   /* Set up the lockfile and ttyrec */
   var ts = timestamp();
   var progressdir = path.join("/dgldir/inprogress", this.game.uname);
@@ -138,7 +135,7 @@
    * with a complete screen. */
   this.framebuf = new Buffer(1024);
   this.frameoff = 0;
-  logins[lkey].sessions.push(this.sessid);
+  logins[lkey].sessions.push(this.game.uname + "/" + this.pname);
   /* END setup */
   function ttyrec_chunk(datastr) {
     var ts = new Date();
@@ -166,7 +163,7 @@
       this.framebuf.copy(nbuf, 0, 0, this.frameoff);
       this.framebuf = nbuf;
       if (this.framebuf.length > 65536) {
-        tslog("Warning: Game %d frame buffer at %d bytes", this.sessid, 
+        tslog("Warning: Game %d frame buffer at %d bytes", this.tag(), 
                 this.framebuf.length);
       }
     }
@@ -176,18 +173,21 @@
   this.write = function(data) {
     this.term.write(data);
   };
+  this.tag = function() {
+    return this.game.uname + "/" + this.pname;
+  };
   // Teardown.
   this.term.on("exit", function () {
-    var id = ss.sessid;
+    var tag = ss.tag();
     fs.unlink(ss.lock);
     ss.record.end();
     ss.emit('exit');
-    gamemux.emit('end', id, ss.pname, ss.game.uname);
-    delete sessions[id];
-    tslog("Game %s ended.", id);
+    gamemux.emit('end', ss.game.uname, ss.pname);
+    delete sessions[tag];
+    tslog("Game %s ended.", tag);
   });
   this.close = function () {
-    if (this.sessid in sessions)
+    if (this.tag() in sessions)
       this.term.kill('SIGHUP');
   };
 }
@@ -341,12 +341,12 @@
     if (this.alive)
       this.session.close();
   };
-  function openH(success, id) {
+  function openH(success, tag) {
     if (success) {
       ss.alive = true;
-      ss.session = sessions[id];
-      ss.h = sessions[id].h;
-      ss.w = sessions[id].w;
+      ss.session = sessions[tag];
+      ss.h = sessions[tag].h;
+      ss.w = sessions[tag].w;
     }
     callback(ss, success);
   }
@@ -380,8 +380,8 @@
   conn.on('close', function(code, desc) {
     session.removeListener('data', dataH);
     session.removeListener('exit', exitH);
-    if (session.sessid in sessions)
-      tslog("A WebSocket watcher has left game %d", session.sessid);
+    if (session.tag() in sessions)
+      tslog("A WebSocket watcher has left game %d", session.tag());
   });
   conn.sendUTF(JSON.stringify({
             "t": "w", "w": session.w, "h": session.h, 
@@ -413,10 +413,11 @@
     session.close();
   }
   /* These listen on the TermSession. */
-  function openH(success, id) {
+  function openH(success, gname, pname) {
     if (success) {
-      var reply = {"t": "s", "id": id, "w": sessions[id].w, "h": 
-                   sessions[id].h, "p": sessions[id].pname, "g": game};
+      var tag = gname + "/" + pname;
+      var reply = {"t": "s", "tag": tag, "w": sessions[tag].w, "h": 
+                   sessions[tag].h, "p": pname, "g": gname};
       conn = wsReq.accept(null, wsReq.origin);
       conn.sendUTF(JSON.stringify(reply));
       conn.on('message', messageH);
@@ -629,7 +630,9 @@
   return getMsg(msgObj.utf8Data);
 }
 
+/* FIXME sessid removal */
 function reaper() {
+  return; // TODO figure out if this function is useful
   var now = new Date();
   function reapcheck(session) {
     fs.fstat(session.record.fd, function (err, stats) {
@@ -787,23 +790,27 @@
 }
 
 function watch(req, res, formdata) {
-  if (!("n" in formdata)) {
-    sendError(res, 2, "Game number not given");
+  if (!("g" in formdata) | !("p" in formdata)) {
+    sendError(res, 2, "Game or player not given");
     return;
   }
-  var gamenumber = Number(formdata["n"]);
-  if (!(gamenumber in sessions)) {
+  if (!(formdata.g in games)) {
+    sendError(res, 2, "No such game: " + formdata.g);
+    return;
+  }
+  var tag = formdata.g = "/" + formdata.p;
+  if (!(tag in sessions)) {
     sendError(res, 7);
     return;
   }
-  var session = sessions[gamenumber];
+  var session = sessions[tag];
   var watch = new Watcher(session);
-  var reply = {"t": "w", "id": watch.id, "w": session.w, "h": session.h, 
+  var reply = {"t": "w", "w": session.w, "h": session.h, 
                "p": session.pname, "g": session.game.uname};
   res.writeHead(200, {'Content-Type': 'application/json'});
   res.write(JSON.stringify(reply));
   res.end();
-  tslog("Game %d is being watched (key %s)", gamenumber, watch.id);
+  tslog("Game %d is being watched", tag);
 }
 
 /* Sets things up for a new user, like dgamelaunch's commands[register] */
@@ -886,6 +893,7 @@
 }
 
 /* Stops a running game if the request has the proper key. */
+/* TODO does this still work? */
 function stopgame(res, formdata) {
   if (!("key" in formdata) || !(formdata["key"] in logins)) {
     sendError(res, 1);
@@ -932,6 +940,7 @@
   checkprogress(pname, games[gname], checkback, []);
 }
 
+/* TODO remove polling */
 function findClient(formdata, playersOnly) {
   if (typeof(formdata) != "object")
     return null;
@@ -1023,6 +1032,7 @@
   return;
 }
 
+/* TODO remove polling */
 function readFeed(client, res) {
   if (!client) {
     sendError(res, 7, null, true);
@@ -1038,12 +1048,13 @@
   res.end();
 }
 
+/* TODO simplify by storing timestamps instead of callin stat() */
 function getStatus(callback) {
   var now = new Date();
   var statusinfo = {"s": allowlogin, "g": []};
-  function idleset(i, idletime) {
-    if (i >= 0 && i < statusinfo.g.length) {
-      statusinfo.g[i].i = idletime;
+  function idleset(n, idletime) {
+    if (n >= 0 && n < statusinfo.g.length) {
+      statusinfo.g[n].i = idletime;
     }
     for (var j = 0; j < statusinfo.g.length; j++) {
       if (!("i" in statusinfo.g[j]))
@@ -1051,11 +1062,11 @@
     }
     callback(statusinfo);
   }
-  for (var sessid in sessions) {
+  for (var tag in sessions) {
     var gamedesc = {};
-    gamedesc["n"] = sessid;
-    gamedesc["p"] = sessions[sessid].pname;
-    gamedesc["g"] = sessions[sessid].game.uname;
+    gamedesc["tag"] = tag;
+    gamedesc["p"] = sessions[tag].pname;
+    gamedesc["g"] = sessions[tag].game.uname;
     statusinfo["g"].push(gamedesc);
   }
   statusinfo["dgl"] = [];
@@ -1076,9 +1087,10 @@
   }
   for (var i = 0; i < statusinfo.g.length; i++) {
     /* fd sometimes isn't a number, presumably when the file isn't open yet. */
-    var ssid = statusinfo.g[i].n;
-    if (ssid in sessions && typeof(sessions[ssid].record.fd) == 'number') {
-      fs.fstat(sessions[ssid].record.fd, makecallback(i));
+    /* FIXME sessid -> tag */
+    var tag = statusinfo.g[i].tag;
+    if (tag in sessions && typeof(sessions[tag].record.fd) == 'number') {
+      fs.fstat(sessions[tag].record.fd, makecallback(i));
     }
     else {
       idleset(i, null);
@@ -1344,17 +1356,17 @@
 }
 
 function wsHandler(wsRequest) {
-  var watchmatch = wsRequest.resource.match(/^\/watch\/([0-9]*)$/);
+  var watchmatch = wsRequest.resource.match(/^\/watch\/(.*)$/);
   var playmatch = wsRequest.resource.match(/^\/play\//);
   if (watchmatch !== null) {
-    if (watchmatch[1] && Number(watchmatch[1]) in sessions) {
-      var tsession = sessions[Number(watchmatch[1])];
-      var conn = wsRequest.accept(null, wsRequest.origin);
-      new wsWatcher(conn, tsession);
-      tslog("Game %d is being watched via WebSockets", tsession.sessid);
+    if (!(watchmatch[1] in sessions)) {
+      wsRequest.reject(404, errorcodes[7]);
+      return;
     }
-    else
-      wsRequest.reject(404, errorcodes[7]);
+    var tsession = sessions[watchmatch[1]];
+    var conn = wsRequest.accept(null, wsRequest.origin);
+    new wsWatcher(conn, tsession);
+    tslog("Game %d is being watched via WebSockets", tsession.tag());
   }
   else if (playmatch !== null) {
     wsStart(wsRequest);
@@ -1367,14 +1379,14 @@
         conn.sendUTF(JSON.stringify(info));
       });
     }
-    var beginH = function (n, name, game) {
-      conn.sendUTF(JSON.stringify({"t": "b", "n": n, "p": name, "g": game}));
+    var beginH = function (gname, pname) {
+      conn.sendUTF(JSON.stringify({"t": "b", "p": pname, "g": gname}));
     };
     var listH = function (list) {
       conn.sendUTF(JSON.stringify(list));
     };
-    var endH = function (n, pname, gname) {
-      conn.sendUTF(JSON.stringify({"t": "e", "n": n, "p": pname, "g": gname}));
+    var endH = function (gname, pname) {
+      conn.sendUTF(JSON.stringify({"t": "e", "p": pname, "g": gname}));
     };
     gamemux.on('begin', beginH);
     gamemux.on('list', listH);
@@ -1406,13 +1418,13 @@
   process.exit();
 }
 
-function conHandler(chunk) {
+function consoleHandler(chunk) {
   var msg = chunk.toString().split('\n')[0];
   if (msg == "quit") {
     allowlogin = false;
     tslog("Disconnecting...");
-    for (var sessid in sessions) {
-      sessions[sessid].close();
+    for (var tag in sessions) {
+      sessions[tag].close();
     }
     progressWatcher.stdin.end("\n");
     setTimeout(shutdown, 2000);
@@ -1420,8 +1432,8 @@
 }
 
 process.on("exit", function () {
-  for (var sessid in sessions) {
-    sessions[sessid].term.kill('SIGHUP');
+  for (var tag in sessions) {
+    sessions[tag].term.kill('SIGHUP');
   }
   tslog("Quitting...");
   return;
@@ -1455,7 +1467,7 @@
 
 /* Open the control socket before chrooting where it can't be found */
 var ctlServer = net.createServer(function (sock) {
-  sock.on('data', conHandler);
+  sock.on('data', consoleHandler);
 });
 ctlServer.listen(ctlsocket, function () {
   /* rlgwebd.js now assumes that it has been started by the rlgwebd shell