Mercurial > hg > rlgwebd
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 |