Mercurial > hg > rlgwebd
diff rlgwebd.js @ 170:50e4c9feeac2
RLGWebD: fix simultaneous player bug.
Multiple games can now run at the same time, and data will be sent to
the proper place. The interaction of multiple players with watchers
has not yet been tested.
author | John "Elwin" Edwards |
---|---|
date | Fri, 09 Jan 2015 13:06:41 -0500 |
parents | 6f4b7e1b32e8 |
children | 671bed5039aa |
line wrap: on
line diff
--- a/rlgwebd.js Fri Jan 09 09:43:21 2015 -0500 +++ b/rlgwebd.js Fri Jan 09 13:06:41 2015 -0500 @@ -69,35 +69,32 @@ /* Constructor. A TermSession handles a pty and the game running on it. * gname: (String) Name of the game to launch. * pname: (String) The player's name. - * dims: (Array [Number, Number]) Height and width of the pty. - * handlers: (Object) Key-value pairs, event names and functions to - * install to handle them. + * wsReq: (WebSocketRequest) The request from the client. + * * Events: * "data": Data generated by child. Parameters: buf (Buffer) * "exit": Child terminated. Parameters: none */ -function TermSession(gname, pname, dims, handlers) { +function TermSession(gname, pname, wsReq) { var ss = this; /* Subclass EventEmitter to do the hard work. */ events.EventEmitter.call(this); - for (var evname in handlers) - this.on(evname, handlers[evname]); /* Don't launch anything that's not a real game. */ if (gname in games) { this.game = games[gname]; } else { this.failed = true; + wsReq.reject(404, errorcodes[2], "No such game"); + tslog("Game %s is not available", game); return; } this.pname = pname; - /* Grab a spot in the sessions table. */ - sessions[this.game.uname + "/" + this.pname] = this; /* Set up the sizes. */ - this.w = Math.floor(Number(dims[1])); + this.w = Math.floor(Number(wsReq.resourceURL.query.w)); if (!(this.w > 0 && this.w < 256)) this.w = 80; - this.h = Math.floor(Number(dims[0])); + this.h = Math.floor(Number(wsReq.resourceURL.query.h)); if (!(this.h > 0 && this.h < 256)) this.h = 24; /* Environment. */ @@ -111,6 +108,7 @@ this.term = pty.spawn(this.game.path, args, spawnopts); tslog("%s playing %s (pid %d)", this.pname, this.game.uname, this.term.pid); this.failed = false; + sessions[this.game.uname + "/" + this.pname] = this; gamemux.emit('begin', this.game.uname, this.pname); /* Set up the lockfile and ttyrec */ this.lasttime = new Date(); @@ -126,6 +124,7 @@ * with a complete screen. */ this.framebuf = new Buffer(1024); this.frameoff = 0; + this.playerconn = wsReq.accept(null, wsReq.origin); /* END setup */ function ttyrec_chunk(datastr) { ss.lasttime = new Date(); @@ -138,6 +137,10 @@ buf.copy(chunk, 12); ss.record.write(chunk); ss.framepush(buf); + /* Send to the player. */ + var msg = {"t": "d", "d": buf.toString("hex")}; + ss.playerconn.sendUTF(JSON.stringify(msg)); + /* For the benefit of watchers. */ ss.emit('data', buf); } this.term.on("data", ttyrec_chunk); @@ -171,15 +174,39 @@ var tag = ss.tag(); fs.unlink(ss.lock); ss.record.end(); + 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 (this.tag() in sessions) - this.term.kill('SIGHUP'); + 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); } TermSession.prototype = new events.EventEmitter(); @@ -315,64 +342,7 @@ "d": session.framebuf.toString("hex", 0, session.frameoff)})); } -function wsPlay(wsReq, game, pname, dims) { - tslog("wsPlay: running for %s/%s", game, pname); - tslog("Request is for %s", logins[wsReq.resourceURL.query["key"]].name); - var conn; - var session; - /* Listeners on the WebSocket */ - function messageH(message) { - var parsedMsg = getMsgWS(message); - if (parsedMsg.t == 'q') { - session.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"); - session.write(keybuf); - } - } - function closeH() { - session.close(); - } - /* These listen on the TermSession. */ - function dataH(chunk) { - var msg = {}; - msg.t = "d"; - msg.d = chunk.toString("hex"); - conn.sendUTF(JSON.stringify(msg)); - } - function exitH() { - if (conn.connected) - conn.sendUTF(JSON.stringify({"t": "q"})); - conn.close(); - session.removeListener('data', dataH); - session.removeListener('exit', exitH); - } - var handlers = {'data': dataH, 'exit': exitH}; - session = new TermSession(game, pname, dims, handlers); - if (!session.failed) { - var tag = session.game.uname + "/" + session.pname; - var reply = {"t": "s", "tag": tag, "w": session.w, "h": session.h, - "p": session.pname, "g": session.game.uname}; - tslog("Accepting for %s", tag); - tslog("Request is for %s", logins[wsReq.resourceURL.query["key"]].name); - tslog("Session is for %s", session.pname); - conn = wsReq.accept(null, wsReq.origin); - conn.sendUTF(JSON.stringify(reply)); - conn.on('message', messageH); - conn.on('close', closeH); - } - else { - wsReq.reject(500, errorcodes[5]); - tslog("Unable to allocate TTY for %s", game); - } -} - -function wsStart(wsReq) { +function wsStartGame(wsReq) { var playmatch = wsReq.resourceURL.pathname.match(/^\/play\/([^\/]*)$/); if (!playmatch[1] || !(playmatch[1] in games)) { wsReq.reject(404, errorcodes[2]); @@ -393,14 +363,14 @@ return; } var pname = logins[lkey].name; - var dims = [wsReq.resourceURL.query.h, wsReq.resourceURL.query.w]; function progcallback(err, fname) { if (fname) { wsReq.reject(404, errorcodes[4]); tslog("%s is already playing %s", pname, gname); } - else - wsPlay(wsReq, gname, pname, dims); + else { + new TermSession(gname, pname, wsReq); + } }; checkprogress(pname, games[gname], progcallback, []); } @@ -1044,7 +1014,7 @@ tslog("Game %s is being watched via WebSockets", tsession.tag()); } else if (playmatch !== null) { - wsStart(wsRequest); + wsStartGame(wsRequest); } else if (wsRequest.resourceURL.pathname == "/status") { var conn = wsRequest.accept(null, wsRequest.origin);