diff --git a/apps/client/src/hooks/useAppFlow.ts b/apps/client/src/hooks/useAppFlow.ts index ed9d840..bb6bafc 100644 --- a/apps/client/src/hooks/useAppFlow.ts +++ b/apps/client/src/hooks/useAppFlow.ts @@ -19,6 +19,16 @@ const [joinErrorMessage, setJoinErrorMessage] = useState(null); const [isJoining, setIsJoining] = useState(false); const joinTimeoutRef = useRef | null>(null); + const joinRejectedHandlerRef = useRef<((payload: roomTypes.JoinRoomRejectedPayload) => void) | null>(null); + + const clearJoinRejectedHandler = () => { + if (!joinRejectedHandlerRef.current) { + return; + } + + socketManager.title.offJoinRejected(joinRejectedHandlerRef.current); + joinRejectedHandlerRef.current = null; + }; const clearJoinTimeout = () => { if (!joinTimeoutRef.current) { @@ -35,30 +45,15 @@ } clearJoinTimeout(); + clearJoinRejectedHandler(); setJoinErrorMessage(null); setIsJoining(true); - joinTimeoutRef.current = setTimeout(() => { - setIsJoining(false); - setJoinErrorMessage("参加要求がタイムアウトしました,もう一度お試しください"); - joinTimeoutRef.current = null; - }, config.GAME_CONFIG.JOIN_REQUEST_TIMEOUT_MS); - socketManager.title.joinRoom(payload); - }; - useEffect(() => { - const handleConnect = (id: string) => { - setMyId(id); - }; - const handleRoomUpdate = (updatedRoom: roomTypes.Room) => { - clearJoinTimeout(); - setRoom(updatedRoom); - setIsJoining(false); - setJoinErrorMessage(null); - setScenePhase(appConsts.ScenePhase.LOBBY); - }; const handleJoinRejected = (payload: roomTypes.JoinRoomRejectedPayload) => { clearJoinTimeout(); setIsJoining(false); + joinRejectedHandlerRef.current = null; + if (payload.reason === "full") { setJoinErrorMessage(`ルーム ${payload.roomId} は満員です`); return; @@ -68,21 +63,45 @@ setJoinErrorMessage(`ルーム ${payload.roomId} への参加要求が重複しました`); } }; + + joinRejectedHandlerRef.current = handleJoinRejected; + socketManager.title.onceJoinRejected(handleJoinRejected); + + joinTimeoutRef.current = setTimeout(() => { + clearJoinRejectedHandler(); + setIsJoining(false); + setJoinErrorMessage("参加要求がタイムアウトしました,もう一度お試しください"); + joinTimeoutRef.current = null; + }, config.GAME_CONFIG.JOIN_REQUEST_TIMEOUT_MS); + + socketManager.title.joinRoom(payload); + }; + + useEffect(() => { + const handleConnect = (id: string) => { + setMyId(id); + }; + const handleRoomUpdate = (updatedRoom: roomTypes.Room) => { + clearJoinTimeout(); + clearJoinRejectedHandler(); + setRoom(updatedRoom); + setIsJoining(false); + setJoinErrorMessage(null); + setScenePhase(appConsts.ScenePhase.LOBBY); + }; const handleGameStart = () => { setScenePhase(appConsts.ScenePhase.PLAYING); }; socketManager.common.onConnect(handleConnect); - socketManager.title.onJoinRejected(handleJoinRejected); socketManager.lobby.onRoomUpdate(handleRoomUpdate); - socketManager.game.onGameStart(handleGameStart); + socketManager.game.onceGameStart(handleGameStart); return () => { clearJoinTimeout(); + clearJoinRejectedHandler(); socketManager.common.offConnect(handleConnect); - socketManager.title.offJoinRejected(handleJoinRejected); socketManager.lobby.offRoomUpdate(handleRoomUpdate); - socketManager.game.offGameStart(handleGameStart); }; }, []); diff --git a/apps/client/src/network/handlers/GameHandler.ts b/apps/client/src/network/handlers/GameHandler.ts index 926875c..e4b307d 100644 --- a/apps/client/src/network/handlers/GameHandler.ts +++ b/apps/client/src/network/handlers/GameHandler.ts @@ -29,6 +29,7 @@ onUpdateMapCells: (callback: (updates: UpdateMapCellsPayload) => void) => void; offUpdateMapCells: (callback: (updates: UpdateMapCellsPayload) => void) => void; onGameStart: (callback: (data: GameStartPayload) => void) => void; + onceGameStart: (callback: (data: GameStartPayload) => void) => void; offGameStart: (callback: (data: GameStartPayload) => void) => void; sendMove: (x: number, y: number) => void; readyForGame: () => void; @@ -36,7 +37,7 @@ /** ソケットインスタンスからゲーム向けハンドラを生成する */ export const createGameHandler = (socket: Socket): GameHandler => { - const { onEvent, offEvent, emitEvent } = createClientSocketEventBridge(socket); + const { onEvent, onceEvent, offEvent, emitEvent } = createClientSocketEventBridge(socket); return { onCurrentPlayers: (callback) => { @@ -72,6 +73,9 @@ onGameStart: (callback) => { onEvent(protocol.SocketEvents.GAME_START, callback); }, + onceGameStart: (callback) => { + onceEvent(protocol.SocketEvents.GAME_START, callback); + }, offGameStart: (callback) => { offEvent(protocol.SocketEvents.GAME_START, callback); }, diff --git a/apps/client/src/network/handlers/LobbyHandler.ts b/apps/client/src/network/handlers/LobbyHandler.ts index 338b2e8..9b1c594 100644 --- a/apps/client/src/network/handlers/LobbyHandler.ts +++ b/apps/client/src/network/handlers/LobbyHandler.ts @@ -5,17 +5,21 @@ type LobbyHandler = { onRoomUpdate: (callback: (room: PayloadOf) => void) => void; + onceRoomUpdate: (callback: (room: PayloadOf) => void) => void; offRoomUpdate: (callback: (room: PayloadOf) => void) => void; startGame: () => void; }; export const createLobbyHandler = (socket: Socket): LobbyHandler => { - const { onEvent, offEvent, emitEvent } = createClientSocketEventBridge(socket); + const { onEvent, onceEvent, offEvent, emitEvent } = createClientSocketEventBridge(socket); return { onRoomUpdate: (callback) => { onEvent(protocol.SocketEvents.ROOM_UPDATE, callback); }, + onceRoomUpdate: (callback) => { + onceEvent(protocol.SocketEvents.ROOM_UPDATE, callback); + }, offRoomUpdate: (callback) => { offEvent(protocol.SocketEvents.ROOM_UPDATE, callback); }, diff --git a/apps/client/src/network/handlers/TitleHandler.ts b/apps/client/src/network/handlers/TitleHandler.ts index bdf970e..04d3a91 100644 --- a/apps/client/src/network/handlers/TitleHandler.ts +++ b/apps/client/src/network/handlers/TitleHandler.ts @@ -6,11 +6,12 @@ type TitleHandler = { joinRoom: (payload: PayloadOf) => void; onJoinRejected: (callback: (payload: PayloadOf) => void) => void; + onceJoinRejected: (callback: (payload: PayloadOf) => void) => void; offJoinRejected: (callback: (payload: PayloadOf) => void) => void; }; export const createTitleHandler = (socket: Socket): TitleHandler => { - const { onEvent, offEvent, emitEvent } = createClientSocketEventBridge(socket); + const { onEvent, onceEvent, offEvent, emitEvent } = createClientSocketEventBridge(socket); return { joinRoom: (payload) => { @@ -19,6 +20,9 @@ onJoinRejected: (callback) => { onEvent(protocol.SocketEvents.ROOM_JOIN_REJECTED, callback); }, + onceJoinRejected: (callback) => { + onceEvent(protocol.SocketEvents.ROOM_JOIN_REJECTED, callback); + }, offJoinRejected: (callback) => { offEvent(protocol.SocketEvents.ROOM_JOIN_REJECTED, callback); } diff --git a/apps/client/src/network/handlers/socketEventBridge.ts b/apps/client/src/network/handlers/socketEventBridge.ts index 56615ad..aead5bc 100644 --- a/apps/client/src/network/handlers/socketEventBridge.ts +++ b/apps/client/src/network/handlers/socketEventBridge.ts @@ -11,6 +11,13 @@ (socket as any).on(event, callback); }; + const onceEvent = ( + event: TEvent, + callback: (payload: PayloadOf) => void + ) => { + (socket as any).once(event, callback); + }; + const offEvent = ( event: TEvent, callback: (payload: PayloadOf) => void @@ -31,6 +38,7 @@ return { onEvent, + onceEvent, offEvent, emitEvent, }; diff --git a/apps/server/src/network/handlers/socketEventBridge.ts b/apps/server/src/network/handlers/socketEventBridge.ts index 54d0369..1db3c62 100644 --- a/apps/server/src/network/handlers/socketEventBridge.ts +++ b/apps/server/src/network/handlers/socketEventBridge.ts @@ -11,7 +11,15 @@ (socket as any).on(event, callback); }; + const onceEvent = ( + event: TEvent, + callback: (payload: PayloadOf) => void + ) => { + (socket as any).once(event, callback); + }; + return { onEvent, + onceEvent, }; };