# HG changeset patch # User John "Elwin" Edwards # Date 1342156575 25200 # Node ID 3dbfdaf626232f96d05fc54093bf3e90c2c30716 # Parent 2880cc4372bd2a05ba284daa98c59105e288a71e RLG-Web: begin converting to WebSockets. Use WebSockets for watching, if the browser supports it. Functionality is not complete yet. diff -r 2880cc4372bd -r 3dbfdaf62623 rlgterm.js --- a/rlgterm.js Thu Jul 12 07:58:23 2012 -0700 +++ b/rlgterm.js Thu Jul 12 22:16:15 2012 -0700 @@ -82,7 +82,9 @@ lname: null, lcred: null, /* Whether the game is being played or just watched. */ - playing: false + playing: false, + /* WebSocket for communication */ + sock: null }; /* The interval ID for checking the status of current games. */ @@ -655,6 +657,10 @@ function startwatching(gamenumber) { if (session.id != null) return; + if (WebSocket) { + wsWatch(gamenumber); + return; + } var wmsg = {"n": Number(gamenumber)}; var req = new XMLHttpRequest(); req.onerror = errHandler; @@ -692,6 +698,27 @@ return watcher; } +function wsWatch(gamenumber) { + var sockurl = "ws://localhost:8080/watch/" + String(gamenumber); + var ws = new WebSocket(sockurl); + ws.onopen = function (event) { + session.id = true; + session.sock = ws; + message("You are now watching game #" + gamenumber + "."); + setmode("watch"); + }; + ws.onmessage = function (event) { + var msgObject = JSON.parse(event.data); + if (msgObject.t == 'd') { + writeData(msgObject.d); + } + }; + ws.onclose = function (event) { + session.sock = null; + gameover(); + }; +} + function formreg(ev) { ev.preventDefault(); if (session.id != null) @@ -775,6 +802,10 @@ function stop() { if (!session.id) return; + if (session.sock) { + session.sock.close(); + return; + } var req = new XMLHttpRequest(); req.onerror = errHandler; req.onreadystatechange = function () { diff -r 2880cc4372bd -r 3dbfdaf62623 rlgwebd.js --- a/rlgwebd.js Thu Jul 12 07:58:23 2012 -0700 +++ b/rlgwebd.js Thu Jul 12 22:16:15 2012 -0700 @@ -11,6 +11,7 @@ var child_process = require('child_process'); var daemon = require(path.join(localModules, "daemon")); var pty = require(path.join(localModules, "pty.js")); +var WebSocketServer = require(path.join(localModules, "websocket")).server; /* Configuration variables */ // These first two files are NOT in the chroot. @@ -130,6 +131,8 @@ * with a complete screen. */ this.framebuf = new Buffer(1024); this.frameoff = 0; + /* List of WebSockets watching the game. */ + this.watchsocks = []; logins[lkey].sessions.push(this.sessid); /* END setup */ function ttyrec_chunk(datastr) { @@ -168,6 +171,22 @@ this.write = function(data) { this.term.write(data); }; + // WebSocket watchers. + this.addWS = function (conn) { + this.watchsocks.push(conn); + }; + this.removeWS = function (conn) { + var i = this.watchsocks.indexOf(conn); + if (i >= 0) { + if (conn.connected) + conn.close(); + this.watchsocks.splice(i, 1); + return true; + } + else + return false; + }; + // Teardown. this.term.on("exit", function () { fs.unlink(ss.lock); ss.record.end(); @@ -354,6 +373,27 @@ this.session = new TermSession(gamename, lkey, dims, handlers); } +// Also known as WebSocketAndTermSessionClosureGlueFactory +function wsWatcher(conn, session) { + var ss = this; // is this even needed? + var dataH = function(buf) { + conn.sendUTF(JSON.stringify({"t": "d", "d": buf.toString("hex")})); + }; + var exitH = function() { + if (conn.connected) + conn.close(); + } + session.on('data', dataH); + session.on('exit', exitH); + conn.on('close', function(code, desc) { + session.removeListener('data', dataH); + session.removeListener('exit', exitH); + tslog("A WebSocket watcher has left game %d", session.sessid); + }); + conn.sendUTF(JSON.stringify({"t": "d", + "d": session.framebuf.toString("hex", 0, session.frameoff)})); +} + /* Some functions which check whether a player is currently playing or * has a saved game. Maybe someday they will provide information on * the game. */ @@ -1029,6 +1069,18 @@ } +function wsHandler(wsRequest) { + var urlmatch = wsRequest.resource.match(/^\/watch\/([0-9]*)$/); + if (urlmatch !== null && urlmatch[1] && Number(urlmatch[1]) in sessions) { + var tsession = sessions[Number(urlmatch[1])]; + var conn = wsRequest.accept(null, wsRequest.origin); + new wsWatcher(conn, tsession); + tslog("Game %d is being watched via WebSockets", tsession.sessid); + } + else + wsRequest.reject(404, errorcodes[7]); +} + function shutdown () { httpServer.close(); httpServer.removeAllListeners('request'); @@ -1066,6 +1118,7 @@ } var httpServer; // declare here so shutdown() can find it +var wsServer; /* This could be nonblocking, but nothing else can start yet anyway. */ if (fs.existsSync(ctlsocket)) { @@ -1106,5 +1159,8 @@ httpServer.listen(httpPort); tslog('rlgwebd running on port %d', httpPort); setInterval(reaper, playtimeout / 4); + wsServer = new WebSocketServer({"httpServer": httpServer}); + wsServer.on("request", wsHandler); + tslog('WebSockets are online'); });