comparison rlgwebd.js @ 104:7d444ba4739e

RLG-Web server: send status events over WebSockets. A WebSocket connection to /status will be sent periodic listings, along with notifications of the beginning and end of games.
author John "Elwin" Edwards <elwin@sdf.org>
date Fri, 13 Jul 2012 22:26:20 -0700
parents f30495f7ede8
children b64e31c5ec31
comparison
equal deleted inserted replaced
103:f30495f7ede8 104:7d444ba4739e
60 var logins = {}; 60 var logins = {};
61 var sessions = {}; 61 var sessions = {};
62 var clients = {}; 62 var clients = {};
63 var allowlogin = true; 63 var allowlogin = true;
64 var nextsession = 0; 64 var nextsession = 0;
65 var gamemux = new events.EventEmitter();
65 66
66 /* Constructor. A TermSession handles a pty and the game running on it. 67 /* Constructor. A TermSession handles a pty and the game running on it.
67 * game: (String) Name of the game to launch. 68 * game: (String) Name of the game to launch.
68 * lkey: (String, key) The user's id, a key into logins. 69 * lkey: (String, key) The user's id, a key into logins.
69 * dims: (Array [Number, Number]) Height and width of the pty. 70 * dims: (Array [Number, Number]) Height and width of the pty.
116 "name": "xterm-256color"}; 117 "name": "xterm-256color"};
117 this.term = pty.spawn(this.game.path, args, spawnopts); 118 this.term = pty.spawn(this.game.path, args, spawnopts);
118 tslog("%s playing %s (index %d, pid %d)", this.pname, this.game.uname, 119 tslog("%s playing %s (index %d, pid %d)", this.pname, this.game.uname,
119 this.sessid, this.term.pid); 120 this.sessid, this.term.pid);
120 this.emit('open', true, this.sessid); 121 this.emit('open', true, this.sessid);
122 gamemux.emit('begin', this.sessid, this.pname, this.game.uname);
121 /* Set up the lockfile and ttyrec */ 123 /* Set up the lockfile and ttyrec */
122 var ts = timestamp(); 124 var ts = timestamp();
123 var progressdir = "/dgldir/inprogress-" + this.game.uname; 125 var progressdir = "/dgldir/inprogress-" + this.game.uname;
124 this.lock = path.join(progressdir, this.pname + ":node:" + ts + ".ttyrec"); 126 this.lock = path.join(progressdir, this.pname + ":node:" + ts + ".ttyrec");
125 var lmsg = this.term.pid.toString() + '\n' + this.w + '\n' + this.h + '\n'; 127 var lmsg = this.term.pid.toString() + '\n' + this.w + '\n' + this.h + '\n';
175 ss.record.end(); 177 ss.record.end();
176 ss.emit('exit'); 178 ss.emit('exit');
177 var id = ss.sessid; 179 var id = ss.sessid;
178 delete sessions[id]; 180 delete sessions[id];
179 tslog("Game %s ended.", id); 181 tslog("Game %s ended.", id);
182 gamemux.emit('end', id);
180 }); 183 });
181 this.close = function () { 184 this.close = function () {
182 this.term.kill('SIGHUP'); 185 this.term.kill('SIGHUP');
183 }; 186 };
184 } 187 }
1060 req.on('end', respond); 1063 req.on('end', respond);
1061 1064
1062 } 1065 }
1063 1066
1064 function wsHandler(wsRequest) { 1067 function wsHandler(wsRequest) {
1065 var urlmatch = wsRequest.resource.match(/^\/watch\/([0-9]*)$/); 1068 var watchmatch = wsRequest.resource.match(/^\/watch\/([0-9]*)$/);
1066 if (urlmatch !== null && urlmatch[1] && Number(urlmatch[1]) in sessions) { 1069 if (watchmatch !== null) {
1067 var tsession = sessions[Number(urlmatch[1])]; 1070 if (watchmatch[1] && Number(watchmatch[1]) in sessions) {
1071 var tsession = sessions[Number(watchmatch[1])];
1072 var conn = wsRequest.accept(null, wsRequest.origin);
1073 new wsWatcher(conn, tsession);
1074 tslog("Game %d is being watched via WebSockets", tsession.sessid);
1075 }
1076 else
1077 wsRequest.reject(404, errorcodes[7]);
1078 }
1079 else if (wsRequest.resource == "/status") {
1068 var conn = wsRequest.accept(null, wsRequest.origin); 1080 var conn = wsRequest.accept(null, wsRequest.origin);
1069 new wsWatcher(conn, tsession); 1081 var tell = function () {
1070 tslog("Game %d is being watched via WebSockets", tsession.sessid); 1082 getStatus(function (info) {
1083 info["t"] = "t";
1084 conn.sendUTF(JSON.stringify(info));
1085 });
1086 }
1087 var beginH = function (n, name, game) {
1088 conn.sendUTF(JSON.stringify({"t": "b", "n": n, "p": name, "g": game}));
1089 };
1090 var listH = function (list) {
1091 conn.sendUTF(JSON.stringify(list));
1092 };
1093 var endH = function (n) {
1094 conn.sendUTF(JSON.stringify({"t": "e", "n": n}));
1095 };
1096 gamemux.on('begin', beginH);
1097 gamemux.on('list', listH);
1098 gamemux.on('end', endH);
1099 conn.on('message', tell);
1100 conn.on('close', function () {
1101 gamemux.removeListener('begin', beginH);
1102 gamemux.removeListener('list', listH);
1103 gamemux.removeListener('end', endH);
1104 });
1105 tell();
1071 } 1106 }
1072 else 1107 else
1073 wsRequest.reject(404, errorcodes[7]); 1108 wsRequest.reject(404, "No such resource.");
1109 }
1110
1111 function pushStatus() {
1112 getStatus(function(info) {
1113 info["t"] = "t";
1114 gamemux.emit('list', info);
1115 });
1074 } 1116 }
1075 1117
1076 function shutdown () { 1118 function shutdown () {
1077 httpServer.close(); 1119 httpServer.close();
1078 httpServer.removeAllListeners('request'); 1120 httpServer.removeAllListeners('request');
1152 tslog('rlgwebd running on port %d', httpPort); 1194 tslog('rlgwebd running on port %d', httpPort);
1153 setInterval(reaper, playtimeout / 4); 1195 setInterval(reaper, playtimeout / 4);
1154 wsServer = new WebSocketServer({"httpServer": httpServer}); 1196 wsServer = new WebSocketServer({"httpServer": httpServer});
1155 wsServer.on("request", wsHandler); 1197 wsServer.on("request", wsHandler);
1156 tslog('WebSockets are online'); 1198 tslog('WebSockets are online');
1199 setInterval(pushStatus, 4000);
1157 }); 1200 });
1158 1201