import { CommonMancalaGame, MancalaGame } from "mancala.js"; import { RTMT } from "../rtmt/rtmt"; import { GameStore } from "./gamestore/GameStore"; import { generateKey } from "../util/key_factory"; import { channel_game_move, channel_leave_game, 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 } from "../consts/channel_names"; import { GameMove } from "../models/GameMove"; import { GameCrashManager } from "./GameCrashManager"; import { MatchMaker } from "../matchmaker/MatchMaker"; import { Game, GameUsersConnectionInfo } from "../models/Game"; export class GameManager { gameStore: GameStore; rtmt: RTMT; matchMaker: MatchMaker; constructor(params: { gameStore: GameStore, rtmt: RTMT, matchMaker: MatchMaker }) { this.gameStore = params.gameStore; this.rtmt = params.rtmt; this.matchMaker = params.matchMaker; this.initialize(); } private initialize() { this.listenOnPlayersPaired(); this.listenRtmtMessages(); this.listenUserConnectionChange(); } private listenRtmtMessages() { this.rtmt.listenMessage(channel_new_game, (userKey: string, message: Object) => { this.matchMaker.join(userKey); }); this.rtmt.listenMessage(channel_game_move, (userKey: string, message: Object) => { this.onGameMove(userKey, message as GameMove); }); this.rtmt.listenMessage(channel_leave_game, (userKey: string, message: Object) => { this.onPlayerLeave(userKey) }); } private onGameMove(userKey: string, gameMove: GameMove) { const game = this.gameStore.getGameByUser(userKey); if (game) { try { game.mancalaGame.moveByPlayerPit(userKey, gameMove.index); this.rtmt.sendMessage(game.mancalaGame.player1Id, channel_on_game_update, game); this.rtmt.sendMessage(game.mancalaGame.player2Id, channel_on_game_update, game); if (game.mancalaGame.state == "ended") { this.deleteGame(game); } } catch (err: any) { this.onGameError(game, err) } } else { console.log("Game not found!"); } } private onGameError(game: Game, error: any) { 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); } 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); } } private listenOnPlayersPaired() { this.matchMaker.listenOnPlayersPaired((player1Id: string, player2Id: string) => { const game = this.createMancalaGame(player1Id, player2Id); this.startGame(game); }); } public fireOnPlayerConnected(playerId: string) {} public createMancalaGame(userKey1: string, userKey2: string): Game { const mancalaGame = new CommonMancalaGame(generateKey(), userKey1, userKey2); const random = Math.random(); mancalaGame.turnPlayerId = random > 0.5 ? userKey1 : userKey2; const game: Game = { id: mancalaGame.id, mancalaGame, gameUsersConnectionInfo: this.getGameUsersConnectionInfoFromUsers(mancalaGame.player1Id, mancalaGame.player2Id), }; this.gameStore.set(game.id, game); this.gameStore.setGameByUser(userKey1, game); this.gameStore.setGameByUser(userKey2, game); return game; } public getGameUsersConnectionInfo(game: Game): GameUsersConnectionInfo { return this.getGameUsersConnectionInfoFromUsers(game.mancalaGame.player1Id, game.mancalaGame.player2Id); } public getGameUsersConnectionInfoFromUsers(user1Id: string, user2Id: string): GameUsersConnectionInfo { const isPlayer1Online = this.rtmt.isClientOnline(user1Id); const isPlayer2Online = this.rtmt.isClientOnline(user2Id); return { user1ConnectionInfo: { userId: user1Id, isOnline: isPlayer1Online }, user2ConnectionInfo: { userId: user2Id, isOnline: isPlayer2Online } }; } 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.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); } public deleteGame(game: Game) { if (game) { this.gameStore.removeGameByPlayer(game.mancalaGame.player1Id); this.gameStore.removeGameByPlayer(game.mancalaGame.player2Id); } } private listenUserConnectionChange() { this.rtmt.listenOnClientConnectionChange((clientId: string, isOnline: boolean) => { const game = this.gameStore.getGameByUser(clientId); if (game) this.sendUserConnectionInfo(game); }); } }