server for my chess game
| -rw-r--r-- | src/index.js | 156 |
1 files changed, 98 insertions, 58 deletions
diff --git a/src/index.js b/src/index.js index 1671be3..f6e5e54 100644 --- a/src/index.js +++ b/src/index.js @@ -18,23 +18,19 @@ const HEADERS = { info: "I", move: "M", undo: "<", + spectate: "0", // the eye }; -// { gamecode: {clients: [], ids: [], infos: {names: [], countrys:[]} turn = true, pgn: ""} } +// { gamecode: {clients: {}, ids: {}, infos: {names: [], countrys:[]} turn = true, pgn: ""} } let games = {}; function str_obj(obj) { return JSON.stringify(obj, null, 0); } -function send_packet(data, header, client) { - const packet = putVar({ data: data, header: header }); - client.send(packet); -} - function send_group_packet(data, header, clients) { clients.forEach((client) => { - if (client) send_packet(data, header, client); + if (client) client.send_packet(data, header); }); } @@ -42,22 +38,34 @@ const interval = setInterval(() => { function cleanup_games() { const keys = Object.keys(games); keys.forEach((key) => { - let clients = games[key].clients; + // deal with clients dieing + const game = games[key]; + let clients = game.clients; clients.forEach((client) => { if (client) { if (client.is_alive === false) { client.terminate(); wss.clients.delete(client); - clients[clients.indexOf(client)] = undefined; + game.remove_client(client); console.log("removed dead client from", key); } else { client.is_alive = false; // becomes true on next ping - client.ping(); + client.ping(); // this ping! } } if (clients[0] == undefined && clients[1] == undefined) { + console.log(`dead game: ${key} deleted (${game.pgn})`); delete games[key]; - console.log(`dead game: ${key} deleted`); + return; + } + }); + // deal with spectators being ded + let specs = game.spectators; + specs.forEach((spec) => { + if (spec.is_alive === false) { + spec.terminate(); + wss.clients.delete(spec); + game.remove_spectator(spec); } }); }); @@ -84,15 +92,16 @@ console.log(`Server started on port ${PORT}`); wss.on("connection", (ws, req) => { console.log(`client connected (${req.socket.remoteAddress})`); ws.is_alive = true; + ws.send_packet = function (data, header) { + const packet = putVar({ data: data, header: header }); + this.send(packet); + }; ws.on("pong", heartbeat); // on message recieved ws.on("message", (message) => { let recieve = getVar(Buffer.from(message)).value; let data = recieve.data; let header = recieve.header; - console.log( - `recieved ${str_obj(recieve)} from ${req.socket.remoteAddress}` - ); if (header) { switch (header) { case HEADERS.relay: @@ -119,6 +128,9 @@ wss.on("connection", (ws, req) => { case HEADERS.undo: handle_undo(data, ws); break; + case HEADERS.spectate: + handle_spectate(data, ws); + break; case HEADERS.move: handle_move(data, ws); break; @@ -139,15 +151,15 @@ async function get_propertys(name) { async function signin(data, ws) { const c = `SELECT id, country FROM users WHERE name = '${data.name}' AND password = '${data.password}';`; const res = await command(c); - if (res.rows[0]) send_packet(res.rows[0], HEADERS.signin, ws); - else send_packet("Incorrect credentials", HEADERS.signin, ws); + if (res.rows[0]) ws.send_packet(res.rows[0], HEADERS.signin); + else ws.send_packet("Incorrect credentials", HEADERS.signin); } async function signup(data, ws) { console.log("attempting to create user"); const res = await get_propertys(data.name); if (res) { - send_packet("err: user already exists", HEADERS.create_user, ws); + ws.send_packet("err: user already exists", HEADERS.create_user); console.error("user already exists"); return; // if existing, send err } @@ -163,11 +175,11 @@ async function signup(data, ws) { id = await init_user(); } catch (e) { const packet = `err: could not create user(${e.stack})`; - send_packet(packet, HEADERS.create_user, ws); + ws.send_packet(packet, HEADERS.create_user); console.error(e.stack); return; } - send_packet(id, HEADERS.create_user, ws); + ws.send_packet(id, HEADERS.create_user); } function handle_joinrequest(data, ws) { @@ -181,12 +193,12 @@ function handle_joinrequest(data, ws) { game.infos.names.push(data.name); game.infos.countrys.push(data.country); } - send_packet(game.infos.get(1), HEADERS.info, game.clients[0]); - send_packet(game.infos.get(0), HEADERS.info, game.clients[1]); // give each their data + game.clients[0].send_packet(game.infos.get(1), HEADERS.info); + game.clients[1].send_packet(game.infos.get(0), HEADERS.info); // give each their data if (!game.pgn) send_group_packet(game.pgn, HEADERS.loadpgn, game.clients); - else send_packet(game.pgn, HEADERS.loadpgn, ws); // only send it to the new guy + else ws.send_packet(game.pgn, HEADERS.loadpgn); // only send it to the new guy // if its empty send it to both(it acts as startgame signal) - send_packet({ idx: i }, HEADERS.joinrequest, ws); + ws.send_packet({ idx: i }, HEADERS.joinrequest); } if (data.gamecode !== undefined && data.id !== undefined) if (game !== undefined) @@ -200,10 +212,10 @@ function handle_joinrequest(data, ws) { else { const packet = "err: game full ( if rejoining, please try again in 10-20 seconds )"; - send_packet(packet, HEADERS.joinrequest, ws); + ws.send_packet(packet, HEADERS.joinrequest); } - else send_packet("err: game does not exist", HEADERS.joinrequest, ws); - else send_packet("err: gamecode or id not defined", HEADERS.joinrequest, ws); + else ws.send_packet("err: game does not exist", HEADERS.joinrequest); + else ws.send_packet("err: gamecode or id not defined", HEADERS.joinrequest); } function handle_hostrequest(data, ws) { @@ -212,7 +224,7 @@ function handle_hostrequest(data, ws) { console.log("hostrequest:", data.gamecode); games[data.gamecode] = { clients: [ws, undefined], - ids: [data.id], + ids: [data.id], // not a set so i can play against myself infos: { names: [data.name], countrys: [data.country], @@ -223,6 +235,7 @@ function handle_hostrequest(data, ws) { }; }, }, + spectators: [], moves: [], pgn: "", fullmoves: 1, @@ -240,67 +253,94 @@ function handle_hostrequest(data, ws) { this.turn = !this.turn; if (!this.turn) this.fullmoves--; this.pgn = this.moves.join(" "); - console.log("POPPED"); + }, + remove_client(ws) { + this.clients[this.clients.indexOf(ws)] = undefined; + }, + 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.spectators); + }, + send_signal_packet(data, ws, header) { + let i = this.clients.indexOf(ws); + if (i !== -1) { + let sendto = this.clients[i ? 0 : 1]; + delete data.gamecode; // dont send the gamecode to the other player: waste of bytes + if (sendto) { + sendto.send_packet(data, header); + console.log(`sending signal ${str_obj(data)}`); + send_group_packet(data, header, this.spectators); // give it to the specs + return true; + } + } else console.log(`could not find client in game ${data.gamecode}`); + return false; }, }; - send_packet({ idx: 0 }, HEADERS.hostrequest, ws); + ws.send_packet({ idx: 0 }, HEADERS.hostrequest); console.log(`game ${data.gamecode} created`); } else { const err_packet = `err: "${data.gamecode}" already exists`; - send_packet(err_packet, HEADERS.hostrequest, ws); + ws.send_packet(err_packet, HEADERS.hostrequest); } - } else - send_packet("err: gamecode or id not defined", HEADERS.hostrequest, ws); + } else ws.send_packet("err: gamecode or id not defined", HEADERS.hostrequest); } function handle_stop(data, ws) { if (games.hasOwnProperty(data.gamecode)) { if (games[data.gamecode].clients.includes(ws)) { console.log("stopgame " + data.gamecode); - send_group_packet( - data.reason, - HEADERS.stopgame, - games[data.gamecode].clients - ); - delete games[data.gamecode]; + games[data.gamecode].send_group_packet(data.reason, HEADERS.stopgame); + delete games[data.gamecode]; // kill } } } function handle_move(data, ws) { - if (dual_relay(data, ws, HEADERS.move)) - games[data.gamecode].add_turn(data.move); + const gc = data.gamecode; + if (dual_relay(data, ws, HEADERS.move)) games[gc].add_turn(data.move); } function handle_undo(data, ws) { - if (signal_other(data, ws, HEADERS.undo) && data.accepted == true) { - console.log("calling popper"); - games[data.gamecode].pop_move(); - } else console.warn(data.accepted); + const gc = data.gamecode; + if (signal_other(data, ws, HEADERS.undo) && data.accepted == true) + games[gc].pop_move(); +} + +function handle_spectate(data, ws) { + if (games.hasOwnProperty(data.gamecode)) { + const game = games[data.gamecode]; + console.log("spectate " + data.gamecode); + game.add_spectator(ws); + const packet = { + white: game.infos.get(0), + black: game.infos.get(1), + pgn: game.pgn, + }; // spectator starter kit + // provides white info, black info, and pgn + ws.send_packet(packet, HEADERS.spectate); + } else ws.send_packet("err: game does not exist", HEADERS.spectate); } // relays to both clients function dual_relay(data, ws, header = HEADERS.relay) { if (games.hasOwnProperty(data.gamecode)) { - if (games[data.gamecode].clients.includes(ws)) { - send_group_packet(data, header, games[data.gamecode].clients); - console.log(`relaying ${str_obj(data)} to both clients`); - return true; - } else console.log(`requester is not in game ${data.gamecode}`); + games[data.gamecode].send_group_packet(data, header); + console.log(`relaying ${str_obj(data)} to both clients`); + return true; } else console.log(`dual relay: game ${data.gamecode} does not exist`); return false; } // relays to the other client function signal_other(data, ws, header = HEADERS.signal) { - if (games.hasOwnProperty(data.gamecode)) { - let i = games[data.gamecode].clients.indexOf(ws); - if (i !== -1) { - let sendto = games[data.gamecode].clients[i ? 0 : 1]; - send_packet(data, header, sendto); - console.log(`sending signal ${str_obj(data)}`); - return true; - } else console.log(`could not find client in game ${data.gamecode}`); - } + if (games.hasOwnProperty(data.gamecode)) + return games[data.gamecode].send_signal_packet(data, ws, header); return false; } |