Merge pull request #15 from jhalitaksoy/feature/spectator
Feature/spectator
This commit is contained in:
commit
b9101e17bc
@ -9,3 +9,5 @@ export const channel_on_game_user_leave = "on_game_user_leave"
|
|||||||
export const channel_ping = "ping"
|
export const channel_ping = "ping"
|
||||||
export const channel_pong = "pong"
|
export const channel_pong = "pong"
|
||||||
export const channel_on_user_connection_change = "channel_on_user_connection_change"
|
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"
|
||||||
@ -5,12 +5,14 @@ import { generateKey } from "../util/key_factory";
|
|||||||
import {
|
import {
|
||||||
channel_game_move,
|
channel_game_move,
|
||||||
channel_leave_game,
|
channel_leave_game,
|
||||||
|
channel_listen_game_events,
|
||||||
channel_new_game,
|
channel_new_game,
|
||||||
channel_on_game_crashed,
|
channel_on_game_crashed,
|
||||||
channel_on_game_start,
|
channel_on_game_start,
|
||||||
channel_on_game_update,
|
channel_on_game_update,
|
||||||
channel_on_game_user_leave,
|
channel_on_game_user_leave,
|
||||||
channel_on_user_connection_change
|
channel_on_user_connection_change,
|
||||||
|
channel_unlisten_game_events
|
||||||
} from "../consts/channel_names";
|
} from "../consts/channel_names";
|
||||||
import { GameMove } from "../models/GameMove";
|
import { GameMove } from "../models/GameMove";
|
||||||
import { GameCrashManager } from "./GameCrashManager";
|
import { GameCrashManager } from "./GameCrashManager";
|
||||||
@ -47,6 +49,14 @@ export class GameManager {
|
|||||||
this.rtmt.listenMessage(channel_leave_game, (userKey: string, message: Object) => {
|
this.rtmt.listenMessage(channel_leave_game, (userKey: string, message: Object) => {
|
||||||
this.onPlayerLeave(userKey)
|
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) {
|
private onGameMove(userKey: string, gameMove: GameMove) {
|
||||||
@ -55,10 +65,9 @@ export class GameManager {
|
|||||||
try {
|
try {
|
||||||
game.mancalaGame.moveByPlayerPit(userKey, gameMove.index);
|
game.mancalaGame.moveByPlayerPit(userKey, gameMove.index);
|
||||||
game.gameUsersConnectionInfo = this.getGameUsersConnectionInfo(game);
|
game.gameUsersConnectionInfo = this.getGameUsersConnectionInfo(game);
|
||||||
this.rtmt.sendMessage(game.mancalaGame.player1Id, channel_on_game_update, game);
|
this.sendMessageToPlayersAndSpectators(game, channel_on_game_update, game);
|
||||||
this.rtmt.sendMessage(game.mancalaGame.player2Id, channel_on_game_update, game);
|
|
||||||
if (game.mancalaGame.state == "ended") {
|
if (game.mancalaGame.state == "ended") {
|
||||||
this.deleteGame(game);
|
this.onGameEnd(game);
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
this.onGameError(game, err)
|
this.onGameError(game, err)
|
||||||
@ -72,19 +81,54 @@ export class GameManager {
|
|||||||
console.error(error);
|
console.error(error);
|
||||||
const crashFileName = GameCrashManager.logGameCrash(error, game);
|
const crashFileName = GameCrashManager.logGameCrash(error, game);
|
||||||
console.info(`Game crash saved to file : ${crashFileName}`);
|
console.info(`Game crash saved to file : ${crashFileName}`);
|
||||||
this.rtmt.sendMessage(game.mancalaGame.player1Id, channel_on_game_crashed, error);
|
this.sendMessageToPlayersAndSpectators(game, channel_on_game_crashed, error);
|
||||||
this.rtmt.sendMessage(game.mancalaGame.player2Id, channel_on_game_crashed, error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private onPlayerLeave(userKey: string) {
|
private onPlayerLeave(userKey: string) {
|
||||||
const game = this.gameStore.getGameByUser(userKey);
|
const game = this.gameStore.getGameByUser(userKey);
|
||||||
if (game) {
|
if (game) {
|
||||||
this.deleteGame(game);
|
this.deleteGame(game);
|
||||||
this.rtmt.sendMessage(game.mancalaGame.player1Id, channel_on_game_user_leave, userKey);
|
this.sendMessageToPlayersAndSpectators(game, channel_on_game_user_leave, userKey)
|
||||||
this.rtmt.sendMessage(game.mancalaGame.player2Id, 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() {
|
private listenOnPlayersPaired() {
|
||||||
this.matchMaker.listenOnPlayersPaired((player1Id: string, player2Id: string) => {
|
this.matchMaker.listenOnPlayersPaired((player1Id: string, player2Id: string) => {
|
||||||
const game = this.createMancalaGame(player1Id, player2Id);
|
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 {
|
public createMancalaGame(userKey1: string, userKey2: string): Game {
|
||||||
const mancalaGame = new CommonMancalaGame(generateKey(), userKey1, userKey2);
|
const mancalaGame = new CommonMancalaGame(generateKey(), userKey1, userKey2);
|
||||||
@ -103,6 +147,7 @@ export class GameManager {
|
|||||||
id: mancalaGame.id,
|
id: mancalaGame.id,
|
||||||
mancalaGame,
|
mancalaGame,
|
||||||
gameUsersConnectionInfo: this.getGameUsersConnectionInfoFromUsers(mancalaGame.player1Id, mancalaGame.player2Id),
|
gameUsersConnectionInfo: this.getGameUsersConnectionInfoFromUsers(mancalaGame.player1Id, mancalaGame.player2Id),
|
||||||
|
spectatorIds: []
|
||||||
};
|
};
|
||||||
|
|
||||||
this.gameStore.set(game.id, game);
|
this.gameStore.set(game.id, game);
|
||||||
@ -125,21 +170,14 @@ export class GameManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public startGame(game: Game) {
|
public startGame(game: Game) {
|
||||||
const mancalaGame = game.mancalaGame;
|
this.sendMessageToPlayersAndSpectators(game, channel_on_game_start, game);
|
||||||
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);
|
||||||
this.sendUserConnectionInfo(game);
|
this.sendUserConnectionInfo(game);
|
||||||
}
|
}
|
||||||
|
|
||||||
public sendUserConnectionInfo(game: Game) {
|
public sendUserConnectionInfo(game: Game) {
|
||||||
const user1 = game.mancalaGame.player1Id;
|
|
||||||
const user2 = game.mancalaGame.player2Id;
|
|
||||||
const gameUsersConnectionInfo = this.getGameUsersConnectionInfo(game);
|
const gameUsersConnectionInfo = this.getGameUsersConnectionInfo(game);
|
||||||
|
this.sendMessageToPlayersAndSpectators(game, channel_on_user_connection_change, gameUsersConnectionInfo);
|
||||||
//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) {
|
public deleteGame(game: Game) {
|
||||||
@ -153,6 +191,8 @@ export class GameManager {
|
|||||||
this.rtmt.listenOnClientConnectionChange((clientId: string, isOnline: boolean) => {
|
this.rtmt.listenOnClientConnectionChange((clientId: string, isOnline: boolean) => {
|
||||||
const game = this.gameStore.getGameByUser(clientId);
|
const game = this.gameStore.getGameByUser(clientId);
|
||||||
if (game) this.sendUserConnectionInfo(game);
|
if (game) this.sendUserConnectionInfo(game);
|
||||||
|
const speactatingGameIds = this.gameStore.getSpeactatingGameIdsByUser(clientId);
|
||||||
|
speactatingGameIds.forEach((gameId) => this.removeSpectator(clientId, gameId));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -12,4 +12,6 @@ export interface GameStore {
|
|||||||
setGameByUser(userId: string, game: Game): void;
|
setGameByUser(userId: string, game: Game): void;
|
||||||
getGameByUser(userId: string): Game | undefined;
|
getGameByUser(userId: string): Game | undefined;
|
||||||
removeGameByPlayer(userId: string): void;
|
removeGameByPlayer(userId: string): void;
|
||||||
|
|
||||||
|
getSpeactatingGameIdsByUser(userId: string): string[]
|
||||||
}
|
}
|
||||||
@ -35,4 +35,14 @@ export class GameStoreImpl implements GameStore {
|
|||||||
removeGameByPlayer(userId: string): void {
|
removeGameByPlayer(userId: string): void {
|
||||||
this.removeGameIdByPlayer(userId);
|
this.removeGameIdByPlayer(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getSpeactatingGameIdsByUser(userId: string): string[] {
|
||||||
|
const speactatingGameIds = [];
|
||||||
|
for (const gameId in this.gameStore.keys()) {
|
||||||
|
const game = this.gameStore.get(gameId) as Game;
|
||||||
|
const isSpectator = game.spectatorIds.find((value) => value === userId);
|
||||||
|
isSpectator && speactatingGameIds.push(game.id);
|
||||||
|
}
|
||||||
|
return speactatingGameIds;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -5,6 +5,7 @@ export interface Game {
|
|||||||
id: string;
|
id: string;
|
||||||
mancalaGame: MancalaGame;
|
mancalaGame: MancalaGame;
|
||||||
gameUsersConnectionInfo: GameUsersConnectionInfo;
|
gameUsersConnectionInfo: GameUsersConnectionInfo;
|
||||||
|
spectatorIds: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GameUsersConnectionInfo {
|
export interface GameUsersConnectionInfo {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user