From 920c6e882942b17d7e44ae96b30c9939875a4e96 Mon Sep 17 00:00:00 2001 From: "John \"Elwin\" Edwards" Date: Thu, 1 Jan 2015 15:56:22 -0500 Subject: [PATCH] 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. --- rlgwebd.js | 128 +++++++++++++++++++++++++++++------------------------ 1 file changed, 70 insertions(+), 58 deletions(-) diff --git a/rlgwebd.js b/rlgwebd.js index 821e774..ae68cd7 100755 --- a/rlgwebd.js +++ b/rlgwebd.js @@ -66,7 +66,6 @@ var sessions = {}; 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 @@ function TermSession(game, lkey, dims, handlers) { 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 @@ function TermSession(game, lkey, dims, handlers) { 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 @@ function TermSession(game, lkey, dims, handlers) { * 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 @@ function TermSession(game, lkey, dims, handlers) { 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 @@ function TermSession(game, lkey, dims, handlers) { 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 @@ function Player(gamename, lkey, dims, callback) { 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 @@ function wsWatcher(conn, session) { 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 @@ function wsPlay(wsReq, game, lkey, dims) { 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 @@ function getMsgWS(msgObj) { 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 startgame(req, res, formdata) { } 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 @@ function endgame(client, res) { } /* 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 @@ function stopgame(res, formdata) { checkprogress(pname, games[gname], checkback, []); } +/* TODO remove polling */ function findClient(formdata, playersOnly) { if (typeof(formdata) != "object") return null; @@ -1023,6 +1032,7 @@ function serveStatic(req, res, fname) { return; } +/* TODO remove polling */ function readFeed(client, res) { if (!client) { sendError(res, 7, null, true); @@ -1038,12 +1048,13 @@ function readFeed(client, res) { 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 @@ function getStatus(callback) { } 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 @@ function getStatus(callback) { } 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 webHandler(req, res) { } 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); - } - else + if (!(watchmatch[1] in sessions)) { wsRequest.reject(404, errorcodes[7]); + return; + } + 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 @@ function wsHandler(wsRequest) { 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 @@ function shutdown () { 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 @@ function conHandler(chunk) { } 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 @@ if (fs.existsSync(ctlsocket)) { /* 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