fel kesz game backend
This commit is contained in:
@@ -0,0 +1,303 @@
|
||||
import { StartGameCommand } from './commands/StartGameCommand';
|
||||
import { StartGameCommandHandler } from './commands/StartGameCommandHandler';
|
||||
import { JoinGameCommand } from './commands/JoinGameCommand';
|
||||
import { JoinGameCommandHandler } from './commands/JoinGameCommandHandler';
|
||||
import { StartGamePlayCommand } from './commands/StartGamePlayCommand';
|
||||
import { StartGamePlayCommandHandler, GameStartResult } from './commands/StartGamePlayCommandHandler';
|
||||
import { GameAggregate, LoginType } from '../../Domain/Game/GameAggregate';
|
||||
import { logOther, logError } from '../Services/Logger';
|
||||
|
||||
export class GameService {
|
||||
private startGameHandler: StartGameCommandHandler;
|
||||
private joinGameHandler: JoinGameCommandHandler;
|
||||
private startGamePlayHandler: StartGamePlayCommandHandler;
|
||||
|
||||
constructor() {
|
||||
this.startGameHandler = new StartGameCommandHandler();
|
||||
this.joinGameHandler = new JoinGameCommandHandler();
|
||||
this.startGamePlayHandler = new StartGamePlayCommandHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a new game with the provided deck IDs
|
||||
* @param deckids Array of deck IDs (should contain 3 types: LUCK, JOKER, QUESTION)
|
||||
* @param maxplayers Maximum number of players allowed in the game
|
||||
* @param logintype How players can join the game (PUBLIC, PRIVATE, ORGANIZATION)
|
||||
* @param userid Optional ID of the user creating the game
|
||||
* @returns Promise<GameAggregate> The created game
|
||||
*/
|
||||
async startGame(
|
||||
deckids: string[],
|
||||
maxplayers: number,
|
||||
logintype: LoginType,
|
||||
userid?: string,
|
||||
orgid?: string | null
|
||||
): Promise<GameAggregate> {
|
||||
const startTime = performance.now();
|
||||
|
||||
try {
|
||||
logOther('GameService.startGame called', {
|
||||
deckCount: deckids.length,
|
||||
maxplayers,
|
||||
logintype,
|
||||
userid,
|
||||
orgid
|
||||
});
|
||||
|
||||
// Validate input parameters
|
||||
this.validateStartGameInput(deckids, maxplayers, logintype);
|
||||
|
||||
// Create and execute the command
|
||||
const command: StartGameCommand = {
|
||||
deckids,
|
||||
maxplayers,
|
||||
logintype,
|
||||
userid,
|
||||
orgid
|
||||
};
|
||||
|
||||
const game = await this.startGameHandler.handle(command);
|
||||
|
||||
const endTime = performance.now();
|
||||
logOther('Game started successfully', {
|
||||
gameId: game.id,
|
||||
gameCode: game.gamecode,
|
||||
deckCount: game.gamedecks.length,
|
||||
totalCards: game.gamedecks.reduce((sum, deck) => sum + deck.cards.length, 0),
|
||||
executionTime: Math.round(endTime - startTime)
|
||||
});
|
||||
|
||||
return game;
|
||||
|
||||
} catch (error) {
|
||||
const endTime = performance.now();
|
||||
logError('GameService.startGame failed', error instanceof Error ? error : new Error(String(error)));
|
||||
logOther('Game start failed', {
|
||||
executionTime: Math.round(endTime - startTime),
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Join an existing game using game code
|
||||
* @param gameCode 6-character game code
|
||||
* @param playerId ID of the player joining (optional for public games)
|
||||
* @param playerName Display name for the player
|
||||
* @param orgId Organization ID (for organization games)
|
||||
* @param loginType Type of join being attempted
|
||||
* @returns Promise<GameAggregate> The updated game with new player
|
||||
*/
|
||||
async joinGame(
|
||||
gameCode: string,
|
||||
playerId?: string,
|
||||
playerName?: string,
|
||||
orgId?: string | null,
|
||||
loginType?: LoginType
|
||||
): Promise<GameAggregate> {
|
||||
const startTime = performance.now();
|
||||
|
||||
try {
|
||||
logOther('GameService.joinGame called', {
|
||||
gameCode,
|
||||
playerId: playerId || 'anonymous',
|
||||
playerName,
|
||||
orgId,
|
||||
loginType
|
||||
});
|
||||
|
||||
// Validate input parameters
|
||||
this.validateJoinGameInput(gameCode, playerId, loginType);
|
||||
|
||||
// Create and execute the command
|
||||
const command: JoinGameCommand = {
|
||||
gameCode,
|
||||
playerId,
|
||||
playerName,
|
||||
orgId,
|
||||
loginType: loginType || LoginType.PUBLIC
|
||||
};
|
||||
|
||||
const game = await this.joinGameHandler.handle(command);
|
||||
|
||||
const endTime = performance.now();
|
||||
logOther('Player joined game successfully', {
|
||||
gameId: game.id,
|
||||
gameCode: game.gamecode,
|
||||
playerId,
|
||||
playerCount: game.players.length,
|
||||
maxPlayers: game.maxplayers,
|
||||
executionTime: Math.round(endTime - startTime)
|
||||
});
|
||||
|
||||
return game;
|
||||
|
||||
} catch (error) {
|
||||
const endTime = performance.now();
|
||||
logError('GameService.joinGame failed', error instanceof Error ? error : new Error(String(error)));
|
||||
logOther('Game join failed', {
|
||||
gameCode,
|
||||
playerId,
|
||||
executionTime: Math.round(endTime - startTime),
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start an existing game (move from WAITING to ACTIVE)
|
||||
* Initializes all player positions to 0 and assigns random turn order
|
||||
* @param gameId Game ID to start
|
||||
* @param userId User ID of the game master (optional for public games)
|
||||
* @returns Promise<GameAggregate> The updated game
|
||||
*/
|
||||
async startGamePlay(
|
||||
gameId: string,
|
||||
userId?: string
|
||||
): Promise<GameStartResult> {
|
||||
const startTime = performance.now();
|
||||
|
||||
try {
|
||||
logOther('GameService.startGamePlay called', {
|
||||
gameId,
|
||||
userId: userId || 'system'
|
||||
});
|
||||
|
||||
// Validate input parameters
|
||||
this.validateStartGamePlayInput(gameId);
|
||||
|
||||
// Create and execute the command
|
||||
const command: StartGamePlayCommand = {
|
||||
gameId,
|
||||
userId
|
||||
};
|
||||
|
||||
const result = await this.startGamePlayHandler.handle(command);
|
||||
|
||||
const endTime = performance.now();
|
||||
logOther('Game play started successfully', {
|
||||
gameId: result.game.id,
|
||||
gameCode: result.game.gamecode,
|
||||
playerCount: result.game.players.length,
|
||||
gameState: result.game.state,
|
||||
executionTime: Math.round(endTime - startTime)
|
||||
});
|
||||
|
||||
return result;
|
||||
|
||||
} catch (error) {
|
||||
const endTime = performance.now();
|
||||
logError('GameService.startGamePlay failed', error instanceof Error ? error : new Error(String(error)));
|
||||
logOther('Game play start failed', {
|
||||
gameId,
|
||||
userId,
|
||||
executionTime: Math.round(endTime - startTime),
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
private validateStartGamePlayInput(gameId: string): void {
|
||||
// Validate game ID
|
||||
if (!gameId || typeof gameId !== 'string') {
|
||||
throw new Error('Game ID is required and must be a string');
|
||||
}
|
||||
|
||||
logOther('Start game play input validation passed', {
|
||||
gameId
|
||||
});
|
||||
}
|
||||
|
||||
private validateJoinGameInput(gameCode: string, playerId?: string, loginType?: LoginType): void {
|
||||
// Validate game code
|
||||
if (!gameCode || typeof gameCode !== 'string') {
|
||||
throw new Error('Game code is required and must be a string');
|
||||
}
|
||||
|
||||
if (gameCode.length !== 6) {
|
||||
throw new Error('Game code must be exactly 6 characters long');
|
||||
}
|
||||
|
||||
// Validate login type specific requirements
|
||||
if (loginType === LoginType.PRIVATE || loginType === LoginType.ORGANIZATION) {
|
||||
if (!playerId || typeof playerId !== 'string') {
|
||||
throw new Error(`Player ID is required for ${LoginType[loginType]} games`);
|
||||
}
|
||||
}
|
||||
|
||||
logOther('Join game input validation passed', {
|
||||
gameCode,
|
||||
playerId: playerId || 'anonymous',
|
||||
loginType
|
||||
});
|
||||
}
|
||||
|
||||
private validateStartGameInput(deckids: string[], maxplayers: number, logintype: LoginType): void {
|
||||
// Validate deck IDs
|
||||
if (!deckids || deckids.length === 0) {
|
||||
throw new Error('At least one deck ID must be provided');
|
||||
}
|
||||
|
||||
if (deckids.length < 3) {
|
||||
throw new Error('At least 3 decks are required to start a game (one for each type: LUCK, JOKER, QUESTION)');
|
||||
}
|
||||
|
||||
// Validate max players
|
||||
if (!maxplayers || maxplayers < 2) {
|
||||
throw new Error('Maximum players must be at least 2');
|
||||
}
|
||||
|
||||
if (maxplayers > 8) {
|
||||
throw new Error('Maximum players cannot exceed 8');
|
||||
}
|
||||
|
||||
// Validate login type
|
||||
if (logintype < 0 || logintype > 2) {
|
||||
throw new Error('Invalid login type. Must be PUBLIC (0), PRIVATE (1), or ORGANIZATION (2)');
|
||||
}
|
||||
|
||||
// Check for duplicate deck IDs
|
||||
const uniqueIds = new Set(deckids);
|
||||
if (uniqueIds.size !== deckids.length) {
|
||||
throw new Error('Duplicate deck IDs are not allowed');
|
||||
}
|
||||
|
||||
logOther('Start game input validation passed', {
|
||||
deckCount: deckids.length,
|
||||
maxplayers,
|
||||
logintype
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Game flow explanation (to be implemented later):
|
||||
*
|
||||
* 1. START GAME (implemented above):
|
||||
* - Input: deckids, maxplayers, logintype, gamecode
|
||||
* - Process: Fetch decks, validate types, shuffle cards, create game
|
||||
* - Output: Game with shuffled deck objects
|
||||
*
|
||||
* 2. JOIN GAME (to be implemented):
|
||||
* - Input: gamecode, playerid
|
||||
* - Process: Find game, validate capacity, add player
|
||||
* - Output: Updated game with new player
|
||||
*
|
||||
* 3. GAME ROUNDS (to be implemented):
|
||||
* - Input: gameid, current player
|
||||
* - Process: Manage turn order, track game state
|
||||
* - Output: Current player information
|
||||
*
|
||||
* 4. PICK CARD (to be implemented):
|
||||
* - Input: gameid, playerid, deck type
|
||||
* - Process: Draw card from specific deck, apply consequence
|
||||
* - Output: Card details and consequence effects
|
||||
*
|
||||
* 5. END GAME (to be implemented):
|
||||
* - Input: gameid, winner
|
||||
* - Process: Set game as finished, record winner
|
||||
* - Output: Final game state
|
||||
*/
|
||||
}
|
||||
Reference in New Issue
Block a user