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:
@@ -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,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user