working flow
This commit is contained in:
		@@ -5,9 +5,12 @@ import { PlayerMove } from "./entities/PlayerMove";
 | 
			
		||||
import { PlayerInterface } from "./entities/player/PlayerInterface";
 | 
			
		||||
import { Tile } from "./entities/Tile";
 | 
			
		||||
import { LoggingService } from "../common/LoggingService";
 | 
			
		||||
import { printBoard, printLine, uuid, wait, whileNotUndefined } from '../common/utilities';
 | 
			
		||||
import { printBoard, printLine, uuid, wait, whileNot, whileNotUndefined } from '../common/utilities';
 | 
			
		||||
import { PlayerNotificationService } from '../server/services/PlayerNotificationService';
 | 
			
		||||
import { GameState } from './dto/GameState';
 | 
			
		||||
import { PlayerHuman } from './entities/player/PlayerHuman';
 | 
			
		||||
import { PlayerAI } from './entities/player/PlayerAI';
 | 
			
		||||
import { GameSummary } from './dto/GameSummary';
 | 
			
		||||
 | 
			
		||||
export class DominoesGame extends EventEmitter {
 | 
			
		||||
  private id: string;
 | 
			
		||||
@@ -29,6 +32,7 @@ export class DominoesGame extends EventEmitter {
 | 
			
		||||
  lastMove: PlayerMove | null = null;
 | 
			
		||||
  forcedInitialPlayerIndex: number | null = null;
 | 
			
		||||
  canAskNextPlayerMove: boolean = true;
 | 
			
		||||
  clientsReady: string[] = [];
 | 
			
		||||
 | 
			
		||||
  constructor(public players: PlayerInterface[], seed: PRNG) {
 | 
			
		||||
    super();
 | 
			
		||||
@@ -39,6 +43,10 @@ export class DominoesGame extends EventEmitter {
 | 
			
		||||
    this.initializeGame();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get numHumanPlayers() {
 | 
			
		||||
    return this.players.filter(player => player instanceof PlayerHuman).length;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async initializeGame() {
 | 
			
		||||
    this.gameOver = false;
 | 
			
		||||
    this.gameBlocked = false;
 | 
			
		||||
@@ -83,7 +91,13 @@ export class DominoesGame extends EventEmitter {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  isBlocked(): boolean {
 | 
			
		||||
    return this.blockedCount === this.players.length;
 | 
			
		||||
    const freeEnds = this.board.getFreeEnds();
 | 
			
		||||
    const tiles = []
 | 
			
		||||
    for (let player of this.players) {
 | 
			
		||||
      tiles.push(...player.hand);
 | 
			
		||||
    }
 | 
			
		||||
    const canPlay = tiles.some(tile => tile.pips[0] === freeEnds[0] || tile.pips[1] === freeEnds[0] || tile.pips[0] === freeEnds[1] || tile.pips[1] === freeEnds[1]);
 | 
			
		||||
    return !canPlay;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  isGameOver(): boolean {
 | 
			
		||||
@@ -132,6 +146,7 @@ export class DominoesGame extends EventEmitter {
 | 
			
		||||
      const player = this.players[this.currentPlayerIndex];
 | 
			
		||||
      this.notificationService.sendEventToPlayers('server:next-turn', this.players, this.getGameState());
 | 
			
		||||
      this.logger.debug(`${player.name}'s turn (${player.hand.length} tiles)`);
 | 
			
		||||
      this.printPlayerHand(player);
 | 
			
		||||
      printBoard(this.board)
 | 
			
		||||
      player.askForMove(this.board);   
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
@@ -139,23 +154,40 @@ export class DominoesGame extends EventEmitter {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  finishTurn(playerMove: PlayerMove | null) {
 | 
			
		||||
  async checkAllClientsReadyToContinue() {
 | 
			
		||||
    try {
 | 
			
		||||
      const conditionFn = () => {
 | 
			
		||||
        this.logger.trace(`Clients ready: ${this.clientsReady.length}/${this.numHumanPlayers}`);
 | 
			
		||||
        return this.clientsReady.length === this.numHumanPlayers   
 | 
			
		||||
      }
 | 
			
		||||
      await whileNot(conditionFn, 100);
 | 
			
		||||
      this.clientsReady = [];
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      this.logger.error(error, 'Error starting game');
 | 
			
		||||
      throw new Error('Error starting game (checkAllClientsReadyToContinue)');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async finishTurn(playerMove: PlayerMove | null) {
 | 
			
		||||
    try {
 | 
			
		||||
      this.lastMove = playerMove;
 | 
			
		||||
      if (playerMove === null) {
 | 
			
		||||
        console.log('Player cannot move');
 | 
			
		||||
        this.logger.info('Player cannot move');
 | 
			
		||||
        this.blockedCount += 1;
 | 
			
		||||
        
 | 
			
		||||
        this.logger.trace(`Blocked count: ${this.blockedCount}`);
 | 
			
		||||
        this.gameBlocked = this.isBlocked();
 | 
			
		||||
        this.notificationService.sendEventToPlayers('server:server-player-move', this.players, { move: playerMove });
 | 
			
		||||
        if (this.gameBlocked) {
 | 
			
		||||
          this.gameEnded();
 | 
			
		||||
          return;
 | 
			
		||||
        } else {
 | 
			
		||||
          this.nextPlayer();
 | 
			
		||||
          await this.checkAllClientsReadyToContinue()
 | 
			
		||||
          this.playTurn();
 | 
			
		||||
        }
 | 
			
		||||
        this.nextPlayer();     
 | 
			
		||||
        this.playTurn();
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      const player = this.players[this.currentPlayerIndex];
 | 
			
		||||
      const skipWaitForConfirmation = player instanceof PlayerAI && playerMove === null;
 | 
			
		||||
      this.blockedCount = 0;
 | 
			
		||||
      this.board.play(playerMove);
 | 
			
		||||
      player.hand = player.hand.filter(tile => tile !== playerMove.tile);
 | 
			
		||||
@@ -164,8 +196,10 @@ export class DominoesGame extends EventEmitter {
 | 
			
		||||
      // whileNotUndefined(() => this.canAskNextPlayerMove === true ? {} : undefined);
 | 
			
		||||
      this.gameOver = this.isGameOver();
 | 
			
		||||
      if (!this.gameOver) {
 | 
			
		||||
        this.printPlayersHand();
 | 
			
		||||
        this.nextPlayer();    
 | 
			
		||||
        this.nextPlayer();  
 | 
			
		||||
        if (!skipWaitForConfirmation) {
 | 
			
		||||
          await this.checkAllClientsReadyToContinue()  
 | 
			
		||||
        }
 | 
			
		||||
        this.playTurn();
 | 
			
		||||
      } else {
 | 
			
		||||
        this.gameEnded();
 | 
			
		||||
@@ -179,12 +213,13 @@ export class DominoesGame extends EventEmitter {
 | 
			
		||||
    this.gameInProgress = false;
 | 
			
		||||
    this.winner =  this.getWinner();
 | 
			
		||||
    this.setScores();
 | 
			
		||||
    const summary = {
 | 
			
		||||
    const summary: GameSummary = {
 | 
			
		||||
      gameId: this.id,
 | 
			
		||||
      isBlocked: this.gameBlocked,
 | 
			
		||||
      isTied: this.gameTied,
 | 
			
		||||
      winner: this.winner?.getState(),
 | 
			
		||||
      score: this.players.map(player => ({name: player.name, score: player.score}))
 | 
			
		||||
      score: this.players.map(player => ({id: player.id, name: player.name, score: player.score})),
 | 
			
		||||
      players: this.players.map(player => player.getState())
 | 
			
		||||
    }
 | 
			
		||||
    this.emit('game-over', summary);
 | 
			
		||||
  }
 | 
			
		||||
@@ -210,22 +245,25 @@ export class DominoesGame extends EventEmitter {
 | 
			
		||||
 | 
			
		||||
  printPlayersHand() {
 | 
			
		||||
    for (let player of this.players) {
 | 
			
		||||
      this.logger.debug(`${player.name}'s hand (${player.hand.length}): ${player.hand.map(tile => tile.toString())}`);
 | 
			
		||||
      this.printPlayerHand(player);
 | 
			
		||||
    }    
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  printPlayerHand(player: PlayerInterface) {
 | 
			
		||||
    this.logger.debug(`${player.name}'s hand (${player.hand.length}): ${player.hand.map(tile => tile.toString())}`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async start(): Promise<void> {
 | 
			
		||||
    try {
 | 
			
		||||
      // Initalize game
 | 
			
		||||
      this.gameInProgress = false;
 | 
			
		||||
      this.resetPlayersScore();
 | 
			
		||||
      this.tileSelectionPhase = true;
 | 
			
		||||
      // await this.notificationManager.notifyGameState(this);
 | 
			
		||||
      // await this.notificationManager.notifyPlayersState(this.players);
 | 
			
		||||
      this.deal();
 | 
			
		||||
      const extractStates =  (p: PlayerInterface) => {
 | 
			
		||||
        return p.getState()
 | 
			
		||||
      };
 | 
			
		||||
      const extractStates =  (p: PlayerInterface) => ({
 | 
			
		||||
        player:  p.getState(true),
 | 
			
		||||
        gameState: this.getGameState()
 | 
			
		||||
      });
 | 
			
		||||
      await this.notificationService.sendEventToPlayers('server:hand-dealt', this.players, extractStates);
 | 
			
		||||
      
 | 
			
		||||
      this.tileSelectionPhase = false;
 | 
			
		||||
@@ -237,10 +275,9 @@ export class DominoesGame extends EventEmitter {
 | 
			
		||||
      printLine(`${this.players[this.currentPlayerIndex].name} is the starting player:`);
 | 
			
		||||
      this.logger.debug("Before play turn")
 | 
			
		||||
      this.playTurn();
 | 
			
		||||
      // await this.notificationManager.notifyGameState(this);
 | 
			
		||||
      // await this.notificationManager.notifyPlayersState(this.players);
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      this.logger.error(error, 'Error starting game');
 | 
			
		||||
      throw new Error('Error starting game');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -272,8 +309,6 @@ export class DominoesGame extends EventEmitter {
 | 
			
		||||
    while (this.board.boneyard.length > 0) {
 | 
			
		||||
      for (let player of this.players) {
 | 
			
		||||
        const choosen = await player.chooseTile(this.board);
 | 
			
		||||
        // await this.notificationService.notifyGameState(this);
 | 
			
		||||
        // await this.notificationService.notifyPlayersState(this.players);
 | 
			
		||||
        if (this.board.boneyard.length === 0) {
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
@@ -292,16 +327,22 @@ export class DominoesGame extends EventEmitter {
 | 
			
		||||
      gameBlocked: this.gameBlocked,
 | 
			
		||||
      gameTied: this.gameTied,
 | 
			
		||||
      gameId: this.id,
 | 
			
		||||
      boneyard: this.board.boneyard.map(tile => ({ id: tile.id})),
 | 
			
		||||
      boneyard: this.board.boneyard.map(tile => tile.getState(false)),
 | 
			
		||||
      players: this.players.map(player => player.getState()),
 | 
			
		||||
      currentPlayer: currentPlayer.getState(),
 | 
			
		||||
      board: this.board.tiles.map(tile => ({
 | 
			
		||||
        id: tile.id,
 | 
			
		||||
        playerId: tile.playerId,
 | 
			
		||||
        pips: tile.pips
 | 
			
		||||
      })),
 | 
			
		||||
      board: this.board.tiles.map(tile => tile.getState(true)),
 | 
			
		||||
      boardFreeEnds: this.board.getFreeEnds(),
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setClientReady(userId: string) {
 | 
			
		||||
    this.logger.trace(`${userId} - ${this.clientsReady}`);
 | 
			
		||||
    if (!this.clientsReady.includes(userId)) {
 | 
			
		||||
      this.logger.trace(`Client ${userId} is ready`)
 | 
			
		||||
      this.clientsReady.push(userId);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -22,6 +22,7 @@ export class MatchSession {
 | 
			
		||||
  private notificationService = new PlayerNotificationService();
 | 
			
		||||
  private winnerIndex: number | null = null;
 | 
			
		||||
  private clientsReady: string[] = [];
 | 
			
		||||
  private gameSummaries: GameSummary[] = [];
 | 
			
		||||
  
 | 
			
		||||
  id: string;
 | 
			
		||||
  matchInProgress: boolean = false;
 | 
			
		||||
@@ -35,7 +36,7 @@ export class MatchSession {
 | 
			
		||||
  scoreboard: Map<string, number> = new Map();
 | 
			
		||||
  seed!: string
 | 
			
		||||
  sessionInProgress: boolean = false;
 | 
			
		||||
  state: string = 'created'
 | 
			
		||||
  status: string = 'created'
 | 
			
		||||
 | 
			
		||||
  constructor(public creator: PlayerInterface, public name?: string, seed?: string) {
 | 
			
		||||
    this.seed = seed || getRandomSeed();
 | 
			
		||||
@@ -71,6 +72,13 @@ export class MatchSession {
 | 
			
		||||
      this.logger.trace(`Client ${userId} is ready`)
 | 
			
		||||
      this.clientsReady.push(userId);
 | 
			
		||||
    }
 | 
			
		||||
    this.logger.trace(`${this.clientsReady.length}`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setCurrentGameClientReady(userId: string) {
 | 
			
		||||
    if (this.currentGame) {
 | 
			
		||||
      this.currentGame.setClientReady(userId);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async checkAllClientsReadyBeforeStart() {
 | 
			
		||||
@@ -80,13 +88,15 @@ export class MatchSession {
 | 
			
		||||
          this.logger.trace(`Clients ready: ${this.clientsReady.length}/${this.numHumanPlayers}`);
 | 
			
		||||
          return this.clientsReady.length === this.numHumanPlayers   
 | 
			
		||||
        }
 | 
			
		||||
        await whileNot(conditionFn, 10);
 | 
			
		||||
        await whileNot(conditionFn, 50);
 | 
			
		||||
        this.logger.info(`Game #${this.gameNumber} started`);
 | 
			
		||||
        this.currentGame.start();
 | 
			
		||||
        this.gameInProgress = true;
 | 
			
		||||
        this.clientsReady = [];
 | 
			
		||||
      }
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      this.logger.error(error, 'Error starting game');
 | 
			
		||||
      throw new Error('Error starting game (checkAllClientsReadyBeforeStart)');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -95,6 +105,8 @@ export class MatchSession {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  playerMove(move: any) {
 | 
			
		||||
    this.logger.trace('Handling player move (playerMove)');
 | 
			
		||||
    this.logger.trace(`${this.clientsReady.length}`);
 | 
			
		||||
    if (this.currentGame) {
 | 
			
		||||
      if ((move === null) || (move === undefined) || move.type === 'pass') {
 | 
			
		||||
        this.currentGame.finishTurn(null);
 | 
			
		||||
@@ -131,6 +143,7 @@ export class MatchSession {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private continueMatch(gameSummary: GameSummary) {
 | 
			
		||||
    this.gameSummaries.push(gameSummary);
 | 
			
		||||
    this.winnerIndex = this.players.findIndex(player => player.id === gameSummary?.winner?.id);
 | 
			
		||||
    if (this.winnerIndex !== null) {
 | 
			
		||||
      this.currentGame?.setForcedInitialPlayerIndex(this.winnerIndex);
 | 
			
		||||
@@ -139,19 +152,20 @@ export class MatchSession {
 | 
			
		||||
    this.checkMatchWinner();
 | 
			
		||||
    this.resetPlayers();
 | 
			
		||||
    if (!this.matchInProgress) {
 | 
			
		||||
      this.state = 'end'
 | 
			
		||||
      this.status = 'end'
 | 
			
		||||
      this.notificationService.sendEventToPlayers('server:match-finished', this.players, {
 | 
			
		||||
        lastGame: gameSummary,
 | 
			
		||||
        sessionState: this.getState(),
 | 
			
		||||
      });
 | 
			
		||||
    } else {
 | 
			
		||||
      this.state = 'waiting'
 | 
			
		||||
      this.status = 'waiting'
 | 
			
		||||
      // await this.playerNotificationManager.notifyMatchState(this);
 | 
			
		||||
      this.notificationService.sendEventToPlayers('server:game-finished', this.players, {
 | 
			
		||||
        lastGame: gameSummary,
 | 
			
		||||
        sessionState: this.getState()
 | 
			
		||||
      });
 | 
			
		||||
      this.waitingForPlayers = true;   
 | 
			
		||||
      this.waitingForPlayers = true; 
 | 
			
		||||
      this.startGame();
 | 
			
		||||
    }    
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -169,7 +183,7 @@ export class MatchSession {
 | 
			
		||||
 | 
			
		||||
  private async startMatch(seed: string) {
 | 
			
		||||
    try {
 | 
			
		||||
      this.state = 'in-game'
 | 
			
		||||
      this.status = 'in-game'
 | 
			
		||||
      this.rng = seedrandom(seed);
 | 
			
		||||
      this.resetScoreboard()
 | 
			
		||||
      this.gameNumber = 0;
 | 
			
		||||
@@ -180,7 +194,7 @@ export class MatchSession {
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      this.logger.error(error);
 | 
			
		||||
      this.matchInProgress = false;
 | 
			
		||||
      this.state = 'error'
 | 
			
		||||
      this.status = 'error'
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -296,11 +310,7 @@ export class MatchSession {
 | 
			
		||||
      id: this.id,
 | 
			
		||||
      name: this.name!,
 | 
			
		||||
      creator: this.creator.id,
 | 
			
		||||
      players: this.players.map(player =>( {
 | 
			
		||||
        id: player.id,
 | 
			
		||||
        name: player.name,
 | 
			
		||||
        ready: player.ready,
 | 
			
		||||
      })),
 | 
			
		||||
      players: this.players.map(player => player.getState()),
 | 
			
		||||
      playersReady: this.numPlayersReady,
 | 
			
		||||
      sessionInProgress: this.sessionInProgress,
 | 
			
		||||
      maxPlayers: this.maxPlayers,
 | 
			
		||||
@@ -313,7 +323,8 @@ export class MatchSession {
 | 
			
		||||
      status: this.sessionInProgress ? 'in progress' : 'waiting',
 | 
			
		||||
      scoreboard: [...this.scoreboard.entries()],
 | 
			
		||||
      matchWinner: this.matchWinner?.getState() || null,
 | 
			
		||||
      matchInProgress: this.matchInProgress
 | 
			
		||||
      matchInProgress: this.matchInProgress,
 | 
			
		||||
      gameSummaries: this.gameSummaries,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -34,13 +34,14 @@ export class PlayerInteractionAI implements PlayerInteractionInterface {
 | 
			
		||||
    } else {
 | 
			
		||||
      move = this.chooseTileGreed(board);
 | 
			
		||||
    }
 | 
			
		||||
    const rndWait = Math.floor(Math.random() * 1500) + 2000;
 | 
			
		||||
    this.logger.debug(`AI move: ${move?.tile.pips}`);
 | 
			
		||||
    const rndWait = Math.floor(Math.random() * 1000) + 1000;
 | 
			
		||||
    setTimeout(() => {
 | 
			
		||||
      this.interactionService.playerMove({
 | 
			
		||||
      this.interactionService.playerMoveAI({
 | 
			
		||||
        sessionId: (<PlayerAI>this.player).sessionId,
 | 
			
		||||
        move
 | 
			
		||||
      });
 | 
			
		||||
      this.logger.trace('Move sent to server (AI');
 | 
			
		||||
      this.logger.trace('Move sent to server (AI)');
 | 
			
		||||
    }, rndWait);
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
 
 | 
			
		||||
@@ -8,17 +8,20 @@ import { NetworkPlayer } from './entities/player/NetworkPlayer';
 | 
			
		||||
import { PlayerMoveSide, PlayerMoveSideType } from './constants';
 | 
			
		||||
import { SocketDisconnectedError } from '../common/errors/SocketDisconnectedError';
 | 
			
		||||
import { InteractionService } from '../server/services/InteractionService';
 | 
			
		||||
import { LoggingService } from '../common/LoggingService';
 | 
			
		||||
 | 
			
		||||
export class PlayerInteractionNetwork implements PlayerInteractionInterface {
 | 
			
		||||
    player: PlayerInterface;
 | 
			
		||||
    interactionService: InteractionService = new InteractionService();
 | 
			
		||||
    clientNotifier = new NetworkClientNotifier();
 | 
			
		||||
    logger: LoggingService = new LoggingService();
 | 
			
		||||
 | 
			
		||||
    constructor(player: PlayerInterface) {
 | 
			
		||||
        this.player = player;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    askForMove(board: Board): void {
 | 
			
		||||
      this.logger.trace('Asking for move (Player)');
 | 
			
		||||
      this.clientNotifier.sendEvent(this.player as NetworkPlayer, 'server:player-turn', {
 | 
			
		||||
        freeHands: board.getFreeEnds(),
 | 
			
		||||
        isFirstMove: board.tiles.length === 0
 | 
			
		||||
 
 | 
			
		||||
@@ -5,5 +5,6 @@ export interface GameSummary {
 | 
			
		||||
  isBlocked: boolean;
 | 
			
		||||
  isTied: boolean;
 | 
			
		||||
  winner: PlayerDto;
 | 
			
		||||
  score: { name: string; score: number; }[]
 | 
			
		||||
  score: { id: string, name: string; score: number; }[],
 | 
			
		||||
  players?: PlayerDto[];
 | 
			
		||||
}
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
import { GameSummary } from "./GameSummary";
 | 
			
		||||
import { PlayerDto } from "./PlayerDto";
 | 
			
		||||
 | 
			
		||||
export interface MatchSessionState {
 | 
			
		||||
@@ -17,5 +18,6 @@ export interface MatchSessionState {
 | 
			
		||||
  scoreboard: [string, number][];
 | 
			
		||||
  matchWinner: PlayerDto | null;
 | 
			
		||||
  matchInProgress: boolean;
 | 
			
		||||
  playersReady: number
 | 
			
		||||
  playersReady: number,
 | 
			
		||||
  gameSummaries: GameSummary[];
 | 
			
		||||
}
 | 
			
		||||
@@ -1,8 +1,16 @@
 | 
			
		||||
export interface TileDto {
 | 
			
		||||
  id: string;
 | 
			
		||||
  pips?: number[];
 | 
			
		||||
  flipped: boolean;
 | 
			
		||||
  revealed: boolean;
 | 
			
		||||
  playerId: string | undefined;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface PlayerDto {
 | 
			
		||||
  id: string;
 | 
			
		||||
  name: string;
 | 
			
		||||
  score?: number;
 | 
			
		||||
  hand?: any[];
 | 
			
		||||
  hand?: TileDto[];
 | 
			
		||||
  teamedWith?: PlayerDto | null;
 | 
			
		||||
  ready: boolean;
 | 
			
		||||
}
 | 
			
		||||
@@ -28,6 +28,16 @@ export class Tile {
 | 
			
		||||
    this.flipped = !this.flipped;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getState(showPips: boolean = false) {
 | 
			
		||||
    return {
 | 
			
		||||
      id: this.id,
 | 
			
		||||
      pips: showPips ? this.pips : undefined,
 | 
			
		||||
      flipped: this.flipped,
 | 
			
		||||
      revealed: this.revealed,
 | 
			
		||||
      playerId: this.playerId,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  toString(): string {
 | 
			
		||||
    if (!this.revealed) {
 | 
			
		||||
      return '[ | ]';
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ import { LoggingService } from "../../../common/LoggingService";
 | 
			
		||||
import { EventEmitter } from "stream";
 | 
			
		||||
import { PlayerInteractionInterface } from "../../PlayerInteractionInterface";
 | 
			
		||||
import { uuid } from "../../../common/utilities";
 | 
			
		||||
import { PlayerDto } from "../../dto/PlayerDto";
 | 
			
		||||
import { PlayerDto, TileDto } from "../../dto/PlayerDto";
 | 
			
		||||
 | 
			
		||||
export abstract class AbstractPlayer extends EventEmitter implements PlayerInterface {
 | 
			
		||||
  hand: Tile[] = [];
 | 
			
		||||
@@ -56,19 +56,8 @@ export abstract class AbstractPlayer extends EventEmitter implements PlayerInter
 | 
			
		||||
      id: this.id,
 | 
			
		||||
      name: this.name,
 | 
			
		||||
      score: this.score,
 | 
			
		||||
      hand: this.hand.map(tile => {
 | 
			
		||||
        const d = { 
 | 
			
		||||
          id: tile.id,
 | 
			
		||||
          pips: tile.pips,
 | 
			
		||||
          flipped: tile.revealed,
 | 
			
		||||
          playerId: tile.playerId,
 | 
			
		||||
        };
 | 
			
		||||
        if (showPips) {
 | 
			
		||||
          d.pips = tile.pips;
 | 
			
		||||
        }
 | 
			
		||||
        return d;
 | 
			
		||||
      }),
 | 
			
		||||
      teamedWith: this.teamedWith?.getState() ?? null,
 | 
			
		||||
      hand: this.hand.map(tile => tile.getState(showPips)),
 | 
			
		||||
      teamedWith: this.teamedWith?.getState(showPips) ?? null,
 | 
			
		||||
      ready: this.ready,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,7 @@
 | 
			
		||||
import { PlayerInteractionInterface } from "../../PlayerInteractionInterface";
 | 
			
		||||
import { Board } from "../Board";
 | 
			
		||||
import { GameState } from "../../dto/GameState";
 | 
			
		||||
import { PlayerMove } from "../PlayerMove";
 | 
			
		||||
import { Tile } from "../Tile";
 | 
			
		||||
import { MatchSessionState } from "../../dto/MatchSessionState";
 | 
			
		||||
import { PlayerDto } from "../../dto/PlayerDto";
 | 
			
		||||
 | 
			
		||||
export interface PlayerInterface {
 | 
			
		||||
@@ -24,5 +22,5 @@ export interface PlayerInterface {
 | 
			
		||||
  sendEvent(event: string, data: any): Promise<void>;
 | 
			
		||||
  sendEventWithAck(event: string, data: any): Promise<any>;
 | 
			
		||||
 | 
			
		||||
  getState(): PlayerDto;
 | 
			
		||||
  getState(showPips?: boolean): PlayerDto;
 | 
			
		||||
}
 | 
			
		||||
@@ -22,7 +22,7 @@ export class ApiKeyController extends BaseController{
 | 
			
		||||
  async createApiKey(req: Request, res: Response) {
 | 
			
		||||
    try {
 | 
			
		||||
      const token: Token = this._createTokenObject(req);
 | 
			
		||||
      await this.apiTokenManager.addToken(token);
 | 
			
		||||
      await this.apiTokenManager.create(token);
 | 
			
		||||
      res.status(201).end();
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      this.handleError(res, error);
 | 
			
		||||
@@ -62,7 +62,7 @@ export class ApiKeyController extends BaseController{
 | 
			
		||||
  async createNamespaceApiKey(req: Request, res: Response) {
 | 
			
		||||
    try {
 | 
			
		||||
      const token = this._createTokenObject(req);
 | 
			
		||||
      await this.apiTokenManager.addToken(token);
 | 
			
		||||
      await this.apiTokenManager.create(token);
 | 
			
		||||
      res.status(201).end();
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      this.handleError(res, error);
 | 
			
		||||
@@ -81,9 +81,9 @@ export class ApiKeyController extends BaseController{
 | 
			
		||||
      type 
 | 
			
		||||
    };
 | 
			
		||||
    if (type === 'namespace') {
 | 
			
		||||
      newToken.namespaceId = toObjectId(namespaceId);
 | 
			
		||||
      newToken.namespaceId = namespaceId;
 | 
			
		||||
    } else if (type === 'user') {
 | 
			
		||||
      newToken.userId = toObjectId(userId);
 | 
			
		||||
      newToken.userId = userId;
 | 
			
		||||
    }
 | 
			
		||||
    return newToken;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -142,7 +142,7 @@ export class UserController extends BaseController {
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      this.temporalTokenManager.deleteAllByUserAndType(userId.toString(), TemporalTokenMongoManager.Types.PASSWORD_RECOVERY);
 | 
			
		||||
      this.temporalTokenManager.addToken(temporalToken);
 | 
			
		||||
      this.temporalTokenManager.create(temporalToken);
 | 
			
		||||
      await this.mailService.sendRecoveryPasswordEmail(firstname, lastname, email, pin);
 | 
			
		||||
      res.status(200).end();
 | 
			
		||||
    } catch (error: any) {
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@ export function matchSessionAdapter(session: MatchSession) : DbMatchSession {
 | 
			
		||||
        numPlayers: session.numPlayers,
 | 
			
		||||
        scoreboard: Array.from(session.scoreboard.entries()).map(([player, score]) => ({ player, score })),
 | 
			
		||||
        matchWinner: session.matchWinner ? session.matchWinner.id : null,
 | 
			
		||||
        state: session.state
 | 
			
		||||
        status: session.status
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,14 +1,12 @@
 | 
			
		||||
import { ObjectId } from "mongodb";
 | 
			
		||||
 | 
			
		||||
export interface Entity {
 | 
			
		||||
  createdAt?: number | null;
 | 
			
		||||
  modifiedAt?: number | null;
 | 
			
		||||
  createdBy?: ObjectId | null;
 | 
			
		||||
  modifiedBy?: ObjectId | null;
 | 
			
		||||
  createdAt?: number;
 | 
			
		||||
  modifiedAt?: number;
 | 
			
		||||
  createdBy?: string;
 | 
			
		||||
  modifiedBy?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface EntityMongo extends Entity {
 | 
			
		||||
  _id?: ObjectId;
 | 
			
		||||
  _id?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface Score {
 | 
			
		||||
@@ -21,14 +19,14 @@ export interface Namespace extends EntityMongo {
 | 
			
		||||
  description?: string;
 | 
			
		||||
  default: boolean;
 | 
			
		||||
  type: string | null;
 | 
			
		||||
  ownerId?: ObjectId;
 | 
			
		||||
  ownerId?: string;
 | 
			
		||||
  users?: any[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface User extends EntityMongo {
 | 
			
		||||
  id: string,
 | 
			
		||||
  username: string;
 | 
			
		||||
  namespaceId: ObjectId;
 | 
			
		||||
  namespaceId: string;
 | 
			
		||||
  hash?: string;
 | 
			
		||||
  roles: string[];
 | 
			
		||||
  firstname?: string;
 | 
			
		||||
@@ -55,13 +53,13 @@ export interface DbMatchSession extends EntityMongo {
 | 
			
		||||
  numPlayers: number;
 | 
			
		||||
  scoreboard: Score[];
 | 
			
		||||
  matchWinner: string | null;
 | 
			
		||||
  state: string;
 | 
			
		||||
  status: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface DbUser extends EntityMongo {
 | 
			
		||||
  id: string,
 | 
			
		||||
  username: string;
 | 
			
		||||
  namespaceId: ObjectId;
 | 
			
		||||
  namespaceId: string;
 | 
			
		||||
  hash?: string;
 | 
			
		||||
  roles: string[];
 | 
			
		||||
  firstname?: string;
 | 
			
		||||
@@ -77,16 +75,16 @@ export interface DbNamespace extends EntityMongo {
 | 
			
		||||
  description?: string;
 | 
			
		||||
  default: boolean;
 | 
			
		||||
  type: string | null;
 | 
			
		||||
  ownerId?: ObjectId;
 | 
			
		||||
  ownerId?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface Token extends EntityMongo {
 | 
			
		||||
  token: string;
 | 
			
		||||
  userId?: ObjectId;
 | 
			
		||||
  userId?: string;
 | 
			
		||||
  roles?: string[];
 | 
			
		||||
  expiresAt?: number | null;
 | 
			
		||||
  type: string;
 | 
			
		||||
  namespaceId?: ObjectId
 | 
			
		||||
  namespaceId?: string
 | 
			
		||||
  description?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -100,3 +98,14 @@ export interface Role {
 | 
			
		||||
  permissions: string[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface DbListResponse{
 | 
			
		||||
  pagination?: {
 | 
			
		||||
    page: number;
 | 
			
		||||
    next?: number;
 | 
			
		||||
    size: number;
 | 
			
		||||
    total: number;
 | 
			
		||||
    totalPages: number;
 | 
			
		||||
  }
 | 
			
		||||
  sort?: any;
 | 
			
		||||
  data: EntityMongo[];
 | 
			
		||||
}
 | 
			
		||||
@@ -5,13 +5,6 @@ import { Token } from '../interfaces';
 | 
			
		||||
export class ApiTokenMongoManager  extends BaseMongoManager{
 | 
			
		||||
  collection = 'tokens';
 | 
			
		||||
 | 
			
		||||
  async addToken(token: Token) {
 | 
			
		||||
    return await mongoExecute(async ({ collection }) => {
 | 
			
		||||
      await collection?.insertOne(token);
 | 
			
		||||
      return token;
 | 
			
		||||
    }, { colName: this.collection });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async getTokens(userId: string): Promise<Token[]> {
 | 
			
		||||
    return await mongoExecute(async ({ collection }) => {
 | 
			
		||||
      return await collection?.find({ userId: this.toObjectId(userId) }).toArray();
 | 
			
		||||
 
 | 
			
		||||
@@ -11,19 +11,6 @@ export class NamespacesMongoManager extends BaseMongoManager{
 | 
			
		||||
    super();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async createNamespace(namespace: Namespace): Promise<string> {
 | 
			
		||||
    return await mongoExecute(async ({collection}) => {
 | 
			
		||||
      const now = new Date().getTime();
 | 
			
		||||
      delete namespace.users;
 | 
			
		||||
      const result = await collection?.insertOne({
 | 
			
		||||
        ...namespace,
 | 
			
		||||
        createdAt: now,
 | 
			
		||||
        modifiedAt: now
 | 
			
		||||
      });
 | 
			
		||||
      return result?.insertedId.toString() || '';
 | 
			
		||||
    }, {colName: this.collection})
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async updateNamespace(id: string, namespace: Namespace): Promise<number> {
 | 
			
		||||
    return await mongoExecute(async ({collection}) => {
 | 
			
		||||
      const now = new Date().getTime();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
import { ObjectId } from "mongodb";
 | 
			
		||||
import { mongoExecute } from "./mongoDBPool";
 | 
			
		||||
import { Entity, EntityMongo } from "../../interfaces";
 | 
			
		||||
import { DbListResponse, Entity, EntityMongo } from "../../interfaces";
 | 
			
		||||
import { LoggingService } from "../../../../common/LoggingService";
 | 
			
		||||
import toObjectId from "./mongoUtils";
 | 
			
		||||
 | 
			
		||||
@@ -9,12 +8,12 @@ export abstract class BaseMongoManager {
 | 
			
		||||
  protected abstract collection?: string;
 | 
			
		||||
  logger = new LoggingService().logger;
 | 
			
		||||
 | 
			
		||||
  async create(data: Entity): Promise<ObjectId | undefined> {
 | 
			
		||||
  async create(data: EntityMongo): Promise<string | undefined> {
 | 
			
		||||
    this.stampEntity(data);
 | 
			
		||||
    return mongoExecute(
 | 
			
		||||
      async ({ collection }) => {
 | 
			
		||||
        const result = await collection?.insertOne(data as any);
 | 
			
		||||
        return result?.insertedId;
 | 
			
		||||
        return result?.insertedId.toString() || undefined;
 | 
			
		||||
      },
 | 
			
		||||
      { colName: this.collection }
 | 
			
		||||
    );
 | 
			
		||||
@@ -58,7 +57,7 @@ export abstract class BaseMongoManager {
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async list(sortCriteria?: any, pagination?: {pageSize: number, page: number}): Promise<EntityMongo[]> {
 | 
			
		||||
  async list(sortCriteria?: any, pagination?: {pageSize: number, page: number}): Promise<DbListResponse> {
 | 
			
		||||
    return mongoExecute(
 | 
			
		||||
      async ({ collection }) => {
 | 
			
		||||
        const cursor = collection?.find();
 | 
			
		||||
@@ -68,13 +67,39 @@ export abstract class BaseMongoManager {
 | 
			
		||||
        if (pagination) {
 | 
			
		||||
          cursor?.skip(pagination.pageSize * (pagination.page - 1)).limit(pagination.pageSize);
 | 
			
		||||
        }
 | 
			
		||||
        return await cursor?.toArray();
 | 
			
		||||
        const cursorArray = (await cursor?.toArray()) || []
 | 
			
		||||
        const data = cursorArray.map((item: any) => {
 | 
			
		||||
          item._id = item._id.toString();
 | 
			
		||||
          return item;
 | 
			
		||||
        }) as EntityMongo[]
 | 
			
		||||
 | 
			
		||||
        const listResponse: DbListResponse = {
 | 
			
		||||
          data
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if (pagination) {
 | 
			
		||||
          const total = await collection?.countDocuments() || 0;  
 | 
			
		||||
          const totalPages = Math.ceil(total / pagination.pageSize);
 | 
			
		||||
          listResponse.pagination = {
 | 
			
		||||
            page: pagination.page,
 | 
			
		||||
            size: pagination.pageSize,
 | 
			
		||||
            total,
 | 
			
		||||
            totalPages,
 | 
			
		||||
            next: totalPages > pagination.page ? pagination.page + 1 : undefined,
 | 
			
		||||
          };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (sortCriteria) {
 | 
			
		||||
          listResponse.sort = sortCriteria;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return listResponse;
 | 
			
		||||
      },
 | 
			
		||||
      { colName: this.collection }
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async listByFilter(filter: any, sortCriteria?: any, pagination?: {pageSize: number, page: number}): Promise<EntityMongo[]> {
 | 
			
		||||
  async listByFilter(filter: any, sortCriteria?: any, pagination?: {pageSize: number, page: number}): Promise<DbListResponse> {
 | 
			
		||||
    return mongoExecute(
 | 
			
		||||
      async ({ collection }) => {
 | 
			
		||||
        const cursor = collection?.find(filter);
 | 
			
		||||
@@ -84,7 +109,35 @@ export abstract class BaseMongoManager {
 | 
			
		||||
        if (pagination) {
 | 
			
		||||
          cursor?.skip(pagination.pageSize * (pagination.page - 1)).limit(pagination.pageSize);
 | 
			
		||||
        }
 | 
			
		||||
        return await cursor?.toArray();
 | 
			
		||||
 | 
			
		||||
        const cursorArray = (await cursor?.toArray()) || []
 | 
			
		||||
        const data = cursorArray.map((item: any) => {
 | 
			
		||||
          item._id = item._id.toString();
 | 
			
		||||
          return item;
 | 
			
		||||
        }) as EntityMongo[]
 | 
			
		||||
 | 
			
		||||
        const listResponse: DbListResponse = {
 | 
			
		||||
          data
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if (pagination) {
 | 
			
		||||
          const total = await collection?.countDocuments(filter) || 0;  
 | 
			
		||||
          const totalPages = Math.ceil(total / pagination.pageSize);
 | 
			
		||||
          listResponse.pagination = {
 | 
			
		||||
            page: pagination.page,
 | 
			
		||||
            size: pagination.pageSize,
 | 
			
		||||
            total,
 | 
			
		||||
            totalPages,
 | 
			
		||||
            next: totalPages > pagination.page ? pagination.page + 1 : undefined,
 | 
			
		||||
          };
 | 
			
		||||
            
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (sortCriteria) {
 | 
			
		||||
          listResponse.sort = sortCriteria;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return listResponse;
 | 
			
		||||
      },
 | 
			
		||||
      { colName: this.collection }
 | 
			
		||||
    );
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,8 @@
 | 
			
		||||
import { ObjectId } from "mongodb";
 | 
			
		||||
 | 
			
		||||
export default function toObjectId(id: string) {
 | 
			
		||||
  return ObjectId.createFromHexString(id);
 | 
			
		||||
export default function toObjectId(oid: string | ObjectId): ObjectId {
 | 
			
		||||
  if (oid instanceof ObjectId) {
 | 
			
		||||
    return oid;
 | 
			
		||||
  }
 | 
			
		||||
  return ObjectId.createFromHexString(oid);
 | 
			
		||||
}
 | 
			
		||||
@@ -27,14 +27,18 @@ export class InteractionService extends ServiceBase{
 | 
			
		||||
    this.logger.trace(`Handling event: ${event}`);
 | 
			
		||||
    switch(event) {
 | 
			
		||||
      case 'client:player-move':
 | 
			
		||||
        this.onClientMoveResponse(eventData);
 | 
			
		||||
        this.playerMoveHuman(eventData);
 | 
			
		||||
        break;
 | 
			
		||||
      case 'client:set-client-ready-for-next-game':
 | 
			
		||||
      case 'client:set-client-ready':
 | 
			
		||||
        this.onClientReady(eventData);
 | 
			
		||||
        break;
 | 
			
		||||
      case EventActions.PLAYER_READY:
 | 
			
		||||
        this.onPlayerReady(eventData);
 | 
			
		||||
        break;
 | 
			
		||||
      case 'client:animation-ended':
 | 
			
		||||
        this.onClientsAnimationEnded(eventData);
 | 
			
		||||
        break;
 | 
			
		||||
      default:
 | 
			
		||||
        PubSub.publish(event, eventData);
 | 
			
		||||
        break;
 | 
			
		||||
@@ -60,7 +64,9 @@ export class InteractionService extends ServiceBase{
 | 
			
		||||
      for (let i = 0; i < missingHumans; i++) {
 | 
			
		||||
        session.addPlayerToSession(session.createPlayerAI(i));
 | 
			
		||||
      }
 | 
			
		||||
      this.notifyService.sendEventToPlayers('server:match-starting', session.players);
 | 
			
		||||
      this.notifyService.sendEventToPlayers('server:match-starting', session.players, { 
 | 
			
		||||
        sessionState: session.getState()
 | 
			
		||||
      });
 | 
			
		||||
      session.start();      
 | 
			
		||||
      return {
 | 
			
		||||
        status: 'ok'
 | 
			
		||||
@@ -68,15 +74,23 @@ export class InteractionService extends ServiceBase{
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public playerMove(data: any) {
 | 
			
		||||
    this.onClientMoveResponse(data);
 | 
			
		||||
  public playerMoveAI(data: any) {
 | 
			
		||||
    this.onClientMoveResponse(data, false);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private onClientMoveResponse(data: any): any {
 | 
			
		||||
  public playerMoveHuman(data: any) {
 | 
			
		||||
    this.onClientMoveResponse(data, true);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private onClientMoveResponse(data: any, notifyClientReady: boolean = false): any {
 | 
			
		||||
    this.logger.trace('Handling player move (onClientMoveResponse)');
 | 
			
		||||
    const { sessionId, move }: { sessionId: string, move: PlayerMove } = data;
 | 
			
		||||
    const session: MatchSession | undefined = this.sessionManager.getSession(sessionId);
 | 
			
		||||
    if (session !== undefined) {
 | 
			
		||||
      session.playerMove(move);
 | 
			
		||||
      if (notifyClientReady) {
 | 
			
		||||
        session.setCurrentGameClientReady(move.playerId);
 | 
			
		||||
      }
 | 
			
		||||
      session.playerMove(move);      
 | 
			
		||||
      return {
 | 
			
		||||
        status: 'ok'
 | 
			
		||||
      };
 | 
			
		||||
@@ -100,8 +114,19 @@ export class InteractionService extends ServiceBase{
 | 
			
		||||
    return {
 | 
			
		||||
      status: 'ok'
 | 
			
		||||
    }
 | 
			
		||||
  }  
 | 
			
		||||
 | 
			
		||||
  private onClientsAnimationEnded(data: any): any {
 | 
			
		||||
    const { sessionId, userId } = data;
 | 
			
		||||
    const session: MatchSession | undefined = this.sessionManager.getSession(sessionId);
 | 
			
		||||
    session?.setCurrentGameClientReady(userId);
 | 
			
		||||
    return {
 | 
			
		||||
      status: 'ok'
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  public updateSocketId(sessionId: string, userId: string, socketId: string): any {
 | 
			
		||||
    return this.sessionManager.updateSocketId(sessionId, userId, socketId);
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
import { Namespace, User } from "../db/interfaces";
 | 
			
		||||
import toObjectId from "../db/mongo/common/mongoUtils";
 | 
			
		||||
import { NamespacesMongoManager } from "../db/mongo/NamespacesMongoManager";
 | 
			
		||||
import { UsersService } from "./UsersService";
 | 
			
		||||
 | 
			
		||||
@@ -7,8 +8,12 @@ export class NamespacesService {
 | 
			
		||||
  usersService = new UsersService();
 | 
			
		||||
 | 
			
		||||
  async createNamespace(namespace: Namespace, user: User) {
 | 
			
		||||
    const insertedId = await this.namespacesManager.createNamespace({ ownerId: user._id, ...namespace, createdBy: user._id });
 | 
			
		||||
    await this._updateNamespaceUsers(namespace.users ?? [], insertedId);
 | 
			
		||||
    const userId = user._id
 | 
			
		||||
    if (userId === undefined) return undefined;
 | 
			
		||||
 | 
			
		||||
    const insertedId = await this.namespacesManager.create({ ownerId: userId, createdBy: userId, ...namespace });
 | 
			
		||||
    if (insertedId === undefined) return undefined;
 | 
			
		||||
    await this._updateNamespaceUsers(namespace.users ?? [], insertedId.toString());
 | 
			
		||||
    return insertedId;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ import { NetworkPlayer } from "../../game/entities/player/NetworkPlayer";
 | 
			
		||||
import { MatchSession } from "../../game/MatchSession";
 | 
			
		||||
import { PlayerNotificationService } from "./PlayerNotificationService";
 | 
			
		||||
import { matchSessionAdapter } from "../db/DbAdapter";
 | 
			
		||||
import { DbMatchSession, DbMatchSessionUpdate } from "../db/interfaces";
 | 
			
		||||
import { DbListResponse, DbMatchSession, DbMatchSessionUpdate } from "../db/interfaces";
 | 
			
		||||
import { MatchSessionMongoManager } from "../db/mongo/MatchSessionMongoManager";
 | 
			
		||||
import { SessionManager } from "../managers/SessionManager";
 | 
			
		||||
import { ServiceBase } from "./ServiceBase";
 | 
			
		||||
@@ -62,11 +62,11 @@ export class SessionService extends ServiceBase{
 | 
			
		||||
    return sessionId
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public async listJoinableSessions(): Promise<DbMatchSession[]> {
 | 
			
		||||
  public async listJoinableSessions(): Promise<DbListResponse> {
 | 
			
		||||
    return await this.dbManager.listByFilter(
 | 
			
		||||
      { state: 'created' }, 
 | 
			
		||||
      { status: 'created' }, 
 | 
			
		||||
      { createdAt: -1 }, 
 | 
			
		||||
      { page: 1, pageSize: 5 }) as DbMatchSession[];
 | 
			
		||||
      { page: 1, pageSize: 12 }) as DbListResponse;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public async getSession(sessionId: string): Promise<DbMatchSession | undefined> {
 | 
			
		||||
@@ -76,8 +76,8 @@ export class SessionService extends ServiceBase{
 | 
			
		||||
  public async deleteSession(sessionId: string): Promise<any> {
 | 
			
		||||
    this.sessionManager.deleteSession(sessionId);
 | 
			
		||||
    const session = {
 | 
			
		||||
      _id: toObjectId(sessionId),
 | 
			
		||||
      state: 'deleted'
 | 
			
		||||
      _id: sessionId,
 | 
			
		||||
      status: 'deleted'
 | 
			
		||||
    } as DbMatchSessionUpdate;
 | 
			
		||||
    return this.dbManager.update(session);
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -106,7 +106,7 @@ export class SocketIoService  extends ServiceBase{
 | 
			
		||||
        if (event.startsWith('client:') && args.length > 0) {
 | 
			
		||||
          logStr = `${logStr} (${args[0].event})`;
 | 
			
		||||
        }
 | 
			
		||||
        this.logger.debug(logStr);
 | 
			
		||||
        this.logger.trace(logStr);
 | 
			
		||||
      });
 | 
			
		||||
      
 | 
			
		||||
      this.pingClients()
 | 
			
		||||
 
 | 
			
		||||
@@ -69,7 +69,7 @@ export class UsersService extends ServiceBase {
 | 
			
		||||
 | 
			
		||||
    this.logger.info(`${password === undefined}`);
 | 
			
		||||
    if (_id !== undefined) {
 | 
			
		||||
      user._id = toObjectId(_id);
 | 
			
		||||
      user._id = _id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (password !== undefined && typeof password === 'string' && password.length > 0) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										34
									
								
								src/test.ts
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								src/test.ts
									
									
									
									
									
								
							@@ -1,7 +1,7 @@
 | 
			
		||||
import { PlayerAI } from "./game/entities/player/PlayerAI";
 | 
			
		||||
import { PlayerHuman } from "./game/entities/player/PlayerHuman";
 | 
			
		||||
import {LoggingService} from "./common/LoggingService";
 | 
			
		||||
import { GameSession } from "./game/GameSession";
 | 
			
		||||
import { MatchSession } from "./game/MatchSession";
 | 
			
		||||
 | 
			
		||||
console.log('process.arg :>> ', process.argv);
 | 
			
		||||
 | 
			
		||||
@@ -18,35 +18,35 @@ console.log('process.arg :>> ', process.argv);
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
async function playSolo(seed?: string) {
 | 
			
		||||
  const session = new GameSession(new PlayerHuman( "Jose"), "Test Game");
 | 
			
		||||
  const session = new MatchSession(new PlayerHuman( "Jose"), "Test Game");
 | 
			
		||||
  console.log(`Session (${session.id}) created by: ${session.creator.name}`);
 | 
			
		||||
  setTimeout(() => session.addPlayer(new PlayerAI("AI 2")), 1000);
 | 
			
		||||
  setTimeout(() => session.addPlayer(new PlayerAI("AI 3")), 2000);
 | 
			
		||||
  setTimeout(() => session.addPlayer(new PlayerAI("AI 4")), 3000);
 | 
			
		||||
  setTimeout(() => session.addPlayerToSession(new PlayerAI("AI 2")), 1000);
 | 
			
		||||
  setTimeout(() => session.addPlayerToSession(new PlayerAI("AI 3")), 2000);
 | 
			
		||||
  setTimeout(() => session.addPlayerToSession(new PlayerAI("AI 4")), 3000);
 | 
			
		||||
  session.start(seed);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function playHumans(seed?: string) {
 | 
			
		||||
  const session = new GameSession(new PlayerHuman("Jose"), "Test Game");
 | 
			
		||||
  session.addPlayer(new PlayerHuman("Pepe"));
 | 
			
		||||
  session.addPlayer(new PlayerHuman("Juan"));
 | 
			
		||||
  session.addPlayer(new PlayerHuman("Luis"));
 | 
			
		||||
  const session = new MatchSession(new PlayerHuman("Jose"), "Test Game");
 | 
			
		||||
  session.addPlayerToSession(new PlayerHuman("Pepe"));
 | 
			
		||||
  session.addPlayerToSession(new PlayerHuman("Juan"));
 | 
			
		||||
  session.addPlayerToSession(new PlayerHuman("Luis"));
 | 
			
		||||
  session.start(seed);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function playAIs(seed?: string) {
 | 
			
		||||
  const session = new GameSession(new PlayerAI("AI 1"), "Test Game");
 | 
			
		||||
  session.addPlayer(new PlayerAI("AI 2"));
 | 
			
		||||
  session.addPlayer(new PlayerAI("AI 3"));
 | 
			
		||||
  session.addPlayer(new PlayerAI("AI 4"));
 | 
			
		||||
  const session = new MatchSession(new PlayerAI("AI 1"), "Test Game");
 | 
			
		||||
  session.addPlayerToSession(new PlayerAI("AI 2"));
 | 
			
		||||
  session.addPlayerToSession(new PlayerAI("AI 3"));
 | 
			
		||||
  session.addPlayerToSession(new PlayerAI("AI 4"));
 | 
			
		||||
  session.start(seed);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function playTeams(seed?: string) {
 | 
			
		||||
  const session = new GameSession(new PlayerHuman("Jose"), "Test Game");
 | 
			
		||||
  session.addPlayer(new PlayerAI("AI 1"));
 | 
			
		||||
  session.addPlayer(new PlayerHuman("Juan"));
 | 
			
		||||
  session.addPlayer(new PlayerAI("AI 2"));
 | 
			
		||||
  const session = new MatchSession(new PlayerHuman("Jose"), "Test Game");
 | 
			
		||||
  session.addPlayerToSession(new PlayerAI("AI 1"));
 | 
			
		||||
  session.addPlayerToSession(new PlayerHuman("Juan"));
 | 
			
		||||
  session.addPlayerToSession(new PlayerAI("AI 2"));
 | 
			
		||||
  session.start(seed);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user