diff --git a/frontend/src/pages/pong/pong.ts b/frontend/src/pages/pong/pong.ts index 86cc536..7e6c204 100644 --- a/frontend/src/pages/pong/pong.ts +++ b/frontend/src/pages/pong/pong.ts @@ -194,15 +194,15 @@ function pongClient(_url: string, _args: RouteHandlerParams): RouteHandlerReturn showError("couldn't find your id in game"); }); // TODO: notif user of new game w "ready up" btn - socket.on("gameEnd", () => { + socket.on("gameEnd", (winner) => { queueBtn.innerHTML = QueueState.Iddle; queueBtn.style.color = 'white'; if (!isNullish(currentGame)) { let new_div = document.createElement('div'); let end_txt = ""; - if ((user.id === currentGame.left.id && currentGame.left.score > currentGame.right.score) || - (user.id === currentGame.right.id && currentGame.right.score > currentGame.left.score)) + if ((user.id === currentGame.left.id && winner === 'left') || + (user.id === currentGame.right.id && winner === 'right')) end_txt = 'won! #yippe'; else end_txt = 'lost #sadge'; diff --git a/frontend/src/pages/pong/socket.ts b/frontend/src/pages/pong/socket.ts index 62291c9..2800909 100644 --- a/frontend/src/pages/pong/socket.ts +++ b/frontend/src/pages/pong/socket.ts @@ -48,7 +48,7 @@ export interface ServerToClient { updateInformation: (info: UpdateInfo) => void, newGame: (initState: GameUpdate) => void, // <- consider this the gameProc eg not start of game but wait for client to "ready up" gameUpdate: (state: GameUpdate) => void, - gameEnd: () => void; + gameEnd: (winner: 'left' | 'right') => void; }; export type SSocket = Socket; diff --git a/src/pong/src/game.ts b/src/pong/src/game.ts index 59675ca..8fd2a60 100644 --- a/src/pong/src/game.ts +++ b/src/pong/src/game.ts @@ -98,6 +98,8 @@ function makeAngle(i: number): [number, number, number, number] { export class Pong { public gameUpdate: NodeJS.Timeout | null = null; + public static readonly CONCEDED_TIMEOUT: number = 1500; + public static readonly BALL_START_ANGLES: number[] = [ ...makeAngle(4), ...makeAngle(6), @@ -127,6 +129,11 @@ export class Pong { public score: [number, number] = [0, 0]; public local: boolean = false; + public rightLastSeen: number = -1; + public leftLastSeen: number = -1; + + private cachedWinner: 'left' | 'right' | null = null; + public static makeLocal(owner: UserId): Pong { const game = new Pong(owner, owner); game.local = true; @@ -200,7 +207,9 @@ export class Pong { if ( (Math.abs(this.ball.angle) > Math.PI / 2 && side !== 'left') || (Math.abs(this.ball.angle) < Math.PI / 2 && side !== 'right') - ) {return false;} + ) { + return false; + } // now we check only if the ball is near enought in the y axis to permform the collision if ( @@ -212,7 +221,9 @@ export class Pong { this.ball.y < paddle.y + paddle.height + this.ball.size ) ) - ) {return false;} + ) { + return false; + } // so we know that the y is close enougth to be a bit, so we check the X. are we closer than the ball size ? if yes -> hit if ( @@ -222,14 +233,49 @@ export class Pong { paddle.width * (side === 'left' ? 1 : 0) - this.ball.x, ) < this.ball.size - ) {return true;} + ) { + return true; + } return false; } + public updateLastSeen(user: UserId) { + if (this.local && this.userLeft === user) { + this.leftLastSeen = Date.now(); + this.rightLastSeen = Date.now(); + } + else if (this.userLeft === user) { + this.leftLastSeen = Date.now(); + } + else if (this.userRight === user) { + this.rightLastSeen = Date.now(); + } + } + public checkWinner(): 'left' | 'right' | null { - if (this.score[0] >= 5) return 'left'; - if (this.score[1] >= 5) return 'right'; - return null; + const checkInner = () => { + if (this.score[0] >= 5) return 'left'; + if (this.score[1] >= 5) return 'right'; + + 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; + }; + if (this.cachedWinner === null) { + this.cachedWinner = checkInner(); + } + return this.cachedWinner; } public movePaddle(user: UserId | ('left' | 'right'), dir: 'up' | 'down') { diff --git a/src/pong/src/socket.ts b/src/pong/src/socket.ts index 4af97e8..ab9b066 100644 --- a/src/pong/src/socket.ts +++ b/src/pong/src/socket.ts @@ -47,7 +47,7 @@ export interface ServerToClient { updateInformation: (info: UpdateInfo) => void, newGame: (initState: GameUpdate) => void, gameUpdate: (state: GameUpdate) => void, - gameEnd: () => void; + gameEnd: (winner: 'left' | 'right') => void; }; export type SSocket = Socket; diff --git a/src/pong/src/state.ts b/src/pong/src/state.ts index baf4eb1..cf7d4e5 100644 --- a/src/pong/src/state.ts +++ b/src/pong/src/state.ts @@ -128,6 +128,7 @@ class StateI { if (u.moveRight !== null) { game.movePaddle('right', u.moveRight); } } else if (u.move !== null) { game.movePaddle(user.id, u.move); } + game.updateLastSeen(user.id); } @@ -174,14 +175,15 @@ class StateI { private cleanupGame(gameId: GameId, game: Pong): void { clearInterval(game.gameUpdate ?? undefined); this.games.delete(gameId); + const winner = game.checkWinner() ?? 'left'; let player: PUser | undefined = undefined; if ((player = this.users.get(game.userLeft)) !== undefined) { player.currentGame = null; - player.socket.emit('gameEnd'); + player.socket.emit('gameEnd', winner); } if ((player = this.users.get(game.userRight)) !== undefined) { player.currentGame = null; - player.socket.emit('gameEnd'); + player.socket.emit('gameEnd', winner); } const rOutcome = game.checkWinner(); let outcome: PongGameOutcome = 'other';