Donat nezd majd át mert a backenden is lett valtozas nem tudom pontosan hogy kell e vagy sem Köszi

This commit is contained in:
2025-11-14 15:21:59 +01:00
parent a7ce891098
commit 0f85356154
10 changed files with 2007 additions and 71 deletions
@@ -5,7 +5,7 @@ import { API_CONFIG } from '../api/userApi';
const isDev = import.meta.env.DEV;
const log = (...args) => isDev && console.log(...args);
const warn = (...args) => isDev && console.warn(...args);
const error = (...args) => console.error(...args);
const logError = (...args) => console.error(...args);
/**
* Optimized WebSocket hook for game connection
@@ -20,29 +20,53 @@ export const useGameWebSocket = (gameToken) => {
const [error, setError] = useState(null);
const [isGamemaster, setIsGamemaster] = useState(false);
const [gameStarted, setGameStarted] = useState(false);
const [pendingPlayers, setPendingPlayers] = useState([]); // Players waiting for approval
const [approvalStatus, setApprovalStatus] = useState(null); // 'pending' | 'approved' | 'denied' | null
const eventListenersRef = useRef(new Map());
// Memoized derived values - no extra state needed
const players = useMemo(() => {
// Backend sends different player fields depending on game state
// connectedPlayers: array of player names (strings) who are connected via WebSocket
// players: full player objects with game data (positions, etc.)
// players: array of player IDs (UUIDs) - NOT USEFUL for display
// currentPlayers: full player objects with game data (positions, etc.)
const connectedPlayers = gameState?.connectedPlayers || [];
const gamePlayers = gameState?.players || [];
const currentPlayers = gameState?.currentPlayers || [];
// If we have full player objects, use those
if (currentPlayers.length > 0) return currentPlayers;
if (gamePlayers.length > 0) return gamePlayers;
// Debug: log what we received
if (import.meta.env.DEV) {
console.log('🎮 Computing players list:');
console.log(' - connectedPlayers:', connectedPlayers);
console.log(' - currentPlayers:', currentPlayers);
}
// Otherwise, map connected player names to basic player objects
return connectedPlayers.map((name, index) => ({
id: `player-${index}`,
name: typeof name === 'string' ? name : name.playerName || `Player ${index + 1}`,
isOnline: true,
isReady: gameState?.readyPlayers?.includes(name) || false,
}));
}, [gameState?.connectedPlayers, gameState?.players, gameState?.currentPlayers, gameState?.readyPlayers]);
// If we have full player objects with positions, use those (during game)
if (currentPlayers.length > 0) {
console.log('✅ Using currentPlayers');
return currentPlayers;
}
// Otherwise, use connectedPlayers (player names from Redis)
if (connectedPlayers.length > 0) {
console.log('✅ Mapping connectedPlayers to player objects');
return connectedPlayers.map((nameOrObj, index) => {
// Handle both string names and objects
const playerName = typeof nameOrObj === 'string'
? nameOrObj
: (nameOrObj.playerName || nameOrObj.name || `Player ${index + 1}`);
return {
id: `player-${index}`,
name: playerName,
isOnline: true,
isReady: gameState?.readyPlayers?.includes(playerName) || false,
};
});
}
console.log('⚠️ No players found');
return [];
}, [gameState?.connectedPlayers, gameState?.currentPlayers, gameState?.readyPlayers]);
const currentTurn = useMemo(() => gameState?.currentPlayer || null, [gameState?.currentPlayer]);
// Connect to game WebSocket - only once per token
@@ -83,44 +107,92 @@ export const useGameWebSocket = (gameToken) => {
// Game state handlers - batch updates
const handleGameState = (state) => {
log('📊 Game state:', state);
log('📊 Game state received:', state);
log(' - connectedPlayers:', state?.connectedPlayers);
log(' - players:', state?.players);
log(' - currentPlayers:', state?.currentPlayers);
log(' - isGamemaster in state:', state?.isGamemaster);
// EXTRA DEBUG: Show full state structure
if (import.meta.env.DEV) {
console.log('🔍 FULL STATE OBJECT:', JSON.stringify(state, null, 2));
}
// If state contains isGamemaster flag, update it
if (state?.isGamemaster !== undefined) {
log('✅ Setting isGamemaster from state:', state.isGamemaster);
setIsGamemaster(state.isGamemaster);
}
setGameState(state);
};
const handleGameJoined = (data) => {
log('✅ Joined game:', data);
// EXTRA DEBUG: Show full joined data
if (import.meta.env.DEV) {
console.log('🔍 FULL JOINED DATA:', JSON.stringify(data, null, 2));
}
// Store if this user is the gamemaster
if (data.isGamemaster !== undefined) {
log('✅ Setting isGamemaster from joined event:', data.isGamemaster);
setIsGamemaster(data.isGamemaster);
} else {
log('⚠️ No isGamemaster flag in joined event');
}
// Backend will send game:state next
};
const handlePlayerJoined = (data) => {
log('👤 Player joined:', data.playerName);
// EXTRA DEBUG
if (import.meta.env.DEV) {
console.log('🔍 PLAYER JOINED EVENT:', JSON.stringify(data, null, 2));
}
// Update game state to add the new player to connectedPlayers
setGameState(prev => {
if (!prev) return prev;
if (!prev) {
log('⚠️ No previous game state, cannot add player');
return prev;
}
const currentConnected = prev.connectedPlayers || [];
// Only add if not already in the list
if (!currentConnected.includes(data.playerName)) {
log('✅ Adding player to connectedPlayers:', data.playerName);
log(' - Current list:', currentConnected);
log(' - New list:', [...currentConnected, data.playerName]);
return {
...prev,
connectedPlayers: [...currentConnected, data.playerName]
};
}
log('⚠️ Player already in connectedPlayers:', data.playerName);
return prev;
});
};
const handleGameStarted = (data) => {
log('🎮 Game started:', data);
// Batch state updates
if (data.boardData) setBoardData(data.boardData);
if (data.gameState) setGameState(data.gameState);
// EXTRA DEBUG
if (import.meta.env.DEV) {
console.log('🔍 GAME STARTED EVENT:', JSON.stringify(data, null, 2));
}
// Signal that game has started
setGameStarted(true);
// Request updated game state from server (includes boardData and currentPlayers)
const socket = socketRef.current;
if (socket && socket.connected) {
log('📡 Requesting updated game state after game start');
// The server will send game:state event with full data
}
};
const handlePlayerMoved = (moveData) => {
@@ -145,10 +217,68 @@ export const useGameWebSocket = (gameToken) => {
};
const handleError = (err) => {
error('❌ Game error:', err);
logError('❌ Game error:', err);
setError(err.message);
};
// Approval system handlers (PRIVATE games only)
const handlePendingApproval = (data) => {
log('⏳ Pending gamemaster approval:', data);
setApprovalStatus('pending');
setError('Waiting for gamemaster approval...');
};
const handlePlayerRequestingJoin = (data) => {
log('🔔 Player requesting to join:', data.playerName);
// Add to pending players list (for gamemaster)
setPendingPlayers(prev => {
if (prev.some(p => p.playerName === data.playerName)) return prev;
return [...prev, {
playerName: data.playerName,
isAuthenticated: data.isAuthenticated,
timestamp: data.timestamp
}];
});
};
const handleApprovalGranted = (data) => {
log('✅ Join request approved:', data);
setApprovalStatus('approved');
setError(null);
// Player should now join the game rooms
const socket = socketRef.current;
if (socket && data.gameRoomName) {
// Emit join-approved to notify backend
socket.emit('game:join-approved', { gameToken });
}
};
const handleApprovalDenied = (data) => {
error('❌ Join request denied:', data.reason || data.message);
setApprovalStatus('denied');
setError(data.reason || 'Your request to join was denied');
};
const handlePlayerApproved = (data) => {
log('✅ Player approved by gamemaster:', data.playerName);
// Remove from pending players list
setPendingPlayers(prev => prev.filter(p => p.playerName !== data.playerName));
// Add to connected players
setGameState(prev => {
if (!prev) return prev;
const currentConnected = prev.connectedPlayers || [];
if (!currentConnected.includes(data.playerName)) {
return {
...prev,
connectedPlayers: [...currentConnected, data.playerName]
};
}
return prev;
});
};
// Register all handlers
socket.on('connect', handleConnect);
socket.on('connect_error', handleConnectError);
@@ -161,6 +291,13 @@ export const useGameWebSocket = (gameToken) => {
socket.on('game:player-moved', handlePlayerMoved);
socket.on('game:turn-changed', handleTurnChanged);
socket.on('game:error', handleError);
// Approval system events (PRIVATE games)
socket.on('game:pending-approval', handlePendingApproval);
socket.on('game:player-requesting-join', handlePlayerRequestingJoin);
socket.on('game:approval-granted', handleApprovalGranted);
socket.on('game:approval-denied', handleApprovalDenied);
socket.on('game:player-approved', handlePlayerApproved);
// Cleanup
return () => {
@@ -247,6 +384,115 @@ export const useGameWebSocket = (gameToken) => {
return true;
}, [isConnected, gameState?.gameCode]);
// Joker approval methods
const approveJoker = useCallback((playerId, cardId, requestId) => {
const socket = socketRef.current;
if (!socket || !isConnected) {
warn('⚠️ Cannot approve joker: not connected');
return false;
}
log('✅ Approving joker for player:', playerId);
socket.emit('game:gamemaster-decision', {
gameCode: gameState?.gameCode,
requestId: requestId || `joker-${playerId}-${cardId}`,
decision: 'approve',
});
return true;
}, [isConnected, gameState?.gameCode]);
const rejectJoker = useCallback((playerId, cardId, requestId) => {
const socket = socketRef.current;
if (!socket || !isConnected) {
warn('⚠️ Cannot reject joker: not connected');
return false;
}
log('❌ Rejecting joker for player:', playerId);
socket.emit('game:gamemaster-decision', {
gameCode: gameState?.gameCode,
requestId: requestId || `joker-${playerId}-${cardId}`,
decision: 'reject',
});
return true;
}, [isConnected, gameState?.gameCode]);
// Card answer submission
const submitAnswer = useCallback((cardId, answer) => {
const socket = socketRef.current;
if (!socket || !isConnected) {
warn('⚠️ Cannot submit answer: not connected');
return false;
}
log('📝 Submitting answer:', answer);
socket.emit('game:card-answer', {
gameCode: gameState?.gameCode,
cardId,
answer,
});
return true;
}, [isConnected, gameState?.gameCode]);
// Position guess submission
const submitPositionGuess = useCallback((guessedPosition) => {
const socket = socketRef.current;
if (!socket || !isConnected) {
warn('⚠️ Cannot submit position guess: not connected');
return false;
}
log('🎯 Submitting position guess:', guessedPosition);
socket.emit('game:position-guess', {
gameCode: gameState?.gameCode,
guessedPosition,
});
return true;
}, [isConnected, gameState?.gameCode]);
// Approve player (gamemaster only, PRIVATE games)
const approvePlayer = useCallback((playerName) => {
const socket = socketRef.current;
if (!socket || !isConnected) {
warn('⚠️ Cannot approve player: not connected');
return false;
}
if (!isGamemaster) {
warn('⚠️ Only gamemaster can approve players');
return false;
}
log('✅ Approving player:', playerName);
socket.emit('game:approve-player', {
gameCode: gameState?.gameCode,
playerName,
});
return true;
}, [isConnected, isGamemaster, gameState?.gameCode]);
// Reject player (gamemaster only, PRIVATE games)
const rejectPlayer = useCallback((playerName, reason = 'Join request denied') => {
const socket = socketRef.current;
if (!socket || !isConnected) {
warn('⚠️ Cannot reject player: not connected');
return false;
}
if (!isGamemaster) {
warn('⚠️ Only gamemaster can reject players');
return false;
}
log('❌ Rejecting player:', playerName);
socket.emit('game:reject-player', {
gameCode: gameState?.gameCode,
playerName,
reason,
});
return true;
}, [isConnected, isGamemaster, gameState?.gameCode]);
return {
socket: socketRef.current,
isConnected,
@@ -257,11 +503,19 @@ export const useGameWebSocket = (gameToken) => {
error,
isGamemaster,
gameStarted,
pendingPlayers,
approvalStatus,
// Methods
rollDice,
sendMessage,
setReady,
leaveGame,
approveJoker,
rejectJoker,
submitAnswer,
submitPositionGuess,
approvePlayer,
rejectPlayer,
addEventListener,
removeEventListener,
};