From e2498963095724955d6bdff700e658fdad44d28f Mon Sep 17 00:00:00 2001 From: Halit Aksoy Date: Fri, 2 Sep 2022 00:33:50 +0300 Subject: [PATCH] feature: spectator --- src/consts/channel_names.ts | 4 +- src/game/GameManager.ts | 76 ++++++++++++++++++++++++++++--------- 2 files changed, 61 insertions(+), 19 deletions(-) diff --git a/src/consts/channel_names.ts b/src/consts/channel_names.ts index 789d501..d088b0b 100644 --- a/src/consts/channel_names.ts +++ b/src/consts/channel_names.ts @@ -8,4 +8,6 @@ export const channel_on_game_crashed = "on_game_crashed" export const channel_on_game_user_leave = "on_game_user_leave" export const channel_ping = "ping" export const channel_pong = "pong" -export const channel_on_user_connection_change = "channel_on_user_connection_change" \ No newline at end of file +export const channel_on_user_connection_change = "channel_on_user_connection_change" +export const channel_listen_game_events = "channel_listen_game_events" +export const channel_unlisten_game_events = "channel_unlisten_game_events" \ No newline at end of file diff --git a/src/game/GameManager.ts b/src/game/GameManager.ts index 0c7269b..a1c88e2 100644 --- a/src/game/GameManager.ts +++ b/src/game/GameManager.ts @@ -5,12 +5,14 @@ import { generateKey } from "../util/key_factory"; import { channel_game_move, channel_leave_game, + channel_listen_game_events, channel_new_game, channel_on_game_crashed, channel_on_game_start, channel_on_game_update, channel_on_game_user_leave, - channel_on_user_connection_change + channel_on_user_connection_change, + channel_unlisten_game_events } from "../consts/channel_names"; import { GameMove } from "../models/GameMove"; import { GameCrashManager } from "./GameCrashManager"; @@ -47,6 +49,14 @@ export class GameManager { this.rtmt.listenMessage(channel_leave_game, (userKey: string, message: Object) => { this.onPlayerLeave(userKey) }); + + this.rtmt.listenMessage(channel_listen_game_events, (userKey: string, message: Object) => { + this.addSpectator(userKey, message as string); + }); + + this.rtmt.listenMessage(channel_unlisten_game_events, (userKey: string, message: Object) => { + this.removeSpectator(userKey, message as string); + }); } private onGameMove(userKey: string, gameMove: GameMove) { @@ -55,10 +65,9 @@ export class GameManager { try { game.mancalaGame.moveByPlayerPit(userKey, gameMove.index); game.gameUsersConnectionInfo = this.getGameUsersConnectionInfo(game); - this.rtmt.sendMessage(game.mancalaGame.player1Id, channel_on_game_update, game); - this.rtmt.sendMessage(game.mancalaGame.player2Id, channel_on_game_update, game); + this.sendMessageToPlayersAndSpectators(game, channel_on_game_update, game); if (game.mancalaGame.state == "ended") { - this.deleteGame(game); + this.onGameEnd(game); } } catch (err: any) { this.onGameError(game, err) @@ -72,19 +81,54 @@ export class GameManager { console.error(error); const crashFileName = GameCrashManager.logGameCrash(error, game); console.info(`Game crash saved to file : ${crashFileName}`); - this.rtmt.sendMessage(game.mancalaGame.player1Id, channel_on_game_crashed, error); - this.rtmt.sendMessage(game.mancalaGame.player2Id, channel_on_game_crashed, error); + this.sendMessageToPlayersAndSpectators(game, channel_on_game_crashed, error); } private onPlayerLeave(userKey: string) { const game = this.gameStore.getGameByUser(userKey); if (game) { this.deleteGame(game); - this.rtmt.sendMessage(game.mancalaGame.player1Id, channel_on_game_user_leave, userKey); - this.rtmt.sendMessage(game.mancalaGame.player2Id, channel_on_game_user_leave, userKey); + this.sendMessageToPlayersAndSpectators(game, channel_on_game_user_leave, userKey) } } + private addSpectator(userId: string, gameId: string) { + const game = this.gameStore.get(gameId); + if (game) { + const isUserPlayer = this.checkUserIdisPlayer(game, userId); + const isAlreadySpectator = game.spectatorIds.find((value) => value === userId); + if (!isAlreadySpectator && !isUserPlayer) { + game.spectatorIds.push(userId); + } + } + } + + private removeSpectator(userId: string, gameId: string) { + const game = this.gameStore.get(gameId); + if (game) { + const userIdIndex = game.spectatorIds.findIndex((value) => value === userId); + if (userIdIndex >= 0) { + game.spectatorIds.splice(userIdIndex, 1); + } + } + } + + private checkUserIdisPlayer(game: Game, userId: string): boolean { + return game.mancalaGame.player1Id === userId || game.mancalaGame.player2Id === userId; + } + + private sendMessageToPlayersAndSpectators(game: Game, channel: string, message: Object) { + const sendMessage = (userId: string) => this.rtmt.sendMessage(userId, channel, message); + sendMessage(game.mancalaGame.player1Id); + sendMessage(game.mancalaGame.player2Id); + game.spectatorIds.forEach((spectatorId) => sendMessage(spectatorId)); + } + + private onGameEnd(game: Game) { + this.deleteGame(game); + game.spectatorIds = []; + } + private listenOnPlayersPaired() { this.matchMaker.listenOnPlayersPaired((player1Id: string, player2Id: string) => { const game = this.createMancalaGame(player1Id, player2Id); @@ -92,7 +136,7 @@ export class GameManager { }); } - public fireOnPlayerConnected(playerId: string) {} + public fireOnPlayerConnected(playerId: string) { } public createMancalaGame(userKey1: string, userKey2: string): Game { const mancalaGame = new CommonMancalaGame(generateKey(), userKey1, userKey2); @@ -103,6 +147,7 @@ export class GameManager { id: mancalaGame.id, mancalaGame, gameUsersConnectionInfo: this.getGameUsersConnectionInfoFromUsers(mancalaGame.player1Id, mancalaGame.player2Id), + spectatorIds: [] }; this.gameStore.set(game.id, game); @@ -125,21 +170,14 @@ export class GameManager { } public startGame(game: Game) { - const mancalaGame = game.mancalaGame; - this.rtmt.sendMessage(mancalaGame.player1Id, channel_on_game_start, game); - this.rtmt.sendMessage(mancalaGame.player2Id, channel_on_game_start, game); + this.sendMessageToPlayersAndSpectators(game, channel_on_game_start, game); this.sendUserConnectionInfo(game); this.sendUserConnectionInfo(game); } public sendUserConnectionInfo(game: Game) { - const user1 = game.mancalaGame.player1Id; - const user2 = game.mancalaGame.player2Id; const gameUsersConnectionInfo = this.getGameUsersConnectionInfo(game); - - //todo: reimplement when watch game feature added - this.rtmt.sendMessage(user1, channel_on_user_connection_change, gameUsersConnectionInfo); - this.rtmt.sendMessage(user2, channel_on_user_connection_change, gameUsersConnectionInfo); + this.sendMessageToPlayersAndSpectators(game, channel_on_user_connection_change, gameUsersConnectionInfo); } public deleteGame(game: Game) { @@ -153,6 +191,8 @@ export class GameManager { this.rtmt.listenOnClientConnectionChange((clientId: string, isOnline: boolean) => { const game = this.gameStore.getGameByUser(clientId); if (game) this.sendUserConnectionInfo(game); + const speactatingGameIds = this.gameStore.getSpeactatingGameIdsByUser(clientId); + speactatingGameIds.forEach((gameId) => this.removeSpectator(clientId, gameId)); }); } } \ No newline at end of file