diff --git a/apps/server/src/application/coordinators/disconnectCoordinator.ts b/apps/server/src/application/coordinators/disconnectCoordinator.ts index 47df6fe..739dee6 100644 --- a/apps/server/src/application/coordinators/disconnectCoordinator.ts +++ b/apps/server/src/application/coordinators/disconnectCoordinator.ts @@ -16,11 +16,15 @@ import { disconnectUseCase } from "@server/domains/game/application/useCases/disconnectUseCase"; import { roomDisconnectUseCase } from "@server/domains/room/application/useCases/roomDisconnectUseCase"; +type DisconnectDeps = { + roomManager: DisconnectRoomPort & FindRoomByPlayerPort & FindRoomByIdPort; + runtimeRegistry: FindGameByPlayerPort & CleanupGameRuntimePort; +}; + /** 切断調停で利用する入力ポートと出力ポートの契約 */ export type DisconnectCoordinatorParams = { socketId: string; - roomManager: DisconnectRoomPort & FindRoomByPlayerPort & FindRoomByIdPort; - runtimeRegistry: FindGameByPlayerPort & CleanupGameRuntimePort; +} & DisconnectDeps & { gameOutput: Pick; roomOutput: Pick; }; @@ -47,9 +51,8 @@ roomDisconnectUseCase({ roomManager, + runtimeRegistry, socketId, output: roomOutput, }); - - runtimeRegistry.cleanupDisposedRoomRuntimes(); }; diff --git a/apps/server/src/application/coordinators/readyForGameCoordinator.ts b/apps/server/src/application/coordinators/readyForGameCoordinator.ts index 0f64720..19fbd7c 100644 --- a/apps/server/src/application/coordinators/readyForGameCoordinator.ts +++ b/apps/server/src/application/coordinators/readyForGameCoordinator.ts @@ -8,10 +8,14 @@ import type { FindGameByPlayerPort, FindRoomByPlayerPort } from "@server/domains/room/application/ports/roomUseCasePorts"; import { readyForGameUseCase } from "@server/domains/game/application/useCases/readyForGameUseCase"; -type ReadyForGameCoordinatorParams = { - socketId: string; +type ReadyForGameDeps = { roomManager: FindRoomByPlayerPort; runtimeRegistry: FindGameByPlayerPort; +}; + +type ReadyForGameCoordinatorParams = { + socketId: string; +} & ReadyForGameDeps & { output: Pick; }; diff --git a/apps/server/src/application/coordinators/startGameCoordinator.ts b/apps/server/src/application/coordinators/startGameCoordinator.ts index e5143d9..d7c43e0 100644 --- a/apps/server/src/application/coordinators/startGameCoordinator.ts +++ b/apps/server/src/application/coordinators/startGameCoordinator.ts @@ -15,10 +15,14 @@ import { gameUseCaseLogEvents, logResults, logScopes } from "@server/logging/index"; import { roomConsts } from "@repo/shared"; -type StartGameCoordinatorParams = { - ownerId: string; +type StartGameDeps = { roomManager: FindRoomByOwnerPort & RoomPhaseTransitionPort; runtimeRegistry: FindGameByRoomPort; +}; + +type StartGameCoordinatorParams = { + ownerId: string; +} & StartGameDeps & { output: Pick< GameOutputPort, | "publishUpdatePlayersToSocket" diff --git a/apps/server/src/domains/room/RoomManager.ts b/apps/server/src/domains/room/RoomManager.ts index 47a86ed..4205a81 100644 --- a/apps/server/src/domains/room/RoomManager.ts +++ b/apps/server/src/domains/room/RoomManager.ts @@ -3,9 +3,9 @@ * ルーム状態の保持とルーム操作サービスへの委譲を担うマネージャ */ import type { roomTypes } from "@repo/shared"; -import { roomConsts } from "@repo/shared"; import { RoomJoinService } from "./application/services/RoomJoinService"; import { RoomExitService } from "./application/services/RoomExitService"; +import { RoomPhaseService } from "./application/services/RoomPhaseService"; import { RoomQueryService } from "./application/services/RoomQueryService"; import type { JoinRoomResult } from "./application/ports/roomUseCasePorts"; @@ -14,11 +14,13 @@ private rooms: Map = new Map(); private roomJoinService: RoomJoinService; private roomExitService: RoomExitService; + private roomPhaseService: RoomPhaseService; private roomQueryService: RoomQueryService; constructor() { this.roomJoinService = new RoomJoinService(this.rooms); this.roomExitService = new RoomExitService(this.rooms); + this.roomPhaseService = new RoomPhaseService(this.rooms); this.roomQueryService = new RoomQueryService(this.rooms); } @@ -49,23 +51,11 @@ // ルーム状態をPLAYINGへ更新する public markRoomPlaying(roomId: string): roomTypes.Room | undefined { - const room = this.roomQueryService.getRoomById(roomId); - if (!room) { - return undefined; - } - - room.status = roomConsts.RoomPhase.PLAYING; - return room; + return this.roomPhaseService.markRoomPlaying(roomId); } // ルーム状態をWAITINGへ更新する public markRoomWaiting(roomId: string): roomTypes.Room | undefined { - const room = this.roomQueryService.getRoomById(roomId); - if (!room) { - return undefined; - } - - room.status = roomConsts.RoomPhase.WAITING; - return room; + return this.roomPhaseService.markRoomWaiting(roomId); } } \ No newline at end of file diff --git a/apps/server/src/domains/room/application/services/RoomPhaseService.ts b/apps/server/src/domains/room/application/services/RoomPhaseService.ts new file mode 100644 index 0000000..997c27a --- /dev/null +++ b/apps/server/src/domains/room/application/services/RoomPhaseService.ts @@ -0,0 +1,31 @@ +/** + * RoomPhaseService + * ルーム状態のフェーズ更新処理を提供する + */ +import { roomConsts } from "@repo/shared"; +import type { roomTypes } from "@repo/shared"; + +/** ルーム状態のフェーズ遷移を管理するサービス */ +export class RoomPhaseService { + constructor(private rooms: Map) {} + + public markRoomPlaying(roomId: string): roomTypes.Room | undefined { + const room = this.rooms.get(roomId); + if (!room) { + return undefined; + } + + room.status = roomConsts.RoomPhase.PLAYING; + return room; + } + + public markRoomWaiting(roomId: string): roomTypes.Room | undefined { + const room = this.rooms.get(roomId); + if (!room) { + return undefined; + } + + room.status = roomConsts.RoomPhase.WAITING; + return room; + } +} diff --git a/apps/server/src/domains/room/application/useCases/joinRoomUseCase.ts b/apps/server/src/domains/room/application/useCases/joinRoomUseCase.ts index d1ddd59..f83aa9b 100644 --- a/apps/server/src/domains/room/application/useCases/joinRoomUseCase.ts +++ b/apps/server/src/domains/room/application/useCases/joinRoomUseCase.ts @@ -4,6 +4,7 @@ */ import type { roomTypes } from "@repo/shared"; import type { + EnsureGameRuntimePort, JoinRoomPort, JoinRoomResult, RoomOutputPort, @@ -13,6 +14,7 @@ type JoinRoomUseCaseParams = { roomManager: JoinRoomPort; + runtimeRegistry: EnsureGameRuntimePort; socketId: string; data: roomTypes.JoinRoomPayload; output: Pick; @@ -21,6 +23,7 @@ /** 参加イベントを受け取り,参加可否を判定する */ export const joinRoomUseCase = ({ roomManager, + runtimeRegistry, socketId, data, output, @@ -51,5 +54,7 @@ return joinResult; } + runtimeRegistry.ensureGameManagerForRoom(roomId); + return joinResult; }; diff --git a/apps/server/src/domains/room/application/useCases/roomDisconnectUseCase.ts b/apps/server/src/domains/room/application/useCases/roomDisconnectUseCase.ts index f534d14..9d3c9ce 100644 --- a/apps/server/src/domains/room/application/useCases/roomDisconnectUseCase.ts +++ b/apps/server/src/domains/room/application/useCases/roomDisconnectUseCase.ts @@ -2,13 +2,14 @@ * roomDisconnectUseCase * 切断時のルーム退出処理と状態更新配信を行うユースケース */ -import type { DisconnectRoomPort } from "../ports/roomUseCasePorts"; +import type { CleanupGameRuntimePort, DisconnectRoomPort } from "../ports/roomUseCasePorts"; import type { RoomOutputPort } from "../ports/roomUseCasePorts"; import { logEvent } from "@server/logging/logger"; import { logResults, logScopes, roomUseCaseLogEvents } from "@server/logging/index"; type RoomDisconnectUseCaseParams = { roomManager: DisconnectRoomPort; + runtimeRegistry: CleanupGameRuntimePort; socketId: string; output: Pick; }; @@ -16,6 +17,7 @@ /** 切断ソケットを各ルームから退出させ,更新ルームを配信する */ export const roomDisconnectUseCase = ({ roomManager, + runtimeRegistry, socketId, output, }: RoomDisconnectUseCaseParams) => { @@ -38,4 +40,6 @@ totalPlayers: room.players.length, }); }); + + runtimeRegistry.cleanupDisposedRoomRuntimes(); }; diff --git a/apps/server/src/network/handlers/game/registerGameHandlers.ts b/apps/server/src/network/handlers/game/registerGameHandlers.ts index 90f5996..97e40a9 100644 --- a/apps/server/src/network/handlers/game/registerGameHandlers.ts +++ b/apps/server/src/network/handlers/game/registerGameHandlers.ts @@ -11,6 +11,7 @@ FindGameByPlayerPort, FindRoomByOwnerPort, FindRoomByPlayerPort, + RoomScopedGamePort, RoomPhaseTransitionPort, } from "@server/domains/room/application/ports/roomUseCasePorts"; import { movePlayerUseCase } from "@server/domains/game/application/useCases/movePlayerUseCase"; @@ -29,6 +30,28 @@ [protocol.SocketEvents.PLACE_BOMB]: isPlaceBombPayload, } as const; +type RuntimeResolution = { + roomId: string; + gameManager: RoomScopedGamePort; +}; + +const resolveRuntimeBySocketId = ( + roomManager: FindRoomByPlayerPort, + runtimeRegistry: FindGameByPlayerPort, + socketId: string +): RuntimeResolution | undefined => { + const roomId = roomManager.getRoomByPlayerId(socketId)?.roomId; + const gameManager = runtimeRegistry.getGameManagerByPlayerId(socketId); + if (!roomId || !gameManager) { + return undefined; + } + + return { + roomId, + gameManager, + }; +}; + /** ゲームイベントの購読とユースケース呼び出しを設定する */ export const registerGameHandlers = ( io: Server, @@ -90,13 +113,13 @@ return; } - const gameManager = runtimeRegistry.getGameManagerByPlayerId(socket.id); - if (!gameManager) { + const runtime = resolveRuntimeBySocketId(roomManager, runtimeRegistry, socket.id); + if (!runtime) { return; } movePlayerUseCase({ - gameManager, + gameManager: runtime.gameManager, playerId: socket.id, move: data, }); @@ -108,15 +131,14 @@ return; } - const roomId = roomManager.getRoomByPlayerId(socket.id)?.roomId; - const gameManager = runtimeRegistry.getGameManagerByPlayerId(socket.id); - if (!roomId || !gameManager) { + const runtime = resolveRuntimeBySocketId(roomManager, runtimeRegistry, socket.id); + if (!runtime) { return; } placeBombUseCase({ - roomId, - bombStore: gameManager, + roomId: runtime.roomId, + bombStore: runtime.gameManager, input: { socketId: socket.id, payload: data, diff --git a/apps/server/src/network/handlers/room/registerRoomHandlers.ts b/apps/server/src/network/handlers/room/registerRoomHandlers.ts index db1b83c..ca247cb 100644 --- a/apps/server/src/network/handlers/room/registerRoomHandlers.ts +++ b/apps/server/src/network/handlers/room/registerRoomHandlers.ts @@ -48,6 +48,7 @@ const joinResult = joinRoomUseCase({ roomManager, + runtimeRegistry, socketId: socket.id, data, output: roomOutputAdapter, @@ -74,7 +75,6 @@ return; case "joined": - runtimeRegistry.ensureGameManagerForRoom(roomId); await socket.join(roomId); roomOutputAdapter.publishRoomUpdateToRoom(roomId, joinResult.room); logEvent(logScopes.ROOM_USE_CASE, { diff --git a/apps/server/src/network/types/connectionPorts.ts b/apps/server/src/network/types/connectionPorts.ts index 2b1f4a2..ca79e45 100644 --- a/apps/server/src/network/types/connectionPorts.ts +++ b/apps/server/src/network/types/connectionPorts.ts @@ -29,6 +29,24 @@ & FindGameByRoomPort & FindGameByPlayerPort; +/** START_GAME調停で利用する依存集合 */ +export type StartGameDeps = { + roomManager: FindRoomByOwnerPort & RoomPhaseTransitionPort; + runtimeRegistry: FindGameByRoomPort; +}; + +/** READY_FOR_GAME調停で利用する依存集合 */ +export type ReadyForGameDeps = { + roomManager: FindRoomByPlayerPort; + runtimeRegistry: FindGameByPlayerPort; +}; + +/** DISCONNECT調停で利用する依存集合 */ +export type DisconnectDeps = { + roomManager: DisconnectRoomPort & FindRoomByPlayerPort & FindRoomByIdPort; + runtimeRegistry: FindGameByPlayerPort & CleanupGameRuntimePort; +}; + /** ソケット接続全体で利用するルーム管理ポート集合 */ export type SocketConnectionRoomPort = & ConnectionRoomPort @@ -41,7 +59,7 @@ & CleanupGameRuntimePort; /** ソケット接続ハンドラで受け取るマネージャ依存の束 */ -export type SocketConnectionManagerBundle = { +export type SocketConnectionManagerBundle = DisconnectDeps & { roomManager: SocketConnectionRoomPort; runtimeRegistry: SocketConnectionRuntimePort; };