RLGWebD: excise polling.
WebSockets are supported nearly everywhere now. Listing current games and watching them are still broken.
This commit is contained in:
parent
920c6e8829
commit
c7fc6418ff
2 changed files with 36 additions and 666 deletions
338
rlgterm.js
338
rlgterm.js
|
|
@ -1,60 +1,5 @@
|
|||
/* rlgterm.js: Roguelike Gallery's driver for termemu.js */
|
||||
|
||||
// A state machine that keeps track of polling the server.
|
||||
var ajaxstate = {
|
||||
state: 0,
|
||||
timerID: null,
|
||||
clear: function () {
|
||||
if (this.timerID != null) {
|
||||
window.clearTimeout(this.timerID);
|
||||
this.timerID = null;
|
||||
}
|
||||
},
|
||||
set: function (ms) {
|
||||
this.clear();
|
||||
this.timerID = window.setTimeout(getData, ms);
|
||||
},
|
||||
gotdata: function () {
|
||||
this.set(1000);
|
||||
this.state = 1;
|
||||
},
|
||||
gotnothing: function () {
|
||||
if (this.state == 0) {
|
||||
this.set(1000);
|
||||
this.state = 1;
|
||||
}
|
||||
else if (this.state < 4) {
|
||||
this.set(4000);
|
||||
this.state++;
|
||||
}
|
||||
else if (session.playing) {
|
||||
if (this.state < 8) {
|
||||
this.set(15000);
|
||||
this.state++;
|
||||
}
|
||||
else {
|
||||
/* It's been over a minute. Stop polling. */
|
||||
this.clear();
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* If watching, it can't stop polling entirely, because there
|
||||
* are no POST events to start it up again. */
|
||||
this.set(10000);
|
||||
}
|
||||
},
|
||||
posted: function (wasdata) {
|
||||
if (wasdata) {
|
||||
this.set(1000);
|
||||
this.state = 1;
|
||||
}
|
||||
else {
|
||||
this.set(200);
|
||||
this.state = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* Data on the available games. */
|
||||
var games = {
|
||||
"rogue3": {
|
||||
|
|
@ -81,7 +26,7 @@ var games = {
|
|||
|
||||
var session = {
|
||||
/* The session id assigned by the server. */
|
||||
id: null,
|
||||
connect: false,
|
||||
/* Login name and key are now in sessionStorage. */
|
||||
/* Whether the game is being played or just watched. */
|
||||
playing: false,
|
||||
|
|
@ -165,119 +110,15 @@ function writeData(hexstr) {
|
|||
return;
|
||||
}
|
||||
|
||||
/* State for sending and receiving messages. */
|
||||
var nsend = 0; // The number of the next packet to send.
|
||||
var nrecv = 0; // The next packet expected.
|
||||
var msgQ = []; // Queue for out-of-order messages.
|
||||
|
||||
/* Processes a message from the server, returning true or false if it was a
|
||||
* data message with or without data, null if not data.
|
||||
* All non-special responseTexts should be handed directly to this function.
|
||||
*/
|
||||
function processMsg(msg) {
|
||||
var msgDicts;
|
||||
var havedata = null; // eventual return value
|
||||
try {
|
||||
msgDicts = JSON.parse(msg);
|
||||
} catch (e) {
|
||||
if (e instanceof SyntaxError)
|
||||
return null;
|
||||
}
|
||||
if (msgDicts.length === 0)
|
||||
return false;
|
||||
for (var j = 0; j < msgDicts.length; j++) {
|
||||
if (!msgDicts[j].t)
|
||||
continue;
|
||||
else if (msgDicts[j].t == "E") {
|
||||
if (msgDicts[j].c == 1 || msgDicts[j].c == 6 || msgDicts[j].c == 7) {
|
||||
gameover();
|
||||
if (msgDicts[j].c == 1) {
|
||||
logout();
|
||||
}
|
||||
}
|
||||
debug(1, "Server error: " + msgDicts[j].s);
|
||||
}
|
||||
// A data message
|
||||
else if (msgDicts[j].t == "d") {
|
||||
if (msgDicts[j].n === nrecv) {
|
||||
writeData(msgDicts[j].d);
|
||||
nrecv++;
|
||||
/* Process anything in the queue that's now ready. */
|
||||
var next;
|
||||
while ((next = msgQ.shift()) !== undefined) {
|
||||
writeData(next.d);
|
||||
nrecv++;
|
||||
}
|
||||
}
|
||||
else if (msgDicts[j].n > nrecv) {
|
||||
/* The current message comes after one still missing. Queue this one
|
||||
* for later use. */
|
||||
debug(1, "Got packet " + msgDicts[j].n + ", expected " + nrecv);
|
||||
msgQ[msgDicts[j].n - nrecv - 1] = msgDicts[j];
|
||||
}
|
||||
else {
|
||||
/* This message's number was encountered previously. */
|
||||
debug(1, "Discarding packet " + msgDicts[j].n + ", expected " + nrecv);
|
||||
}
|
||||
havedata = true;
|
||||
}
|
||||
else if (msgDicts[j].t == "T") {
|
||||
setTitle(msgDicts[j].d);
|
||||
}
|
||||
else if (msgDicts[j].t == "q") {
|
||||
gameover();
|
||||
}
|
||||
else {
|
||||
debug(1, "Unrecognized server message " + msg);
|
||||
}
|
||||
}
|
||||
return havedata;
|
||||
}
|
||||
|
||||
function getData() {
|
||||
if (session.id == null)
|
||||
return;
|
||||
var datareq = new XMLHttpRequest();
|
||||
var msg = JSON.stringify({"id": session.id, "t": "n"});
|
||||
datareq.onerror = errHandler;
|
||||
datareq.onreadystatechange = function () {
|
||||
if (datareq.readyState == 4 && datareq.status == 200) {
|
||||
var wasdata = processMsg(datareq.responseText);
|
||||
if (wasdata != null) {
|
||||
if (wasdata)
|
||||
ajaxstate.gotdata();
|
||||
else
|
||||
ajaxstate.gotnothing();
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
datareq.open('POST', '/feed', true);
|
||||
datareq.send(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
function postResponseHandler() {
|
||||
if (this.readyState == 4 && this.status == 200) {
|
||||
// We might want to do something with wasdata someday.
|
||||
var wasdata = processMsg(this.responseText);
|
||||
ajaxstate.posted();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function errHandler() {
|
||||
message("Unable to connect to the server.", "warn");
|
||||
}
|
||||
|
||||
function sendback(str) {
|
||||
/* For responding to terminal queries. */
|
||||
var msgDict = {"id": session.id, "t": "d", "n": nsend++, "d": str};
|
||||
var datareq = new XMLHttpRequest();
|
||||
datareq.onerror = errHandler;
|
||||
datareq.onreadystatechange = postResponseHandler;
|
||||
datareq.open('POST', '/feed', true);
|
||||
datareq.send(JSON.stringify(msgDict));
|
||||
if (session.sock) {
|
||||
session.sock.send(JSON.stringify({"t": "d", "d": str}));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -323,14 +164,7 @@ function sendkey(ev) {
|
|||
if (session.sock) {
|
||||
session.sock.send(JSON.stringify({"t": "d", "d": code}));
|
||||
}
|
||||
else {
|
||||
var datareq = new XMLHttpRequest();
|
||||
var msgDict = {"id": session.id, "t": "d", "n": nsend++, "d": code};
|
||||
datareq.onerror = errHandler;
|
||||
datareq.onreadystatechange = postResponseHandler;
|
||||
datareq.open('POST', '/feed', true);
|
||||
datareq.send(JSON.stringify(msgDict));
|
||||
}
|
||||
/* Otherwise it is disconnected */
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -386,14 +220,6 @@ function vkey(c) {
|
|||
if (session.sock) {
|
||||
session.sock.send(JSON.stringify({"t": "d", "d": keystr}));
|
||||
}
|
||||
else {
|
||||
var datareq = new XMLHttpRequest();
|
||||
var msgDict = {"id": session.id, "t": "d", "n": nsend++, "d": keystr};
|
||||
datareq.onerror = errHandler;
|
||||
datareq.onreadystatechange = postResponseHandler;
|
||||
datareq.open('POST', '/feed', true);
|
||||
datareq.send(JSON.stringify(msgDict));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -426,8 +252,8 @@ function setup() {
|
|||
break;
|
||||
}
|
||||
if (!window.WebSocket) {
|
||||
message("Your browser does not support WebSockets. You can still play, " +
|
||||
"but it will be slower, and may not work in the future.", "warn");
|
||||
message("Your browser does not support WebSockets. " +
|
||||
"This Web app will not work.", "warn");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -454,8 +280,7 @@ function togglectrl() {
|
|||
|
||||
function formlogin(ev) {
|
||||
ev.preventDefault();
|
||||
if (session.id != null)
|
||||
return;
|
||||
/* What to do if logged in already? */
|
||||
var loginmsg = {};
|
||||
loginmsg["name"] = document.getElementById("input_name").value;
|
||||
loginmsg["pw"] = document.getElementById("input_pw").value;
|
||||
|
|
@ -490,6 +315,7 @@ function formlogin(ev) {
|
|||
return;
|
||||
}
|
||||
|
||||
/* FIXME game list API has changed */
|
||||
function tableCurrent(gamelist) {
|
||||
var gamediv = document.getElementById("gametable");
|
||||
while (gamediv.children.length > 2)
|
||||
|
|
@ -533,7 +359,7 @@ function tableCurrent(gamelist) {
|
|||
function wsCurrent() {
|
||||
if (!window.WebSocket)
|
||||
return;
|
||||
if (session.id) {
|
||||
if (session.connect) {
|
||||
/* Don't bother with status if already playing/watching. */
|
||||
if (statsock) {
|
||||
statsock.close();
|
||||
|
|
@ -569,11 +395,12 @@ function wsCurrent() {
|
|||
}
|
||||
}
|
||||
|
||||
/* FIXME gamelist API has changed */
|
||||
function getcurrent(clear) {
|
||||
if (window.WebSocket) {
|
||||
return;
|
||||
}
|
||||
if (session.id || clear) {
|
||||
if (session.connect || clear) {
|
||||
if (statInterval) {
|
||||
window.clearInterval(statInterval);
|
||||
statInterval = null;
|
||||
|
|
@ -608,7 +435,7 @@ function getcurrent(clear) {
|
|||
}
|
||||
|
||||
function getchoices() {
|
||||
if (session.id != null || !("lcred" in sessionStorage))
|
||||
if (session.connect || !("lcred" in sessionStorage))
|
||||
return;
|
||||
var req = new XMLHttpRequest();
|
||||
req.onerror = errHandler;
|
||||
|
|
@ -680,62 +507,6 @@ function makeStarter(gname) {
|
|||
return starter;
|
||||
}
|
||||
|
||||
function startgame(game) {
|
||||
if (session.id != null || !("lcred" in sessionStorage))
|
||||
return;
|
||||
if (window.WebSocket) {
|
||||
wsStart(game);
|
||||
return;
|
||||
}
|
||||
var smsg = {};
|
||||
smsg["key"] = sessionStorage.getItem("lcred");
|
||||
smsg["game"] = game.uname;
|
||||
smsg["h"] = 24;
|
||||
smsg["w"] = 80;
|
||||
var req = new XMLHttpRequest();
|
||||
req.onerror = errHandler;
|
||||
req.onreadystatechange = function () {
|
||||
if (req.readyState != 4 || req.status != 200)
|
||||
return;
|
||||
var reply = JSON.parse(req.responseText);
|
||||
if (reply.t == 's') {
|
||||
/* Success */
|
||||
session.id = reply.id;
|
||||
session.playing = true;
|
||||
termemu.resize(reply.h, reply.w);
|
||||
message("You are now playing " + game.name + ".");
|
||||
setmode("play");
|
||||
getData();
|
||||
}
|
||||
else if (reply.t == 'E') {
|
||||
if (reply.c == 1) {
|
||||
logout();
|
||||
message("The server forgot about you, please log in again.", "warn");
|
||||
}
|
||||
else if (reply.c == 4) {
|
||||
if (reply.s == "dgamelaunch") {
|
||||
message("You are already playing " + game.name + " over SSH.",
|
||||
"warn");
|
||||
}
|
||||
else {
|
||||
message("You are already playing " + game.name +
|
||||
" in another browser window.", "warn");
|
||||
}
|
||||
}
|
||||
else if (reply.c == 7) {
|
||||
message("The game is being saved, try again in a few seconds.");
|
||||
}
|
||||
else {
|
||||
message("The server says it can't start your game because \"" +
|
||||
reply.s + "\". This is probably a bug.", "warn");
|
||||
}
|
||||
}
|
||||
};
|
||||
req.open('POST', '/play', true);
|
||||
req.send(JSON.stringify(smsg));
|
||||
return;
|
||||
}
|
||||
|
||||
function makeStopper(gname) {
|
||||
if (!(gname in games))
|
||||
return null;
|
||||
|
|
@ -774,12 +545,17 @@ function stopgame(game) {
|
|||
return;
|
||||
}
|
||||
|
||||
function wsStart(game) {
|
||||
function startgame(game) {
|
||||
if (!("lcred" in sessionStorage) || session.connect)
|
||||
return;
|
||||
if (!window.WebSocket) {
|
||||
return;
|
||||
}
|
||||
var sockurl = "ws://" + window.location.host + "/play/" + game.uname;
|
||||
sockurl += "?key=" + sessionStorage.getItem("lcred") + "&w=80&h=24";
|
||||
ws = new WebSocket(sockurl);
|
||||
ws.onopen = function (event) {
|
||||
session.id = true;
|
||||
session.connect = true;
|
||||
session.playing = true;
|
||||
session.sock = ws;
|
||||
setmode("play");
|
||||
|
|
@ -800,55 +576,20 @@ function wsStart(game) {
|
|||
};
|
||||
}
|
||||
|
||||
function startwatching(gamenumber) {
|
||||
if (session.id != null)
|
||||
return;
|
||||
if (window.WebSocket) {
|
||||
wsWatch(gamenumber);
|
||||
return;
|
||||
}
|
||||
var wmsg = {"n": Number(gamenumber)};
|
||||
var req = new XMLHttpRequest();
|
||||
req.onerror = errHandler;
|
||||
req.onreadystatechange = function () {
|
||||
if (req.readyState != 4 || req.status != 200)
|
||||
return;
|
||||
var reply = JSON.parse(req.responseText);
|
||||
if (reply.t == 'w') {
|
||||
/* Success */
|
||||
session.id = reply.id;
|
||||
session.playing = false;
|
||||
termemu.resize(reply.h, reply.w);
|
||||
termemu.reset();
|
||||
termemu.toAltBuf();
|
||||
var pname = reply.p;
|
||||
var gname = games[reply.g].name;
|
||||
message("You are now watching " + pname + " play " + gname + ".");
|
||||
setmode("watch");
|
||||
getData();
|
||||
}
|
||||
else if (reply.t == 'E') {
|
||||
message("The game could not be watched: " + reply.s, "warn");
|
||||
getcurrent();
|
||||
}
|
||||
};
|
||||
req.open('POST', '/watch', true);
|
||||
req.send(JSON.stringify(wmsg));
|
||||
return;
|
||||
}
|
||||
|
||||
function makeWatcher(n) {
|
||||
function makeWatcher(t) {
|
||||
function watcher(ev) {
|
||||
startwatching(n);
|
||||
startwatching(t);
|
||||
}
|
||||
return watcher;
|
||||
}
|
||||
|
||||
function wsWatch(gamenumber) {
|
||||
var sockurl = "ws://" + window.location.host + "/watch/" + String(gamenumber);
|
||||
function startwatching(tag) {
|
||||
if (session.connect)
|
||||
return;
|
||||
var sockurl = "ws://" + window.location.host + "/watch/" + tag;
|
||||
var ws = new WebSocket(sockurl);
|
||||
ws.onopen = function (event) {
|
||||
session.id = true;
|
||||
session.connect = true;
|
||||
session.sock = ws;
|
||||
setmode("watch");
|
||||
};
|
||||
|
|
@ -874,7 +615,8 @@ function wsWatch(gamenumber) {
|
|||
|
||||
function formreg(ev) {
|
||||
ev.preventDefault();
|
||||
if (session.id != null)
|
||||
/* This ought to check for being logged in instead. */
|
||||
if (session.connect)
|
||||
return;
|
||||
var regmsg = {};
|
||||
regmsg["name"] = document.getElementById("regin_name").value;
|
||||
|
|
@ -925,20 +667,16 @@ function formreg(ev) {
|
|||
}
|
||||
|
||||
function gameover() {
|
||||
if (session.id == null)
|
||||
if (!session.connect)
|
||||
return;
|
||||
/* TODO IFACE2 If the end was unexpected, tell player the game was saved. */
|
||||
if (session.playing)
|
||||
message("Finished playing.");
|
||||
else
|
||||
message("Finished watching.");
|
||||
session.id = null;
|
||||
session.connect = false;
|
||||
session.playing = false;
|
||||
ajaxstate.clear();
|
||||
termemu.toNormBuf();
|
||||
nsend = 0;
|
||||
nrecv = 0;
|
||||
msgQ = [];
|
||||
if ("lcred" in sessionStorage)
|
||||
setmode("choose");
|
||||
else
|
||||
|
|
@ -952,24 +690,14 @@ function logout() {
|
|||
setmode("login");
|
||||
}
|
||||
|
||||
/* TODO determine whether this is needed */
|
||||
function stop() {
|
||||
if (!session.id)
|
||||
if (!session.connect)
|
||||
return;
|
||||
if (session.sock) {
|
||||
session.sock.close();
|
||||
return;
|
||||
}
|
||||
var req = new XMLHttpRequest();
|
||||
req.onerror = errHandler;
|
||||
req.onreadystatechange = function () {
|
||||
if (req.readyState == 4 && req.status == 200) {
|
||||
processMsg(req.responseText);
|
||||
return;
|
||||
}
|
||||
};
|
||||
req.open('POST', '/feed', true);
|
||||
req.send(JSON.stringify({"id": session.id, "t": "q"}));
|
||||
return;
|
||||
}
|
||||
|
||||
function setmode(mode, ev) {
|
||||
|
|
|
|||
364
rlgwebd.js
364
rlgwebd.js
|
|
@ -63,7 +63,6 @@ var games = {
|
|||
/* Global state */
|
||||
var logins = {};
|
||||
var sessions = {};
|
||||
var clients = {};
|
||||
var dglgames = {};
|
||||
var allowlogin = true;
|
||||
var gamemux = new events.EventEmitter();
|
||||
|
|
@ -193,178 +192,6 @@ function TermSession(game, lkey, dims, handlers) {
|
|||
}
|
||||
TermSession.prototype = new events.EventEmitter();
|
||||
|
||||
function Watcher(session) {
|
||||
var ss = this; // that
|
||||
this.session = session;
|
||||
this.alive = true;
|
||||
/* State for messaging. */
|
||||
this.nsend = 0;
|
||||
this.sendQ = [];
|
||||
/* Get a place in the table. */
|
||||
this.id = randkey(2);
|
||||
while (this.id in clients) {
|
||||
this.id = randkey(2);
|
||||
}
|
||||
clients[this.id] = this;
|
||||
/* Recreate the current screen state from the session's buffer. */
|
||||
this.sendQ.push({"t": "d", "n": this.nsend++,
|
||||
"d": session.framebuf.toString("hex", 0, session.frameoff)});
|
||||
function dataH(buf) {
|
||||
var reply = {};
|
||||
reply.t = "d";
|
||||
reply.n = ss.nsend++;
|
||||
reply.d = buf.toString("hex");
|
||||
ss.sendQ.push(reply);
|
||||
}
|
||||
function exitH() {
|
||||
ss.alive = false;
|
||||
ss.sendQ.push({"t": "q"});
|
||||
}
|
||||
session.on('data', dataH);
|
||||
session.on('exit', exitH);
|
||||
this.read = function() {
|
||||
/* Returns an array of all outstanding messages, empty if none. */
|
||||
var temp = this.sendQ;
|
||||
this.sendQ = [];
|
||||
/* Clean up if finished. */
|
||||
if (!this.alive) {
|
||||
delete clients[this.id];
|
||||
}
|
||||
return temp;
|
||||
};
|
||||
this.quit = function() {
|
||||
this.session.removeListener('data', dataH);
|
||||
this.session.removeListener('exit', exitH);
|
||||
delete clients[this.id];
|
||||
};
|
||||
}
|
||||
|
||||
function Player(gamename, lkey, dims, callback) {
|
||||
var ss = this;
|
||||
this.alive = false;
|
||||
/* State for messaging. */
|
||||
this.nsend = 0;
|
||||
this.nrecv = 0;
|
||||
this.sendQ = [];
|
||||
this.recvQ = []
|
||||
this.Qtimeout = null;
|
||||
/* Get a place in the table. */
|
||||
this.id = randkey(2);
|
||||
while (this.id in clients) {
|
||||
this.id = randkey(2);
|
||||
}
|
||||
clients[this.id] = this;
|
||||
|
||||
this.read = function() {
|
||||
var temp = this.sendQ;
|
||||
this.sendQ = [];
|
||||
/* Clean up if finished. */
|
||||
if (!this.alive) {
|
||||
clearTimeout(this.Qtimeout);
|
||||
delete clients[this.id];
|
||||
}
|
||||
return temp;
|
||||
};
|
||||
this.write = function (data, n) {
|
||||
if (!this.alive || typeof (n) != "number") {
|
||||
return;
|
||||
}
|
||||
var oindex = n - this.nrecv;
|
||||
if (oindex === 0) {
|
||||
this.session.write(data);
|
||||
this.nrecv++;
|
||||
var next;
|
||||
while ((next = this.recvQ.shift()) !== undefined) {
|
||||
this.session.write(next);
|
||||
this.nrecv++;
|
||||
}
|
||||
if (this.recvQ.length == 0 && this.Qtimeout) {
|
||||
clearTimeout(this.Qtimeout);
|
||||
this.Qtimeout = null;
|
||||
}
|
||||
}
|
||||
else if (oindex > 0 && oindex <= 1024) {
|
||||
tslog("Client %s: Stashing message %d at %d", this.id, n, oindex - 1);
|
||||
this.recvQ[oindex - 1] = data;
|
||||
if (!this.Qtimeout) {
|
||||
var nextn = this.nrecv + this.recvQ.length + 1;
|
||||
this.Qtimeout = setTimeout(this.flushQ, 30000, this, nextn);
|
||||
}
|
||||
}
|
||||
/* Otherwise, discard it */
|
||||
return;
|
||||
};
|
||||
this.flushQ = function (client, n) {
|
||||
/* Callback for when an unreceived message times out.
|
||||
* n is the first empty space that will not be given up on. */
|
||||
if (!client.alive || client.nrecv >= n)
|
||||
return;
|
||||
client.nrecv++;
|
||||
var next;
|
||||
/* Clear the queue up to n */
|
||||
while (client.nrecv < n) {
|
||||
next = client.recvQ.shift();
|
||||
if (next !== undefined)
|
||||
client.session.write(next);
|
||||
client.nrecv++;
|
||||
}
|
||||
/* Clear out anything that's ready. */
|
||||
while ((next = client.recvQ.shift()) !== undefined) {
|
||||
client.session.write(next);
|
||||
client.nrecv++;
|
||||
}
|
||||
/* Now set another timeout if necessary. */
|
||||
if (client.recvQ.length != 0) {
|
||||
var nextn = client.nrecv + client.recvQ.length + 1;
|
||||
client.Qtimeout = setTimeout(client.flushQ, 30000, client, nextn);
|
||||
}
|
||||
tslog("Flushing queue for player %s", player.id);
|
||||
};
|
||||
this.reset = function () {
|
||||
/* To be called when the game is taken over. */
|
||||
if (this.Qtimeout) {
|
||||
clearTimeout(this.Qtimeout);
|
||||
this.Qtimeout = null;
|
||||
}
|
||||
for (var i = 0; i < this.recvQ.length; i++) {
|
||||
if (this.recvQ[i] !== undefined) {
|
||||
this.session.write(this.recvQ[i]);
|
||||
}
|
||||
}
|
||||
this.recvQ = [];
|
||||
this.nrecv = 0;
|
||||
this.nsend = 0;
|
||||
this.sendQ = [{"t": "d", "n": this.nsend++,
|
||||
"d": this.session.framebuf.toString("hex", 0, this.session.frameoff)}];
|
||||
};
|
||||
this.quit = function() {
|
||||
if (this.alive)
|
||||
this.session.close();
|
||||
};
|
||||
function openH(success, tag) {
|
||||
if (success) {
|
||||
ss.alive = true;
|
||||
ss.session = sessions[tag];
|
||||
ss.h = sessions[tag].h;
|
||||
ss.w = sessions[tag].w;
|
||||
}
|
||||
callback(ss, success);
|
||||
}
|
||||
function dataH(chunk) {
|
||||
var reply = {};
|
||||
reply.t = "d";
|
||||
reply.n = ss.nsend++;
|
||||
reply.d = chunk.toString("hex");
|
||||
ss.sendQ.push(reply);
|
||||
}
|
||||
function exitH() {
|
||||
ss.alive = false;
|
||||
ss.sendQ.push({"t": "q"});
|
||||
}
|
||||
var handlers = {'open': openH, 'data': dataH, 'exit': exitH};
|
||||
this.session = new TermSession(gamename, lkey, dims, handlers);
|
||||
}
|
||||
|
||||
// Also known as WebSocketAndTermSessionClosureGlueFactory
|
||||
function wsWatcher(conn, session) {
|
||||
var ss = this; // is this even needed?
|
||||
|
|
@ -716,103 +543,6 @@ function login(req, res, formdata) {
|
|||
return;
|
||||
}
|
||||
|
||||
function startgame(req, res, formdata) {
|
||||
if (!allowlogin) {
|
||||
sendError(res, 6, null);
|
||||
return;
|
||||
}
|
||||
if (!("key" in formdata)) {
|
||||
sendError(res, 2, "No key given.");
|
||||
return;
|
||||
}
|
||||
else if (!("game" in formdata)) {
|
||||
sendError(res, 2, "No game specified.");
|
||||
return;
|
||||
}
|
||||
var lkey = String(formdata["key"]);
|
||||
if (!(lkey in logins)) {
|
||||
sendError(res, 1, null);
|
||||
return;
|
||||
}
|
||||
var username = logins[lkey].name;
|
||||
var gname = formdata["game"];
|
||||
// If dims are not given or invalid, the constructor will handle it.
|
||||
var dims = [formdata["h"], formdata["w"]];
|
||||
if (!(gname in games)) {
|
||||
sendError(res, 2, "No such game: " + gname);
|
||||
tslog("Request for nonexistant game \"%s\"", gname);
|
||||
return;
|
||||
}
|
||||
// A callback to pass to the game-in-progress checker.
|
||||
var launch = function(err, fname) {
|
||||
var nodematch = new RegExp("^" + username + ":node:");
|
||||
if (fname && (fname.match(nodematch) === null)) {
|
||||
/* It's being played in dgamelaunch. */
|
||||
sendError(res, 4, "dgamelaunch");
|
||||
tslog("%s is already playing %s", username, gname);
|
||||
return;
|
||||
}
|
||||
// Game starting has been approved.
|
||||
var respondlaunch = function(nclient, success) {
|
||||
if (success) {
|
||||
res.writeHead(200, {'Content-Type': 'application/json'});
|
||||
var reply = {"t": "s", "id": nclient.id, "w": nclient.w, "h":
|
||||
nclient.h, "p": username, "g": gname};
|
||||
res.write(JSON.stringify(reply));
|
||||
res.end();
|
||||
}
|
||||
else {
|
||||
sendError(res, 5, "Failed to open TTY");
|
||||
tslog("Unable to allocate TTY for %s", gname);
|
||||
}
|
||||
};
|
||||
if (fname) {
|
||||
for (var cid in clients) {
|
||||
cli = clients[cid];
|
||||
if ((cli instanceof Player) &&
|
||||
cli.session.pname == username &&
|
||||
cli.session.game.uname == gname) {
|
||||
cli.reset();
|
||||
respondlaunch(cli, true);
|
||||
tslog("Game %d has been taken over.", cli.session.sessid);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* If there's no player, it's a WebSocket game, and shouldn't be
|
||||
* seized. */
|
||||
sendError(res, 4, "WebSocket");
|
||||
}
|
||||
else {
|
||||
new Player(gname, lkey, dims, respondlaunch);
|
||||
}
|
||||
};
|
||||
checkprogress(username, games[gname], launch, []);
|
||||
}
|
||||
|
||||
function watch(req, res, formdata) {
|
||||
if (!("g" in formdata) | !("p" in formdata)) {
|
||||
sendError(res, 2, "Game or player not given");
|
||||
return;
|
||||
}
|
||||
if (!(formdata.g in games)) {
|
||||
sendError(res, 2, "No such game: " + formdata.g);
|
||||
return;
|
||||
}
|
||||
var tag = formdata.g = "/" + formdata.p;
|
||||
if (!(tag in sessions)) {
|
||||
sendError(res, 7);
|
||||
return;
|
||||
}
|
||||
var session = sessions[tag];
|
||||
var watch = new Watcher(session);
|
||||
var reply = {"t": "w", "w": session.w, "h": session.h,
|
||||
"p": session.pname, "g": session.game.uname};
|
||||
res.writeHead(200, {'Content-Type': 'application/json'});
|
||||
res.write(JSON.stringify(reply));
|
||||
res.end();
|
||||
tslog("Game %d is being watched", tag);
|
||||
}
|
||||
|
||||
/* Sets things up for a new user, like dgamelaunch's commands[register] */
|
||||
function regsetup(username) {
|
||||
function regsetup_l2(err) {
|
||||
|
|
@ -876,24 +606,7 @@ function register(req, res, formdata) {
|
|||
return;
|
||||
}
|
||||
|
||||
/* Ends the game, obviously. Less obviously, stops watching the game if
|
||||
* the client is a Watcher instead of a Player. */
|
||||
function endgame(client, res) {
|
||||
if (!client.alive) {
|
||||
sendError(res, 7, null, true);
|
||||
return;
|
||||
}
|
||||
client.quit();
|
||||
// Give things some time to happen.
|
||||
if (client instanceof Player)
|
||||
setTimeout(readFeed, 200, client, res);
|
||||
else
|
||||
readFeed(client, res);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Stops a running game if the request has the proper key. */
|
||||
/* TODO does this still work? */
|
||||
function stopgame(res, formdata) {
|
||||
if (!("key" in formdata) || !(formdata["key"] in logins)) {
|
||||
sendError(res, 1);
|
||||
|
|
@ -940,19 +653,6 @@ function stopgame(res, formdata) {
|
|||
checkprogress(pname, games[gname], checkback, []);
|
||||
}
|
||||
|
||||
/* TODO remove polling */
|
||||
function findClient(formdata, playersOnly) {
|
||||
if (typeof(formdata) != "object")
|
||||
return null;
|
||||
if ("id" in formdata) {
|
||||
var id = formdata["id"];
|
||||
if (id in clients && (!playersOnly || clients[id] instanceof Player)) {
|
||||
return clients[id];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function startProgressWatcher() {
|
||||
var watchdirs = [];
|
||||
for (var gname in games) {
|
||||
|
|
@ -1032,22 +732,6 @@ function serveStatic(req, res, fname) {
|
|||
return;
|
||||
}
|
||||
|
||||
/* TODO remove polling */
|
||||
function readFeed(client, res) {
|
||||
if (!client) {
|
||||
sendError(res, 7, null, true);
|
||||
return;
|
||||
}
|
||||
var msgs = client.read();
|
||||
if (!allowlogin && !msgs.length) {
|
||||
sendError(res, 6, null, true);
|
||||
return;
|
||||
}
|
||||
res.writeHead(200, { "Content-Type": "application/json" });
|
||||
res.write(JSON.stringify(msgs));
|
||||
res.end();
|
||||
}
|
||||
|
||||
/* TODO simplify by storing timestamps instead of callin stat() */
|
||||
function getStatus(callback) {
|
||||
var now = new Date();
|
||||
|
|
@ -1272,45 +956,12 @@ function webHandler(req, res) {
|
|||
var target = url.parse(req.url).pathname;
|
||||
/* First figure out if the client is POSTing to a command interface. */
|
||||
if (req.method == 'POST') {
|
||||
if (target == '/feed') {
|
||||
var client = findClient(formdata, false);
|
||||
if (!client) {
|
||||
sendError(res, 7, null, true);
|
||||
return;
|
||||
}
|
||||
if (formdata.t == "q") {
|
||||
/* The client wants to terminate the process. */
|
||||
endgame(client, res);
|
||||
return; // endgame() calls readFeed() itself.
|
||||
}
|
||||
else if (formdata.t == "d" && typeof(formdata.d) == "string") {
|
||||
if (!(client instanceof Player)) {
|
||||
sendError(res, 7, "Watching", true);
|
||||
return;
|
||||
}
|
||||
/* process the keys */
|
||||
var hexstr = formdata.d.replace(/[^0-9a-f]/gi, "");
|
||||
if (hexstr.length % 2 != 0) {
|
||||
sendError(res, 2, "incomplete byte", true);
|
||||
return;
|
||||
}
|
||||
var keybuf = new Buffer(hexstr, "hex");
|
||||
client.write(keybuf, formdata.n);
|
||||
}
|
||||
readFeed(client, res);
|
||||
}
|
||||
else if (target == "/login") {
|
||||
if (target == "/login") {
|
||||
login(req, res, formdata);
|
||||
}
|
||||
else if (target == "/addacct") {
|
||||
register(req, res, formdata);
|
||||
}
|
||||
else if (target == "/play") {
|
||||
startgame(req, res, formdata);
|
||||
}
|
||||
else if (target == "/watch") {
|
||||
watch(req, res, formdata);
|
||||
}
|
||||
else if (target == "/quit") {
|
||||
stopgame(res, formdata);
|
||||
}
|
||||
|
|
@ -1323,16 +974,7 @@ function webHandler(req, res) {
|
|||
}
|
||||
}
|
||||
else if (req.method == 'GET' || req.method == 'HEAD') {
|
||||
if (target == '/feed') {
|
||||
if (req.method == 'HEAD') {
|
||||
res.writeHead(200, {"Content-Type": "application/json"});
|
||||
res.end();
|
||||
}
|
||||
else
|
||||
sendError(res, 7, null, true);
|
||||
return;
|
||||
}
|
||||
else if (target == '/status') {
|
||||
if (target == '/status') {
|
||||
statusmsg(req, res);
|
||||
}
|
||||
else if (target.match(/^\/uinfo\//)) {
|
||||
|
|
@ -1352,7 +994,6 @@ function webHandler(req, res) {
|
|||
return;
|
||||
}
|
||||
req.on('end', respond);
|
||||
|
||||
}
|
||||
|
||||
function wsHandler(wsRequest) {
|
||||
|
|
@ -1403,6 +1044,7 @@ function wsHandler(wsRequest) {
|
|||
wsRequest.reject(404, "No such resource.");
|
||||
}
|
||||
|
||||
/* TODO use a list instead */
|
||||
function pushStatus() {
|
||||
getStatus(function(info) {
|
||||
info["t"] = "t";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue