Move all TermSession methods into the prototype.
This commit is contained in:
parent
4034ab16ef
commit
a2bb3fe97d
1 changed files with 108 additions and 98 deletions
206
rlgwebd.js
206
rlgwebd.js
|
|
@ -78,12 +78,12 @@ var gamemux = new events.EventEmitter();
|
|||
* "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 @@ function TermSession(gname, pname, wsReq) {
|
|||
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 @@ function TermSession(gname, pname, wsReq) {
|
|||
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 @@ TermSession.prototype.framepush = function(chunk) {
|
|||
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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue