Newer
Older
PixelPaintWar / apps / server / src / domains / room / RoomManager.ts
/**
 * RoomManager
 * ルーム状態の保持とルーム操作サービスへの委譲を担うマネージャ
 */
import { domain } from "@repo/shared";
import { RoomJoinService } from "./application/services/RoomJoinService";
import { RoomExitService } from "./application/services/RoomExitService";
import { RoomPhaseService } from "./application/services/RoomPhaseService";
import { RoomQueryService } from "./application/services/RoomQueryService";
import type {
  JoinRoomResult,
  RoomDisconnectResult,
  RoomPhaseTransitionResult,
  SelectTeamResult,
} from "./application/ports/roomUseCasePorts";

/** ルーム操作の公開インターフェースを提供するマネージャ */
export class RoomManager {
  private rooms: Map<string, domain.room.Room> = new Map();
  private roomJoinService: RoomJoinService;
  private roomExitService: RoomExitService;
  private roomPhaseService: RoomPhaseService;
  private roomQueryService: RoomQueryService;

  constructor() {
    this.roomJoinService = new RoomJoinService(this.rooms);
    this.roomExitService = new RoomExitService(this.rooms);
    this.roomPhaseService = new RoomPhaseService(this.rooms);
    this.roomQueryService = new RoomQueryService(this.rooms);
  }

  // ルームにプレイヤーを追加する,ルームが未作成なら新規作成する
  public addPlayerToRoom(roomId: string, socketId: string, playerName: string): JoinRoomResult {
    return this.roomJoinService.addPlayerToRoom(roomId, socketId, playerName);
  }

  // プレイヤーをルームから削除し,更新が発生したルーム配列を返す
  public removePlayer(socketId: string): RoomDisconnectResult {
    return this.roomExitService.removePlayer(socketId);
  }

  // オーナーIDからルームを取得する
  public getRoomByOwnerId(ownerId: string): domain.room.Room | undefined {
    return this.roomQueryService.getRoomByOwnerId(ownerId);
  }

  // ルームIDからルームを取得する
  public getRoomById(roomId: string): domain.room.Room | undefined {
    return this.roomQueryService.getRoomById(roomId);
  }

  // プレイヤーIDから所属ルームを取得する
  public getRoomByPlayerId(playerId: string): domain.room.Room | undefined {
    return this.roomQueryService.getRoomByPlayerId(playerId);
  }

  // ルーム状態をPLAYINGへ更新する
  public markRoomPlaying(roomId: string): RoomPhaseTransitionResult {
    return this.roomPhaseService.markRoomPlaying(roomId);
  }

  // ルーム状態をWAITINGへ更新する
  public markRoomWaiting(roomId: string): RoomPhaseTransitionResult {
    return this.roomPhaseService.markRoomWaiting(roomId);
  }

  // ロビー設定(ゲーム人数・フィールドサイズ・チーム割り当て方式)を更新してルームを返す
  public updateLobbySettings(
    roomId: string,
    targetPlayerCount: number,
    fieldSizePreset: domain.room.Room["fieldSizePreset"],
    teamAssignmentMode: domain.room.TeamAssignmentMode,
  ): domain.room.Room | undefined {
    const room = this.rooms.get(roomId);
    if (!room || room.status !== domain.room.RoomPhase.WAITING) {
      return undefined;
    }

    room.targetPlayerCount = targetPlayerCount;
    room.fieldSizePreset = fieldSizePreset;
    room.teamAssignmentMode = teamAssignmentMode;
    return room;
  }

  // プレイヤーのチーム選択を更新して所属ルームを返す
  // プレイヤーのチーム選択を更新する,チームが満員なら team_full を返す
  public selectTeam(
    playerId: string,
    preferredTeamId: number | null,
  ): SelectTeamResult {
    const room = this.roomQueryService.getRoomByPlayerId(playerId);
    if (!room || room.status !== domain.room.RoomPhase.WAITING) {
      return { status: "not_found" };
    }

    const member = room.players.find((p) => p.id === playerId);
    if (!member) {
      return { status: "not_found" };
    }

    // チーム指定がある場合は人数上限を確認する(上限 = ゲーム人数 / 4)
    if (preferredTeamId !== null) {
      const maxPerTeam = Math.floor(
        (room.targetPlayerCount ?? room.maxPlayers) / 4,
      );
      const currentCount = room.players.filter(
        (p) => p.preferredTeamId === preferredTeamId && p.id !== playerId,
      ).length;
      if (currentCount >= maxPerTeam) {
        return { status: "team_full", teamId: preferredTeamId };
      }
    }

    member.preferredTeamId = preferredTeamId;
    return { status: "ok", room };
  }

  // ルームを削除する
  public deleteRoom(roomId: string): boolean {
    return this.rooms.delete(roomId);
  }
}