diff --git a/experimental/2nub/src/client/components/OnubRoleAssignment.tsx b/experimental/2nub/src/client/components/OnubRoleAssignment.tsx index 62c2238..14bc6cf 100644 --- a/experimental/2nub/src/client/components/OnubRoleAssignment.tsx +++ b/experimental/2nub/src/client/components/OnubRoleAssignment.tsx @@ -1,6 +1,7 @@ import React, { useState } from 'react'; import { GameStateClient, StateNight, StateRoleAssignment } from '../../types'; import '../styles/main.scss'; +import { useSocketEvents } from '../hooks/useSocketEvents'; /** * Remove parentheses and their contents from a string. @@ -17,10 +18,12 @@ interface OnubRoleAssignmentProps { } export const OnubRoleAssignment: React.FC = ({ stateRoleAssignment, currentPlayerId }) => { + const { confirmRoleAssignment } = useSocketEvents(); const roleWithParentheses = stateRoleAssignment.playerData[currentPlayerId].originalRoleId; const role = removeParenthesesNaively(roleWithParentheses); return

Role Assignment

Your role is: {role}

+
; } \ No newline at end of file diff --git a/experimental/2nub/src/client/hooks/useGameEvents.ts b/experimental/2nub/src/client/hooks/useGameEvents.ts index 80f9365..01b2435 100644 --- a/experimental/2nub/src/client/hooks/useGameEvents.ts +++ b/experimental/2nub/src/client/hooks/useGameEvents.ts @@ -26,6 +26,7 @@ export interface UseGameEventsReturn { authenticatePlayer: (gameId: string, playerId: string) => void; updateRuleset: (ruleset: StateLobby["ruleset"]) => void; startGame: () => void; + confirmRoleAssignment: () => void; } const createEventHandler = (eventName: string, handler: (data: T) => void) => { @@ -77,6 +78,13 @@ export function useGameEvents(options: UseGameEventsOptions): UseGameEventsRetur } }, [isConnected, socket, gameId]); + const confirmRoleAssignment = useCallback(() => { + if (socket && isConnected && gameId && playerId) { + console.log(`Confirming role assignment for player ${playerId} in game ${gameId}`); + socket.emit('confirmRoleAssignment', { gameId, playerId }); + } + }, [isConnected, socket, gameId, playerId]); + const forceDisconnectPlayer = useCallback(async (gameId: string, playerId: string) => { try { const response = await fetch(buildApiUrl(`api/games/${gameId}/players/${playerId}/disconnect`), { @@ -125,5 +133,6 @@ export function useGameEvents(options: UseGameEventsOptions): UseGameEventsRetur authenticatePlayer, updateRuleset, startGame, + confirmRoleAssignment, }; } \ No newline at end of file diff --git a/experimental/2nub/src/server/gameState.ts b/experimental/2nub/src/server/gameState.ts index 131ba64..d03c5a3 100644 --- a/experimental/2nub/src/server/gameState.ts +++ b/experimental/2nub/src/server/gameState.ts @@ -339,6 +339,33 @@ class GameStateManager { return game; } + + maybeConfirmPlayerRoleAssignment(gameId: string, playerId: string): GameState | null { + const game = this.games.get(gameId); + if (!game) return null; + + // Only allow confirming role assignment if game is in roleAssignment state + if (game.state.state !== 'roleAssignment') return null; + + // Update the player's confirmation status + game.state.playerConfirmations[playerId] = true; + + // Check if all players have confirmed + const allConfirmed = Object.values(game.state.playerConfirmations).every(confirmed => confirmed); + if (allConfirmed) { + // Transition to night state + game.state = { + state: 'night', + playerIdsByWakeupOrder: game.state.playerIdsByWakeupOrder, + playerData: game.state.playerData, + centerCards: game.state.centerCards, + ruleset: game.state.ruleset, + turn: 0, + }; + } + + return game; + } } export const gameStateManager = new GameStateManager(); \ No newline at end of file diff --git a/experimental/2nub/src/server/websocket.ts b/experimental/2nub/src/server/websocket.ts index 09d22a6..ab03fcf 100644 --- a/experimental/2nub/src/server/websocket.ts +++ b/experimental/2nub/src/server/websocket.ts @@ -94,6 +94,30 @@ export function setupSocketIO(io: Server { + const { gameId, playerId } = data; + console.log(`Player ${playerId} confirming role assignment for game ${gameId} on socket ${socket.id}`); + + // Verify the socket is authenticated for this game and player + if (socket.gameId !== gameId || socket.playerId !== playerId) { + console.error(`Socket ${socket.id} not authenticated for game ${gameId} and player ${playerId}`); + socket.emit('error', { error: 'Not authenticated for this game/player' }); + return; + } + + // Update the player's confirmation status via game state manager + const updatedGameState = gameStateManager.maybeConfirmPlayerRoleAssignment(gameId, playerId); + + if (updatedGameState) { + // Broadcast updated game state to all players in the room + broadcastToGame(gameId, 'gameState', toGameStateClient(updatedGameState)); + console.log(`Player ${playerId} confirmed role assignment for game ${gameId}`); + } else { + console.error(`Failed to confirm role assignment for player ${playerId} in game ${gameId}`); + socket.emit('error', { error: 'Failed to confirm role assignment' }); + } + }); + socket.on('disconnect', () => { console.log('Socket.io client disconnected:', socket.id); diff --git a/experimental/2nub/src/types.ts b/experimental/2nub/src/types.ts index c2cb1d8..819a22e 100644 --- a/experimental/2nub/src/types.ts +++ b/experimental/2nub/src/types.ts @@ -136,6 +136,7 @@ export interface ClientToServerEvents { authenticatePlayer: (data: { gameId: string; playerId: string }) => void; updateRuleset: (data: { gameId: string; ruleset: StateLobby["ruleset"] }) => void; startGame: (data: { gameId: string }) => void; + confirmRoleAssignment: (data: { gameId: string; playerId: string }) => void; } export interface ApiResponse {