final changes

This commit is contained in:
2025-09-22 11:14:32 +02:00
parent cf157643d7
commit bf9ae5f01f
509 changed files with 920 additions and 64152 deletions
@@ -13,8 +13,11 @@ import {
GameActionData,
PlayerPosition,
GameStateUpdateData,
FieldEffectRequest
FieldEffectRequest,
JoinGameData,
LeaveGameData
} from './Interfaces/GameInterfaces';
import { json } from 'stream/consumers';
interface AuthenticatedSocket extends Socket {
userId?: string;
@@ -23,14 +26,6 @@ interface AuthenticatedSocket extends Socket {
isAuthenticated?: boolean;
}
interface JoinGameData {
gameToken: string; // Required game session token
}
interface LeaveGameData {
gameCode: string;
}
interface DiceRollData {
gameCode: string;
diceValue: number; // Value from frontend (1-6)
@@ -91,7 +86,7 @@ export class GameWebSocketService {
private setupGameEventHandlers(socket: AuthenticatedSocket): void {
// Join game room
socket.on('game:join', async (data: JoinGameData) => {
socket.on('game:join', async (data: any) => {
await this.handleJoinGame(socket, data);
});
@@ -141,11 +136,14 @@ export class GameWebSocketService {
});
}
private async handleJoinGame(socket: AuthenticatedSocket, data: JoinGameData): Promise<void> {
private async handleJoinGame(socket: AuthenticatedSocket, data: any): Promise<void> {
try {
const { gameToken } = data;
// Simple data extraction - let Socket.IO handle the parsing
const jsdata = JSON.parse(data);
const gameToken = jsdata?.gameToken;
if (!gameToken) {
logError('Game join failed: No game token provided');
socket.emit('game:error', { message: 'Game token is required' });
return;
}
@@ -153,6 +151,7 @@ export class GameWebSocketService {
// Verify the game token
const gameTokenPayload = this.gameTokenService.verifyGameToken(gameToken);
if (!gameTokenPayload) {
logError('Game join failed: Invalid game token');
socket.emit('game:error', { message: 'Invalid or expired game token' });
return;
}
@@ -162,10 +161,19 @@ export class GameWebSocketService {
// Validate game still exists
const game = await this.gameRepository.findByGameCode(gameCode);
if (!game || game.id !== gameId) {
logError(`Game join failed: Game not found - Code: ${gameCode}`);
socket.emit('game:error', { message: 'Game not found or token invalid' });
return;
}
// Check if player name is already in use by checking connected players
const connectedPlayers = await this.getConnectedPlayers(gameCode);
if (connectedPlayers.includes(playerName)) {
logOther(`Game join failed: Player name "${playerName}" already in use in game ${gameCode}`);
socket.emit('game:error', { message: `Player name "${playerName}" is already in use in this game` });
return;
}
// Set socket properties from game token
socket.gameCode = gameCode;
socket.playerName = playerName;
@@ -185,8 +193,6 @@ export class GameWebSocketService {
// Add to pending players list and notify gamemaster
await this.addToPendingPlayers(gameCode, playerName);
logOther(`Player ${playerName} requesting approval to join private game: ${gameRoomName}`);
// Send pending status to the requesting player
socket.emit('game:pending-approval', {
gameCode,
@@ -210,7 +216,6 @@ export class GameWebSocketService {
await socket.join(gameRoomName);
await socket.join(playerRoomName);
logOther(`Player ${playerName} joined game room: ${gameRoomName} (${isAuthenticated ? 'authenticated' : 'public'}) ${isGamemaster ? '[GAMEMASTER]' : ''}`);
// Send success response to the joining player
socket.emit('game:joined', {
@@ -222,6 +227,7 @@ export class GameWebSocketService {
timestamp: new Date().toISOString()
});
// Notify other players in the game (broadcast)
socket.to(gameRoomName).emit('game:player-joined', {
playerName: playerName,
@@ -230,40 +236,54 @@ export class GameWebSocketService {
timestamp: new Date().toISOString()
});
// Send current game state to the joining player
const gameState = await this.getGameState(gameCode);
socket.emit('game:state', gameState);
// Update Redis with active player connection
await this.updatePlayerConnection(gameCode, playerName, true);
} catch (error) {
logError('Error joining game', error as Error);
socket.emit('game:error', { message: 'Failed to join game' });
socket.emit('game:error', {
message: 'Failed to join game',
error: error instanceof Error ? error.message : 'Unknown error'
});
}
}
private async handleLeaveGame(socket: AuthenticatedSocket, data: LeaveGameData): Promise<void> {
try {
const { gameCode } = data;
const { gameCode } = JSON.parse(data as any);
const playerName = socket.playerName;
// Validate we have the required data
if (!playerName) {
logError('Cannot leave game: socket has no playerName');
socket.emit('game:error', { message: 'Player has no name' });
return;
}
const gameRoomName = `game_${gameCode}`;
const playerRoomName = `game_${gameCode}:${socket.playerName}`;
const playerRoomName = `game_${gameCode}:${playerName}`;
// Leave both rooms
await socket.leave(gameRoomName);
await socket.leave(playerRoomName);
logOther(`Player ${socket.playerName} left game room: ${gameRoomName}`);
logOther(`Player ${playerName} left game room: ${gameRoomName}`);
// Notify other players
socket.to(gameRoomName).emit('game:player-left', {
playerName: socket.playerName,
playerName: playerName,
timestamp: new Date().toISOString()
});
// Update Redis
await this.updatePlayerConnection(gameCode, socket.playerName!, false);
// Update Redis before clearing socket properties
await this.updatePlayerConnection(gameCode, playerName, false);
// Clear socket properties
socket.gameCode = undefined;
socket.playerName = undefined;
@@ -275,7 +295,7 @@ export class GameWebSocketService {
private async handleGameAction(socket: AuthenticatedSocket, data: GameActionData): Promise<void> {
try {
const { gameCode, action, data: actionData } = data;
const { gameCode, action, data: actionData } = JSON.parse(data as any);
if (!socket.gameCode || socket.gameCode !== gameCode) {
socket.emit('game:error', { message: 'You must be in the game to perform actions' });
@@ -317,7 +337,7 @@ export class GameWebSocketService {
private async handleGameChat(socket: AuthenticatedSocket, data: GameChatData): Promise<void> {
try {
const { gameCode, message } = data;
const { gameCode, message } = JSON.parse(data as any);
if (!socket.gameCode || socket.gameCode !== gameCode) {
socket.emit('game:error', { message: 'You must be in the game to chat' });
@@ -343,7 +363,7 @@ export class GameWebSocketService {
private async handlePlayerReady(socket: AuthenticatedSocket, data: { gameCode: string; ready: boolean }): Promise<void> {
try {
const { gameCode, ready } = data;
const { gameCode, ready } = JSON.parse(data as any);
const gameRoomName = `game_${gameCode}`;
// Update player ready status in Redis
@@ -373,7 +393,7 @@ export class GameWebSocketService {
private async handleApprovePlayer(socket: AuthenticatedSocket, data: { gameCode: string; playerName: string }): Promise<void> {
try {
const { gameCode, playerName } = data;
const { gameCode, playerName } = JSON.parse(data as any);
// Verify that the requesting socket is the gamemaster
const game = await this.gameRepository.findByGameCode(gameCode);
@@ -434,7 +454,7 @@ export class GameWebSocketService {
private async handleRejectPlayer(socket: AuthenticatedSocket, data: { gameCode: string; playerName: string; reason?: string }): Promise<void> {
try {
const { gameCode, playerName, reason } = data;
const { gameCode, playerName, reason } = JSON.parse(data as any);
// Verify that the requesting socket is the gamemaster
const game = await this.gameRepository.findByGameCode(gameCode);
@@ -482,7 +502,7 @@ export class GameWebSocketService {
private async handleJoinApproved(socket: AuthenticatedSocket, data: JoinGameData): Promise<void> {
try {
const { gameToken } = data;
const { gameToken } = JSON.parse(data as any);
if (!gameToken) {
socket.emit('game:error', { message: 'Game token is required' });
@@ -560,7 +580,7 @@ export class GameWebSocketService {
private async handleDiceRoll(socket: AuthenticatedSocket, data: DiceRollData): Promise<void> {
try {
const { gameCode, diceValue } = data;
const { gameCode, diceValue } = JSON.parse(data as any);
// Validate input
if (!gameCode || !socket.gameCode || socket.gameCode !== gameCode) {
@@ -772,14 +792,6 @@ export class GameWebSocketService {
// Remove from pending players if they were pending
await this.redisService.setRemove(`game_pending:${gameCode}`, playerName);
// If we have player ID, also clean up ID-based tracking
if (playerId) {
const game = await this.gameRepository.findByGameCode(gameCode);
if (game?.id) {
await this.redisService.setRemove(`active_players:${game.id}`, playerId);
}
}
logOther(`Cleaned up player data for ${playerName} in game ${gameCode}`);
} catch (error) {
@@ -1276,7 +1288,6 @@ export class GameWebSocketService {
if (gameId) {
const gameIdKeys = [
`game:${gameId}`, // Main game data
`active_players:${gameId}`, // Active players set
`game_turns:${gameId}` // Turn data by ID
];