/**
* registerGameHandlers
* ゲーム関連イベントの受信ハンドラを登録する
*/
import { Server, Socket } from "socket.io";
import { protocol } from "@repo/shared";
import { readyForGameCoordinator } from "@server/application/coordinators/readyForGameCoordinator";
import { startGameCoordinator } from "@server/application/coordinators/startGameCoordinator";
import type {
FindRoomByOwnerPort,
FindRoomByPlayerPort,
RoomPhaseTransitionPort,
} from "@server/domains/room/application/ports/roomUseCasePorts";
import type {
BombPlacementPort,
MovePlayerPort,
ReadyForGamePort,
StartGamePort,
} from "@server/domains/game/application/ports/gameUseCasePorts";
import { pingUseCase } from "@server/domains/game/application/useCases/pingUseCase";
import { createCommonHandlerContext } from "@server/network/handlers/CommonHandler";
import { isMovePayload, isPingPayload, isPlaceBombPayload } from "@server/network/validation/socketPayloadValidators";
import { createServerSocketOnBridge } from "@server/network/handlers/socketEventBridge";
import { createPayloadGuard } from "@server/network/handlers/payloadGuard";
import { registerMoveInputHandler } from "./input/registerMoveInputHandler";
import { registerPlaceBombInputHandler } from "./input/registerPlaceBombInputHandler";
import { createGameOutputAdapter } from "./createGameOutputAdapter";
/** ゲーム受信イベントごとの入力検証関数を保持するテーブル */
const gamePayloadValidators = {
[protocol.SocketEvents.PING]: isPingPayload,
[protocol.SocketEvents.MOVE]: isMovePayload,
[protocol.SocketEvents.PLACE_BOMB]: isPlaceBombPayload,
} as const;
/** ゲームイベントの購読とユースケース呼び出しを設定する */
export const registerGameHandlers = (
io: Server,
socket: Socket,
gameManager: StartGamePort & ReadyForGamePort & MovePlayerPort & BombPlacementPort,
roomManager: FindRoomByOwnerPort & FindRoomByPlayerPort & RoomPhaseTransitionPort
) => {
const common = createCommonHandlerContext(io, socket);
const gameOutputAdapter = createGameOutputAdapter(common);
const { onEvent } = createServerSocketOnBridge(socket);
const { guardOnEvent } = createPayloadGuard(socket.id);
const guardPingPayload = guardOnEvent(
protocol.SocketEvents.PING,
gamePayloadValidators[protocol.SocketEvents.PING]
);
const guardMovePayload = guardOnEvent(
protocol.SocketEvents.MOVE,
gamePayloadValidators[protocol.SocketEvents.MOVE]
);
const guardPlaceBombPayload = guardOnEvent(
protocol.SocketEvents.PLACE_BOMB,
gamePayloadValidators[protocol.SocketEvents.PLACE_BOMB]
);
// 遅延計測用のPINGを検証しPONGを返す
onEvent(protocol.SocketEvents.PING, (clientTime) => {
if (!guardPingPayload(clientTime)) {
return;
}
pingUseCase({
clientTime,
output: gameOutputAdapter,
});
});
// オーナー開始要求に応じてゲーム進行ユースケースを起動する
onEvent(protocol.SocketEvents.START_GAME, () => {
startGameCoordinator({
ownerId: socket.id,
gameManager,
roomManager,
output: gameOutputAdapter,
});
});
// 参加者の準備完了通知を受けて現在状態を返す
onEvent(protocol.SocketEvents.READY_FOR_GAME, () => {
readyForGameCoordinator({
socketId: socket.id,
gameManager,
roomManager,
output: gameOutputAdapter,
});
});
registerMoveInputHandler({
socketId: socket.id,
gameManager,
onEvent,
guardMovePayload,
});
registerPlaceBombInputHandler({
socketId: socket.id,
gameManager,
roomManager,
output: gameOutputAdapter,
onEvent,
guardPlaceBombPayload,
});
};