From de26c3c89c91d66b3e5933770a508a2f14785c0e Mon Sep 17 00:00:00 2001 From: LuckyLaszlo Date: Wed, 11 Jan 2023 12:00:03 +0100 Subject: [PATCH] avatar display on game pages + multiples user.status bug unresolved, temp rollback in generateToken() + STATUS enum fix game.service.ts + multiples smalls refactoring --- .../game_back/src/server/wsServer.ts | 35 ++++++---- .../game_back/src/shared_js/class/Event.ts | 9 ++- .../nestjs/api_back/src/game/game.service.ts | 32 +++++----- .../api_front/src/pages/game/Game.svelte | 61 ++++++++++++++++-- .../src/pages/game/GameSpectator.svelte | 64 +++++++++++++++---- .../api_front/src/pages/game/client/init.ts | 4 +- .../api_front/src/pages/game/client/ws.ts | 29 ++++++--- .../src/pages/game/shared_js/class/Event.ts | 9 ++- .../svelte/api_front/src/pieces/Match.ts | 10 +++ .../api_front/src/pieces/MatchListElem.svelte | 11 +--- 10 files changed, 191 insertions(+), 73 deletions(-) create mode 100644 srcs/requirements/svelte/api_front/src/pieces/Match.ts diff --git a/srcs/requirements/game_server/game_back/src/server/wsServer.ts b/srcs/requirements/game_server/game_back/src/server/wsServer.ts index d3bab3d2..35923df1 100644 --- a/srcs/requirements/game_server/game_back/src/server/wsServer.ts +++ b/srcs/requirements/game_server/game_back/src/server/wsServer.ts @@ -94,10 +94,15 @@ async function clientAnnounceListener(this: WebSocket, data: string) "Content-Type": "application/json", }, body: JSON.stringify(body) - }); - if (!response.ok) + }) + .catch(error => console.log("ERROR : " + error)); + if (!response || !response.ok) { - this.send(JSON.stringify( new ev.EventError((await response.json()).message) )); + let errMessage = "validate token error"; + if (response) { + errMessage = (await response.json()).message; + } + this.send(JSON.stringify( new ev.EventError(errMessage) )); clientTerminate(clientsMap.get(this.id)); return; } @@ -154,6 +159,8 @@ function publicMatchmaking(player: ClientPlayer) const compatiblePlayers: ClientPlayer[] = []; compatiblePlayers.push(player); + + /* // Replace with this code to enable the possibility to play against self for (const [id, client] of matchmakingMap) { if (client.matchOptions === matchOptions) @@ -163,10 +170,9 @@ function publicMatchmaking(player: ClientPlayer) break; } } - } + } */ - // TODO: Replace with this code to disable the possibility to play against self -/* for (const [id, client] of matchmakingMap) + for (const [id, client] of matchmakingMap) { if (client.matchOptions === matchOptions && client.username !== player.username) { @@ -175,7 +181,7 @@ function publicMatchmaking(player: ClientPlayer) break; } } - } */ + } if (compatiblePlayers.length >= minPlayersNumber) { compatiblePlayers.forEach((client) => { @@ -231,7 +237,6 @@ function privateMatchmaking(player: ClientPlayer) token : player.token }) }) - .then(x => x.json()) .catch(error => console.log("ERROR : " + error)); clientTerminate(player); } @@ -252,18 +257,26 @@ function createGameSession(playersArr: ClientPlayer[], matchOptions: en.MatchOpt gameSession.unreadyPlayersMap.set(client.id, client); client.socket.once("message", playerReadyConfirmationListener); }); + + let gameSessionPlayersIterator = gameSession.playersMap.values(); + const eventMatchmakingComplete = new ev.EventMatchmakingComplete( + (gameSessionPlayersIterator.next().value).username, + (gameSessionPlayersIterator.next().value).username + ); // REFACTORING: Not pretty, hardcoded two players. // Could be done in gameSession maybe ? - const gameSessionPlayersIterator = gameSession.playersMap.values(); + gameSessionPlayersIterator = gameSession.playersMap.values(); let player: ClientPlayer; player = (gameSessionPlayersIterator.next().value); player.racket = gameSession.components.playerLeft; - player.socket.send(JSON.stringify( new ev.EventMatchmakingComplete(en.PlayerSide.left) )); + eventMatchmakingComplete.side = en.PlayerSide.left; + player.socket.send(JSON.stringify( eventMatchmakingComplete )); player = (gameSessionPlayersIterator.next().value); player.racket = gameSession.components.playerRight; - player.socket.send(JSON.stringify( new ev.EventMatchmakingComplete(en.PlayerSide.right) )); + eventMatchmakingComplete.side = en.PlayerSide.right; + player.socket.send(JSON.stringify( eventMatchmakingComplete )); // REFACTORING setTimeout(function abortMatch() { diff --git a/srcs/requirements/game_server/game_back/src/shared_js/class/Event.ts b/srcs/requirements/game_server/game_back/src/shared_js/class/Event.ts index 0147ead8..e5938e29 100644 --- a/srcs/requirements/game_server/game_back/src/shared_js/class/Event.ts +++ b/srcs/requirements/game_server/game_back/src/shared_js/class/Event.ts @@ -18,10 +18,13 @@ export class EventAssignId extends ServerEvent { } export class EventMatchmakingComplete extends ServerEvent { - side: en.PlayerSide; - constructor(side: en.PlayerSide) { + side: en.PlayerSide = en.PlayerSide.noSide; + playerOneUsername: string; + playerTwoUsername: string; + constructor(playerOneUsername: string, playerTwoUsername: string) { super(en.EventTypes.matchmakingComplete); - this.side = side; + this.playerOneUsername = playerOneUsername; + this.playerTwoUsername = playerTwoUsername; } } diff --git a/srcs/requirements/nestjs/api_back/src/game/game.service.ts b/srcs/requirements/nestjs/api_back/src/game/game.service.ts index 0f2d7556..a6f7cd02 100644 --- a/srcs/requirements/nestjs/api_back/src/game/game.service.ts +++ b/srcs/requirements/nestjs/api_back/src/game/game.service.ts @@ -92,7 +92,8 @@ export class GameService { } this.userRepository.save(user); } - if (grantTicketDto.isGameIsWithInvitation === true && user.status !== STATUS.IN_GAME) + // if (grantTicketDto.isGameIsWithInvitation === true && user.status !== STATUS.IN_GAME) // WIP: need to fix STATUS.IN_GAME + if (grantTicketDto.isGameIsWithInvitation === true) { const secondUser : Partial = await this.userService.findOne(grantTicketDto.playerTwoUsername) if (!secondUser || secondUser.username === user.username) @@ -104,17 +105,18 @@ export class GameService { tok.numberOfRegisteredUser = 0; tok.token = encryptedTextToReturn; this.tokenGameRepository.save(tok); - this.userService.updateStatus(user.id, "In Pool") + this.userService.updateStatus(user.id, STATUS.IN_POOL) return res.status(HttpStatus.OK).json({ token : encryptedTextToReturn }); } - else if (grantTicketDto.isGameIsWithInvitation === false && user.status !== STATUS.IN_GAME) { + // else if (grantTicketDto.isGameIsWithInvitation === false && user.status !== STATUS.IN_GAME) { // WIP: need to fix STATUS.IN_GAME + else if (grantTicketDto.isGameIsWithInvitation === false) { const encryptedTextToReturn = await this.encryptToken(user.username + '_' + grantTicketDto.gameOptions + '_' + grantTicketDto.isGameIsWithInvitation + '_' + new Date()) const tok = this.tokenGameRepository.create(grantTicketDto); tok.numberOfRegisteredUser = 0; tok.token = encryptedTextToReturn; this.tokenGameRepository.save(tok); - this.userService.updateStatus(user.id, "In Pool") + this.userService.updateStatus(user.id, STATUS.IN_POOL) return res.status(HttpStatus.OK).json({ token : encryptedTextToReturn }); } return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({message : "Internal Server Error"}); @@ -140,13 +142,13 @@ export class GameService { const userOne : User = await this.userRepository.createQueryBuilder('user') .where("user.username = :username", {username : tokenGame.playerOneUsername}) .getOne(); - this.userService.updateStatus(userOne.id, "In Game") + this.userService.updateStatus(userOne.id, STATUS.IN_GAME) const userTwo : User = await this.userRepository.createQueryBuilder('user') .where("user.username = :username", {username : tokenGame.playerTwoUsername}) .getOne(); this.deleteToken(userOne) this.deleteToken(userTwo) - this.userService.updateStatus(userTwo.id, "In Game") + this.userService.updateStatus(userTwo.id, STATUS.IN_GAME) } return true; } @@ -166,7 +168,7 @@ export class GameService { const user : User = await this.userRepository.createQueryBuilder('user') .where("user.username = :username", {username : tokenGame.playerOneUsername}) .getOne(); - this.userService.updateStatus(user.id, "In Game") + this.userService.updateStatus(user.id, STATUS.IN_GAME) this.deleteToken(user) return true; } @@ -196,7 +198,7 @@ export class GameService { async declineInvitation(user : User, token : string, @Res() res : Response) { - if (user.status !== "Connected") + if (user.status !== STATUS.CONNECTED) return res.status(HttpStatus.FORBIDDEN).json({message : "You must not be in game to decline an invitation"}); console.log("On décline l'invitation") const tokenGame = await this.tokenGameRepository.createQueryBuilder('tokengame') @@ -221,10 +223,10 @@ export class GameService { { const playerOne = await this.userRepository.findOneBy({username : tokenGame.playerOneUsername}) const playerTwo = await this.userRepository.findOneBy({username : tokenGame.playerTwoUsername}) - if (playerOne.status !== "Disconnected") - this.userService.updateStatus(playerOne.id, "Connected") - if (playerTwo.status !== "Disconnected") - this.userService.updateStatus(playerTwo.id, "Connected") + if (playerOne.status !== STATUS.DISCONNECTED) + this.userService.updateStatus(playerOne.id, STATUS.CONNECTED) + if (playerTwo.status !== STATUS.DISCONNECTED) + this.userService.updateStatus(playerTwo.id, STATUS.CONNECTED) return this.tokenGameRepository.remove(tokenGame); } return new HttpException("Token not found !", HttpStatus.NOT_FOUND) @@ -232,7 +234,7 @@ export class GameService { async acceptInvitation(user : User, token : string, @Res() res : Response) { - if (user.status !== "Connected") + if (user.status !== STATUS.CONNECTED) return res.status(HttpStatus.FORBIDDEN).send("") const tokenGame = await this.tokenGameRepository.createQueryBuilder('tokenGame') .andWhere('tokenGame.playerTwoUsername = :playerTwoUsername', {playerTwoUsername : user.username}) @@ -294,8 +296,8 @@ export class GameService { this.userService.incrementVictories(playerOne.id) this.userService.incrementDefeats(playerTwo.id) } - this.userService.updateStatus(playerOne.id, "Connected") - this.userService.updateStatus(playerTwo.id, "Connected") + this.userService.updateStatus(playerOne.id, STATUS.CONNECTED) + this.userService.updateStatus(playerTwo.id, STATUS.CONNECTED) return HttpStatus.OK } } diff --git a/srcs/requirements/svelte/api_front/src/pages/game/Game.svelte b/srcs/requirements/svelte/api_front/src/pages/game/Game.svelte index 97c56656..3d6f2a9f 100644 --- a/srcs/requirements/svelte/api_front/src/pages/game/Game.svelte +++ b/srcs/requirements/svelte/api_front/src/pages/game/Game.svelte @@ -12,6 +12,9 @@ let user; let allUsers; + let playerOneAvatar; + let playerTwoAvatar; + //Game's stuff const options = new pong.InitOptions(); const gameAreaId = "game_area"; @@ -26,9 +29,10 @@ let showWaitPage = false; let invitations = []; - let watchGameStateInterval; const watchGameStateIntervalRate = 142; + let watchMatchStartInterval; + const watchMatchStartIntervalRate = 111; onMount( async() => { user = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`) @@ -39,6 +43,7 @@ }) onDestroy( async() => { + clearInterval(watchMatchStartInterval); clearInterval(watchGameStateInterval); pong.destroy(); }) @@ -76,6 +81,7 @@ showWaitPage = false; if (response.ok && token) { + watchMatchStartInterval = setInterval(watchMatchStart, watchMatchStartIntervalRate); watchGameStateInterval = setInterval(watchGameState, watchGameStateIntervalRate); pong.init(matchOptions, options, gameAreaId, token); hiddenGame = false; @@ -106,6 +112,7 @@ console.log(invitation); if (invitation.token) { + watchMatchStartInterval = setInterval(watchMatchStart, watchMatchStartIntervalRate); watchGameStateInterval = setInterval(watchGameState, watchGameStateIntervalRate); options.playerOneUsername = invitation.playerOneUsername; options.playerTwoUsername = invitation.playerTwoUsername; @@ -117,16 +124,24 @@ } } + async function watchMatchStart() + { + if (gameState.matchStarted) + { + clearInterval(watchMatchStartInterval); + playerOneAvatar = await fetchAvatar(gameState.playerOneUsername); + playerTwoAvatar = await fetchAvatar(gameState.playerTwoUsername); + gameState.matchStarted = gameState.matchStarted; // trigger Svelte reactivity + } + } + const watchGameState = () => { console.log("watchGameState"); - if (gameState) { // trigger Svelte reactivity - gameState.matchStarted = gameState.matchStarted; - gameState.matchEnded = gameState.matchEnded; - gameState.matchAborted = gameState.matchAborted; - } if (gameState.matchAborted || gameState.matchEnded) { clearInterval(watchGameStateInterval); + gameState.matchEnded = gameState.matchEnded; // trigger Svelte reactivity + gameState.matchAborted = gameState.matchAborted; // trigger Svelte reactivity console.log("watchGameState, end"); setTimeout(() => { resetPage(); @@ -176,10 +191,28 @@ } function leaveMatch() { + clearInterval(watchMatchStartInterval); clearInterval(watchGameStateInterval); resetPage(); }; + async function fetchAvatar(username: string) // TODO: Could be shared with others components + { + return fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/avatar?username=${username}`) + .then((response) => { + if (!response.ok) { + throw new Error("Avatar not retrieved"); + } + return response.blob(); + }) + .then((blob) => { + return URL.createObjectURL(blob); + }) + .catch((error) => { + console.log("catch fetchAvatar: ", error); + }); + } +
@@ -213,6 +246,14 @@ {#if !hiddenGame} + {#if gameState.matchStarted} +
+ player one avatar + '{gameState.playerOneUsername}' VS '{gameState.playerTwoUsername}' + player two avatar +
+ {/if} + {#if gameState.matchStarted && !gameState.matchEnded}
@@ -356,5 +397,11 @@ canvas { font-size: x-large; padding: 10px; } - +.avatar { + min-height: 100px; + min-width: 100px; + + max-width: 100px; + max-height: 100px; +} diff --git a/srcs/requirements/svelte/api_front/src/pages/game/GameSpectator.svelte b/srcs/requirements/svelte/api_front/src/pages/game/GameSpectator.svelte index ecdfa271..5eb53eb4 100644 --- a/srcs/requirements/svelte/api_front/src/pages/game/GameSpectator.svelte +++ b/srcs/requirements/svelte/api_front/src/pages/game/GameSpectator.svelte @@ -1,10 +1,11 @@ @@ -102,6 +127,12 @@
{#if !hiddenGame} +
+ player one avatar + '{gameState.playerOneUsername}' VS '{gameState.playerTwoUsername}' + player two avatar +
+ {#if !gameState.matchEnded}
@@ -127,7 +158,7 @@ {#if matchList.length !== 0} {#each matchList as match} - initGameSpectator(match.gameServerIdOfTheMatch, match.gameOptions)} /> + initGameSpectator(match)} /> {/each} {:else} @@ -195,10 +226,15 @@ canvas { font-size: x-large; padding: 10px; } - #match_list { font-family: 'Courier New', Courier, monospace; font-size: large; } - +.avatar { + min-height: 100px; + min-width: 100px; + + max-width: 100px; + max-height: 100px; +} diff --git a/srcs/requirements/svelte/api_front/src/pages/game/client/init.ts b/srcs/requirements/svelte/api_front/src/pages/game/client/init.ts index 863ebdbd..ba22819a 100644 --- a/srcs/requirements/svelte/api_front/src/pages/game/client/init.ts +++ b/srcs/requirements/svelte/api_front/src/pages/game/client/init.ts @@ -3,7 +3,7 @@ import * as c from "./constants.js" import * as en from "../shared_js/enums.js" import { GameArea } from "./class/GameArea.js"; import { GameComponentsClient } from "./class/GameComponentsClient.js"; -import { socket, resetGameState } from "./ws.js"; +import { socket, gameState } from "./ws.js"; import { initAudio } from "./audio.js"; import type { InitOptions } from "./class/InitOptions.js"; @@ -57,5 +57,5 @@ export function destroyBase() } setGc(null); setMatchOptions(null); - resetGameState(); + gameState.resetGameState(); } diff --git a/srcs/requirements/svelte/api_front/src/pages/game/client/ws.ts b/srcs/requirements/svelte/api_front/src/pages/game/client/ws.ts index c2d61abf..b3cad96b 100644 --- a/srcs/requirements/svelte/api_front/src/pages/game/client/ws.ts +++ b/srcs/requirements/svelte/api_front/src/pages/game/client/ws.ts @@ -10,16 +10,22 @@ import { muteFlag, soundRoblox } from "./audio.js" import { sleep } from "./utils.js"; import { Vector, VectorInteger } from "../shared_js/class/Vector.js"; -export const gameState = { - matchStarted: false, - matchEnded: false, - matchAborted: false -} - -export function resetGameState() { - gameState.matchStarted = false; - gameState.matchEnded = false; - gameState.matchAborted = false; +class GameState { + matchStarted: boolean; + matchEnded: boolean; + matchAborted: boolean; + playerOneUsername: string; + playerTwoUsername: string; + constructor() { + this.resetGameState(); + } + resetGameState() { + this.matchStarted = false; + this.matchEnded = false; + this.matchAborted = false; + this.playerOneUsername = ""; + this.playerTwoUsername = ""; + } } class ClientInfo { @@ -39,6 +45,7 @@ class ClientInfoSpectator { const wsUrl = "ws://" + process.env.WEBSITE_HOST + ":" + process.env.WEBSITE_PORT + "/pong"; export let socket: WebSocket; +export const gameState = new GameState(); export const clientInfo = new ClientInfo(); export const clientInfoSpectator = new ClientInfoSpectator(); // WIP, could refactor this @@ -85,6 +92,8 @@ function preMatchListener(this: WebSocket, event: MessageEvent) break; case en.EventTypes.matchmakingComplete: clientInfo.side = (data).side; + gameState.playerOneUsername = (data).playerOneUsername; + gameState.playerTwoUsername = (data).playerTwoUsername; if (clientInfo.side === en.PlayerSide.left) { clientInfo.racket = gc.playerLeft; diff --git a/srcs/requirements/svelte/api_front/src/pages/game/shared_js/class/Event.ts b/srcs/requirements/svelte/api_front/src/pages/game/shared_js/class/Event.ts index 0147ead8..e5938e29 100644 --- a/srcs/requirements/svelte/api_front/src/pages/game/shared_js/class/Event.ts +++ b/srcs/requirements/svelte/api_front/src/pages/game/shared_js/class/Event.ts @@ -18,10 +18,13 @@ export class EventAssignId extends ServerEvent { } export class EventMatchmakingComplete extends ServerEvent { - side: en.PlayerSide; - constructor(side: en.PlayerSide) { + side: en.PlayerSide = en.PlayerSide.noSide; + playerOneUsername: string; + playerTwoUsername: string; + constructor(playerOneUsername: string, playerTwoUsername: string) { super(en.EventTypes.matchmakingComplete); - this.side = side; + this.playerOneUsername = playerOneUsername; + this.playerTwoUsername = playerTwoUsername; } } diff --git a/srcs/requirements/svelte/api_front/src/pieces/Match.ts b/srcs/requirements/svelte/api_front/src/pieces/Match.ts new file mode 100644 index 00000000..68e330f6 --- /dev/null +++ b/srcs/requirements/svelte/api_front/src/pieces/Match.ts @@ -0,0 +1,10 @@ + +import type { MatchOptions } from "../pages/game/client/pongSpectator"; +export { MatchOptions } from "../pages/game/client/pongSpectator"; + +export class Match { + gameServerIdOfTheMatch: string; + gameOptions: MatchOptions; + playerOneUsername: string; + playerTwoUsername: string; +} diff --git a/srcs/requirements/svelte/api_front/src/pieces/MatchListElem.svelte b/srcs/requirements/svelte/api_front/src/pieces/MatchListElem.svelte index 5433c23e..29fbaa52 100644 --- a/srcs/requirements/svelte/api_front/src/pieces/MatchListElem.svelte +++ b/srcs/requirements/svelte/api_front/src/pieces/MatchListElem.svelte @@ -1,14 +1,9 @@