server for my chess game
| -rw-r--r-- | src/clients.js | 88 | ||||
| -rw-r--r-- | src/game.js | 82 | ||||
| -rw-r--r-- | src/index.js | 47 | ||||
| -rw-r--r-- | src/infos.js | 30 | ||||
| -rw-r--r-- | src/utils.js | 4 |
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) { |