# HG changeset patch # User John "Elwin" Edwards # Date 1342368279 25200 # Node ID b64e31c5ec31c9488c22dc396129517d844b39e8 # Parent dc1414faee19f5cb1bc7a725b36965f1b3b092b2 RLG-Web server: add playing through WebSockets. Games can be played with a WebSocket connection to /play/?key=&w=&h= diff -r dc1414faee19 -r b64e31c5ec31 rlgwebd.js --- a/rlgwebd.js Sat Jul 14 10:11:13 2012 -0700 +++ b/rlgwebd.js Sun Jul 15 09:04:39 2012 -0700 @@ -182,7 +182,8 @@ gamemux.emit('end', id); }); this.close = function () { - this.term.kill('SIGHUP'); + if (this.sessid in sessions) + this.term.kill('SIGHUP'); }; } TermSession.prototype = new events.EventEmitter(); @@ -385,6 +386,93 @@ "d": session.framebuf.toString("hex", 0, session.frameoff)})); } +function wsPlay(wsReq, game, lkey, dims) { + 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 openH(success, id) { + if (success) { + var reply = {"t": "s", "id": id, "w": sessions[id].w, "h": + sessions[id].h, "p": sessions[id].pname, "g": game}; + 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 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('open', openH); + session.removeListener('data', dataH); + session.removeListener('exit', exitH); + } + var handlers = {'open': openH, 'data': dataH, 'exit': exitH}; + session = new TermSession(game, lkey, dims, handlers); +} + +function wsStart(wsReq) { + var playmatch = wsReq.resourceURL.pathname.match(/^\/play\/([^\/]*)$/); + if (!playmatch[1] || !(playmatch[1] in games)) { + wsReq.reject(404, errorcodes[2]); + return; + } + var gname = playmatch[1]; + if (!allowlogin) { + wsReq.reject(404, errorcodes[6]); + return; + } + if (!("key" in wsReq.resourceURL.query)) { + wsReq.reject(404, "No key given."); + return; + } + var lkey = wsReq.resourceURL.query["key"]; + if (!(lkey in logins)) { + wsReq.reject(404, errorcodes[1]); + 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, lkey, dims); + }; + checkprogress(pname, games[gname], progcallback, []); +} + /* Some functions which check whether a player is currently playing or * has a saved game. Maybe someday they will provide information on * the game. */ @@ -530,6 +618,12 @@ return jsonobj; } +function getMsgWS(msgObj) { + if (msgObj.type != "utf8") + return {}; + return getMsg(msgObj.utf8Data); +} + function reaper() { var now = new Date(); function reapcheck(session) { @@ -1007,12 +1101,12 @@ return; } /* process the keys */ - hexstr = formdata.d.replace(/[^0-9a-f]/gi, ""); + var hexstr = formdata.d.replace(/[^0-9a-f]/gi, ""); if (hexstr.length % 2 != 0) { sendError(res, 2, "incomplete byte", true); return; } - keybuf = new Buffer(hexstr, "hex"); + var keybuf = new Buffer(hexstr, "hex"); client.write(keybuf, formdata.n); } readFeed(client, res); @@ -1066,6 +1160,7 @@ function wsHandler(wsRequest) { var watchmatch = wsRequest.resource.match(/^\/watch\/([0-9]*)$/); + var playmatch = wsRequest.resource.match(/^\/play\//); if (watchmatch !== null) { if (watchmatch[1] && Number(watchmatch[1]) in sessions) { var tsession = sessions[Number(watchmatch[1])]; @@ -1076,7 +1171,10 @@ else wsRequest.reject(404, errorcodes[7]); } - else if (wsRequest.resource == "/status") { + else if (playmatch !== null) { + wsStart(wsRequest); + } + else if (wsRequest.resourceURL.pathname == "/status") { var conn = wsRequest.accept(null, wsRequest.origin); var tell = function () { getStatus(function (info) {