Move TermSession and DglSession common code into a base class.
This commit is contained in:
parent
a2bb3fe97d
commit
677d739bed
1 changed files with 64 additions and 104 deletions
168
rlgwebd.js
168
rlgwebd.js
|
|
@ -66,7 +66,66 @@ var dglgames = {};
|
||||||
var allowlogin = true;
|
var allowlogin = true;
|
||||||
var gamemux = new events.EventEmitter();
|
var gamemux = new events.EventEmitter();
|
||||||
|
|
||||||
/* TODO move TermSession and DglSession methods into the prototypes. */
|
/* A base class. TermSession and DglSession inherit from it. */
|
||||||
|
function BaseGame() {
|
||||||
|
/* Games subclass EventEmitter, though there are few listeners. */
|
||||||
|
events.EventEmitter.call(this);
|
||||||
|
/* Array of watching WebSockets. */
|
||||||
|
this.watchers = [];
|
||||||
|
/* replaybuf holds the output since the last screen clear, so watchers can
|
||||||
|
* begin with a complete screen. replaylen is the number of bytes stored. */
|
||||||
|
this.replaybuf = new Buffer(1024);
|
||||||
|
this.replaylen = 0;
|
||||||
|
/* Time of last activity. */
|
||||||
|
this.lasttime = new Date();
|
||||||
|
}
|
||||||
|
BaseGame.prototype = new events.EventEmitter();
|
||||||
|
|
||||||
|
BaseGame.prototype.tag = function () {
|
||||||
|
if (this.pname === undefined || this.gname === undefined)
|
||||||
|
return "";
|
||||||
|
return this.gname + "/" + this.pname;
|
||||||
|
};
|
||||||
|
|
||||||
|
BaseGame.prototype.framepush = function(chunk) {
|
||||||
|
/* If this chunk resets the screen, discard what preceded it. */
|
||||||
|
if (isclear(chunk)) {
|
||||||
|
this.replaybuf = new Buffer(1024);
|
||||||
|
this.replaylen = 0;
|
||||||
|
}
|
||||||
|
/* Make sure there's space. */
|
||||||
|
while (this.replaybuf.length < chunk.length + this.replaylen) {
|
||||||
|
var nbuf = new Buffer(this.replaybuf.length * 2);
|
||||||
|
this.replaybuf.copy(nbuf, 0, 0, this.replaylen);
|
||||||
|
this.replaybuf = nbuf;
|
||||||
|
if (this.replaybuf.length > 65536) {
|
||||||
|
tslog("Warning: %s frame buffer at %d bytes", this.tag(),
|
||||||
|
this.replaybuf.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chunk.copy(this.replaybuf, this.replaylen);
|
||||||
|
this.replaylen += chunk.length;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Adds a watcher. */
|
||||||
|
BaseGame.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.replaybuf.toString("hex", 0, this.replaylen)}));
|
||||||
|
conn.on('close', this.detach.bind(this, conn));
|
||||||
|
this.watchers.push(conn);
|
||||||
|
};
|
||||||
|
|
||||||
|
BaseGame.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());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/* Constructor. A TermSession handles a pty and the game running on it.
|
/* Constructor. A TermSession handles a pty and the game running on it.
|
||||||
* gname: (String) Name of the game to launch.
|
* gname: (String) Name of the game to launch.
|
||||||
|
|
@ -78,8 +137,7 @@ var gamemux = new events.EventEmitter();
|
||||||
* "exit": Child terminated. Parameters: none
|
* "exit": Child terminated. Parameters: none
|
||||||
*/
|
*/
|
||||||
function TermSession(gname, pname, wsReq) {
|
function TermSession(gname, pname, wsReq) {
|
||||||
/* Subclass EventEmitter to do the hard work. */
|
BaseGame.call(this);
|
||||||
events.EventEmitter.call(this);
|
|
||||||
/* Don't launch anything that's not a real game. */
|
/* Don't launch anything that's not a real game. */
|
||||||
if (gname in games) {
|
if (gname in games) {
|
||||||
this.game = games[gname];
|
this.game = games[gname];
|
||||||
|
|
@ -113,7 +171,6 @@ function TermSession(gname, pname, wsReq) {
|
||||||
sessions[this.gname + "/" + this.pname] = this;
|
sessions[this.gname + "/" + this.pname] = this;
|
||||||
gamemux.emit('begin', this.gname, this.pname, 'rlg');
|
gamemux.emit('begin', this.gname, this.pname, 'rlg');
|
||||||
/* Set up the lockfile and ttyrec */
|
/* Set up the lockfile and ttyrec */
|
||||||
this.lasttime = new Date();
|
|
||||||
var ts = timestamp(this.lasttime);
|
var ts = timestamp(this.lasttime);
|
||||||
var progressdir = path.join("/dgldir/inprogress", this.gname);
|
var progressdir = path.join("/dgldir/inprogress", this.gname);
|
||||||
this.lock = path.join(progressdir, this.pname + ":node:" + ts + ".ttyrec");
|
this.lock = path.join(progressdir, this.pname + ":node:" + ts + ".ttyrec");
|
||||||
|
|
@ -122,16 +179,10 @@ function TermSession(gname, pname, wsReq) {
|
||||||
var ttyrec = path.join("/dgldir/ttyrec", this.pname, this.gname,
|
var ttyrec = path.join("/dgldir/ttyrec", this.pname, this.gname,
|
||||||
ts + ".ttyrec");
|
ts + ".ttyrec");
|
||||||
this.record = fs.createWriteStream(ttyrec, { mode: 0664 });
|
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. */
|
/* The player's WebSocket and its handlers. */
|
||||||
this.playerconn = wsReq.accept(null, wsReq.origin);
|
this.playerconn = wsReq.accept(null, wsReq.origin);
|
||||||
this.playerconn.on('message', this.input_msg.bind(this));
|
this.playerconn.on('message', this.input_msg.bind(this));
|
||||||
this.playerconn.on('close', this.close.bind(this));
|
this.playerconn.on('close', this.close.bind(this));
|
||||||
/* Array for watcher connections. */
|
|
||||||
this.watchers = [];
|
|
||||||
/* Send initial data. */
|
/* Send initial data. */
|
||||||
this.playerconn.sendUTF(JSON.stringify({"t": "s", "w": this.w, "h": this.h,
|
this.playerconn.sendUTF(JSON.stringify({"t": "s", "w": this.w, "h": this.h,
|
||||||
"p": this.pname, "g": this.gname}));
|
"p": this.pname, "g": this.gname}));
|
||||||
|
|
@ -147,33 +198,7 @@ function TermSession(gname, pname, wsReq) {
|
||||||
this.term.on("data", this.write_ttyrec.bind(this));
|
this.term.on("data", this.write_ttyrec.bind(this));
|
||||||
this.term.on("exit", this.destroy.bind(this));
|
this.term.on("exit", this.destroy.bind(this));
|
||||||
}
|
}
|
||||||
TermSession.prototype = new events.EventEmitter();
|
TermSession.prototype = new BaseGame();
|
||||||
|
|
||||||
TermSession.prototype.tag = function () {
|
|
||||||
if (this.pname === undefined || this.gname === undefined)
|
|
||||||
return "";
|
|
||||||
return this.gname + "/" + this.pname;
|
|
||||||
};
|
|
||||||
|
|
||||||
TermSession.prototype.framepush = function(chunk) {
|
|
||||||
/* If this chunk resets the screen, discard what preceded it. */
|
|
||||||
if (isclear(chunk)) {
|
|
||||||
this.framebuf = new Buffer(1024);
|
|
||||||
this.frameoff = 0;
|
|
||||||
}
|
|
||||||
/* Make sure there's space. */
|
|
||||||
while (this.framebuf.length < chunk.length + this.frameoff) {
|
|
||||||
var nbuf = new Buffer(this.framebuf.length * 2);
|
|
||||||
this.framebuf.copy(nbuf, 0, 0, this.frameoff);
|
|
||||||
this.framebuf = nbuf;
|
|
||||||
if (this.framebuf.length > 65536) {
|
|
||||||
tslog("Warning: Game %s frame buffer at %d bytes", this.tag(),
|
|
||||||
this.framebuf.length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
chunk.copy(this.framebuf, this.frameoff);
|
|
||||||
this.frameoff += chunk.length;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Currently this also sends to the player and any watchers. */
|
/* Currently this also sends to the player and any watchers. */
|
||||||
TermSession.prototype.write_ttyrec = function (datastr) {
|
TermSession.prototype.write_ttyrec = function (datastr) {
|
||||||
|
|
@ -244,29 +269,9 @@ TermSession.prototype.destroy = function () {
|
||||||
tslog("Game %s ended.", 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) {
|
function DglSession(filename) {
|
||||||
var ss = this;
|
var ss = this;
|
||||||
events.EventEmitter.call(this);
|
BaseGame.call(this);
|
||||||
var pathcoms = filename.split('/');
|
var pathcoms = filename.split('/');
|
||||||
this.gname = pathcoms[pathcoms.length - 2];
|
this.gname = pathcoms[pathcoms.length - 2];
|
||||||
if (!(this.gname in games)) {
|
if (!(this.gname in games)) {
|
||||||
|
|
@ -282,27 +287,6 @@ function DglSession(filename) {
|
||||||
* getting into a race. */
|
* getting into a race. */
|
||||||
this.reading = false;
|
this.reading = false;
|
||||||
this.rpos = 0;
|
this.rpos = 0;
|
||||||
this.framebuf = new Buffer(1024);
|
|
||||||
this.frameoff = 0;
|
|
||||||
this.framepush = function(chunk) {
|
|
||||||
/* If this chunk resets the screen, discard what preceded it. */
|
|
||||||
if (isclear(chunk)) {
|
|
||||||
this.framebuf = new Buffer(1024);
|
|
||||||
this.frameoff = 0;
|
|
||||||
}
|
|
||||||
/* Make sure there's space. */
|
|
||||||
while (this.framebuf.length < chunk.length + this.frameoff) {
|
|
||||||
var nbuf = new Buffer(this.framebuf.length * 2);
|
|
||||||
this.framebuf.copy(nbuf, 0, 0, this.frameoff);
|
|
||||||
this.framebuf = nbuf;
|
|
||||||
if (this.framebuf.length > 65536) {
|
|
||||||
tslog("Warning: DGL %s frame buffer at %d bytes", this.tag(),
|
|
||||||
this.framebuf.length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
chunk.copy(this.framebuf, this.frameoff);
|
|
||||||
this.frameoff += chunk.length;
|
|
||||||
};
|
|
||||||
this.readchunk = function () {
|
this.readchunk = function () {
|
||||||
if (this.reading)
|
if (this.reading)
|
||||||
return;
|
return;
|
||||||
|
|
@ -375,9 +359,6 @@ function DglSession(filename) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this.tag = function () {
|
|
||||||
return this.gname + "/" + this.pname;
|
|
||||||
};
|
|
||||||
this.close = function () {
|
this.close = function () {
|
||||||
this.recwatcher.close()
|
this.recwatcher.close()
|
||||||
/* Ensure all data is handled before quitting. */
|
/* Ensure all data is handled before quitting. */
|
||||||
|
|
@ -393,29 +374,8 @@ function DglSession(filename) {
|
||||||
gamemux.emit('end', this.gname, this.pname);
|
gamemux.emit('end', this.gname, this.pname);
|
||||||
tslog("DGL %s: closed", ss.tag());
|
tslog("DGL %s: closed", ss.tag());
|
||||||
};
|
};
|
||||||
this.watchers = [];
|
|
||||||
/* 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.gname
|
|
||||||
}));
|
|
||||||
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 DGL game %s", ss.tag());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.watchers.push(conn);
|
|
||||||
};
|
|
||||||
this.lasttime = new Date();
|
|
||||||
}
|
}
|
||||||
DglSession.prototype = new events.EventEmitter();
|
DglSession.prototype = new BaseGame();
|
||||||
|
|
||||||
function wsStartGame(wsReq) {
|
function wsStartGame(wsReq) {
|
||||||
var playmatch = wsReq.resourceURL.pathname.match(/^\/play\/([^\/]*)$/);
|
var playmatch = wsReq.resourceURL.pathname.match(/^\/play\/([^\/]*)$/);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue