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