summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--package.json6
-rw-r--r--public/common/columbia.css (renamed from public/common/battle_abc.css)18
-rw-r--r--public/common/play.css (renamed from public/common/grid.css)9
-rw-r--r--public/common/play.js (renamed from public/common/client.js)245
-rw-r--r--public/images/sherlock-holmes-mirror.svg1
-rw-r--r--server.js205
6 files changed, 280 insertions, 204 deletions
diff --git a/package.json b/package.json
index 2cb73c2..2258084 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,10 @@
"express": "^4.17.1",
"nodemailer": "^6.7.0",
"pug": "^3.0.2",
- "socket.io": "^4.3.1"
+ "ws": "^8.4.0"
+ },
+ "optionalDependencies": {
+ "bufferutil": "^4.0.6",
+ "utf-8-validate": "^5.0.8"
}
}
diff --git a/public/common/battle_abc.css b/public/common/columbia.css
index 3a211a1..1a646b6 100644
--- a/public/common/battle_abc.css
+++ b/public/common/columbia.css
@@ -1,4 +1,4 @@
-/* ABC BATTLE DISPLAY FOR COLUMBIA BLOCK GAMES */
+/* BATTLE DIALOG FOR COLUMBIA BLOCK GAMES */
#battle {
position: absolute;
@@ -11,18 +11,22 @@
box-shadow: 0px 5px 10px 0px rgba(0,0,0,0.5);
visibility: hidden;
}
+
#battle.show {
visibility: visible;
}
+
#battle th {
padding: 0;
border: 1px solid black;
}
+
#battle td {
padding: 0;
border: 1px solid black;
vertical-align: top;
}
+
#battle_header {
font-weight: bold;
height: 2em;
@@ -31,12 +35,14 @@
user-select: none;
cursor: move;
}
+
#battle_message {
font-weight: normal;
height: 2em;
line-height: 2em;
padding: 0 8px;
}
+
#FA, #FB, #FC, #FD, #FF, #FR, #EA, #EB, #EC, #ED, #EF, #ER {
display: flex;
flex-wrap: wrap;
@@ -64,11 +70,6 @@
display: none;
}
-.battle_menu .action:hover { background-color: red; }
-.battle_menu .action.retreat:hover { background-color: #eee; }
-.battle_menu .action.withdraw:hover { background-color: #eee; }
-.battle_menu .action.pass:hover { background-color: gray; }
-
.battle_menu_list {
margin-top: 5px;
min-height: 26px;
@@ -82,6 +83,11 @@
display: none;
}
+.battle_menu .action:hover { background-color: red; }
+.battle_menu .action.retreat:hover { background-color: #eee; }
+.battle_menu .action.withdraw:hover { background-color: #eee; }
+.battle_menu .action.pass:hover { background-color: gray; }
+
.battle_menu.fire .action.fire { display: inline; }
.battle_menu.retreat .action.retreat { display: inline; }
.battle_menu.pass .action.pass { display: inline; }
diff --git a/public/common/grid.css b/public/common/play.css
index 228c0aa..64e9202 100644
--- a/public/common/grid.css
+++ b/public/common/play.css
@@ -25,7 +25,7 @@ body:not(.shift) .debug {
display: none;
}
-body.Observer .resign, body.replay .resign {
+body.Observer .resign {
display: none;
}
@@ -184,7 +184,7 @@ header .replay {
display: flex;
}
-header button.replay_button {
+header .replay button {
margin: 0;
}
@@ -311,12 +311,11 @@ header button.replay_button {
border: 1px solid black;
background-color: white;
box-shadow: 0px 5px 10px 0px rgba(0,0,0,0.5);
- display: none;
- grid-template-rows: auto minmax(0, 1fr) auto;
+ visibility: hidden;
}
#chat_window.show {
- display: grid;
+ visibility: visible;
}
#chat_header {
diff --git a/public/common/client.js b/public/common/play.js
index d9a1f48..ede3a5b 100644
--- a/public/common/client.js
+++ b/public/common/play.js
@@ -117,13 +117,10 @@ window.addEventListener("focus", stop_blinker);
function init_chat() {
// only fetch new messages when we reconnect!
if (chat !== null) {
- console.log("RECONNECT CHAT");
- socket.emit("getchat", chat.log);
+ send_message("getchat", chat.log);
return;
}
- console.log("CONNECT CHAT");
-
let chat_window = document.createElement("div");
chat_window.id = "chat_window";
chat_window.innerHTML = `
@@ -156,7 +153,7 @@ function init_chat() {
let input = document.getElementById("chat_input");
e.preventDefault();
if (input.value) {
- socket.emit("chat", input.value);
+ send_message("chat", input.value);
input.value = "";
} else {
hide_chat();
@@ -179,7 +176,7 @@ function init_chat() {
}
});
- socket.emit("getchat", 0);
+ send_message("getchat", 0);
}
function save_chat() {
@@ -251,6 +248,38 @@ function toggle_chat() {
show_chat();
}
+/* REMATCH BUTTON */
+
+function remove_resign_menu() {
+ document.querySelectorAll(".resign").forEach(x => x.remove());
+}
+
+function goto_rematch() {
+ window.location = "/rematch/" + params.game_id + "/" + params.role;
+}
+
+function goto_replay() {
+ window.location = "/" + params.title_id + "/replay:" + params.game_id;
+}
+
+function on_game_over() {
+ function icon_button(id, img, title, fn) {
+ if (!document.getElementById(id)) {
+ let button = document.createElement("div");
+ button.id = id;
+ button.title = title;
+ button.className = "icon_button";
+ button.innerHTML = '<img src="/images/' + img + '.svg">';
+ button.addEventListener("click", fn);
+ document.querySelector("header").appendChild(button);
+ }
+ }
+ icon_button("replay_button", "sherlock-holmes-mirror", "Watch replay", goto_replay);
+ if (player !== "Observer")
+ icon_button("rematch_button", "cycle", "Propose a rematch!", goto_rematch);
+ remove_resign_menu();
+}
+
/* CONNECT TO GAME SERVER */
function init_player_names(players) {
@@ -261,76 +290,102 @@ function init_player_names(players) {
}
}
-function init_play_client() {
- const ROLE_SEL = [
- ".role.one",
- ".role.two",
- ".role.three",
- ".role.four",
- ".role.five",
- ".role.six",
- ".role.seven",
- ];
+function send_message(cmd, arg) {
+ let data = JSON.stringify([cmd, arg]);
+ console.log("SEND %s %s", cmd, arg);
+ socket.send(data);
+}
- console.log("JOINING", params.title_id + "/" + params.game_id + "/" + params.role);
+let reconnect_count = 0;
+let reconnect_max = 10;
- socket = io({
- transports: ["websocket"],
- query: { title: params.title_id, game: params.game_id, role: params.role },
- });
+function connect_play() {
+ if (reconnect_count >= reconnect_max) {
+ document.title = "DISCONNECTED";
+ document.getElementById("prompt").textContent = "Disconnected.";
+ return;
+ }
- socket.on("connect", () => {
- console.log("CONNECTED");
- document.querySelector("header").classList.remove("disconnected");
- });
+ let protocol = (window.location.protocol === "http:") ? "ws" : "wss";
+ let seen = document.getElementById("log").children.length;
+ let url = `${protocol}://${window.location.host}/play-socket?title=${params.title_id}&game=${params.game_id}&role=${params.role}&seen=${seen}`;
- socket.on("disconnect", () => {
- console.log("DISCONNECTED");
- document.getElementById("prompt").textContent = "Disconnected from server!";
- document.querySelector("header").classList.add("disconnected");
- });
+ console.log("CONNECTING", url);
+ document.getElementById("prompt").textContent = "Connecting... ";
+
+ socket = new WebSocket(url);
- socket.on("roles", (me, players) => {
- console.log("PLAYERS", me, JSON.stringify(players));
- player = me;
- document.querySelector("body").classList.add(player.replace(/ /g, "_"));
- if (player !== "Observer")
- init_chat();
- init_player_names(players);
+ window.addEventListener('beforeunload', function () {
+ socket.close(1000);
});
- socket.on("presence", (presence) => {
- console.log("PRESENCE", JSON.stringify(presence));
- for (let i = 0; i < roles.length; ++i) {
- let elt = document.getElementById(roles[i].id);
- if (roles[i].role in presence)
- elt.classList.add("present");
- else
- elt.classList.remove("present");
+ socket.onopen = function (evt) {
+ console.log("OPEN");
+ document.querySelector("header").classList.remove("disconnected");
+ reconnect_count = 0;
+ }
+
+ socket.onclose = function (evt) {
+ console.log("CLOSE %d", evt.code);
+ if (evt.code === 1000 && evt.reason !== "") {
+ document.getElementById("prompt").textContent = "Disconnected: " + evt.reason;
+ document.title = "DISCONNECTED";
}
- });
+ if (evt.code !== 1000) {
+ document.querySelector("header").classList.add("disconnected");
+ document.getElementById("prompt").textContent = `Reconnecting soon... (${reconnect_count+1}/${reconnect_max})`;
+ let wait = 1000 * (Math.random() + 0.5) * Math.pow(2, reconnect_count++);
+ console.log("WAITING %.1f TO RECONNECT", wait/1000);
+ setTimeout(connect_play, wait);
+ }
+ }
- socket.on("state", (new_view) => {
- console.log("STATE");
- view = new_view;
- on_update_header();
- on_update();
- on_update_log();
- });
+ socket.onmessage = function (evt) {
+ let [ cmd, arg ] = JSON.parse(evt.data);
+ console.log("MESSAGE %s", cmd);
+ switch (cmd) {
+ case 'error':
+ document.getElementById("prompt").textContent = arg;
+ break;
- socket.on("save", (msg) => {
- console.log("SAVE");
- window.localStorage[params.title_id + "/save"] = msg;
- });
+ case 'chat':
+ update_chat(arg[0], arg[1], arg[2], arg[3]);
+ break;
- socket.on("error", (msg) => {
- console.log("ERROR", msg);
- document.getElementById("prompt").textContent = msg;
- });
+ case 'players':
+ player = arg[0];
+ document.querySelector("body").classList.add(player.replace(/ /g, "_"));
+ if (player !== "Observer")
+ init_chat();
+ else
+ remove_resign_menu();
+ init_player_names(arg[1]);
+ break;
- socket.on("chat", function (item) {
- update_chat(item[0], item[1], item[2], item[3]);
- });
+ case 'presence':
+ for (let i = 0; i < roles.length; ++i) {
+ let elt = document.getElementById(roles[i].id);
+ if (roles[i].role in arg)
+ elt.classList.add("present");
+ else
+ elt.classList.remove("present");
+ }
+ break;
+
+ case 'state':
+ view = arg;
+ on_update_header();
+ on_update();
+ on_update_log();
+ if (view.game_over)
+ on_game_over();
+ break;
+
+ case 'save':
+ window.localStorage[params.title_id + "/save"] = msg;
+ break;
+ }
+ }
}
/* HEADER */
@@ -474,17 +529,16 @@ function send_action(verb, noun) {
return;
// Reset action list here so we don't send more than one action per server prompt!
if (noun !== undefined) {
- if (view.actions && view.actions[verb] && view.actions[verb].includes(noun)) {
+ let realnoun = Array.isArray(noun) ? noun[0] : noun;
+ if (view.actions && view.actions[verb] && view.actions[verb].includes(realnoun)) {
view.actions = null;
- console.log("ACTION", verb, JSON.stringify(noun));
- socket.emit("action", verb, noun);
+ send_message("action", [verb, noun]);
return true;
}
} else {
if (view.actions && view.actions[verb]) {
view.actions = null;
- console.log("ACTION", verb);
- socket.emit("action", verb);
+ send_message("action", [verb]);
return true;
}
}
@@ -493,21 +547,21 @@ function send_action(verb, noun) {
function confirm_resign() {
if (window.confirm("Are you sure that you want to resign?"))
- socket.emit("resign");
+ send_message("resign");
}
/* DEBUGGING */
function send_save() {
- socket.emit("save");
+ send_message("save");
}
function send_restore() {
- socket.emit("restore", window.localStorage[params.title_id + "/save"]);
+ send_message("restore", window.localStorage[params.title_id + "/save"]);
}
function send_restart(scenario) {
- socket.emit("restart", scenario);
+ send_message("restart", scenario);
}
/* REPLAY */
@@ -546,9 +600,10 @@ async function require(path) {
let replay = null;
-async function init_replay_client() {
+async function init_replay() {
+ remove_resign_menu();
+
document.getElementById("prompt").textContent = "Loading replay...";
- document.querySelector("body").classList.add("replay");
console.log("LOADING RULES");
let rules = await require("rules.js");
@@ -660,7 +715,7 @@ async function init_replay_client() {
if (viewpoint === "Active") {
player = s.active;
- if (player === "All" || player === "Both" || !player)
+ if (player === "All" || player === "Both" || player === "None" || !player)
player = "Observer";
}
@@ -679,10 +734,13 @@ async function init_replay_client() {
view.game_over = 1;
if (replay.length > 0) {
- if (document.querySelector("body").classList.contains("shift"))
- view.prompt = `[${p}/${replay.length}] ${s.active} / ${s.state} / ${replay[p].action} ${replay[p].arguments}`;
- else
+ if (document.querySelector("body").classList.contains("shift")) {
+ view.prompt = `[${p}/${replay.length}] ${s.active} / ${s.state}`;
+ if (p < replay.length)
+ view.prompt += ` / ${replay[p].action} ${replay[p].arguments}`;
+ } else {
view.prompt = "[" + p + "/" + replay.length + "] " + view.prompt;
+ }
}
if (log_length < view.log.length)
view.log_start = log_length;
@@ -696,11 +754,10 @@ async function init_replay_client() {
on_update_log();
}
- function replay_button(div, label, fn) {
+ function text_button(div, txt, fn) {
let button = document.createElement("button");
button.addEventListener("click", fn);
- button.className = "replay_button";
- button.textContent = label;
+ button.textContent = txt;
div.appendChild(button);
return button;
}
@@ -715,20 +772,20 @@ async function init_replay_client() {
let div = document.createElement("div");
div.className = "replay";
- replay_button(div, "Active", () => set_viewpoint("Active"));
+ text_button(div, "Active", () => set_viewpoint("Active"));
for (let r of roles)
- replay_button(div, r.role, () => set_viewpoint(r.role));
- replay_button(div, "Observer", () => set_viewpoint("Observer"));
+ text_button(div, r.role, () => set_viewpoint(r.role));
+ text_button(div, "Observer", () => set_viewpoint("Observer"));
document.querySelector("header").appendChild(div);
div = document.createElement("div");
div.className = "replay";
- replay_button(div, "<<<", () => goto_replay(1));
- replay_button(div, "<<", () => goto_replay(prev()));
- replay_button(div, "<\xa0", () => goto_replay(p-1));
- replay_button(div, "\xa0>", () => goto_replay(p+1));
- replay_button(div, ">>", () => goto_replay(next()));
- replay_button(div, "Run", play_pause_replay).style.width = "65px";
+ text_button(div, "<<<", () => goto_replay(1));
+ text_button(div, "<<", () => goto_replay(prev()));
+ text_button(div, "<\xa0", () => goto_replay(p-1));
+ text_button(div, "\xa0>", () => goto_replay(p+1));
+ text_button(div, ">>", () => goto_replay(next()));
+ text_button(div, "Run", play_pause_replay).style.width = "65px";
document.querySelector("header").appendChild(div);
if (window.location.hash === "")
@@ -744,7 +801,7 @@ async function init_replay_client() {
}
}
-if (params.mode === "play")
- init_play_client();
if (params.mode === "replay")
- init_replay_client();
+ init_replay();
+if (params.mode === "play")
+ connect_play();
diff --git a/public/images/sherlock-holmes-mirror.svg b/public/images/sherlock-holmes-mirror.svg
new file mode 100644
index 0000000..f2f0f19
--- /dev/null
+++ b/public/images/sherlock-holmes-mirror.svg
@@ -0,0 +1 @@
+<svg style="height:512px;width:512px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M326.582 36.882s24.67 5.747 35.756 15.557c38.586 34.149 51.256 67.652 53.746 105.53l43.97 47.023c-96.221-17.906-207.672-21.92-340.581-20.912 21.002-14.144 41.37-25.753 59.322-36.814 14.473-35.496 33.701-76.597 65.934-95.373 14.145-8.24 29.144-14.024 43.962-16.83 10.736-20.307 35.4-11.819 37.89 1.82zM407.023 214.8c-20.328 40.62-56.635 79.575-89.761 103.012-18.256 12.63-36.742 21.653-51.035 24.144-11.946 2.242-23.46-1.416-28.825-10.672l-.002-.004v-.002c-8.103-14.299-14.714-28.724-20.359-43.197-15.776 1.737-33.408 2.541-38.04-1.178-7.31-5.871 8.751-56.088 16.056-69.443C192 211.24 192 204.665 192 199.88c39.436-2.194 150.638 6.423 215.023 14.92zm-342.142 6.07c13.335.302 24.897 9.857 33.428 22.668 9.413 14.137 16.138 33.34 18.798 55.055 2.66 21.714.771 41.971-4.95 57.963-4.29 11.99-11.133 22.318-20.655 27.515l10.77 108.676-17.913 1.775-10.615-107.13c-12.015-1.592-22.443-10.62-30.299-22.418-9.413-14.137-16.136-33.339-18.797-55.053-2.66-21.714-.77-41.974 4.952-57.965 5.721-15.991 15.983-29.026 31.087-30.877a29.036 29.036 0 0 1 4.194-.209zm-2.004 18.076c-5.494.673-11.846 6.541-16.33 19.075-4.485 12.533-6.397 30.421-4.033 49.71 2.363 19.29 8.538 36.186 15.916 47.266 7.377 11.08 14.955 15.242 20.449 14.568 5.494-.673 11.844-6.54 16.328-19.074 4.485-12.533 6.396-30.42 4.033-49.709-2.363-19.289-8.536-36.187-15.914-47.267-5.011-6.164-12.69-15.168-20.449-14.569zm349.951 3.584c6.243 8.543 13.975 17.27 23.111 25.744 12.11 11.231 26.664 21.827 43.198 30.211-37.101 7.524-77.514 23.385-115.21 42.594-42.326 21.57-80.776 47.18-106.775 71.145l-2.314-24.686c11.388-12.634 23.526-23.83 35.869-34.252 12.152-5.001 24.557-12.203 36.797-20.672 36.371-25.164 70.878-60.916 85.324-90.084zM68.246 253.806c-.036 21.503-3.015 45.534-9.771 64.632-6.729-19.745-7.02-55.246 9.771-64.632zm137.598 54.43a353.014 353.014 0 0 0 7.74 16.574c-7.33 5.423-11.536 10.592-13.904 15.505-3.119 6.47-3.49 13.077-2.176 21.295 2.24 14.002 10.213 31.472 14.32 52.23 9.049 12.67 1.565 56.042-18.265 60.938-44.677 11.03-69.71-35.7-59.614-46.716 9.817-10.713 37.598-19.736 57.92-19.952-4.048-14.733-9.79-28.985-12.136-43.656-1.666-10.412-1.325-21.456 3.736-31.955 4.255-8.827 11.576-16.856 22.379-24.264zm272.875 8.81c4.751 7.296 9.937 15.484 15.281 24.413V494H238.848c2.921-14.12 5.609-28.7 8.763-42.629.65-5.674 5.269-9.093 9.059-13.314 21.957-24.459 66.328-55.92 115.432-80.942 35.16-17.917 72.94-32.625 106.617-40.07z"/></svg> \ No newline at end of file
diff --git a/server.js b/server.js
index 6c4caee..be67657 100644
--- a/server.js
+++ b/server.js
@@ -4,8 +4,9 @@ const fs = require('fs');
const crypto = require('crypto');
const http = require('http');
const https = require('https');
-const socket_io = require('socket.io');
+const { WebSocketServer } = require('ws');
const express = require('express');
+const url = require('url');
const compression = require('compression');
const sqlite3 = require('better-sqlite3');
@@ -100,10 +101,10 @@ app.locals.SITE_NAME = SITE_NAME;
let http_port = process.env.HTTP_PORT || 8080;
let http_server = http.createServer(app);
-let http_io = socket_io(http_server);
+let http_wss = new WebSocketServer({server: http_server});
http_server.keepAliveTimeout = 0;
http_server.listen(http_port, '0.0.0.0', () => console.log('listening HTTP on *:' + http_port));
-let io = http_io;
+let wss = http_wss;
let https_port = process.env.HTTPS_PORT;
if (https_port) {
@@ -111,13 +112,10 @@ if (https_port) {
key: fs.readFileSync(process.env.SSL_KEY || "key.pem"),
cert: fs.readFileSync(process.env.SSL_CERT || "cert.pem")
}, app);
- let https_io = socket_io(https_server);
+ let https_wss = new WebSocketServer({server: https_server});
https_server.listen(https_port, '0.0.0.0', () => console.log('listening HTTPS on *:' + https_port));
- http_server.keepAliveTimeout=0;
- io = {
- use: function (fn) { http_io.use(fn); https_io.use(fn); },
- on: function (ev,fn) { http_io.on(ev,fn); https_io.on(ev,fn); },
- };
+ https_server.keepAliveTimeout = 0;
+ wss = { on: function (ev,fn) { http_wss.on(ev,fn); https_wss.on(ev,fn); } };
}
/*
@@ -129,19 +127,16 @@ function random_seed() {
}
function LOG(req, ...msg) {
- let name;
- if (req.user)
- name = `"${req.user.name}" <${req.user.mail}>`;
- else
- name = "guest";
let time = new Date().toISOString().substring(0,19).replace("T", " ");
+ let name = req.user ? `"${req.user.name}" <${req.user.mail}>` : "guest";
console.log(time, req.connection.remoteAddress, name, ...msg);
}
function SLOG(socket, ...msg) {
let time = new Date().toISOString().substring(0,19).replace("T", " ");
- console.log(time, socket.request.connection.remoteAddress, socket.user_name,
- socket.title_id + "/" + socket.game_id + "/" + socket.role, ...msg);
+ let name = socket.user ? `"${socket.user.name}" <${socket.user.mail}>` : "guest";
+ console.log(time, socket.ip, name,
+ "WS /" + socket.title_id + "/" + socket.game_id + "/" + socket.role, ...msg);
}
function human_date(time) {
@@ -287,19 +282,6 @@ app.use(function (req, res, next) {
return next();
});
-io.use(function (socket, next) {
- let sid = login_cookie(socket.request);
- if (sid)
- socket.user_id = login_sql_select.get(sid);
- else
- socket.user_id = 0;
- if (socket.user_id)
- socket.user_name = SQL_SELECT_USER_NAME.get(socket.user_id);
- else
- socket.user_name = "guest";
- return next();
-});
-
function must_be_logged_in(req, res, next) {
if (!req.user)
return res.redirect('/login?redirect=' + encodeURIComponent(req.originalUrl));
@@ -1378,7 +1360,7 @@ app.get('/:title_id/play\::game_id\::role', must_be_logged_in, function (req, re
let game_id = req.params.game_id;
let role = req.params.role;
if (!SQL_AUTHORIZE_GAME_ROLE.get(title_id, game_id, role, user_id))
- return res.send("You are not assigned that role.");
+ return res.status(404).send("Invalid game ID.");
return res.sendFile(__dirname + '/public/' + title_id + '/play.html');
});
@@ -1515,7 +1497,7 @@ function mail_ready_to_start_notification(user, game_id, interval) {
function mail_your_turn_notification_to_offline_users(game_id, old_active, active) {
function is_online(game_id, user_id) {
for (let other of clients[game_id])
- if (other.user_id === user_id)
+ if (other.user && other.user.user_id === user_id)
return true;
return false;
}
@@ -1572,21 +1554,25 @@ setInterval(notify_ready_to_start_reminder, 5 * 60 * 1000);
let clients = {};
+function send_message(socket, cmd, arg) {
+ socket.send(JSON.stringify([cmd, arg]));
+}
+
function send_state(socket, state) {
try {
let view = socket.rules.view(state, socket.role);
- if (socket.log_length < view.log.length)
- view.log_start = socket.log_length;
+ if (socket.seen < view.log.length)
+ view.log_start = socket.seen;
else
view.log_start = view.log.length;
- socket.log_length = view.log.length;
+ socket.seen = view.log.length;
view.log = view.log.slice(view.log_start);
if (state.state === 'game_over')
view.game_over = 1;
- socket.emit('state', view);
+ send_message(socket, 'state', view);
} catch (err) {
console.log(err);
- return socket.emit('error', err.toString());
+ return send_message(socket, 'error', err.toString());
}
}
@@ -1624,7 +1610,7 @@ function on_action(socket, action, arg) {
put_replay(socket.game_id, socket.role, action, arg);
} catch (err) {
console.log(err);
- return socket.emit('error', err.toString());
+ return send_message(socket, 'error', err.toString());
}
}
@@ -1638,7 +1624,7 @@ function on_resign(socket) {
put_replay(socket.game_id, socket.role, 'resign', null);
} catch (err) {
console.log(err);
- return socket.emit('error', err.toString());
+ return send_message(socket, 'error', err.toString());
}
}
@@ -1648,25 +1634,25 @@ function on_getchat(socket, seen) {
if (chat.length > 0)
SLOG(socket, "GETCHAT", seen, chat.length);
for (let i = 0; i < chat.length; ++i)
- socket.emit('chat', chat[i]);
+ send_message(socket, 'chat', chat[i]);
} catch (err) {
console.log(err);
- return socket.emit('error', err.toString());
+ return send_message(socket, 'error', err.toString());
}
}
function on_chat(socket, message) {
message = message.substring(0,4000);
try {
- let chat = SQL_INSERT_GAME_CHAT.get(socket.game_id, socket.user_id, message);
- chat[2] = socket.user_name;
+ let chat = SQL_INSERT_GAME_CHAT.get(socket.game_id, socket.user.user_id, message);
+ chat[2] = socket.user.name;
SLOG(socket, "CHAT");
for (let other of clients[socket.game_id])
if (other.role !== "Observer")
- other.emit('chat', chat);
+ send_message(other, 'chat', chat);
} catch (err) {
console.log(err);
- return socket.emit('error', err.toString());
+ return send_message(socket, 'error', err.toString());
}
}
@@ -1675,11 +1661,11 @@ function on_debug(socket) {
try {
let game_state = SQL_SELECT_GAME_STATE.get(socket.game_id);
if (!game_state)
- return socket.emit('error', "No game with that ID.");
- socket.emit('debug', game_state);
+ return send_message(socket, 'error', "No game with that ID.");
+ send_message(socket, 'debug', game_state);
} catch (err) {
console.log(err);
- return socket.emit('error', err.toString());
+ return send_message(socket, 'error', err.toString());
}
}
@@ -1688,16 +1674,16 @@ function on_save(socket) {
try {
let game_state = SQL_SELECT_GAME_STATE.get(socket.game_id);
if (!game_state)
- return socket.emit('error', "No game with that ID.");
- socket.emit('save', game_state);
+ return send_message(socket, 'error', "No game with that ID.");
+ send_message(socket, 'save', game_state);
} catch (err) {
console.log(err);
- return socket.emit('error', err.toString());
+ return send_message(socket, 'error', err.toString());
}
}
function on_restore(socket, state_text) {
- SLOG(socket, 'RESTORE');
+ SLOG(socket, "RESTORE");
try {
let state = JSON.parse(state_text);
state.seed = random_seed(); // reseed!
@@ -1709,7 +1695,7 @@ function on_restore(socket, state_text) {
send_state(other, state);
} catch (err) {
console.log(err);
- return socket.emit('error', err.toString());
+ return send_message(socket, 'error', err.toString());
}
}
@@ -1718,89 +1704,112 @@ function broadcast_presence(game_id) {
for (let socket of clients[game_id])
presence[socket.role] = true;
for (let socket of clients[game_id])
- socket.emit('presence', presence);
+ send_message(socket, 'presence', presence);
+}
+
+function on_restart(socket, scenario) {
+ try {
+ let seed = random_seed();
+ let state = socket.rules.setup(seed, scenario, {}, socket.players);
+ put_replay(socket.game_id, null, 'setup', [seed, scenario, null, socket.players]);
+ for (let other of clients[socket.game_id]) {
+ other.seen = 0;
+ send_state(other, state);
+ }
+ let state_text = JSON.stringify(state);
+ SQL_UPDATE_GAME_RESULT.run(1, null, socket.game_id);
+ SQL_UPDATE_GAME_STATE.run(socket.game_id, state_text, state.active);
+ } catch (err) {
+ console.log(err);
+ return send_message(socket, 'error', err.toString());
+ }
}
-io.on('connection', (socket) => {
- socket.title_id = socket.handshake.query.title || "unknown";
- socket.game_id = socket.handshake.query.game | 0;
- socket.role = socket.handshake.query.role;
- socket.log_length = 0;
+function handle_message(socket, cmd, arg) {
+ switch (cmd) {
+ case 'action': on_action(socket, arg[0], arg[1]); break;
+ case 'resign': on_resign(socket); break;
+ case 'getchat': on_getchat(socket, arg); break;
+ case 'chat': on_chat(socket, arg); break;
+ case 'debug': on_debug(socket); break;
+ case 'save': on_save(socket); break;
+ case 'restore': on_restore(socket, arg); break;
+ case 'restart': on_restart(socket, arg); break;
+ }
+}
+
+wss.on('connection', (socket, req, client) => {
+ let u = url.parse(req.url, true);
+ if (u.pathname !== '/play-socket')
+ return setTimeout(() => socket.close(1000, "Invalid request."), 30000);
+ req.query = u.query;
+
+ let user_id = 0;
+ let sid = login_cookie(req);
+ if (sid)
+ user_id = login_sql_select.get(sid);
+ if (user_id)
+ socket.user = SQL_SELECT_USER_INFO.get(user_id);
+
+ socket.ip = req.connection.remoteAddress;
+ socket.title_id = req.query.title || "unknown";
+ socket.game_id = req.query.game | 0;
+ socket.role = req.query.role;
+ socket.seen = req.query.seen | 0;
socket.rules = RULES[socket.title_id];
- SLOG(socket, "CONNECT");
+ SLOG(socket, "OPEN " + socket.seen);
try {
let title_id = SQL_SELECT_GAME_TITLE.get(socket.game_id);
if (title_id !== socket.title_id)
- return socket.emit('error', "Invalid game ID.");
+ return socket.close(1000, "Invalid game ID.");
- let players = SQL_SELECT_PLAYERS_JOIN.all(socket.game_id);
+ let players = socket.players = SQL_SELECT_PLAYERS_JOIN.all(socket.game_id);
if (socket.role !== "Observer") {
- if (!socket.user_id)
- return socket.emit('error', "You are not logged in!");
+ if (!socket.user)
+ return socket.close(1000, "You are not logged in!");
if (socket.role && socket.role !== 'undefined' && socket.role !== 'null') {
- let me = players.find(p => p.user_id === socket.user_id && p.role === socket.role);
- if (!me) {
- socket.role = "Observer";
- return socket.emit('error', "You aren't assigned that role!");
- }
+ let me = players.find(p => p.user_id === socket.user.user_id && p.role === socket.role);
+ if (!me)
+ return socket.close(1000, "You aren't assigned that role!");
} else {
- let me = players.find(p => p.user_id === socket.user_id);
+ let me = players.find(p => p.user_id === socket.user.user_id);
socket.role = me ? me.role : "Observer";
}
}
- socket.emit('roles', socket.role, players);
+ if (socket.seen === 0)
+ send_message(socket, 'players', [socket.role, players]);
if (clients[socket.game_id])
clients[socket.game_id].push(socket);
else
clients[socket.game_id] = [ socket ];
- socket.on('disconnect', () => {
- SLOG(socket, "DISCONNECT");
+ socket.on('close', (code, reason) => {
+ SLOG(socket, "CLOSE " + code);
clients[socket.game_id].splice(clients[socket.game_id].indexOf(socket), 1);
- if (socket.role !== "Observer")
- broadcast_presence(socket.game_id);
+ broadcast_presence(socket.game_id);
});
if (socket.role !== "Observer") {
- socket.on('action', (action, arg) => on_action(socket, action, arg));
- socket.on('resign', () => on_resign(socket));
- socket.on('getchat', (seen) => on_getchat(socket, seen));
- socket.on('chat', (message) => on_chat(socket, message));
-
- socket.on('debug', () => on_debug(socket));
- socket.on('save', () => on_save(socket));
- socket.on('restore', (state) => on_restore(socket, state));
- socket.on('restart', (scenario) => {
+ socket.on('message', (data) => {
try {
- let seed = random_seed();
- let state = socket.rules.setup(seed, scenario, {}, players);
- put_replay(socket.game_id, null, 'setup', [seed, scenario, null, players]);
- for (let other of clients[socket.game_id]) {
- other.log_length = 0;
- send_state(other, state);
- }
- let state_text = JSON.stringify(state);
- SQL_UPDATE_GAME_RESULT.run(1, null, socket.game_id);
- SQL_UPDATE_GAME_STATE.run(socket.game_id, state_text, state.active);
+ let [ cmd, arg ] = JSON.parse(data);
+ handle_message(socket, cmd, arg);
} catch (err) {
- console.log(err);
- return socket.emit('error', err.toString());
+ send_message(socket, 'error', err);
}
});
}
broadcast_presence(socket.game_id);
-
send_state(socket, get_game_state(socket.game_id));
-
} catch (err) {
console.log(err);
- socket.emit('error', err.message);
+ socket.close(1000, err.message);
}
});