diff --git a/apps/client/src/scenes/game/GameManager.ts b/apps/client/src/scenes/game/GameManager.ts index 5cd39fb..36cfdb4 100644 --- a/apps/client/src/scenes/game/GameManager.ts +++ b/apps/client/src/scenes/game/GameManager.ts @@ -8,15 +8,16 @@ BombPlacedAckPayload, BombPlacedPayload, } from "@repo/shared"; -import { socketManager } from "@client/network/SocketManager"; import { AppearanceResolver } from "./application/AppearanceResolver"; import { BombManager } from "./entities/bomb/BombManager"; import { GameNetworkSync } from "./application/GameNetworkSync"; import { GameLoop } from "./application/GameLoop"; +import { GameEventFacade } from "./application/GameEventFacade"; import { SceneLifecycleState } from "./application/lifecycle/SceneLifecycleState"; import { GameSessionFacade } from "./application/lifecycle/GameSessionFacade"; import { CombatLifecycleFacade } from "./application/combat/CombatLifecycleFacade"; import { GameSceneOrchestrator } from "./application/orchestrators/GameSceneOrchestrator"; +import { SocketGameActionSender } from "./application/network/GameActionSender"; import type { GamePlayers } from "./application/game.types"; /** ゲームシーンの実行ライフサイクルを管理するマネージャー */ @@ -31,12 +32,14 @@ private bombManager: BombManager | null = null; private networkSync: GameNetworkSync | null = null; private gameLoop: GameLoop | null = null; + private gameEventFacade: GameEventFacade; private combatFacade: CombatLifecycleFacade; private lifecycleState = new SceneLifecycleState(); + private gameActionSender = new SocketGameActionSender(); // サーバーからゲーム開始通知(と開始時刻)を受け取った時に呼ぶ public setGameStart(startTime: number) { - this.sessionFacade.setGameStart(startTime); + this.gameEventFacade.handleGameStart(startTime); } public getStartCountdownSec(): number { @@ -58,16 +61,16 @@ const placed = this.bombManager.placeBomb(); if (!placed) return null; - socketManager.game.sendPlaceBomb(placed.payload); + this.gameActionSender.sendPlaceBomb(placed.payload); return placed.tempBombId; } public applyPlacedBombFromOthers(payload: BombPlacedPayload): void { - this.bombManager?.applyPlacedBombFromOthers(payload); + this.gameEventFacade.handleBombPlacedFromOthers(payload); } public applyPlacedBombAck(payload: BombPlacedAckPayload): void { - this.bombManager?.applyPlacedBombAck(payload); + this.gameEventFacade.handleBombPlacedAck(payload); } // 入力と状態管理 @@ -84,12 +87,18 @@ this.app = new Application(); this.worldContainer = new Container(); this.worldContainer.sortableChildren = true; + this.gameEventFacade = new GameEventFacade({ + onGameStart: (startTime) => { + this.sessionFacade.setGameStart(startTime); + }, + getBombManager: () => this.bombManager, + }); this.combatFacade = new CombatLifecycleFacade({ players: this.players, myId: this.myId, acquireInputLock: this.lockInput.bind(this), onSendBombHitReport: (bombId) => { - socketManager.game.sendBombHitReport({ bombId }); + this.gameActionSender.sendBombHitReport(bombId); }, }); } @@ -116,7 +125,7 @@ this.initializeSceneSubsystems(); // サーバーへゲーム準備完了を通知 - socketManager.game.readyForGame(); + this.gameActionSender.readyForGame(); // メインループの登録 this.app.ticker.add(this.tick); @@ -147,13 +156,13 @@ appearanceResolver: this.appearanceResolver, getElapsedMs: () => this.sessionFacade.getElapsedMs(), getJoystickInput: () => this.joystickInput, - onGameStart: this.setGameStart.bind(this), + onGameStart: this.gameEventFacade.handleGameStart.bind(this.gameEventFacade), onGameEnd: this.lockInput.bind(this), onBombPlacedFromOthers: (payload) => { - this.applyPlacedBombFromOthers(payload); + this.gameEventFacade.handleBombPlacedFromOthers(payload); }, onBombPlacedAckFromNetwork: (payload) => { - this.applyPlacedBombAck(payload); + this.gameEventFacade.handleBombPlacedAck(payload); }, onPlayerDeadFromNetwork: (payload) => { this.combatFacade.handleNetworkPlayerDead(payload); diff --git a/apps/client/src/scenes/game/application/GameEventFacade.ts b/apps/client/src/scenes/game/application/GameEventFacade.ts new file mode 100644 index 0000000..065b982 --- /dev/null +++ b/apps/client/src/scenes/game/application/GameEventFacade.ts @@ -0,0 +1,39 @@ +/** + * GameEventFacade + * ゲーム中に受信したイベントの適用窓口を提供する + * セッション開始同期と爆弾反映の処理を集約する + */ +import type { BombPlacedAckPayload, BombPlacedPayload } from "@repo/shared"; +import type { BombManager } from "../entities/bomb/BombManager"; + +/** GameEventFacade の初期化入力 */ +export type GameEventFacadeOptions = { + onGameStart: (startTime: number) => void; + getBombManager: () => BombManager | null; +}; + +/** 受信イベントの適用窓口を提供する */ +export class GameEventFacade { + private readonly onGameStart: (startTime: number) => void; + private readonly getBombManager: () => BombManager | null; + + constructor({ onGameStart, getBombManager }: GameEventFacadeOptions) { + this.onGameStart = onGameStart; + this.getBombManager = getBombManager; + } + + /** サーバー同期のゲーム開始時刻を適用する */ + public handleGameStart(startTime: number): void { + this.onGameStart(startTime); + } + + /** 他プレイヤー爆弾設置を反映する */ + public handleBombPlacedFromOthers(payload: BombPlacedPayload): void { + this.getBombManager()?.applyPlacedBombFromOthers(payload); + } + + /** 爆弾設置ACKを反映する */ + public handleBombPlacedAck(payload: BombPlacedAckPayload): void { + this.getBombManager()?.applyPlacedBombAck(payload); + } +} \ No newline at end of file diff --git a/apps/client/src/scenes/game/application/network/GameActionSender.ts b/apps/client/src/scenes/game/application/network/GameActionSender.ts new file mode 100644 index 0000000..8c22eb2 --- /dev/null +++ b/apps/client/src/scenes/game/application/network/GameActionSender.ts @@ -0,0 +1,32 @@ +/** + * GameActionSender + * ゲーム中の送信アクションを提供する + * マネージャー層からソケット実装を分離する + */ +import type { PlaceBombPayload } from "@repo/shared"; +import { socketManager } from "@client/network/SocketManager"; + +/** ゲーム中送信アクションのインターフェース型 */ +export type GameActionSender = { + readyForGame: () => void; + sendPlaceBomb: (payload: PlaceBombPayload) => void; + sendBombHitReport: (bombId: string) => void; +}; + +/** ソケット経由でゲーム中送信アクションを実行する実装 */ +export class SocketGameActionSender implements GameActionSender { + /** ゲーム準備完了をサーバーへ送信する */ + public readyForGame(): void { + socketManager.game.readyForGame(); + } + + /** 爆弾設置要求をサーバーへ送信する */ + public sendPlaceBomb(payload: PlaceBombPayload): void { + socketManager.game.sendPlaceBomb(payload); + } + + /** 被弾報告をサーバーへ送信する */ + public sendBombHitReport(bombId: string): void { + socketManager.game.sendBombHitReport({ bombId }); + } +} \ No newline at end of file