# HG changeset patch # User John "Elwin" Edwards # Date 1421696667 18000 # Node ID ecedc6f7e4ac90d0824a31786f0db987a53a07e1 # Parent db2f5ab112e95efde83ebdbd71e7b7c173532f6d Move TermSession and DglSession common code into a base class. diff -r db2f5ab112e9 -r ecedc6f7e4ac rlgwebd.js --- a/rlgwebd.js Mon Jan 19 08:32:29 2015 -0500 +++ b/rlgwebd.js Mon Jan 19 14:44:27 2015 -0500 @@ -66,7 +66,66 @@ var allowlogin = true; 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. * gname: (String) Name of the game to launch. @@ -78,8 +137,7 @@ * "exit": Child terminated. Parameters: none */ function TermSession(gname, pname, wsReq) { - /* Subclass EventEmitter to do the hard work. */ - events.EventEmitter.call(this); + BaseGame.call(this); /* Don't launch anything that's not a real game. */ if (gname in games) { this.game = games[gname]; @@ -113,7 +171,6 @@ 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.gname); this.lock = path.join(progressdir, this.pname + ":node:" + ts + ".ttyrec"); @@ -122,16 +179,10 @@ 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 = []; /* Send initial data. */ this.playerconn.sendUTF(JSON.stringify({"t": "s", "w": this.w, "h": this.h, "p": this.pname, "g": this.gname})); @@ -147,33 +198,7 @@ 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.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; -}; +TermSession.prototype = new BaseGame(); /* Currently this also sends to the player and any watchers. */ TermSession.prototype.write_ttyrec = function (datastr) { @@ -244,29 +269,9 @@ 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); + BaseGame.call(this); var pathcoms = filename.split('/'); this.gname = pathcoms[pathcoms.length - 2]; if (!(this.gname in games)) { @@ -282,27 +287,6 @@ * getting into a race. */ this.reading = false; 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 () { if (this.reading) return; @@ -375,9 +359,6 @@ } }); }); - this.tag = function () { - return this.gname + "/" + this.pname; - }; this.close = function () { this.recwatcher.close() /* Ensure all data is handled before quitting. */ @@ -393,29 +374,8 @@ gamemux.emit('end', this.gname, this.pname); 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) { var playmatch = wsReq.resourceURL.pathname.match(/^\/play\/([^\/]*)$/);