2022-07-15 21:43:16 +03:00
|
|
|
import { CommonMancalaGame, MancalaGame } from "mancala.js";
|
|
|
|
|
import { RTMT } from "../rtmt/rtmt";
|
|
|
|
|
import { GameStore } from "./gamestore/GameStore";
|
|
|
|
|
import { generateKey } from "../util/key_factory";
|
|
|
|
|
import {
|
2024-03-31 17:54:07 +03:00
|
|
|
channel_cancel_new_game,
|
2022-07-15 21:43:16 +03:00
|
|
|
channel_game_move,
|
|
|
|
|
channel_leave_game,
|
2022-09-02 00:33:50 +03:00
|
|
|
channel_listen_game_events,
|
2022-07-15 21:43:16 +03:00
|
|
|
channel_new_game,
|
|
|
|
|
channel_on_game_crashed,
|
|
|
|
|
channel_on_game_start,
|
|
|
|
|
channel_on_game_update,
|
2022-07-23 00:32:55 +03:00
|
|
|
channel_on_game_user_leave,
|
2022-09-02 00:33:50 +03:00
|
|
|
channel_on_user_connection_change,
|
|
|
|
|
channel_unlisten_game_events
|
2022-07-15 21:43:16 +03:00
|
|
|
} from "../consts/channel_names";
|
|
|
|
|
import { GameMove } from "../models/GameMove";
|
|
|
|
|
import { GameCrashManager } from "./GameCrashManager";
|
|
|
|
|
import { MatchMaker } from "../matchmaker/MatchMaker";
|
2022-08-01 21:36:47 +03:00
|
|
|
import { Game, GameUsersConnectionInfo } from "../models/Game";
|
2022-07-15 21:43:16 +03:00
|
|
|
|
|
|
|
|
export class GameManager {
|
|
|
|
|
gameStore: GameStore;
|
|
|
|
|
rtmt: RTMT;
|
|
|
|
|
matchMaker: MatchMaker;
|
2022-07-23 00:32:55 +03:00
|
|
|
|
2022-07-15 21:43:16 +03:00
|
|
|
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();
|
2022-07-23 00:32:55 +03:00
|
|
|
this.listenUserConnectionChange();
|
2022-07-15 21:43:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private listenRtmtMessages() {
|
|
|
|
|
this.rtmt.listenMessage(channel_new_game, (userKey: string, message: Object) => {
|
|
|
|
|
this.matchMaker.join(userKey);
|
|
|
|
|
});
|
|
|
|
|
|
2024-03-31 17:54:07 +03:00
|
|
|
this.rtmt.listenMessage(channel_cancel_new_game, (userKey: string, message: Object) => {
|
|
|
|
|
this.matchMaker.cancel(userKey);
|
|
|
|
|
});
|
|
|
|
|
|
2022-07-15 21:43:16 +03:00
|
|
|
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)
|
2022-07-23 00:32:55 +03:00
|
|
|
});
|
2022-09-02 00:33:50 +03:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
});
|
2022-07-15 21:43:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private onGameMove(userKey: string, gameMove: GameMove) {
|
2022-07-31 00:10:34 +03:00
|
|
|
const game = this.gameStore.getGameByUser(userKey);
|
2022-07-15 21:43:16 +03:00
|
|
|
if (game) {
|
|
|
|
|
try {
|
2022-08-01 21:36:47 +03:00
|
|
|
game.mancalaGame.moveByPlayerPit(userKey, gameMove.index);
|
2022-08-01 22:38:03 +03:00
|
|
|
game.gameUsersConnectionInfo = this.getGameUsersConnectionInfo(game);
|
2022-09-02 00:33:50 +03:00
|
|
|
this.sendMessageToPlayersAndSpectators(game, channel_on_game_update, game);
|
2022-08-01 21:36:47 +03:00
|
|
|
if (game.mancalaGame.state == "ended") {
|
2022-09-02 00:33:50 +03:00
|
|
|
this.onGameEnd(game);
|
2022-07-15 21:43:16 +03:00
|
|
|
}
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
this.onGameError(game, err)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
console.log("Game not found!");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-01 21:36:47 +03:00
|
|
|
private onGameError(game: Game, error: any) {
|
2022-07-15 21:43:16 +03:00
|
|
|
console.error(error);
|
|
|
|
|
const crashFileName = GameCrashManager.logGameCrash(error, game);
|
|
|
|
|
console.info(`Game crash saved to file : ${crashFileName}`);
|
2022-09-02 00:33:50 +03:00
|
|
|
this.sendMessageToPlayersAndSpectators(game, channel_on_game_crashed, error);
|
2022-07-15 21:43:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private onPlayerLeave(userKey: string) {
|
2022-07-31 00:10:34 +03:00
|
|
|
const game = this.gameStore.getGameByUser(userKey);
|
2022-07-15 21:43:16 +03:00
|
|
|
if (game) {
|
|
|
|
|
this.deleteGame(game);
|
2022-09-02 00:33:50 +03:00
|
|
|
this.sendMessageToPlayersAndSpectators(game, channel_on_game_user_leave, userKey)
|
2022-07-15 21:43:16 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-02 00:33:50 +03:00
|
|
|
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 = [];
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-15 21:43:16 +03:00
|
|
|
private listenOnPlayersPaired() {
|
|
|
|
|
this.matchMaker.listenOnPlayersPaired((player1Id: string, player2Id: string) => {
|
|
|
|
|
const game = this.createMancalaGame(player1Id, player2Id);
|
|
|
|
|
this.startGame(game);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-02 00:33:50 +03:00
|
|
|
public fireOnPlayerConnected(playerId: string) { }
|
2022-07-15 21:43:16 +03:00
|
|
|
|
2022-08-01 21:36:47 +03:00
|
|
|
public createMancalaGame(userKey1: string, userKey2: string): Game {
|
|
|
|
|
const mancalaGame = new CommonMancalaGame(generateKey(), userKey1, userKey2);
|
2022-07-15 21:43:16 +03:00
|
|
|
const random = Math.random();
|
2022-08-01 21:36:47 +03:00
|
|
|
mancalaGame.turnPlayerId = random > 0.5 ? userKey1 : userKey2;
|
|
|
|
|
|
|
|
|
|
const game: Game = {
|
|
|
|
|
id: mancalaGame.id,
|
|
|
|
|
mancalaGame,
|
|
|
|
|
gameUsersConnectionInfo: this.getGameUsersConnectionInfoFromUsers(mancalaGame.player1Id, mancalaGame.player2Id),
|
2022-09-02 00:33:50 +03:00
|
|
|
spectatorIds: []
|
2022-08-01 21:36:47 +03:00
|
|
|
};
|
2022-07-15 21:43:16 +03:00
|
|
|
|
2022-07-31 00:10:34 +03:00
|
|
|
this.gameStore.set(game.id, game);
|
|
|
|
|
this.gameStore.setGameByUser(userKey1, game);
|
|
|
|
|
this.gameStore.setGameByUser(userKey2, game);
|
2022-07-15 21:43:16 +03:00
|
|
|
return game;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-01 21:36:47 +03:00
|
|
|
public getGameUsersConnectionInfo(game: Game): GameUsersConnectionInfo {
|
|
|
|
|
return this.getGameUsersConnectionInfoFromUsers(game.mancalaGame.player1Id, game.mancalaGame.player2Id);
|
2022-07-23 00:32:55 +03:00
|
|
|
}
|
|
|
|
|
|
2022-08-01 21:36:47 +03:00
|
|
|
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) {
|
2022-09-02 00:33:50 +03:00
|
|
|
this.sendMessageToPlayersAndSpectators(game, channel_on_game_start, game);
|
2022-08-01 21:36:47 +03:00
|
|
|
this.sendUserConnectionInfo(game);
|
|
|
|
|
this.sendUserConnectionInfo(game);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public sendUserConnectionInfo(game: Game) {
|
|
|
|
|
const gameUsersConnectionInfo = this.getGameUsersConnectionInfo(game);
|
2022-09-02 00:33:50 +03:00
|
|
|
this.sendMessageToPlayersAndSpectators(game, channel_on_user_connection_change, gameUsersConnectionInfo);
|
2022-07-15 21:43:16 +03:00
|
|
|
}
|
|
|
|
|
|
2022-08-01 21:36:47 +03:00
|
|
|
public deleteGame(game: Game) {
|
2022-07-15 21:43:16 +03:00
|
|
|
if (game) {
|
2022-08-01 21:36:47 +03:00
|
|
|
this.gameStore.removeGameByPlayer(game.mancalaGame.player1Id);
|
|
|
|
|
this.gameStore.removeGameByPlayer(game.mancalaGame.player2Id);
|
2022-07-15 21:43:16 +03:00
|
|
|
}
|
|
|
|
|
}
|
2022-07-23 00:32:55 +03:00
|
|
|
|
|
|
|
|
private listenUserConnectionChange() {
|
|
|
|
|
this.rtmt.listenOnClientConnectionChange((clientId: string, isOnline: boolean) => {
|
2022-07-31 00:10:34 +03:00
|
|
|
const game = this.gameStore.getGameByUser(clientId);
|
2022-08-01 21:36:47 +03:00
|
|
|
if (game) this.sendUserConnectionInfo(game);
|
2022-09-07 21:13:06 +03:00
|
|
|
if (!isOnline) {
|
|
|
|
|
const speactatingGameIds = this.gameStore.getSpeactatingGameIdsByUser(clientId);
|
|
|
|
|
speactatingGameIds.forEach((gameId) => this.removeSpectator(clientId, gameId));
|
|
|
|
|
} else {
|
|
|
|
|
game && this.rtmt.sendMessage(clientId, channel_on_game_update, game);
|
|
|
|
|
}
|
2022-07-23 00:32:55 +03:00
|
|
|
});
|
|
|
|
|
}
|
2022-07-15 21:43:16 +03:00
|
|
|
}
|