Mercurial > hg > rlgwebd
comparison rlgterm.js @ 59:00b985b8ba6a
RLG-Web client: implement watching.
It is now possible to watch games currently being played through
RLG-Web, but not dgamelaunch. Also, there are some deficiencies, like
getting chaos until an absolute cursor addressing happens.
| author | John "Elwin" Edwards <elwin@sdf.org> |
|---|---|
| date | Tue, 19 Jun 2012 16:19:50 -0700 |
| parents | 7a50b4412fea |
| children | 071ec6b1ec03 |
comparison
equal
deleted
inserted
replaced
| 58:7a50b4412fea | 59:00b985b8ba6a |
|---|---|
| 66 "name": "Super-Rogue", | 66 "name": "Super-Rogue", |
| 67 "uname": "srogue" | 67 "uname": "srogue" |
| 68 } | 68 } |
| 69 }; | 69 }; |
| 70 | 70 |
| 71 /* Login name and key */ | 71 var session = { |
| 72 var lname = null; | 72 /* The session id assigned by the server. */ |
| 73 var lcred = null; | 73 id: null, |
| 74 /* The session id assigned by the server. */ | 74 /* Login name and key */ |
| 75 var sessid = null; | 75 lname: null, |
| 76 lcred: null, | |
| 77 /* Whether the game is being played or just watched. */ | |
| 78 playing: false | |
| 79 }; | |
| 76 | 80 |
| 77 function writeData(hexstr) { | 81 function writeData(hexstr) { |
| 78 var codenum; | 82 var codenum; |
| 79 var codes = []; | 83 var codes = []; |
| 80 var nc; | 84 var nc; |
| 211 } | 215 } |
| 212 return havedata; | 216 return havedata; |
| 213 } | 217 } |
| 214 | 218 |
| 215 function getData() { | 219 function getData() { |
| 216 if (sessid == null) | 220 if (session.id == null) |
| 217 return; | 221 return; |
| 218 var datareq = new XMLHttpRequest(); | 222 var datareq = new XMLHttpRequest(); |
| 219 var msg = JSON.stringify({"id": sessid, "t": "n"}); | 223 var msg = JSON.stringify({"id": session.id, "t": "n"}); |
| 220 datareq.onreadystatechange = function () { | 224 datareq.onreadystatechange = function () { |
| 221 if (datareq.readyState == 4 && datareq.status == 200) { | 225 if (datareq.readyState == 4 && datareq.status == 200) { |
| 222 var wasdata = processMsg(datareq.responseText); | 226 var wasdata = processMsg(datareq.responseText); |
| 223 if (wasdata != null) { | 227 if (wasdata != null) { |
| 224 if (wasdata) | 228 if (wasdata) |
| 243 } | 247 } |
| 244 } | 248 } |
| 245 | 249 |
| 246 function sendback(str) { | 250 function sendback(str) { |
| 247 /* For responding to terminal queries. */ | 251 /* For responding to terminal queries. */ |
| 248 var msgDict = {"id": sessid, "t": "d", "n": nsend++, "d": str}; | 252 var msgDict = {"id": session.id, "t": "d", "n": nsend++, "d": str}; |
| 249 var datareq = new XMLHttpRequest(); | 253 var datareq = new XMLHttpRequest(); |
| 250 datareq.onreadystatechange = postResponseHandler; | 254 datareq.onreadystatechange = postResponseHandler; |
| 251 datareq.open('POST', '/feed', true); | 255 datareq.open('POST', '/feed', true); |
| 252 datareq.send(JSON.stringify(msgDict)); | 256 datareq.send(JSON.stringify(msgDict)); |
| 253 return; | 257 return; |
| 254 } | 258 } |
| 255 | 259 |
| 256 function sendkey(ev) { | 260 function sendkey(ev) { |
| 257 if (sessid == null) | 261 if (!session.playing) |
| 258 return; | 262 return; |
| 259 var keynum = ev.keyCode; | 263 var keynum = ev.keyCode; |
| 260 var code; | 264 var code; |
| 261 if (keynum >= ev.DOM_VK_A && keynum <= ev.DOM_VK_Z) { | 265 if (keynum >= ev.DOM_VK_A && keynum <= ev.DOM_VK_Z) { |
| 262 /* Letters. This assumes the codes are 65-90. */ | 266 /* Letters. This assumes the codes are 65-90. */ |
| 289 } | 293 } |
| 290 else { | 294 else { |
| 291 debug(1, "Ignoring keycode " + keynum); | 295 debug(1, "Ignoring keycode " + keynum); |
| 292 return; | 296 return; |
| 293 } | 297 } |
| 294 // Isn't this check redundant? | 298 ev.preventDefault(); |
| 295 if (sessid != null) | |
| 296 ev.preventDefault(); | |
| 297 var datareq = new XMLHttpRequest(); | 299 var datareq = new XMLHttpRequest(); |
| 298 var msgDict = {"id": sessid, "t": "d", "n": nsend++, "d": code}; | 300 var msgDict = {"id": session.id, "t": "d", "n": nsend++, "d": code}; |
| 299 datareq.onreadystatechange = postResponseHandler; | 301 datareq.onreadystatechange = postResponseHandler; |
| 300 datareq.open('POST', '/feed', true); | 302 datareq.open('POST', '/feed', true); |
| 301 datareq.send(JSON.stringify(msgDict)); | 303 datareq.send(JSON.stringify(msgDict)); |
| 302 return; | 304 return; |
| 303 } | 305 } |
| 305 var charshifts = { '-': "5f", '=': "2b", '[': "7b", ']': "7d", '\\': "7c", | 307 var charshifts = { '-': "5f", '=': "2b", '[': "7b", ']': "7d", '\\': "7c", |
| 306 ';': "3a", '\'': "22", ',': "3c", '.': "3e", '/': "3f", '`': "7e" | 308 ';': "3a", '\'': "22", ',': "3c", '.': "3e", '/': "3f", '`': "7e" |
| 307 } | 309 } |
| 308 | 310 |
| 309 function vkey(c) { | 311 function vkey(c) { |
| 310 if (sessid == null) | 312 if (!session.playing) |
| 311 return; | 313 return; |
| 312 var keystr; | 314 var keystr; |
| 313 if (c.match(/^[a-z]$/)) { | 315 if (c.match(/^[a-z]$/)) { |
| 314 if (termemu.ctrlp()) { | 316 if (termemu.ctrlp()) { |
| 315 var n = c.charCodeAt(0) - 96; | 317 var n = c.charCodeAt(0) - 96; |
| 343 keystr = c.charCodeAt(0).toString(16); | 345 keystr = c.charCodeAt(0).toString(16); |
| 344 } | 346 } |
| 345 else | 347 else |
| 346 return; | 348 return; |
| 347 var datareq = new XMLHttpRequest(); | 349 var datareq = new XMLHttpRequest(); |
| 348 var msgDict = {"id": sessid, "t": "d", "n": nsend++, "d": keystr}; | 350 var msgDict = {"id": session.id, "t": "d", "n": nsend++, "d": keystr}; |
| 349 datareq.onreadystatechange = postResponseHandler; | 351 datareq.onreadystatechange = postResponseHandler; |
| 350 datareq.open('POST', '/feed', true); | 352 datareq.open('POST', '/feed', true); |
| 351 datareq.send(JSON.stringify(msgDict)); | 353 datareq.send(JSON.stringify(msgDict)); |
| 352 return; | 354 return; |
| 353 } | 355 } |
| 380 return; | 382 return; |
| 381 } | 383 } |
| 382 | 384 |
| 383 function formlogin(ev) { | 385 function formlogin(ev) { |
| 384 ev.preventDefault(); | 386 ev.preventDefault(); |
| 385 if (sessid != null) | 387 if (session.id != null) |
| 386 return; | 388 return; |
| 387 var loginmsg = {}; | 389 var loginmsg = {}; |
| 388 loginmsg["name"] = document.getElementById("input_name").value; | 390 loginmsg["name"] = document.getElementById("input_name").value; |
| 389 loginmsg["pw"] = document.getElementById("input_pw").value; | 391 loginmsg["pw"] = document.getElementById("input_pw").value; |
| 390 var req = new XMLHttpRequest(); | 392 var req = new XMLHttpRequest(); |
| 392 if (req.readyState != 4 || req.status != 200) | 394 if (req.readyState != 4 || req.status != 200) |
| 393 return; | 395 return; |
| 394 var reply = JSON.parse(req.responseText); | 396 var reply = JSON.parse(req.responseText); |
| 395 if (reply.t == 'l') { | 397 if (reply.t == 'l') { |
| 396 /* Success */ | 398 /* Success */ |
| 397 lcred = reply.k; | 399 session.lcred = reply.k; |
| 398 lname = reply.u; | 400 session.lname = reply.u; |
| 399 setTitle("Logged in as " + reply.u); | 401 setTitle("Logged in as " + reply.u); |
| 400 debug(1, "Logged in as " + reply.u + " with id " + reply.k); | 402 debug(1, "Logged in as " + reply.u + " with id " + reply.k); |
| 401 setmode("choose"); | 403 setmode("choose"); |
| 402 } | 404 } |
| 403 else if (reply.t == 'E') { | 405 else if (reply.t == 'E') { |
| 410 req.send(JSON.stringify(loginmsg)); | 412 req.send(JSON.stringify(loginmsg)); |
| 411 return; | 413 return; |
| 412 } | 414 } |
| 413 | 415 |
| 414 function getcurrent() { | 416 function getcurrent() { |
| 415 if (sessid) | 417 if (session.id) |
| 416 return; | 418 return; |
| 417 var req = new XMLHttpRequest(); | 419 var req = new XMLHttpRequest(); |
| 418 req.onreadystatechange = function () { | 420 req.onreadystatechange = function () { |
| 419 if (req.readyState != 4 || req.status != 200) | 421 if (req.readyState != 4 || req.status != 200) |
| 420 return; | 422 return; |
| 428 if (!reply.s) { | 430 if (!reply.s) { |
| 429 return; | 431 return; |
| 430 } | 432 } |
| 431 var gamediv = document.getElementById("gametable"); | 433 var gamediv = document.getElementById("gametable"); |
| 432 while (gamediv.children.length > 2) | 434 while (gamediv.children.length > 2) |
| 433 gamediv.removeChild(optdiv.children[2]); | 435 gamediv.removeChild(gamediv.children[2]); |
| 434 if (reply.g.length === 0) { | 436 if (reply.g.length === 0) { |
| 435 gamediv.style.display = "none"; | 437 gamediv.style.display = "none"; |
| 436 document.getElementById("nogames").style.display = "block"; | 438 document.getElementById("nogames").style.display = "block"; |
| 437 } | 439 } |
| 438 else { | 440 else { |
| 441 } | 443 } |
| 442 for (var i = 0; i < reply.g.length; i++) { | 444 for (var i = 0; i < reply.g.length; i++) { |
| 443 var row = document.createElement("div"); | 445 var row = document.createElement("div"); |
| 444 var cell1 = document.createElement("div"); | 446 var cell1 = document.createElement("div"); |
| 445 var cell2 = document.createElement("div"); | 447 var cell2 = document.createElement("div"); |
| 448 var cell3 = document.createElement("div"); | |
| 446 cell1.appendChild(document.createTextNode(reply.g[i].p)); | 449 cell1.appendChild(document.createTextNode(reply.g[i].p)); |
| 447 cell2.appendChild(document.createTextNode(reply.g[i].g)); | 450 cell2.appendChild(document.createTextNode(reply.g[i].g)); |
| 451 var button = document.createElement("span"); | |
| 452 button.appendChild(document.createTextNode("Watch")); | |
| 453 button.onclick = makeWatcher(reply.g[i].n); | |
| 454 button.className = "ibutton"; | |
| 455 cell3.appendChild(button); | |
| 448 row.appendChild(cell1); | 456 row.appendChild(cell1); |
| 449 row.appendChild(cell2); | 457 row.appendChild(cell2); |
| 458 row.appendChild(cell3); | |
| 450 gamediv.appendChild(row); | 459 gamediv.appendChild(row); |
| 451 } | 460 } |
| 452 }; | 461 }; |
| 453 req.open('GET', '/status', true); | 462 req.open('GET', '/status', true); |
| 454 req.send(); | 463 req.send(); |
| 455 return; | 464 return; |
| 456 } | 465 } |
| 457 | 466 |
| 458 function getchoices() { | 467 function getchoices() { |
| 459 if (sessid != null || !lcred) | 468 if (session.id != null || !session.lcred) |
| 460 return; | 469 return; |
| 461 var req = new XMLHttpRequest(); | 470 var req = new XMLHttpRequest(); |
| 462 req.onreadystatechange = function () { | 471 req.onreadystatechange = function () { |
| 463 if (req.readyState != 4 || req.status != 200) | 472 if (req.readyState != 4 || req.status != 200) |
| 464 return; | 473 return; |
| 467 reply = JSON.parse(req.responseText); | 476 reply = JSON.parse(req.responseText); |
| 468 } catch (e) { | 477 } catch (e) { |
| 469 if (e instanceof SyntaxError) | 478 if (e instanceof SyntaxError) |
| 470 return; | 479 return; |
| 471 } | 480 } |
| 472 if (!("name" in reply) || reply["name"] != lname || !("stat" in reply)) | 481 if (!("name" in reply) || reply["name"] != session.lname || |
| 482 !("stat" in reply)) | |
| 473 return; | 483 return; |
| 474 var optdiv = document.getElementById("opttable"); | 484 var optdiv = document.getElementById("opttable"); |
| 475 /* Don't remove the first child, it's the header. */ | 485 /* Don't remove the first child, it's the header. */ |
| 476 while (optdiv.childNodes.length > 1) | 486 while (optdiv.childNodes.length > 1) |
| 477 optdiv.removeChild(optdiv.childNodes[1]); | 487 optdiv.removeChild(optdiv.childNodes[1]); |
| 501 rowdiv.appendChild(gamediv); | 511 rowdiv.appendChild(gamediv); |
| 502 rowdiv.appendChild(actdiv); | 512 rowdiv.appendChild(actdiv); |
| 503 optdiv.appendChild(rowdiv); | 513 optdiv.appendChild(rowdiv); |
| 504 } | 514 } |
| 505 }; | 515 }; |
| 506 req.open('GET', '/pstatus/' + lname, true); | 516 req.open('GET', '/pstatus/' + session.lname, true); |
| 507 req.send(); | 517 req.send(); |
| 508 return; | 518 return; |
| 509 } | 519 } |
| 510 | 520 |
| 511 /* This can't be in the loop in getchoices(), or the closure's scope will | 521 /* This can't be in the loop in getchoices(), or the closure's scope will |
| 521 } | 531 } |
| 522 return starter; | 532 return starter; |
| 523 } | 533 } |
| 524 | 534 |
| 525 function startgame(game) { | 535 function startgame(game) { |
| 526 if (sessid != null || !lcred) | 536 if (session.id != null || !session.lcred) |
| 527 return; | 537 return; |
| 528 var smsg = {}; | 538 var smsg = {}; |
| 529 smsg["key"] = lcred; | 539 smsg["key"] = session.lcred; |
| 530 smsg["game"] = game.uname; | 540 smsg["game"] = game.uname; |
| 531 smsg["h"] = 24; | 541 smsg["h"] = 24; |
| 532 smsg["w"] = 80; | 542 smsg["w"] = 80; |
| 533 var req = new XMLHttpRequest(); | 543 var req = new XMLHttpRequest(); |
| 534 req.onreadystatechange = function () { | 544 req.onreadystatechange = function () { |
| 535 if (req.readyState != 4 || req.status != 200) | 545 if (req.readyState != 4 || req.status != 200) |
| 536 return; | 546 return; |
| 537 var reply = JSON.parse(req.responseText); | 547 var reply = JSON.parse(req.responseText); |
| 538 if (reply.t == 's') { | 548 if (reply.t == 's') { |
| 539 /* Success */ | 549 /* Success */ |
| 540 sessid = reply.id; | 550 session.id = reply.id; |
| 551 session.playing = true; | |
| 541 termemu.resize(reply.h, reply.w); | 552 termemu.resize(reply.h, reply.w); |
| 542 setTitle("Playing as " + lname); | 553 setTitle("Playing as " + session.lname); |
| 543 debug(1, "Playing with id " + sessid); | 554 debug(1, "Playing with id " + session.id); |
| 544 setmode("play"); | 555 setmode("play"); |
| 545 getData(); | 556 getData(); |
| 546 } | 557 } |
| 547 else if (reply.t == 'E') { | 558 else if (reply.t == 'E') { |
| 548 debug(1, "Could not start game: " + reply.s); | 559 debug(1, "Could not start game: " + reply.s); |
| 554 req.open('POST', '/play', true); | 565 req.open('POST', '/play', true); |
| 555 req.send(JSON.stringify(smsg)); | 566 req.send(JSON.stringify(smsg)); |
| 556 return; | 567 return; |
| 557 } | 568 } |
| 558 | 569 |
| 570 function startwatching(gamenumber) { | |
| 571 if (session.id != null) | |
| 572 return; | |
| 573 var wmsg = {"n": Number(gamenumber)}; | |
| 574 var req = new XMLHttpRequest(); | |
| 575 req.onreadystatechange = function () { | |
| 576 if (req.readyState != 4 || req.status != 200) | |
| 577 return; | |
| 578 var reply = JSON.parse(req.responseText); | |
| 579 if (reply.t == 'w') { | |
| 580 /* Success */ | |
| 581 session.id = reply.id; | |
| 582 session.playing = false; | |
| 583 termemu.resize(reply.h, reply.w); | |
| 584 setTitle("Watching"); | |
| 585 debug(1, "Watching with id " + session.id); | |
| 586 setmode("play"); | |
| 587 getData(); | |
| 588 } | |
| 589 else if (reply.t == 'E') { | |
| 590 debug(1, "Could not watch game " + gamenumber + ": " + reply.s); | |
| 591 } | |
| 592 }; | |
| 593 req.open('POST', '/watch', true); | |
| 594 req.send(JSON.stringify(wmsg)); | |
| 595 return; | |
| 596 } | |
| 597 | |
| 598 function makeWatcher(n) { | |
| 599 function watcher(ev) { | |
| 600 startwatching(n); | |
| 601 } | |
| 602 return watcher; | |
| 603 } | |
| 604 | |
| 559 function formreg(ev) { | 605 function formreg(ev) { |
| 560 ev.preventDefault(); | 606 ev.preventDefault(); |
| 561 if (sessid != null) | 607 if (session.id != null) |
| 562 return; | 608 return; |
| 563 var regmsg = {}; | 609 var regmsg = {}; |
| 564 regmsg["name"] = document.getElementById("regin_name").value; | 610 regmsg["name"] = document.getElementById("regin_name").value; |
| 565 regmsg["pw"] = document.getElementById("regin_pw").value; | 611 regmsg["pw"] = document.getElementById("regin_pw").value; |
| 566 regmsg["email"] = document.getElementById("regin_email").value; | 612 regmsg["email"] = document.getElementById("regin_email").value; |
| 570 return; | 616 return; |
| 571 var reply = JSON.parse(req.responseText); | 617 var reply = JSON.parse(req.responseText); |
| 572 if (reply.t == 'r') { | 618 if (reply.t == 'r') { |
| 573 /* Success */ | 619 /* Success */ |
| 574 debug(1, "Registered account: " + reply.d); | 620 debug(1, "Registered account: " + reply.d); |
| 575 lcred = reply.k; | 621 session.lcred = reply.k; |
| 576 lname = reply.u; | 622 session.lname = reply.u; |
| 577 setTitle("Logged in as " + lname); | 623 setTitle("Logged in as " + session.lname); |
| 578 debug(1, "Logged in as " + lname + "with id " + lcred); | 624 debug(1, "Logged in as " + session.lname + "with id " + session.lcred); |
| 579 setmode("choose"); | 625 setmode("choose"); |
| 580 } | 626 } |
| 581 else if (reply.t == 'E') { | 627 else if (reply.t == 'E') { |
| 582 debug(1, "Could not register: " + reply.s); | 628 debug(1, "Could not register: " + reply.s); |
| 583 document.getElementById("regin_name").value = ""; | 629 document.getElementById("regin_name").value = ""; |
| 589 req.send(JSON.stringify(regmsg)); | 635 req.send(JSON.stringify(regmsg)); |
| 590 return; | 636 return; |
| 591 } | 637 } |
| 592 | 638 |
| 593 function gameover() { | 639 function gameover() { |
| 594 if (sessid == null) | 640 if (session.id == null) |
| 595 return; | 641 return; |
| 596 /* TODO IFACE2 If the end was unexpected, tell player the game was saved. */ | 642 /* TODO IFACE2 If the end was unexpected, tell player the game was saved. */ |
| 597 sessid = null; | 643 session.id = null; |
| 644 session.playing = false; | |
| 598 ajaxstate.clear(); | 645 ajaxstate.clear(); |
| 599 setTitle("Game over."); | 646 setTitle("Game over."); |
| 600 nsend = 0; | 647 nsend = 0; |
| 601 nrecv = 0; | 648 nrecv = 0; |
| 602 msgQ = []; | 649 msgQ = []; |
| 603 setmode("choose"); | 650 if (session.lcred != null) |
| 651 setmode("choose"); | |
| 652 else | |
| 653 setmode("login"); | |
| 604 return; | 654 return; |
| 605 } | 655 } |
| 606 | 656 |
| 607 function logout() { | 657 function logout() { |
| 608 lcred = null; | 658 session.lcred = null; |
