diff --git a/apps/server/src/application/coordinators/startGameCoordinator.ts b/apps/server/src/application/coordinators/startGameCoordinator.ts new file mode 100644 index 0000000..a4308fb --- /dev/null +++ b/apps/server/src/application/coordinators/startGameCoordinator.ts @@ -0,0 +1,77 @@ +import { roomConsts } from "@repo/shared"; +import { GameManager } from "@server/domains/game/GameManager"; +import { type GameOutputPort } from "@server/domains/game/application/ports/gameUseCasePorts"; +import { startGameUseCase } from "@server/domains/game/application/useCases/startGameUseCase"; +import { RoomManager } from "@server/domains/room/RoomManager"; +import { logEvent } from "@server/logging/logEvent"; + +type StartGameCoordinatorParams = { + ownerId: string; + gameManager: GameManager; + roomManager: RoomManager; + output: Pick< + GameOutputPort, + | "publishUpdatePlayerToRoom" + | "publishMapCellUpdatesToRoom" + | "publishGameEndToRoom" + | "publishGameStartToRoom" + >; +}; + +export const startGameCoordinator = ({ + ownerId, + gameManager, + roomManager, + output, +}: StartGameCoordinatorParams) => { + const room = roomManager.getRoomByOwnerId(ownerId); + if (!room) { + logEvent("GameUseCase", { + event: "START_GAME", + result: "ignored_no_room", + socketId: ownerId, + }); + return; + } + + if (room.status === roomConsts.RoomPhase.PLAYING) { + logEvent("GameUseCase", { + event: "START_GAME", + result: "ignored_already_playing", + roomId: room.roomId, + socketId: ownerId, + }); + return; + } + + const updatedRoom = roomManager.markRoomPlaying(room.roomId); + if (!updatedRoom) { + logEvent("GameUseCase", { + event: "START_GAME", + result: "ignored_room_not_found", + roomId: room.roomId, + socketId: ownerId, + }); + return; + } + + logEvent("GameUseCase", { + event: "START_GAME", + result: "accepted", + roomId: updatedRoom.roomId, + socketId: ownerId, + totalPlayers: updatedRoom.players.length, + }); + + const playerIds = updatedRoom.players.map((player) => player.id); + + startGameUseCase({ + roomId: updatedRoom.roomId, + playerIds, + gameManager, + onGameEnd: () => { + roomManager.markRoomWaiting(updatedRoom.roomId); + }, + output, + }); +}; diff --git a/apps/server/src/domains/game/application/useCases/startGameUseCase.ts b/apps/server/src/domains/game/application/useCases/startGameUseCase.ts index e492412..0b853c8 100644 --- a/apps/server/src/domains/game/application/useCases/startGameUseCase.ts +++ b/apps/server/src/domains/game/application/useCases/startGameUseCase.ts @@ -1,12 +1,11 @@ -import { roomConsts } from "@repo/shared"; -import { RoomManager } from "@server/domains/room/RoomManager"; import type { GameOutputPort, StartGamePort } from "../ports/gameUseCasePorts"; import { logEvent } from "@server/logging/logEvent"; type StartGameUseCaseParams = { - ownerId: string; + roomId: string; + playerIds: string[]; gameManager: StartGamePort; - roomManager: RoomManager; + onGameEnd: () => void; output: Pick< GameOutputPort, | "publishUpdatePlayerToRoom" @@ -17,67 +16,36 @@ }; export const startGameUseCase = ({ - ownerId, + roomId, + playerIds, gameManager, - roomManager, + onGameEnd, output, }: StartGameUseCaseParams) => { - const room = roomManager.getRoomByOwnerId(ownerId); - if (!room) { - logEvent("GameUseCase", { - event: "START_GAME", - result: "ignored_no_room", - socketId: ownerId, - }); - return; - } - - if (room.status === roomConsts.RoomPhase.PLAYING) { - logEvent("GameUseCase", { - event: "START_GAME", - result: "ignored_already_playing", - roomId: room.roomId, - socketId: ownerId, - }); - return; - } - - logEvent("GameUseCase", { - event: "START_GAME", - result: "accepted", - roomId: room.roomId, - socketId: ownerId, - totalPlayers: room.players.length, - }); - - room.status = roomConsts.RoomPhase.PLAYING; - - const playerIds = room.players.map((p: { id: string }) => p.id); - gameManager.startRoomSession( - room.roomId, + roomId, playerIds, (tickData) => { tickData.players.forEach((playerData) => { - output.publishUpdatePlayerToRoom(room.roomId, playerData); + output.publishUpdatePlayerToRoom(roomId, playerData); }); if (tickData.cellUpdates.length > 0) { - output.publishMapCellUpdatesToRoom(room.roomId, tickData.cellUpdates); + output.publishMapCellUpdatesToRoom(roomId, tickData.cellUpdates); } }, () => { logEvent("GameUseCase", { event: "GAME_END", result: "emitted", - roomId: room.roomId, + roomId, reason: "duration_elapsed", }); - output.publishGameEndToRoom(room.roomId); - room.status = roomConsts.RoomPhase.WAITING; + output.publishGameEndToRoom(roomId); + onGameEnd(); } ); - const startTime = gameManager.getRoomStartTime(room.roomId) || Date.now(); - output.publishGameStartToRoom(room.roomId, { startTime }); + const startTime = gameManager.getRoomStartTime(roomId) || Date.now(); + output.publishGameStartToRoom(roomId, { startTime }); }; diff --git a/apps/server/src/domains/room/RoomManager.ts b/apps/server/src/domains/room/RoomManager.ts index 4197846..47a86ed 100644 --- a/apps/server/src/domains/room/RoomManager.ts +++ b/apps/server/src/domains/room/RoomManager.ts @@ -3,6 +3,7 @@ * ルーム状態の保持とルーム操作サービスへの委譲を担うマネージャ */ import type { roomTypes } from "@repo/shared"; +import { roomConsts } from "@repo/shared"; import { RoomJoinService } from "./application/services/RoomJoinService"; import { RoomExitService } from "./application/services/RoomExitService"; import { RoomQueryService } from "./application/services/RoomQueryService"; @@ -45,4 +46,26 @@ public getRoomByPlayerId(playerId: string): roomTypes.Room | undefined { return this.roomQueryService.getRoomByPlayerId(playerId); } + + // ルーム状態を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; + } + + // ルーム状態を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; + } } \ No newline at end of file diff --git a/apps/server/src/network/handlers/game/registerGameHandlers.ts b/apps/server/src/network/handlers/game/registerGameHandlers.ts index f64b1b0..bdd4cc0 100644 --- a/apps/server/src/network/handlers/game/registerGameHandlers.ts +++ b/apps/server/src/network/handlers/game/registerGameHandlers.ts @@ -7,13 +7,13 @@ import { RoomManager } from "@server/domains/room/RoomManager"; import { protocol } from "@repo/shared"; import { pingUseCase } from "@server/domains/game/application/useCases/pingUseCase"; -import { startGameUseCase } from "@server/domains/game/application/useCases/startGameUseCase"; import { readyForGameUseCase } from "@server/domains/game/application/useCases/readyForGameUseCase"; import { movePlayerUseCase } from "@server/domains/game/application/useCases/movePlayerUseCase"; import { createCommonHandlerContext } from "@server/network/handlers/CommonHandler"; import { createGameOutputAdapter } from "./createGameOutputAdapter"; import { logEvent } from "@server/logging/logEvent"; import { isMovePayload, isPingPayload } from "@server/network/validation/socketPayloadValidators"; +import { startGameCoordinator } from "@server/application/coordinators/startGameCoordinator"; /** ゲームイベントの購読とユースケース呼び出しを設定する */ export const registerGameHandlers = ( @@ -44,7 +44,7 @@ // オーナー開始要求に応じてゲーム進行ユースケースを起動する socket.on(protocol.SocketEvents.START_GAME, () => { - startGameUseCase({ + startGameCoordinator({ ownerId: socket.id, gameManager, roomManager, diff --git a/test/load-bot.constants.ts b/test/load-bot.constants.ts index 12216fd..61fae65 100644 --- a/test/load-bot.constants.ts +++ b/test/load-bot.constants.ts @@ -4,7 +4,7 @@ export const URL = NETWORK_CONFIG.PROD_SERVER_URL; export const DEV_URL = NETWORK_CONFIG.DEV_SERVER_URL; -export const BOTS = 20; +export const BOTS = 10; export const DURATION_MS = Infinity; export const JOIN_DELAY_MS = 25; export const MOVE_TICK_MS = GAME_CONFIG.PLAYER_POSITION_UPDATE_MS;