Mercurial > hg > rlgwebd
comparison rlgwebd.js @ 107:b64e31c5ec31
RLG-Web server: add playing through WebSockets.
Games can be played with a WebSocket connection to
/play/<game>?key=<loginkey>&w=<cols>&h=<rows>
| author | John "Elwin" Edwards <elwin@sdf.org> |
|---|---|
| date | Sun, 15 Jul 2012 09:04:39 -0700 |
| parents | 7d444ba4739e |
| children | 67b393f10c2b |
comparison
equal
deleted
inserted
replaced
| 106:dc1414faee19 | 107:b64e31c5ec31 |
|---|---|
| 180 delete sessions[id]; | 180 delete sessions[id]; |
| 181 tslog("Game %s ended.", id); | 181 tslog("Game %s ended.", id); |
| 182 gamemux.emit('end', id); | 182 gamemux.emit('end', id); |
| 183 }); | 183 }); |
| 184 this.close = function () { | 184 this.close = function () { |
| 185 this.term.kill('SIGHUP'); | 185 if (this.sessid in sessions) |
| 186 this.term.kill('SIGHUP'); | |
| 186 }; | 187 }; |
| 187 } | 188 } |
| 188 TermSession.prototype = new events.EventEmitter(); | 189 TermSession.prototype = new events.EventEmitter(); |
| 189 | 190 |
| 190 function Watcher(session) { | 191 function Watcher(session) { |
| 383 })); | 384 })); |
| 384 conn.sendUTF(JSON.stringify({"t": "d", | 385 conn.sendUTF(JSON.stringify({"t": "d", |
| 385 "d": session.framebuf.toString("hex", 0, session.frameoff)})); | 386 "d": session.framebuf.toString("hex", 0, session.frameoff)})); |
| 386 } | 387 } |
| 387 | 388 |
| 389 function wsPlay(wsReq, game, lkey, dims) { | |
| 390 var conn; | |
| 391 var session; | |
| 392 /* Listeners on the WebSocket */ | |
| 393 function messageH(message) { | |
| 394 var parsedMsg = getMsgWS(message); | |
| 395 if (parsedMsg.t == 'q') { | |
| 396 session.close(); | |
| 397 } | |
| 398 else if (parsedMsg.t == 'd') { | |
| 399 var hexstr = parsedMsg.d.replace(/[^0-9a-f]/gi, ""); | |
| 400 if (hexstr.length % 2 != 0) { | |
| 401 hexstr = hexstr.slice(0, -1); | |
| 402 } | |
| 403 var keybuf = new Buffer(hexstr, "hex"); | |
| 404 session.write(keybuf); | |
| 405 } | |
| 406 } | |
| 407 function closeH() { | |
| 408 session.close(); | |
| 409 } | |
| 410 /* These listen on the TermSession. */ | |
| 411 function openH(success, id) { | |
| 412 if (success) { | |
| 413 var reply = {"t": "s", "id": id, "w": sessions[id].w, "h": | |
| 414 sessions[id].h, "p": sessions[id].pname, "g": game}; | |
| 415 conn = wsReq.accept(null, wsReq.origin); | |
| 416 conn.sendUTF(JSON.stringify(reply)); | |
| 417 conn.on('message', messageH); | |
| 418 conn.on('close', closeH); | |
| 419 } | |
| 420 else { | |
| 421 wsReq.reject(500, errorcodes[5]); | |
| 422 tslog("Unable to allocate TTY for %s", game); | |
| 423 } | |
| 424 } | |
| 425 function dataH(chunk) { | |
| 426 var msg = {}; | |
| 427 msg.t = "d"; | |
| 428 msg.d = chunk.toString("hex"); | |
| 429 conn.sendUTF(JSON.stringify(msg)); | |
| 430 } | |
| 431 function exitH() { | |
| 432 if (conn.connected) | |
| 433 conn.sendUTF(JSON.stringify({"t": "q"})); | |
| 434 conn.close(); | |
| 435 session.removeListener('open', openH); | |
| 436 session.removeListener('data', dataH); | |
| 437 session.removeListener('exit', exitH); | |
| 438 } | |
| 439 var handlers = {'open': openH, 'data': dataH, 'exit': exitH}; | |
| 440 session = new TermSession(game, lkey, dims, handlers); | |
| 441 } | |
| 442 | |
| 443 function wsStart(wsReq) { | |
| 444 var playmatch = wsReq.resourceURL.pathname.match(/^\/play\/([^\/]*)$/); | |
| 445 if (!playmatch[1] || !(playmatch[1] in games)) { | |
| 446 wsReq.reject(404, errorcodes[2]); | |
| 447 return; | |
| 448 } | |
| 449 var gname = playmatch[1]; | |
| 450 if (!allowlogin) { | |
| 451 wsReq.reject(404, errorcodes[6]); | |
| 452 return; | |
| 453 } | |
| 454 if (!("key" in wsReq.resourceURL.query)) { | |
| 455 wsReq.reject(404, "No key given."); | |
| 456 return; | |
| 457 } | |
| 458 var lkey = wsReq.resourceURL.query["key"]; | |
| 459 if (!(lkey in logins)) { | |
| 460 wsReq.reject(404, errorcodes[1]); | |
| 461 return; | |
| 462 } | |
| 463 var pname = logins[lkey].name; | |
| 464 var dims = [wsReq.resourceURL.query.h, wsReq.resourceURL.query.w]; | |
| 465 function progcallback(err, fname) { | |
| 466 if (fname) { | |
| 467 wsReq.reject(404, errorcodes[4]); | |
| 468 tslog("%s is already playing %s", pname, gname); | |
| 469 } | |
| 470 else | |
| 471 wsPlay(wsReq, gname, lkey, dims); | |
| 472 }; | |
| 473 checkprogress(pname, games[gname], progcallback, []); | |
| 474 } | |
| 475 | |
| 388 /* Some functions which check whether a player is currently playing or | 476 /* Some functions which check whether a player is currently playing or |
| 389 * has a saved game. Maybe someday they will provide information on | 477 * has a saved game. Maybe someday they will provide information on |
| 390 * the game. */ | 478 * the game. */ |
| 391 function checkprogress(user, game, callback, args) { | 479 function checkprogress(user, game, callback, args) { |
| 392 var progressdir = "/dgldir/inprogress-" + game.uname; | 480 var progressdir = "/dgldir/inprogress-" + game.uname; |
| 526 return {}; | 614 return {}; |
| 527 } | 615 } |
| 528 if (typeof(jsonobj) != "object") | 616 if (typeof(jsonobj) != "object") |
| 529 return {}; | 617 return {}; |
| 530 return jsonobj; | 618 return jsonobj; |
| 619 } | |
| 620 | |
| 621 function getMsgWS(msgObj) { | |
| 622 if (msgObj.type != "utf8") | |
| 623 return {}; | |
| 624 return getMsg(msgObj.utf8Data); | |
| 531 } | 625 } |
| 532 | 626 |
| 533 function reaper() { | 627 function reaper() { |
| 534 var now = new Date(); | 628 var now = new Date(); |
| 535 function reapcheck(session) { | 629 function reapcheck(session) { |
| 1005 if (!(client instanceof Player)) { | 1099 if (!(client instanceof Player)) { |
| 1006 sendError(res, 7, "Watching", true); | 1100 sendError(res, 7, "Watching", true); |
| 1007 return; | 1101 return; |
| 1008 } | 1102 } |
| 1009 /* process the keys */ | 1103 /* process the keys */ |
| 1010 hexstr = formdata.d.replace(/[^0-9a-f]/gi, ""); | 1104 var hexstr = formdata.d.replace(/[^0-9a-f]/gi, ""); |
| 1011 if (hexstr.length % 2 != 0) { | 1105 if (hexstr.length % 2 != 0) { |
| 1012 sendError(res, 2, "incomplete byte", true); | 1106 sendError(res, 2, "incomplete byte", true); |
| 1013 return; | 1107 return; |
| 1014 } | 1108 } |
| 1015 keybuf = new Buffer(hexstr, "hex"); | 1109 var keybuf = new Buffer(hexstr, "hex"); |
| 1016 client.write(keybuf, formdata.n); | 1110 client.write(keybuf, formdata.n); |
| 1017 } | 1111 } |
| 1018 readFeed(client, res); | 1112 readFeed(client, res); |
| 1019 } | 1113 } |
| 1020 else if (target == "/login") { | 1114 else if (target == "/login") { |
| 1064 | 1158 |
| 1065 } | 1159 } |
| 1066 | 1160 |
| 1067 function wsHandler(wsRequest) { | 1161 function wsHandler(wsRequest) { |
| 1068 var watchmatch = wsRequest.resource.match(/^\/watch\/([0-9]*)$/); | 1162 var watchmatch = wsRequest.resource.match(/^\/watch\/([0-9]*)$/); |
| 1163 var playmatch = wsRequest.resource.match(/^\/play\//); | |
| 1069 if (watchmatch !== null) { | 1164 if (watchmatch !== null) { |
| 1070 if (watchmatch[1] && Number(watchmatch[1]) in sessions) { | 1165 if (watchmatch[1] && Number(watchmatch[1]) in sessions) { |
| 1071 var tsession = sessions[Number(watchmatch[1])]; | 1166 var tsession = sessions[Number(watchmatch[1])]; |
| 1072 var conn = wsRequest.accept(null, wsRequest.origin); | 1167 var conn = wsRequest.accept(null, wsRequest.origin); |
| 1073 new wsWatcher(conn, tsession); | 1168 new wsWatcher(conn, tsession); |
| 1074 tslog("Game %d is being watched via WebSockets", tsession.sessid); | 1169 tslog("Game %d is being watched via WebSockets", tsession.sessid); |
| 1075 } | 1170 } |
| 1076 else | 1171 else |
| 1077 wsRequest.reject(404, errorcodes[7]); | 1172 wsRequest.reject(404, errorcodes[7]); |
| 1078 } | 1173 } |
| 1079 else if (wsRequest.resource == "/status") { | 1174 else if (playmatch !== null) { |
| 1175 wsStart(wsRequest); | |
| 1176 } | |
| 1177 else if (wsRequest.resourceURL.pathname == "/status") { | |
| 1080 var conn = wsRequest.accept(null, wsRequest.origin); | 1178 var conn = wsRequest.accept(null, wsRequest.origin); |
| 1081 var tell = function () { | 1179 var tell = function () { |
| 1082 getStatus(function (info) { | 1180 getStatus(function (info) { |
| 1083 info["t"] = "t"; | 1181 info["t"] = "t"; |
| 1084 conn.sendUTF(JSON.stringify(info)); | 1182 conn.sendUTF(JSON.stringify(info)); |
