fel kesz game backend

This commit is contained in:
2025-09-15 19:00:35 +02:00
parent 7963f28021
commit 3af8de2797
267 changed files with 15655 additions and 347 deletions
@@ -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
*/
}