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.
This commit is contained in:
parent
0a0f754135
commit
920c6e8829
1 changed files with 70 additions and 58 deletions
128
rlgwebd.js
128
rlgwebd.js
|
|
@ -66,7 +66,6 @@ var sessions = {};
|
||||||
var clients = {};
|
var clients = {};
|
||||||
var dglgames = {};
|
var dglgames = {};
|
||||||
var allowlogin = true;
|
var allowlogin = true;
|
||||||
var nextsession = 0;
|
|
||||||
var gamemux = new events.EventEmitter();
|
var gamemux = new events.EventEmitter();
|
||||||
|
|
||||||
/* Constructor. A TermSession handles a pty and the game running on it.
|
/* Constructor. A TermSession handles a pty and the game running on it.
|
||||||
|
|
@ -103,8 +102,7 @@ function TermSession(game, lkey, dims, handlers) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* Grab a spot in the sessions table. */
|
/* Grab a spot in the sessions table. */
|
||||||
this.sessid = nextsession++;
|
sessions[this.game.uname + "/" + this.pname] = this;
|
||||||
sessions[this.sessid] = this;
|
|
||||||
/* Set up the sizes. */
|
/* Set up the sizes. */
|
||||||
this.w = Math.floor(Number(dims[1]));
|
this.w = Math.floor(Number(dims[1]));
|
||||||
if (!(this.w > 0 && this.w < 256))
|
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,
|
var spawnopts = {"env": childenv, "cwd": "/", "rows": this.h, "cols": this.w,
|
||||||
"name": "xterm-256color"};
|
"name": "xterm-256color"};
|
||||||
this.term = pty.spawn(this.game.path, args, spawnopts);
|
this.term = pty.spawn(this.game.path, args, spawnopts);
|
||||||
tslog("%s playing %s (index %d, pid %d)", this.pname, this.game.uname,
|
tslog("%s playing %s (pid %d)", this.pname, this.game.uname, this.term.pid);
|
||||||
this.sessid, this.term.pid);
|
this.emit('open', true, this.game.uname, this.pname);
|
||||||
this.emit('open', true, this.sessid);
|
gamemux.emit('begin', this.game.uname, this.pname);
|
||||||
gamemux.emit('begin', this.sessid, this.pname, this.game.uname);
|
|
||||||
/* Set up the lockfile and ttyrec */
|
/* Set up the lockfile and ttyrec */
|
||||||
var ts = timestamp();
|
var ts = timestamp();
|
||||||
var progressdir = path.join("/dgldir/inprogress", this.game.uname);
|
var progressdir = path.join("/dgldir/inprogress", this.game.uname);
|
||||||
|
|
@ -138,7 +135,7 @@ function TermSession(game, lkey, dims, handlers) {
|
||||||
* with a complete screen. */
|
* with a complete screen. */
|
||||||
this.framebuf = new Buffer(1024);
|
this.framebuf = new Buffer(1024);
|
||||||
this.frameoff = 0;
|
this.frameoff = 0;
|
||||||
logins[lkey].sessions.push(this.sessid);
|
logins[lkey].sessions.push(this.game.uname + "/" + this.pname);
|
||||||
/* END setup */
|
/* END setup */
|
||||||
function ttyrec_chunk(datastr) {
|
function ttyrec_chunk(datastr) {
|
||||||
var ts = new Date();
|
var ts = new Date();
|
||||||
|
|
@ -166,7 +163,7 @@ function TermSession(game, lkey, dims, handlers) {
|
||||||
this.framebuf.copy(nbuf, 0, 0, this.frameoff);
|
this.framebuf.copy(nbuf, 0, 0, this.frameoff);
|
||||||
this.framebuf = nbuf;
|
this.framebuf = nbuf;
|
||||||
if (this.framebuf.length > 65536) {
|
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);
|
this.framebuf.length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -176,18 +173,21 @@ function TermSession(game, lkey, dims, handlers) {
|
||||||
this.write = function(data) {
|
this.write = function(data) {
|
||||||
this.term.write(data);
|
this.term.write(data);
|
||||||
};
|
};
|
||||||
|
this.tag = function() {
|
||||||
|
return this.game.uname + "/" + this.pname;
|
||||||
|
};
|
||||||
// Teardown.
|
// Teardown.
|
||||||
this.term.on("exit", function () {
|
this.term.on("exit", function () {
|
||||||
var id = ss.sessid;
|
var tag = ss.tag();
|
||||||
fs.unlink(ss.lock);
|
fs.unlink(ss.lock);
|
||||||
ss.record.end();
|
ss.record.end();
|
||||||
ss.emit('exit');
|
ss.emit('exit');
|
||||||
gamemux.emit('end', id, ss.pname, ss.game.uname);
|
gamemux.emit('end', ss.game.uname, ss.pname);
|
||||||
delete sessions[id];
|
delete sessions[tag];
|
||||||
tslog("Game %s ended.", id);
|
tslog("Game %s ended.", tag);
|
||||||
});
|
});
|
||||||
this.close = function () {
|
this.close = function () {
|
||||||
if (this.sessid in sessions)
|
if (this.tag() in sessions)
|
||||||
this.term.kill('SIGHUP');
|
this.term.kill('SIGHUP');
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -341,12 +341,12 @@ function Player(gamename, lkey, dims, callback) {
|
||||||
if (this.alive)
|
if (this.alive)
|
||||||
this.session.close();
|
this.session.close();
|
||||||
};
|
};
|
||||||
function openH(success, id) {
|
function openH(success, tag) {
|
||||||
if (success) {
|
if (success) {
|
||||||
ss.alive = true;
|
ss.alive = true;
|
||||||
ss.session = sessions[id];
|
ss.session = sessions[tag];
|
||||||
ss.h = sessions[id].h;
|
ss.h = sessions[tag].h;
|
||||||
ss.w = sessions[id].w;
|
ss.w = sessions[tag].w;
|
||||||
}
|
}
|
||||||
callback(ss, success);
|
callback(ss, success);
|
||||||
}
|
}
|
||||||
|
|
@ -380,8 +380,8 @@ function wsWatcher(conn, session) {
|
||||||
conn.on('close', function(code, desc) {
|
conn.on('close', function(code, desc) {
|
||||||
session.removeListener('data', dataH);
|
session.removeListener('data', dataH);
|
||||||
session.removeListener('exit', exitH);
|
session.removeListener('exit', exitH);
|
||||||
if (session.sessid in sessions)
|
if (session.tag() in sessions)
|
||||||
tslog("A WebSocket watcher has left game %d", session.sessid);
|
tslog("A WebSocket watcher has left game %d", session.tag());
|
||||||
});
|
});
|
||||||
conn.sendUTF(JSON.stringify({
|
conn.sendUTF(JSON.stringify({
|
||||||
"t": "w", "w": session.w, "h": session.h,
|
"t": "w", "w": session.w, "h": session.h,
|
||||||
|
|
@ -413,10 +413,11 @@ function wsPlay(wsReq, game, lkey, dims) {
|
||||||
session.close();
|
session.close();
|
||||||
}
|
}
|
||||||
/* These listen on the TermSession. */
|
/* These listen on the TermSession. */
|
||||||
function openH(success, id) {
|
function openH(success, gname, pname) {
|
||||||
if (success) {
|
if (success) {
|
||||||
var reply = {"t": "s", "id": id, "w": sessions[id].w, "h":
|
var tag = gname + "/" + pname;
|
||||||
sessions[id].h, "p": sessions[id].pname, "g": game};
|
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 = wsReq.accept(null, wsReq.origin);
|
||||||
conn.sendUTF(JSON.stringify(reply));
|
conn.sendUTF(JSON.stringify(reply));
|
||||||
conn.on('message', messageH);
|
conn.on('message', messageH);
|
||||||
|
|
@ -629,7 +630,9 @@ function getMsgWS(msgObj) {
|
||||||
return getMsg(msgObj.utf8Data);
|
return getMsg(msgObj.utf8Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* FIXME sessid removal */
|
||||||
function reaper() {
|
function reaper() {
|
||||||
|
return; // TODO figure out if this function is useful
|
||||||
var now = new Date();
|
var now = new Date();
|
||||||
function reapcheck(session) {
|
function reapcheck(session) {
|
||||||
fs.fstat(session.record.fd, function (err, stats) {
|
fs.fstat(session.record.fd, function (err, stats) {
|
||||||
|
|
@ -787,23 +790,27 @@ function startgame(req, res, formdata) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function watch(req, res, formdata) {
|
function watch(req, res, formdata) {
|
||||||
if (!("n" in formdata)) {
|
if (!("g" in formdata) | !("p" in formdata)) {
|
||||||
sendError(res, 2, "Game number not given");
|
sendError(res, 2, "Game or player not given");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var gamenumber = Number(formdata["n"]);
|
if (!(formdata.g in games)) {
|
||||||
if (!(gamenumber in sessions)) {
|
sendError(res, 2, "No such game: " + formdata.g);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var tag = formdata.g = "/" + formdata.p;
|
||||||
|
if (!(tag in sessions)) {
|
||||||
sendError(res, 7);
|
sendError(res, 7);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var session = sessions[gamenumber];
|
var session = sessions[tag];
|
||||||
var watch = new Watcher(session);
|
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};
|
"p": session.pname, "g": session.game.uname};
|
||||||
res.writeHead(200, {'Content-Type': 'application/json'});
|
res.writeHead(200, {'Content-Type': 'application/json'});
|
||||||
res.write(JSON.stringify(reply));
|
res.write(JSON.stringify(reply));
|
||||||
res.end();
|
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] */
|
/* 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. */
|
/* Stops a running game if the request has the proper key. */
|
||||||
|
/* TODO does this still work? */
|
||||||
function stopgame(res, formdata) {
|
function stopgame(res, formdata) {
|
||||||
if (!("key" in formdata) || !(formdata["key"] in logins)) {
|
if (!("key" in formdata) || !(formdata["key"] in logins)) {
|
||||||
sendError(res, 1);
|
sendError(res, 1);
|
||||||
|
|
@ -932,6 +940,7 @@ function stopgame(res, formdata) {
|
||||||
checkprogress(pname, games[gname], checkback, []);
|
checkprogress(pname, games[gname], checkback, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TODO remove polling */
|
||||||
function findClient(formdata, playersOnly) {
|
function findClient(formdata, playersOnly) {
|
||||||
if (typeof(formdata) != "object")
|
if (typeof(formdata) != "object")
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -1023,6 +1032,7 @@ function serveStatic(req, res, fname) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TODO remove polling */
|
||||||
function readFeed(client, res) {
|
function readFeed(client, res) {
|
||||||
if (!client) {
|
if (!client) {
|
||||||
sendError(res, 7, null, true);
|
sendError(res, 7, null, true);
|
||||||
|
|
@ -1038,12 +1048,13 @@ function readFeed(client, res) {
|
||||||
res.end();
|
res.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TODO simplify by storing timestamps instead of callin stat() */
|
||||||
function getStatus(callback) {
|
function getStatus(callback) {
|
||||||
var now = new Date();
|
var now = new Date();
|
||||||
var statusinfo = {"s": allowlogin, "g": []};
|
var statusinfo = {"s": allowlogin, "g": []};
|
||||||
function idleset(i, idletime) {
|
function idleset(n, idletime) {
|
||||||
if (i >= 0 && i < statusinfo.g.length) {
|
if (n >= 0 && n < statusinfo.g.length) {
|
||||||
statusinfo.g[i].i = idletime;
|
statusinfo.g[n].i = idletime;
|
||||||
}
|
}
|
||||||
for (var j = 0; j < statusinfo.g.length; j++) {
|
for (var j = 0; j < statusinfo.g.length; j++) {
|
||||||
if (!("i" in statusinfo.g[j]))
|
if (!("i" in statusinfo.g[j]))
|
||||||
|
|
@ -1051,11 +1062,11 @@ function getStatus(callback) {
|
||||||
}
|
}
|
||||||
callback(statusinfo);
|
callback(statusinfo);
|
||||||
}
|
}
|
||||||
for (var sessid in sessions) {
|
for (var tag in sessions) {
|
||||||
var gamedesc = {};
|
var gamedesc = {};
|
||||||
gamedesc["n"] = sessid;
|
gamedesc["tag"] = tag;
|
||||||
gamedesc["p"] = sessions[sessid].pname;
|
gamedesc["p"] = sessions[tag].pname;
|
||||||
gamedesc["g"] = sessions[sessid].game.uname;
|
gamedesc["g"] = sessions[tag].game.uname;
|
||||||
statusinfo["g"].push(gamedesc);
|
statusinfo["g"].push(gamedesc);
|
||||||
}
|
}
|
||||||
statusinfo["dgl"] = [];
|
statusinfo["dgl"] = [];
|
||||||
|
|
@ -1076,9 +1087,10 @@ function getStatus(callback) {
|
||||||
}
|
}
|
||||||
for (var i = 0; i < statusinfo.g.length; i++) {
|
for (var i = 0; i < statusinfo.g.length; i++) {
|
||||||
/* fd sometimes isn't a number, presumably when the file isn't open yet. */
|
/* fd sometimes isn't a number, presumably when the file isn't open yet. */
|
||||||
var ssid = statusinfo.g[i].n;
|
/* FIXME sessid -> tag */
|
||||||
if (ssid in sessions && typeof(sessions[ssid].record.fd) == 'number') {
|
var tag = statusinfo.g[i].tag;
|
||||||
fs.fstat(sessions[ssid].record.fd, makecallback(i));
|
if (tag in sessions && typeof(sessions[tag].record.fd) == 'number') {
|
||||||
|
fs.fstat(sessions[tag].record.fd, makecallback(i));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
idleset(i, null);
|
idleset(i, null);
|
||||||
|
|
@ -1344,17 +1356,17 @@ function webHandler(req, res) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function wsHandler(wsRequest) {
|
function wsHandler(wsRequest) {
|
||||||
var watchmatch = wsRequest.resource.match(/^\/watch\/([0-9]*)$/);
|
var watchmatch = wsRequest.resource.match(/^\/watch\/(.*)$/);
|
||||||
var playmatch = wsRequest.resource.match(/^\/play\//);
|
var playmatch = wsRequest.resource.match(/^\/play\//);
|
||||||
if (watchmatch !== null) {
|
if (watchmatch !== null) {
|
||||||
if (watchmatch[1] && Number(watchmatch[1]) in sessions) {
|
if (!(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
|
|
||||||
wsRequest.reject(404, errorcodes[7]);
|
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) {
|
else if (playmatch !== null) {
|
||||||
wsStart(wsRequest);
|
wsStart(wsRequest);
|
||||||
|
|
@ -1367,14 +1379,14 @@ function wsHandler(wsRequest) {
|
||||||
conn.sendUTF(JSON.stringify(info));
|
conn.sendUTF(JSON.stringify(info));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
var beginH = function (n, name, game) {
|
var beginH = function (gname, pname) {
|
||||||
conn.sendUTF(JSON.stringify({"t": "b", "n": n, "p": name, "g": game}));
|
conn.sendUTF(JSON.stringify({"t": "b", "p": pname, "g": gname}));
|
||||||
};
|
};
|
||||||
var listH = function (list) {
|
var listH = function (list) {
|
||||||
conn.sendUTF(JSON.stringify(list));
|
conn.sendUTF(JSON.stringify(list));
|
||||||
};
|
};
|
||||||
var endH = function (n, pname, gname) {
|
var endH = function (gname, pname) {
|
||||||
conn.sendUTF(JSON.stringify({"t": "e", "n": n, "p": pname, "g": gname}));
|
conn.sendUTF(JSON.stringify({"t": "e", "p": pname, "g": gname}));
|
||||||
};
|
};
|
||||||
gamemux.on('begin', beginH);
|
gamemux.on('begin', beginH);
|
||||||
gamemux.on('list', listH);
|
gamemux.on('list', listH);
|
||||||
|
|
@ -1406,13 +1418,13 @@ function shutdown () {
|
||||||
process.exit();
|
process.exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
function conHandler(chunk) {
|
function consoleHandler(chunk) {
|
||||||
var msg = chunk.toString().split('\n')[0];
|
var msg = chunk.toString().split('\n')[0];
|
||||||
if (msg == "quit") {
|
if (msg == "quit") {
|
||||||
allowlogin = false;
|
allowlogin = false;
|
||||||
tslog("Disconnecting...");
|
tslog("Disconnecting...");
|
||||||
for (var sessid in sessions) {
|
for (var tag in sessions) {
|
||||||
sessions[sessid].close();
|
sessions[tag].close();
|
||||||
}
|
}
|
||||||
progressWatcher.stdin.end("\n");
|
progressWatcher.stdin.end("\n");
|
||||||
setTimeout(shutdown, 2000);
|
setTimeout(shutdown, 2000);
|
||||||
|
|
@ -1420,8 +1432,8 @@ function conHandler(chunk) {
|
||||||
}
|
}
|
||||||
|
|
||||||
process.on("exit", function () {
|
process.on("exit", function () {
|
||||||
for (var sessid in sessions) {
|
for (var tag in sessions) {
|
||||||
sessions[sessid].term.kill('SIGHUP');
|
sessions[tag].term.kill('SIGHUP');
|
||||||
}
|
}
|
||||||
tslog("Quitting...");
|
tslog("Quitting...");
|
||||||
return;
|
return;
|
||||||
|
|
@ -1455,7 +1467,7 @@ if (fs.existsSync(ctlsocket)) {
|
||||||
|
|
||||||
/* Open the control socket before chrooting where it can't be found */
|
/* Open the control socket before chrooting where it can't be found */
|
||||||
var ctlServer = net.createServer(function (sock) {
|
var ctlServer = net.createServer(function (sock) {
|
||||||
sock.on('data', conHandler);
|
sock.on('data', consoleHandler);
|
||||||
});
|
});
|
||||||
ctlServer.listen(ctlsocket, function () {
|
ctlServer.listen(ctlsocket, function () {
|
||||||
/* rlgwebd.js now assumes that it has been started by the rlgwebd shell
|
/* rlgwebd.js now assumes that it has been started by the rlgwebd shell
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue