diff --git a/frontend/src/pages/pong/pong.ts b/frontend/src/pages/pong/pong.ts index d4834fb..a69d59b 100644 --- a/frontend/src/pages/pong/pong.ts +++ b/frontend/src/pages/pong/pong.ts @@ -393,10 +393,12 @@ function pongClient(_url: string, _args: RouteHandlerParams): RouteHandlerReturn let end_txt: string = ''; if ((user.id === currentGame.game.left.id && winner === 'left') || (user.id === currentGame.game.right.id && winner === 'right')) - end_txt = 'won! #yippe'; + end_txt = 'you won! #yippe'; else - end_txt = 'lost #sadge'; - end_scr.innerText = 'you ' + end_txt; + end_txt = 'you lost #sadge'; + if (currentGame.spectating) + end_txt = `${winner === 'left' ? currentGame.playerL.name : currentGame.playerR.name} won #gg`; + end_scr.innerText = end_txt; end_scr.classList.remove("hidden"); setTimeout(() => { end_scr.classList.add("hidden"); @@ -434,7 +436,7 @@ function pongClient(_url: string, _args: RouteHandlerParams): RouteHandlerReturn break; case "playing": tournamentBtn.innerText = TourBtnState.Started; - tour_infos.innerText = `${TourInfoState.Running} ${s.players.length}👤 ?▮•▮`; + tour_infos.innerText = `${TourInfoState.Running} ${s.players.length}👤 ${s.remainingMatches ?? "?"}▮•▮`; break; case "prestart": tour_infos.innerText = `${imOwner ? TourInfoState.Owner : (weIn ? TourInfoState.Registered : TourInfoState.NotRegisted)} ${s.players.length}👤 ?▮•▮`; diff --git a/frontend/src/pages/pong/socket.ts b/frontend/src/pages/pong/socket.ts index 13dc8b6..874d749 100644 --- a/frontend/src/pages/pong/socket.ts +++ b/frontend/src/pages/pong/socket.ts @@ -34,7 +34,7 @@ export type TourInfo = { ownerId: string; state: 'prestart' | 'playing' | 'ended' | 'canceled'; players: { id: string; name: string; score: number }[]; - currentGameInfo: GameUpdate | null; + remainingMatches: number | null, }; export interface ClientToServer { diff --git a/src/pong/src/game.ts b/src/pong/src/game.ts index 934e081..c3c81ee 100644 --- a/src/pong/src/game.ts +++ b/src/pong/src/game.ts @@ -95,8 +95,8 @@ function makeAngle(i: number): [number, number, number, number] { -Math.PI / i + Math.PI, ]; } -const LEFT :number = 0; -const RIGHT :number = 1; +const LEFT: number = 0; +const RIGHT: number = 1; export class Pong { @@ -130,9 +130,9 @@ export class Pong { Pong.BALL_START_ANGLES[this.ballAngleIdx++], ); - public sendSig : boolean = false; - public ready_checks: [boolean, boolean] = [false, false]; - public rdy_timer : number = Date.now(); + public sendSig: boolean = false; + public ready_checks: [boolean, boolean] = [false, false]; + public rdy_timer: number = Date.now(); public score: [number, number] = [0, 0]; public local: boolean = false; @@ -142,6 +142,8 @@ export class Pong { private cachedWinner: 'left' | 'right' | null = null; + public onEnd: (() => void) | null = null; + public static makeLocal(owner: UserId): Pong { const game = new Pong(owner, owner); game.local = true; @@ -153,7 +155,7 @@ export class Pong { public userRight: UserId, ) { } - public readyup(uid : UserId) { + public readyup(uid: UserId) { if (uid === this.userLeft) { this.ready_checks[LEFT] = true; } @@ -161,16 +163,16 @@ export class Pong { this.ready_checks[RIGHT] = true; } } - public readydown(uid : UserId) { + public readydown(uid: UserId) { // is everyone already ready? - if (this.ready_checks[LEFT] === true && this.ready_checks[RIGHT] === true) return ; + if (this.ready_checks[LEFT] === true && this.ready_checks[RIGHT] === true) return; if (uid === this.userLeft) { this.ready_checks[LEFT] = false; } if (uid === this.userRight) { this.ready_checks[RIGHT] = false; } } public tick() { - if (!this.local && (this.ready_checks[LEFT] !== true || this.ready_checks[RIGHT] !== true)) { return;} + if (!this.local && (this.ready_checks[LEFT] !== true || this.ready_checks[RIGHT] !== true)) { return; } if (this.paddleCollision(this.leftPaddle, 'left')) { this.ball.collided( 'left', @@ -287,8 +289,8 @@ export class Pong { if (!this.ready_checks[0]) return ('right'); if (!this.ready_checks[1]) return ('left'); } - if (this.leftLastSeen !== -1 && Date.now() - this.leftLastSeen > Pong.CONCEDED_TIMEOUT) { return 'right';} - if (this.rightLastSeen !== -1 && Date.now() - this.rightLastSeen > Pong.CONCEDED_TIMEOUT) { return 'left';} + if (this.leftLastSeen !== -1 && Date.now() - this.leftLastSeen > Pong.CONCEDED_TIMEOUT) { return 'right'; } + if (this.rightLastSeen !== -1 && Date.now() - this.rightLastSeen > Pong.CONCEDED_TIMEOUT) { return 'left'; } return null; }; diff --git a/src/pong/src/socket.ts b/src/pong/src/socket.ts index 78cc1e9..435a8ca 100644 --- a/src/pong/src/socket.ts +++ b/src/pong/src/socket.ts @@ -34,7 +34,7 @@ export type TourInfo = { ownerId: string; state: 'prestart' | 'playing' | 'ended' | 'canceled'; players: { id: string; name: string; score: number }[]; - currentGameInfo: GameUpdate | null; + remainingMatches: number | null, }; export interface ClientToServer { diff --git a/src/pong/src/state.ts b/src/pong/src/state.ts index 17b39a2..5d94cb9 100644 --- a/src/pong/src/state.ts +++ b/src/pong/src/state.ts @@ -10,11 +10,11 @@ import { Tournament } from './tour'; type PUser = { id: UserId; currentGame: null | GameId; - socket: SSocket, - windowId: string, - updateInterval: NodeJS.Timeout, - killSelfInterval: NodeJS.Timeout, - lastSeen: number, + socket: SSocket; + windowId: string; + updateInterval: NodeJS.Timeout; + killSelfInterval: NodeJS.Timeout; + lastSeen: number; }; type GameId = PongGameId; @@ -24,16 +24,18 @@ class StateI { public static readonly KEEP_ALIVE_MS: number = 30 * 1000; public static readonly START_TIMER_TOURNAMENT: number = 60 * 2 * 1000; - private users: Map = new Map(); - private queue: Set = new Set(); - private queueInterval: NodeJS.Timeout; - private tournamentInterval: NodeJS.Timeout; - private games: Map = new Map(); - private tournament: Tournament | null = null; + public users: Map = new Map(); + public queue: Set = new Set(); + public queueInterval: NodeJS.Timeout; + public tournamentInterval: NodeJS.Timeout; + public games: Map = new Map(); + public tournament: Tournament | null = null; - public constructor(private fastify: FastifyInstance) { + public constructor(public fastify: FastifyInstance) { this.queueInterval = setInterval(() => this.queuerFunction()); - this.tournamentInterval = setInterval(() => this.tournamentIntervalFunc()); + this.tournamentInterval = setInterval(() => + this.tournamentIntervalFunc(), + ); void this.queueInterval; void this.tournamentInterval; } @@ -41,13 +43,80 @@ class StateI { private static getGameUpdateData(id: GameId, g: Pong): GameUpdate { return { gameId: id, - left: { id: g.userLeft, score: g.score[0], paddle: { x: g.leftPaddle.x, y: g.leftPaddle.y, width: g.leftPaddle.width, height: g.leftPaddle.height } }, - right: { id: g.userRight, score: g.score[1], paddle: { x: g.rightPaddle.x, y: g.rightPaddle.y, width: g.rightPaddle.width, height: g.rightPaddle.height } }, + left: { + id: g.userLeft, + score: g.score[0], + paddle: { + x: g.leftPaddle.x, + y: g.leftPaddle.y, + width: g.leftPaddle.width, + height: g.leftPaddle.height, + }, + }, + right: { + id: g.userRight, + score: g.score[1], + paddle: { + x: g.rightPaddle.x, + y: g.rightPaddle.y, + width: g.rightPaddle.width, + height: g.rightPaddle.height, + }, + }, ball: { x: g.ball.x, y: g.ball.y, size: g.ball.size }, local: g.local, }; } + public initGame( + g: Pong | null, + gameId: GameId, + id1: UserId, + id2: UserId, + ): Pong | null { + const u1 = this.users.get(id1); + const u2 = this.users.get(id2); + + if (isNullish(u1) || isNullish(u2)) return null; + + this.fastify.log.info({ + msg: 'init new game', + user1: u1.id, + user2: u2.id, + }); + if (g === null) g = new Pong(u1.id, u2.id); + const iState: GameUpdate = StateI.getGameUpdateData(gameId, g); + + u1.socket.emit('newGame', iState); + u2.socket.emit('newGame', iState); + g.rdy_timer = Date.now(); + this.games.set(gameId, g); + + u1.currentGame = gameId; + u2.currentGame = gameId; + + g.gameUpdate = setInterval(() => { + g.tick(); + if ( + g.sendSig === false && + g.ready_checks[0] === true && + g.ready_checks[1] === true + ) { + u1.socket.emit('rdyEnd'); + u2.socket.emit('rdyEnd'); + g.sendSig = true; + } + if (g.ready_checks[0] === true && g.ready_checks[1] === true) { + this.gameUpdate(gameId, u1.socket); + this.gameUpdate(gameId, u2.socket); + } + if (g.checkWinner() !== null) { + this.cleanupGame(gameId, g); + } + }, 1000 / StateI.UPDATE_INTERVAL_FRAMES); + return g; + } + private getHello(socket: SSocket) { const user = this.users.get(socket.authUser.id); if (isNullish(user)) return; @@ -60,21 +129,33 @@ class StateI { if (isNullish(user)) return; if (isNullish(this.tournament)) { - sock.emit('tournamentRegister', { kind: 'failure', msg: 'No tournament exists' }); + sock.emit('tournamentRegister', { + kind: 'failure', + msg: 'No tournament exists', + }); return; } if (this.tournament.state !== 'prestart') { - sock.emit('tournamentRegister', { kind: 'failure', msg: 'No tournament already started' }); + sock.emit('tournamentRegister', { + kind: 'failure', + msg: 'No tournament already started', + }); return; } const udb = this.fastify.db.getUser(user.id); if (isNullish(udb)) { - sock.emit('tournamentRegister', { kind: 'failure', msg: 'User not found' }); + sock.emit('tournamentRegister', { + kind: 'failure', + msg: 'User not found', + }); return; } this.tournament.addUser(user.id, name ?? udb.name); - sock.emit('tournamentRegister', { kind: 'success', msg: 'Registered to Tournament' }); + sock.emit('tournamentRegister', { + kind: 'success', + msg: 'Registered to Tournament', + }); return; } @@ -83,16 +164,25 @@ class StateI { if (isNullish(user)) return; if (isNullish(this.tournament)) { - sock.emit('tournamentRegister', { kind: 'failure', msg: 'No tournament exists' }); + sock.emit('tournamentRegister', { + kind: 'failure', + msg: 'No tournament exists', + }); return; } if (this.tournament.state !== 'prestart') { - sock.emit('tournamentRegister', { kind: 'failure', msg: 'No tournament already started' }); + sock.emit('tournamentRegister', { + kind: 'failure', + msg: 'No tournament already started', + }); return; } this.tournament.removeUser(user.id); - sock.emit('tournamentRegister', { kind: 'success', msg: 'Unregistered to Tournament' }); + sock.emit('tournamentRegister', { + kind: 'success', + msg: 'Unregistered to Tournament', + }); return; } @@ -101,21 +191,25 @@ class StateI { if (isNullish(user)) return; if (this.tournament !== null) { - sock.emit('tournamentCreateMsg', { kind: 'failure', msg: 'A tournament already exists' }); + sock.emit('tournamentCreateMsg', { + kind: 'failure', + msg: 'A tournament already exists', + }); return; } this.tournament = new Tournament(user.id); this.registerForTournament(sock, null); - this.tournament.startTimeout = setTimeout(() => this.tournament?.start(), StateI.START_TIMER_TOURNAMENT); + this.tournament.startTimeout = setTimeout( + () => this.tournament?.start(), + StateI.START_TIMER_TOURNAMENT, + ); } private cleanupTournament() { if (this.tournament === null) return; - // we remove all the games we might have done for the tournament, and voila :D - this.tournament.games.keys().forEach((g) => this.games.delete(g)); - this.tournament = null; + this.fastify.log.info('Tournament has been ended'); } private startTournament(sock: SSocket) { @@ -129,7 +223,12 @@ class StateI { } public newPausedGame(suid1: string, suid2: string): GameId | undefined { - if (!this.users.has(suid1 as UserId) || !this.users.has(suid2 as UserId)) { return (undefined); } + if ( + !this.users.has(suid1 as UserId) || + !this.users.has(suid2 as UserId) + ) { + return undefined; + } const uid1: UserId = suid1 as UserId; const uid2: UserId = suid2 as UserId; const g = new Pong(uid1, uid2); @@ -137,45 +236,24 @@ class StateI { const gameId = newUUID() as unknown as GameId; this.games.set(gameId, g); - return (gameId); + return gameId; } + public startPausedGame(g_id: PongGameId): boolean { let game: Pong | undefined; - if (!this.games.has(g_id) || (game = this.games.get(g_id)) === undefined) { return (false); } - game.rdy_timer = Date.now(); - - const id1 = game.userLeft; - const id2 = game.userRight; - - if (!this.users.has(id1) || !this.users.has(id2)) { return (false); } - const usr1 = this.users.get(id1); - const usr2 = this.users.get(id2); - if (isNullish(usr1) || isNullish(usr2)) { return (false); } - - const iState: GameUpdate = StateI.getGameUpdateData(g_id, game); - - usr1.socket.emit('newGame', iState); usr1.currentGame = g_id; - usr2.socket.emit('newGame', iState); usr2.currentGame = g_id; - game.gameUpdate = setInterval(() => { - game.tick(); - if (game.sendSig === false && game.ready_checks[0] === true && game.ready_checks[1] === true) { - usr1.socket.emit('rdyEnd'); - usr2.socket.emit('rdyEnd'); - game.sendSig = true; - } - if (game.ready_checks[0] === true && game.ready_checks[1] === true) { - this.gameUpdate(g_id, usr1.socket); - this.gameUpdate(g_id, usr2.socket); - } - if (game.checkWinner() !== null) { this.cleanupGame(g_id, game); } - }, 1000 / StateI.UPDATE_INTERVAL_FRAMES); - return (true); + if ((game = this.games.get(g_id)) === undefined) { + return false; + } + this.initGame(game, g_id, game.userLeft, game.userRight); + return true; } private tournamentIntervalFunc() { const broadcastTourEnding = (msg: string) => { - this.users.forEach((u) => { u.socket.emit('tourEnding', msg); }); + this.users.forEach((u) => { + u.socket.emit('tourEnding', msg); + }); }; if (this.tournament) { if (this.tournament.state === 'canceled') { @@ -187,7 +265,25 @@ class StateI { this.cleanupTournament(); } else if (this.tournament.state === 'playing') { - this.tournament.checkCurrentGame(); + const currentgame = this.tournament.currentGame; + if (currentgame) { + const game = this.games.get(currentgame); + if (game) { + const gameData = StateI.getGameUpdateData( + currentgame, + game, + ); + for (const user of this.tournament.users + .keys() + .map((id) => this.users.get(id)) + .filter((v) => !isNullish(v))) { + user.socket.emit('gameUpdate', gameData); + } + } + } + else { + this.fastify.log.warn('NO NEXT GAME ?'); + } } } } @@ -213,29 +309,7 @@ class StateI { this.queue.delete(id2); const gameId = newUUID() as unknown as GameId; - const g = new Pong(u1.id, u2.id); - const iState: GameUpdate = StateI.getGameUpdateData(gameId, g); - - u1.socket.emit('newGame', iState); - u2.socket.emit('newGame', iState); - this.games.set(gameId, g); - - u1.currentGame = gameId; - u2.currentGame = gameId; - - g.gameUpdate = setInterval(() => { - g.tick(); - if (g.sendSig === false && g.ready_checks[0] === true && g.ready_checks[1] === true) { - u1.socket.emit('rdyEnd'); - u2.socket.emit('rdyEnd'); - g.sendSig = true; - } - if (g.ready_checks[0] === true && g.ready_checks[1] === true) { - this.gameUpdate(gameId, u1.socket); - this.gameUpdate(gameId, u2.socket); - } - if (g.checkWinner() !== null) { this.cleanupGame(gameId, g); } - }, 1000 / StateI.UPDATE_INTERVAL_FRAMES); + this.initGame(null, gameId, u1.id, u2.id); } } @@ -251,7 +325,7 @@ class StateI { this.games.set(gameId, g); user.currentGame = gameId; - + // here we dont use this.initGame because we are in a local game... g.gameUpdate = setInterval(() => { g.tick(); this.gameUpdate(gameId, user.socket); @@ -259,9 +333,10 @@ class StateI { user.socket.emit('rdyEnd'); g.sendSig = true; } - if (g.checkWinner() !== null) { this.cleanupGame(gameId, g); } + if (g.checkWinner() !== null) { + this.cleanupGame(gameId, g); + } }, 1000 / StateI.UPDATE_INTERVAL_FRAMES); - } private gameUpdate(id: GameId, sock: SSocket) { @@ -271,7 +346,10 @@ class StateI { if (!this.users.has(sock.authUser.id)) return; // is the client associated with that game ? if (this.users.get(sock.authUser.id)!.currentGame !== id) return; - sock.emit('gameUpdate', StateI.getGameUpdateData(id, this.games.get(id)!)); + sock.emit( + 'gameUpdate', + StateI.getGameUpdateData(id, this.games.get(id)!), + ); } private gameMove(socket: SSocket, u: GameMove) { @@ -279,14 +357,22 @@ class StateI { if (!this.users.has(socket.authUser.id)) return; const user = this.users.get(socket.authUser.id)!; // does the user have a game and do we know such game ? - if (user.currentGame === null || !this.games.has(user.currentGame)) return; + if (user.currentGame === null || !this.games.has(user.currentGame)) { + return; + } const game = this.games.get(user.currentGame)!; if (game.local) { - if (u.move !== null) { game.movePaddle('left', u.move); } - if (u.moveRight !== null) { game.movePaddle('right', u.moveRight); } + if (u.move !== null) { + game.movePaddle('left', u.move); + } + if (u.moveRight !== null) { + game.movePaddle('right', u.moveRight); + } + } + else if (u.move !== null) { + game.movePaddle(user.id, u.move); } - else if (u.move !== null) { game.movePaddle(user.id, u.move); } game.updateLastSeen(user.id); } @@ -299,7 +385,6 @@ class StateI { this.cleanupUser(sock); } - public registerUser(socket: SSocket): void { this.fastify.log.info('Registering new user'); if (this.users.has(socket.authUser.id)) { @@ -312,7 +397,10 @@ class StateI { id: socket.authUser.id, windowId: socket.id, updateInterval: setInterval(() => this.updateClient(socket), 100), - killSelfInterval: setInterval(() => this.checkKillSelf(socket), 100), + killSelfInterval: setInterval( + () => this.checkKillSelf(socket), + 100, + ), currentGame: null, lastSeen: Date.now(), }); @@ -329,7 +417,9 @@ class StateI { socket.on('localGame', () => this.newLocalGame(socket)); // todo: allow passing nickname - socket.on('tourRegister', () => this.registerForTournament(socket, null)); + socket.on('tourRegister', () => + this.registerForTournament(socket, null), + ); socket.on('tourUnregister', () => this.unregisterForTournament(socket)); socket.on('tourCreate', () => this.createTournament(socket)); @@ -349,12 +439,10 @@ class StateI { ownerId: this.tournament.owner, state: this.tournament.state, players: this.tournament.users.values().toArray(), - currentGameInfo: (() => { - if (this.tournament.currentGame === null) return null; - const game = this.games.get(this.tournament.currentGame); - if (isNullish(game)) return null; - return StateI.getGameUpdateData(this.tournament.currentGame, game); - })(), + remainingMatches: + this.tournament.state === 'playing' + ? this.tournament.matchup.length + : null, }; } socket.emit('tournamentInfo', tourInfo); @@ -368,9 +456,11 @@ class StateI { this.users.delete(socket.authUser.id); this.queue.delete(socket.authUser.id); - // if the user is in the tournament, and the tournament owner isn't the owner => we remove the user from the tournament ! - if (this.tournament?.users.has(socket.authUser.id) && this.tournament?.owner !== socket.authUser.id) { + if ( + this.tournament?.users.has(socket.authUser.id) && + this.tournament?.owner !== socket.authUser.id + ) { this.tournament.removeUser(socket.authUser.id); } } @@ -378,6 +468,7 @@ class StateI { private async cleanupGame(gameId: GameId, game: Pong): Promise { let chat_text = 'A game ended between '; clearInterval(game.gameUpdate ?? undefined); + if (game.onEnd) game.onEnd(); this.games.delete(gameId); const winner = game.checkWinner() ?? 'left'; let player: PUser | undefined = undefined; @@ -385,20 +476,33 @@ class StateI { player.currentGame = null; player.socket.emit('gameEnd', winner); } - chat_text += (this.fastify.db.getUser(game.userLeft)?.name ?? game.userLeft) + ' and '; + chat_text += + (this.fastify.db.getUser(game.userLeft)?.name ?? game.userLeft) + + ' and '; if ((player = this.users.get(game.userRight)) !== undefined) { player.currentGame = null; player.socket.emit('gameEnd', winner); } - chat_text += (this.fastify.db.getUser(game.userRight)?.name ?? game.userRight); + chat_text += + this.fastify.db.getUser(game.userRight)?.name ?? game.userRight; const rOutcome = game.checkWinner(); let outcome: PongGameOutcome = 'other'; - if (rOutcome === 'left') { outcome = 'winL'; } - if (rOutcome === 'right') { outcome = 'winR'; } - this.fastify.db.setPongGameOutcome(gameId, { id: game.userLeft, score: game.score[0] }, { id: game.userRight, score: game.score[1] }, outcome, game.local); + if (rOutcome === 'left') { + outcome = 'winL'; + } + if (rOutcome === 'right') { + outcome = 'winR'; + } + this.fastify.db.setPongGameOutcome( + gameId, + { id: game.userLeft, score: game.score[0] }, + { id: game.userRight, score: game.score[1] }, + outcome, + game.local, + ); this.fastify.log.info('SetGameOutcome !'); if (!game.local) { - const payload = { 'nextGame': chat_text }; + const payload = { nextGame: chat_text }; try { const resp = await fetch('http://app-chat/api/chat/broadcast', { method: 'POST', @@ -406,12 +510,14 @@ class StateI { body: JSON.stringify(payload), }); - if (!resp.ok) { throw (resp); } - else { this.fastify.log.info('game-end info to chat success'); } + if (!resp.ok) { + throw resp; + } + else { + this.fastify.log.info('game-end info to chat success'); + } } - // disable eslint for err catching - // eslint-disable-next-line @typescript-eslint/no-explicit-any - catch (e: any) { + catch (e: unknown) { this.fastify.log.error(`game-end info to chat failed: ${e}`); } } @@ -442,7 +548,9 @@ class StateI { if (!this.users.has(socket.authUser.id)) return; const user = this.users.get(socket.authUser.id)!; // does the user have a game and do we know such game ? - if (user.currentGame === null || !this.games.has(user.currentGame)) return; + if (user.currentGame === null || !this.games.has(user.currentGame)) { + return; + } const game = this.games.get(user.currentGame)!; // is this a local game? if (game.local === true) return; @@ -453,12 +561,13 @@ class StateI { if (!this.users.has(socket.authUser.id)) return; const user = this.users.get(socket.authUser.id)!; // does the user have a game and do we know such game ? - if (user.currentGame === null || !this.games.has(user.currentGame)) return; + if (user.currentGame === null || !this.games.has(user.currentGame)) { + return; + } const game = this.games.get(user.currentGame)!; if (game.local === true) return; game.readyup(user.id); } - } export let State: StateI = undefined as unknown as StateI; diff --git a/src/pong/src/tour.ts b/src/pong/src/tour.ts index e63aa8a..5752959 100644 --- a/src/pong/src/tour.ts +++ b/src/pong/src/tour.ts @@ -1,8 +1,8 @@ import { PongGameId } from '@shared/database/mixin/pong'; import { UserId } from '@shared/database/mixin/user'; -import { shuffle } from '@shared/utils'; +import { isNullish, shuffle } from '@shared/utils'; import { newUUID } from '@shared/utils/uuid'; -import { Pong } from './game'; +import { State } from './state'; type TourUser = { id: UserId; @@ -14,7 +14,7 @@ type TournamentState = 'prestart' | 'playing' | 'ended' | 'canceled'; export class Tournament { public users: Map = new Map(); public currentGame: PongGameId | null = null; - public games: Map = new Map(); + public matchup: [UserId, UserId][] = []; public state: TournamentState = 'prestart'; public startTimeout: NodeJS.Timeout | undefined; @@ -48,18 +48,58 @@ export class Tournament { shuffle(comb); comb.forEach(shuffle); + const result: [UserId, UserId][] = []; - comb.forEach(([u1, u2]) => { - const gameId = newUUID() as PongGameId; - const g = new Pong(u1, u2); - - this.games.set(gameId, g); - - }); - this.currentGame = this.games.keys().next().value ?? null; + comb.forEach(([u1, u2]) => { result.push([u1, u2]); }); + this.matchup = result; + this.setupNextGame(); } - public checkCurrentGame() { - void 0; + private setupNextGame() { + if (this.matchup.length > 0) { + const matchup = this.matchup.shift() ?? null; + if (matchup == null) { + this.state = 'ended'; + return; + } + const [u1, u2] = matchup; + const gameId = newUUID() as PongGameId; + const game = State.initGame(null, gameId, u1, u2); + if (game) { + game.onEnd = () => this.gameEnd(); + } + this.currentGame = gameId; + } + else { + this.state = 'ended'; + } + console.log('next matchup ?:', this.matchup); + if (this.currentGame === null) { this.state = 'ended'; } + } + + public gameEnd() { + console.log(this); + State.fastify.log.info('tournament game ended'); + if (!isNullish(this.currentGame)) { + State.fastify.log.info('HERE2'); + State.fastify.log.info(State.games); + State.fastify.log.info(this.currentGame); + const game = State.games.get(this.currentGame); + if (game) { + State.fastify.log.info('HERE3'); + const winner = game.checkWinner(); + const winnerId = winner === 'left' ? game.userLeft : game.userRight; + + const u = this.users.get(winnerId); + if (u) { this.users.set(winnerId, { id: u.id, name: u.name, score: u.score + 1 }); } + for (const user of this.users + .keys() + .map((id) => State.users.get(id)) + .filter((v) => !isNullish(v))) { + user.socket.emit('gameEnd', winner!); + } + } + } + setTimeout(() => this.setupNextGame(), 3100); } }