Mercurial > hg > rlgwebd
diff rlgterm.js @ 159:a613380ffdc2
RLGWebD: excise polling.
WebSockets are supported nearly everywhere now.
Listing current games and watching them are still broken.
author | John "Elwin" Edwards |
---|---|
date | Sat, 03 Jan 2015 15:23:04 -0500 |
parents | e54018b26ed8 |
children | a2a25b7631f1 |
line wrap: on
line diff
--- a/rlgterm.js Thu Jan 01 15:56:22 2015 -0500 +++ b/rlgterm.js Sat Jan 03 15:23:04 2015 -0500 @@ -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 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 @@ 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 @@ 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 @@ 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 @@ 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 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 @@ 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 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 @@ } } +/* 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 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 @@ 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 @@ 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 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 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 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 @@ 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) {