server for my chess game
-rw-r--r--src/clients.js88
-rw-r--r--src/game.js82
-rw-r--r--src/index.js47
-rw-r--r--src/infos.js30
-rw-r--r--src/utils.js4
5 files changed, 150 insertions, 101 deletions
diff --git a/src/clients.js b/src/clients.js
new file mode 100644
index 0000000..f018a12
--- /dev/null
+++ b/src/clients.js
@@ -0,0 +1,88 @@
+function create_data(ws, id, name, country) {
+ return {
+ name: name,
+ id: id,
+ ws: ws,
+ country: country,
+ };
+}
+
+function create_empty_data() {
+ return {
+ empty: true,
+ };
+}
+
+export class Clients {
+ constructor(ws, id, name, country, is_white) {
+ if (is_white) {
+ this.w = create_data(ws, id, name, country);
+ this.b = create_empty_data();
+ } else {
+ this.b = create_data(ws, id, name, country);
+ this.w = create_empty_data();
+ }
+ }
+
+ get_ws(color) {
+ return color === "w" ? this.w.ws : this.b.ws;
+ }
+
+ get_info(color) {
+ return color === "w"
+ ? { name: this.w.name, country: this.w.country }
+ : { name: this.b.name, country: this.b.country };
+ }
+
+ get client_list() {
+ return [this.get_ws("w"), this.get_ws("b")];
+ }
+
+ get players() {
+ let players = 0;
+ if (!this.w.empty) players++;
+ if (!this.b.empty) players++;
+ return players;
+ }
+
+ get length() {
+ let client_count = 0;
+ if (this.w.ws != undefined) client_count++;
+ if (this.b.ws != undefined) client_count++;
+ return client_count;
+ }
+
+ get_all_info() {
+ return { w: this.get_info("w"), b: this.get_info("b") };
+ }
+
+ add(ws, name, id, country, is_white = this.w.empty) {
+ if (is_white) this.w = create_data(ws, id, name, country);
+ else this.b = create_data(ws, id, name, country);
+ }
+
+ exists(color) {
+ return color === "w" ? !this.w.empty : !this.b.empty;
+ }
+
+ // fake function overloading
+ color_of(nameorws, country, id) {
+ if (typeof nameorws == "string") {
+ // if you check name, country AND id, even if there are duplicate ids (impossible) it wont have problems
+ function check(on) {
+ // if on doesnt have a name property, this will not error, because js
+ return on.name == nameorws && on.country == country && on.id == id;
+ }
+ if (check(this.w)) return "w";
+ else if (check(this.b)) return "b";
+ } else if (typeof nameorws == "object") {
+ if (this.w.ws == nameorws) return "w";
+ if (this.b.ws == nameorws) return "b";
+ }
+ }
+
+ erase(client) {
+ if (this.w.ws == client) this.w.ws = undefined;
+ else if (this.b.ws == client) this.b.ws = undefined;
+ }
+}
diff --git a/src/game.js b/src/game.js
index 1966982..d8cb859 100644
--- a/src/game.js
+++ b/src/game.js
@@ -1,65 +1,58 @@
import { Chess } from "chess.js";
-import { Infos } from "./infos.js";
-import { str_obj, flip_int, send_group_packet } from "./utils.js";
+import { Clients } from "./clients.js";
+import { str_obj, send_group_packet, flip_color } from "./utils.js";
export class Game {
constructor(data, ws, wss) {
this.wss = wss;
- this.hosterIndex = Number(!data.team);
- this.clients = [undefined, undefined];
- this.clients[this.hosterIndex] = ws; // team === true : 0 ? 1
this.gamecode = data.gamecode;
- this.ids = [undefined, undefined]; // not a set so i can play against myself
- this.ids[this.hosterIndex] = data.id;
- this.infos = new Infos(this.hosterIndex, data.name, data.country);
+ this.clients = new Clients(ws, data.id, data.name, data.country, data.team);
this.spectators = [];
this.game = new Chess();
- if (data.hasOwnProperty("moves")) this.game.load_pgn(data.moves.join(" "));
+ if (data.hasOwnProperty("moves")) {
+ if (typeof data.moves == "string") this.game.load_pgn(data.moves);
+ else this.game.load_pgn(data.moves.join(" "));
+ }
+
+ // create func aliases
+ this.get_info = this.clients.get_info.bind(this.clients);
+ this.get_ws = this.clients.get_ws.bind(this.clients);
+ this.color_of = this.clients.color_of.bind(this.clients);
+ this.exists = this.clients.exists.bind(this.clients);
+ this.remove_client = this.clients.erase.bind(this.clients);
+ this.add_spectator = this.spectators.push;
+
+ this.is_game_over = this.game.game_over;
+ this.move = this.game.move;
+ this.undo = this.game.undo;
}
+
get pgn() {
return this.game.pgn();
}
get players() {
- let players = 0;
- this.ids.forEach((id) => {
- if (id !== undefined) players++;
- });
- return players;
+ return this.clients.players;
}
get client_count() {
- let clients = 0;
- this.clients.forEach((client) => {
- if (client !== undefined) clients++;
- });
- return clients;
+ return this.clients.length;
}
- get joinerIndex() {
- return flip_int(this.hosterIndex);
+ get client_list() {
+ return this.clients.client_list;
}
- //true for valid
+
+ // true for valid
validate_move(move) {
const res = this.game.move(move);
if (str_obj(res) == "{}") return false;
this.game.undo();
return true;
}
- move(move) {
- this.game.move(move);
- }
- undo() {
- this.game.undo();
- }
- remove_client(ws) {
- this.clients[this.clients.indexOf(ws)] = undefined;
- }
- add_client(ws, data) {
- this.clients[this.joinerIndex] = ws;
- this.ids[this.joinerIndex] = data.id;
- this.infos.add(data.name, data.country);
+ add_client(ws, data, is_white = this.clients.w.empty) {
+ this.clients.add(ws, data.name, data.id, data.country, is_white);
}
clean_clients(set_is_alive = true) {
@@ -77,7 +70,7 @@ export class Game {
remove_client(spec, this.wss, this.remove_spectator.bind(this));
});
- this.clients.forEach((client) => {
+ this.client_list.forEach((client) => {
remove_client(client, this.wss, this.remove_client.bind(this));
});
}
@@ -86,21 +79,20 @@ export class Game {
return this.client_count === 0 && this.spectators.length == 0;
}
- add_spectator(ws) {
- this.spectators.push(ws);
- }
remove_spectator(ws) {
this.spectators.slice(this.spectators.indexOf(ws));
}
+
send_group_packet(packet, header) {
delete packet.gamecode;
- send_group_packet(packet, header, this.clients);
+ send_group_packet(packet, header, this.clients.client_list);
send_group_packet(packet, header, this.spectators);
}
+
send_signal_packet(data, ws, header) {
- let i = this.clients.indexOf(ws);
- if (i !== -1) {
- let sendto = this.clients[i ? 0 : 1];
+ let us = this.color_of(ws);
+ if (us !== undefined) {
+ let sendto = this.get_ws(flip_color(us));
delete data.gamecode; // dont send the gamecode to the other player: waste of bytes
if (sendto) {
sendto.send_packet(data, header);
@@ -110,9 +102,7 @@ export class Game {
} else console.log(`could not find client in game ${data.gamecode}`);
return false;
}
- is_game_over() {
- return this.game.game_over();
- }
+
reset_game() {
this.game = new Chess();
}
diff --git a/src/index.js b/src/index.js
index f614c13..9e0b53c 100644
--- a/src/index.js
+++ b/src/index.js
@@ -8,7 +8,7 @@ const { putVar, getVar } = utils;
import { command } from "./pg.js";
import { self_ping } from "./ping.js";
import { Game } from "./game.js";
-import { str_obj, flip_int, send_group_packet } from "./utils.js";
+import { flip_color } from "./utils.js";
const HEADERS = {
relay: "R",
@@ -174,14 +174,16 @@ async function signup(data, ws) {
// hoster does *not* call this function with itself. joiner must tell hoster about itself
function handle_joinrequest(data, ws) {
function send_info(they_know_about_me = false) {
- const us = game.ids.indexOf(data.id);
- const them = flip_int(us);
- // in a ideal world, clients dont disconnect and become undefined. we do not live in a ideal world. (sadly) (or they just left the game :/)
- if (game.clients[them] !== undefined && !they_know_about_me) {
- game.clients[them].send_packet(game.infos.get(us), HEADERS.info);
- }
- game.clients[us].send_packet(game.infos.get(them), HEADERS.info);
+ const us = game.clients.color_of(ws);
+ const them = flip_color(us);
+ // in a ideal world, clients dont disconnect and stop existing. we do not live in a ideal world. (sadly) (or they just left the game :/)
+ if (game.exists(them) && !they_know_about_me)
+ game.get_ws(them).send_packet(game.get_info(us), HEADERS.info);
+
+ // send a packet to us
+ game.get_ws(us).send_packet(game.get_info(them), HEADERS.info);
}
+
const game = games[data.gamecode];
if (data.id) {
if (data.gamecode !== undefined) {
@@ -190,24 +192,23 @@ function handle_joinrequest(data, ws) {
// hoster is waiting for someone to join
game.add_client(ws, data);
ws.send_packet({ idx: game.joinerIndex }, HEADERS.joinrequest); // tell them what team they are
- send_group_packet(game.pgn, HEADERS.loadpgn, game.clients); // hoster doesnt send a joinrequest, so tell it to load pgn too
+ game.send_group_packet(game.pgn, HEADERS.loadpgn); // hoster doesnt send a joinrequest, so tell it to load pgn too
send_info();
console.log(`${data.name} joined ${data.gamecode}`);
- } else if (game.ids.includes(data.id)) {
- // someone is trying to rejoin
- console.log(
- `rejoin ${data.name} to ${
- game.ids.indexOf(data.id) === 0 ? "white" : "black"
- }`
- );
- game.clients[game.ids.indexOf(data.id)] = ws;
- ws.send_packet(game.pgn, HEADERS.loadpgn); // pass them the pgn
- send_info(true); // and send them their opponents info
} else {
- console.log(
- `rejected join to ${data.gamecode} (by ${data.name}): game full / id(${data.id}) not included in ${game.ids}`
- );
- ws.send_packet({ err: "FULL" }, HEADERS.joinrequest);
+ let color = game.color_of(data.name, data.country, data.id);
+ if (color != undefined) {
+ // someone is trying to rejoin
+ console.log(`rejoin ${data.name} to ${color}`);
+ game.add_client(ws, data, color == "w");
+ ws.send_packet(game.pgn, HEADERS.loadpgn); // pass them the pgn
+ send_info(true); // and send them their opponents info
+ } else {
+ console.log(
+ `rejected join to ${data.gamecode} (by ${data.name}): game full / id(${data.id}) not included in ${game.ids}`
+ );
+ ws.send_packet({ err: "FULL" }, HEADERS.joinrequest);
+ }
}
} else ws.send_packet({ err: "NOT_EXIST" }, HEADERS.joinrequest);
} else ws.send_packet({ err: "NO_GAMECODE" }, HEADERS.joinrequest);
diff --git a/src/infos.js b/src/infos.js
deleted file mode 100644
index 0e2e129..0000000
--- a/src/infos.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import { flip_int } from "./utils.js";
-
-export class Infos {
- constructor(hosterIndex, hosterName, hosterCountry) {
- this.hosterIndex = hosterIndex;
- this.names = ["Anonymous", "Anonymous"];
- this.names[hosterIndex] = hosterName;
- this.countrys = ["rainbow", "rainbow"];
- this.countrys[hosterIndex] = hosterCountry;
- }
- get(index) {
- return {
- name: this.names[index],
- country: this.countrys[index],
- };
- }
-
- get_all() {
- return { w: this.get(0), b: this.get(1) };
- }
-
- get joinerIndex() {
- return flip_int(this.hosterIndex);
- }
-
- add(name, country) {
- this.names[this.joinerIndex] = name;
- this.countrys[this.joinerIndex] = country;
- }
-}
diff --git a/src/utils.js b/src/utils.js
index 200df5d..fe76518 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -2,8 +2,8 @@ export function str_obj(obj) {
return JSON.stringify(obj, null, 0);
}
-export function flip_int(int) {
- return int === 0 ? 1 : 0;
+export function flip_color(color) {
+ return color === "w" ? "b" : "w";
}
export function send_group_packet(data, header, clients) {