diff --git a/apps/client/src/network/handlers/GameHandler.ts b/apps/client/src/network/handlers/GameHandler.ts index 7783fc6..8927232 100644 --- a/apps/client/src/network/handlers/GameHandler.ts +++ b/apps/client/src/network/handlers/GameHandler.ts @@ -6,6 +6,7 @@ import type { Socket } from "socket.io-client"; import { protocol } from "@repo/shared"; import type { + BombHitReportPayload, BombPlacedAckPayload, BombPlacedPayload, CurrentPlayersPayload, @@ -45,6 +46,7 @@ offBombPlacedAck: (callback: (payload: BombPlacedAckPayload) => void) => void; sendMove: (x: number, y: number) => void; sendPlaceBomb: (payload: PlaceBombPayload) => void; + sendBombHitReport: (payload: BombHitReportPayload) => void; readyForGame: () => void; }; @@ -123,6 +125,9 @@ sendPlaceBomb: (payload) => { emitEvent(protocol.SocketEvents.PLACE_BOMB, payload); }, + sendBombHitReport: (payload) => { + emitEvent(protocol.SocketEvents.BOMB_HIT_REPORT, payload); + }, readyForGame: () => { emitEvent(protocol.SocketEvents.READY_FOR_GAME); } diff --git a/apps/client/src/scenes/game/GameManager.ts b/apps/client/src/scenes/game/GameManager.ts index 2b83226..734cb76 100644 --- a/apps/client/src/scenes/game/GameManager.ts +++ b/apps/client/src/scenes/game/GameManager.ts @@ -183,7 +183,7 @@ appearanceResolver: this.appearanceResolver, onBombExploded: (payload) => { const result = this.bombHitOrchestrator?.handleBombExploded(payload); - this.handleBombHitEvaluation(result); + this.handleBombHitEvaluation(result, payload.bombId); }, }); } @@ -206,9 +206,14 @@ /** 爆弾当たり判定の評価結果を受け取り,後続処理へ接続する */ private handleBombHitEvaluation( - _result: BombHitEvaluationResult | undefined, + result: BombHitEvaluationResult | undefined, + bombId: string, ): void { - // 次フェーズでサーバー通知や被弾演出の接続に利用する + if (result !== "hit") { + return; + } + + socketManager.game.sendBombHitReport({ bombId }); } /** diff --git a/apps/server/src/network/handlers/game/registerGameHandlers.ts b/apps/server/src/network/handlers/game/registerGameHandlers.ts index 83693f9..c6ff320 100644 --- a/apps/server/src/network/handlers/game/registerGameHandlers.ts +++ b/apps/server/src/network/handlers/game/registerGameHandlers.ts @@ -18,7 +18,12 @@ import { pingUseCase } from "@server/domains/game/application/useCases/pingUseCase"; import { resolveRuntimeByPlayerId } from "@server/domains/room/application/services/RoomRuntimeResolver"; import { createCommonHandlerContext } from "@server/network/handlers/CommonHandler"; -import { isMovePayload, isPingPayload, isPlaceBombPayload } from "@server/network/validation/socketPayloadValidators"; +import { + isBombHitReportPayload, + isMovePayload, + isPingPayload, + isPlaceBombPayload, +} from "@server/network/validation/socketPayloadValidators"; import { createServerSocketOnBridge } from "@server/network/handlers/socketEventBridge"; import { createPayloadGuard } from "@server/network/handlers/payloadGuard"; import { createGameOutputAdapter } from "./createGameOutputAdapter"; @@ -28,6 +33,7 @@ [protocol.SocketEvents.PING]: isPingPayload, [protocol.SocketEvents.MOVE]: isMovePayload, [protocol.SocketEvents.PLACE_BOMB]: isPlaceBombPayload, + [protocol.SocketEvents.BOMB_HIT_REPORT]: isBombHitReportPayload, } as const; /** ゲームイベントの購読とユースケース呼び出しを設定する */ @@ -53,6 +59,10 @@ protocol.SocketEvents.PLACE_BOMB, gamePayloadValidators[protocol.SocketEvents.PLACE_BOMB] ); + const guardBombHitReportPayload = guardOnEvent( + protocol.SocketEvents.BOMB_HIT_REPORT, + gamePayloadValidators[protocol.SocketEvents.BOMB_HIT_REPORT] + ); // 遅延計測用のPINGを検証しPONGを返す onEvent(protocol.SocketEvents.PING, (clientTime) => { if (!guardPingPayload(clientTime)) { @@ -125,4 +135,22 @@ output: gameOutputAdapter, }); }); + + // 被弾報告を受信し,検証通過時にテストログを出力する + onEvent(protocol.SocketEvents.BOMB_HIT_REPORT, (data) => { + if (!guardBombHitReportPayload(data)) { + return; + } + + const runtime = resolveRuntimeByPlayerId(roomManager, runtimeRegistry, socket.id); + if (!runtime) { + return; + } + + console.log("[ServerTest] BOMB_HIT_REPORT received", { + roomId: runtime.roomId, + reporterSocketId: socket.id, + bombId: data.bombId, + }); + }); }; diff --git a/apps/server/src/network/handlers/payloadGuard.ts b/apps/server/src/network/handlers/payloadGuard.ts index 5bacded..896b162 100644 --- a/apps/server/src/network/handlers/payloadGuard.ts +++ b/apps/server/src/network/handlers/payloadGuard.ts @@ -11,7 +11,8 @@ | typeof protocol.SocketEvents.JOIN_ROOM | typeof protocol.SocketEvents.PING | typeof protocol.SocketEvents.MOVE - | typeof protocol.SocketEvents.PLACE_BOMB; + | typeof protocol.SocketEvents.PLACE_BOMB + | typeof protocol.SocketEvents.BOMB_HIT_REPORT; type EventBoundPayloadGuard = (payload: unknown) => payload is TPayload; /** @@ -59,6 +60,12 @@ socketId, }); break; + + case protocol.SocketEvents.BOMB_HIT_REPORT: + console.warn("[PayloadGuard] invalid BOMB_HIT_REPORT payload", { + socketId, + }); + break; } return false; diff --git a/apps/server/src/network/validation/socketPayloadValidators.ts b/apps/server/src/network/validation/socketPayloadValidators.ts index 5b62965..5449c57 100644 --- a/apps/server/src/network/validation/socketPayloadValidators.ts +++ b/apps/server/src/network/validation/socketPayloadValidators.ts @@ -2,7 +2,7 @@ * socketPayloadValidators * ソケット受信ペイロードの型ガードを提供する */ -import type { playerTypes, roomTypes, PlaceBombPayload } from "@repo/shared"; +import type { playerTypes, roomTypes, PlaceBombPayload, BombHitReportPayload } from "@repo/shared"; import type { PingPayload } from "@repo/shared"; import { isPlaceBombPayload as isValidPlaceBombPayload } from "@server/domains/game/entities/bomb/bombPayloadValidation"; @@ -34,6 +34,16 @@ return isValidPlaceBombPayload(value); }; +/** BOMB_HIT_REPORTイベントのペイロードが被弾報告であるか判定する */ +export const isBombHitReportPayload = (value: unknown): value is BombHitReportPayload => { + if (typeof value !== "object" || value === null) { + return false; + } + + const candidate = value as Record; + return isNonEmptyString(candidate.bombId); +}; + /** JOIN_ROOMイベントのペイロードが参加情報であるか判定する */ export const isJoinRoomPayload = (value: unknown): value is roomTypes.JoinRoomPayload => { if (typeof value !== "object" || value === null) {