Newer
Older
PixelPaintWar / apps / server / src / network / handlers / room / roomEventOrchestrators.ts
/**
 * roomEventOrchestrators
 * ルーム受信イベントごとの調停処理を提供する
 * 受信ハンドラからユースケース実行責務を分離する
 * 本ファイルではランタイム未解決ログ対象イベントを扱わない
 */
import { domain } from "@repo/shared";
import type { LobbySettingsUpdatePayload } from "@repo/shared";
import { joinRoomUseCase } from "@server/domains/room/application/useCases/joinRoomUseCase";
import { logEvent } from "@server/logging/logger";
import { logResults, logScopes, roomUseCaseLogEvents } from "@server/logging/index";
import type {
  JoinRoomEventRoomUseCasePort,
  JoinRoomEventRuntimeUseCasePort,
  LobbySettingsUpdateEventRoomUseCasePort,
} from "@server/network/types/connectionPorts";
import type { RoomOutputAdapter } from "./createRoomOutputAdapter";

/** JOIN_ROOMイベント調停で利用する依存集合 */
export type JoinRoomOrchestratorDeps = {
  socketId: string;
  roomManager: JoinRoomEventRoomUseCasePort;
  runtimeRegistry: JoinRoomEventRuntimeUseCasePort;
  output: RoomOutputAdapter;
  joinRoom: (roomId: string) => Promise<void>;
};

/** JOIN_ROOMイベントの入力ペイロード型 */
export type JoinRoomEventPayload = Parameters<typeof handleJoinRoomEvent>[1];

/** LOBBY_SETTINGS_UPDATEイベント調停で利用する依存集合 */
export type LobbySettingsUpdateOrchestratorDeps = {
  socketId: string;
  roomManager: LobbySettingsUpdateEventRoomUseCasePort;
  output: RoomOutputAdapter;
};

/** LOBBY_SETTINGS_UPDATEイベントを調停してルーム設定を更新し全員に通知する */
export const handleLobbySettingsUpdateEvent = (
  deps: LobbySettingsUpdateOrchestratorDeps,
  payload: LobbySettingsUpdatePayload,
): void => {
  const room = deps.roomManager.getRoomByOwnerId(deps.socketId);
  if (!room) {
    return;
  }

  const updatedRoom = deps.roomManager.updateLobbySettings(
    room.roomId,
    payload.targetPlayerCount,
    payload.fieldSizePreset,
  );
  if (!updatedRoom) {
    return;
  }

  deps.output.publishRoomUpdateToRoom(room.roomId, updatedRoom);
};

/** JOIN_ROOMイベントを調停して参加ユースケースを実行する */
export const handleJoinRoomEvent = async (
  deps: JoinRoomOrchestratorDeps,
  payload: domain.room.JoinRoomPayload,
): Promise<void> => {
  const joinResult = joinRoomUseCase({
    roomManager: deps.roomManager,
    runtimeRegistry: deps.runtimeRegistry,
    socketId: deps.socketId,
    data: payload,
    output: deps.output,
  });

  switch (joinResult.status) {
    case "full":
      logEvent(logScopes.NETWORK, {
        event: roomUseCaseLogEvents.JOIN_ROOM,
        result: logResults.REJECTED_ROOM_FULL,
        roomId: payload.roomId,
        socketId: deps.socketId,
      });
      return;

    case "playing":
      logEvent(logScopes.NETWORK, {
        event: roomUseCaseLogEvents.JOIN_ROOM,
        result: logResults.REJECTED_ROOM_PLAYING,
        roomId: payload.roomId,
        socketId: deps.socketId,
      });
      return;

    case "duplicate":
      logEvent(logScopes.NETWORK, {
        event: roomUseCaseLogEvents.JOIN_ROOM,
        result: logResults.REJECTED_DUPLICATE,
        roomId: payload.roomId,
        socketId: deps.socketId,
      });
      return;

    case "joined":
      await deps.joinRoom(payload.roomId);
      deps.output.publishRoomUpdateToRoom(payload.roomId, joinResult.room);
      logEvent(logScopes.ROOM_USE_CASE, {
        event: roomUseCaseLogEvents.ROOM_UPDATE,
        result: logResults.EMITTED,
        roomId: payload.roomId,
        socketId: deps.socketId,
        ownerId: joinResult.room.ownerId,
        totalPlayers: joinResult.room.players.length,
      });
      return;

    default:
      return;
  }
};