From 9f9eea9525314c4b2f6bd761486baefa3ecb18e9 Mon Sep 17 00:00:00 2001 From: bgoulard Date: Fri, 9 Jan 2026 15:21:10 +0100 Subject: [PATCH] [wip] wip routes for paused games --- src/pong/src/game.ts | 4 +-- src/pong/src/routes/createPausedGame.ts | 48 +++++++++++++++++++++++++ src/pong/src/routes/startPausedGame.ts | 39 ++++++++++++++++++++ src/pong/src/state.ts | 47 +++++++++++++++++++++++- 4 files changed, 135 insertions(+), 3 deletions(-) create mode 100644 src/pong/src/routes/createPausedGame.ts create mode 100644 src/pong/src/routes/startPausedGame.ts diff --git a/src/pong/src/game.ts b/src/pong/src/game.ts index 92bdacf..5dad0df 100644 --- a/src/pong/src/game.ts +++ b/src/pong/src/game.ts @@ -132,7 +132,7 @@ export class Pong { public sendSig : boolean = false; public ready_checks: [boolean, boolean] = [false, false]; - public game_creation : number = Date.now(); + public rdy_timer : number = Date.now(); // init rdy timer from class creation start public score: [number, number] = [0, 0]; public local: boolean = false; @@ -282,7 +282,7 @@ export class Pong { if (this.score[LEFT] >= 5) return 'left'; if (this.score[RIGHT] >= 5) return 'right'; - if (this.local !== true && this.game_creation !== -1 && Date.now() - this.game_creation > Pong.CONCEDED_TIMEOUT * 10 && (!this.ready_checks[0] || !this.ready_checks[1])) { + if (this.local !== true && this.rdy_timer !== -1 && Date.now() - this.rdy_timer > Pong.CONCEDED_TIMEOUT * 10 && (!this.ready_checks[0] || !this.ready_checks[1])) { if (!this.ready_checks[0] && !this.ready_checks[1]) return (randomInt(1) == 1 ? 'left' : 'right'); if (!this.ready_checks[0]) return ('right'); if (!this.ready_checks[1]) return ('left'); diff --git a/src/pong/src/routes/createPausedGame.ts b/src/pong/src/routes/createPausedGame.ts new file mode 100644 index 0000000..793b8d7 --- /dev/null +++ b/src/pong/src/routes/createPausedGame.ts @@ -0,0 +1,48 @@ +import { isNullish, MakeStaticResponse, typeResponse } from "@shared/utils"; +import { FastifyPluginAsync } from "fastify"; +import { Static, Type } from "typebox"; +import { State } from "../state"; +import { UserId } from "@shared/database/mixin/user"; + +// need: pongPausedParam -> uid1 uid2 +// resp -> game_uid | ?error +// +// q - why does 'type PongHistoryResponse = MakeStaticResponse' why makestatic response? + +// required to game create -> uid1+2 +const CreatePausedGameParam = Type.Object({ + user1: Type.String({ description: '\'id\' | ' }), + user2: Type.String({ description: '\'id\' | ' }), +}); + +type CreatePausedGameParam = Static; + +const CreatePausedGameResponse = { + '200': typeResponse('success', 'createPausedGame.success', { + gameId: Type.String({ description: 'gameId' }), + }), + '404': typeResponse('failure', 'createPausedGame.generic.fail') +} + +type CreatePausedGameResponse = MakeStaticResponse; + +const route: FastifyPluginAsync = async (fastify): Promise => { + fastify.post<{ Body: CreatePausedGameParam }>( + '/createPausedGame', + { + schema: { + body: CreatePausedGameParam, + response: CreatePausedGameResponse, + operationId: 'pongCreatePauseGame', + }, + }, + async function (req, res) { + let resp = State.newPausedGame(req.body.user1 as UserId, req.body.user2 as UserId); + + if (isNullish(resp)) { return (res.makeResponse(404, 'failure', 'createPausedGame.generic.fail')) } + // else + return (res.makeResponse(200, 'success', 'createPausedGame.success')); + } + ) +}; +export default route; \ No newline at end of file diff --git a/src/pong/src/routes/startPausedGame.ts b/src/pong/src/routes/startPausedGame.ts new file mode 100644 index 0000000..37cbb6a --- /dev/null +++ b/src/pong/src/routes/startPausedGame.ts @@ -0,0 +1,39 @@ +import { MakeStaticResponse, typeResponse } from "@shared/utils"; +import { FastifyPluginAsync } from "fastify"; +import Type, { Static } from "typebox"; +import { State } from "../state"; +import { PongGameId } from "@shared/database/mixin/pong"; + +const startPausedGameParam = Type.Object({ + gameId: Type.String({ description: '\'id\' | ' }), +}); + +type startPausedGameParam = Static; + +const startPausedGameResponse = { + '200': typeResponse('success', 'startPausedGame.success', {}), + '404': typeResponse('failure', 'startPausedGame.no_such_game') +} + +type startPausedGameResponse = MakeStaticResponse; + +const route: FastifyPluginAsync = async (fastify): Promise => { + fastify.post<{ Body: startPausedGameParam }>( + '/startPausedGame', + { + schema: { + body: startPausedGameParam, + response: startPausedGameResponse, + operationId: 'pongstartPauseGame', + }, + }, + async function (req, res) { + let resp = State.startPausedGame(req.body.gameId as PongGameId); + + if (resp !== true) { return (res.makeResponse(404, 'failure', 'startPausedGame.generic.fail')) } + // else + return (res.makeResponse(200, 'success', 'startPausedGame.success')); + } + ) +}; +export default route; \ No newline at end of file diff --git a/src/pong/src/state.ts b/src/pong/src/state.ts index 9123031..7021287 100644 --- a/src/pong/src/state.ts +++ b/src/pong/src/state.ts @@ -103,6 +103,52 @@ class StateI { this.tournament.start(); } + public newPausedGame(suid1 : string, suid2 : string) : GameId | 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); + g.rdy_timer = -1; + const gameId = newUUID() as unknown as GameId; + + this.games.set(gameId, g); + 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(); + + let id1 = game.userLeft; + let id2 = game.userRight; + + if (!this.users.has(id1) || !this.users.has(id2)) { return (false); } + let usr1 = this.users.get(id1); + let 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); + } + private queuerFunction(): void { const values = Array.from(this.queue.values()); shuffle(values); @@ -308,7 +354,6 @@ class StateI { } } - private enqueueUser(socket: SSocket): void { if (!this.users.has(socket.authUser.id)) return;