Connection.ts 2.86 KB
import { Socket } from "socket.io";
import {
  RawMessage,
  ServerInboundMessage,
  ServerInboundMessageKey,
  ServerOutboundMessage,
  ServerOutboundMessageKey,
  ServerResponse,
} from "../../common";
import { Room } from "../room/Room";
import { RoomManager } from "../room/RoomManager";
import { User } from "../user/User";
import { MessageValidator } from "./MessageValidator";
import { SocketWrapper } from "./SocketWrapper";

export class Connection {
  public readonly socket: SocketWrapper;
  public readonly roomManager: RoomManager;

  static readonly validator: MessageValidator = new MessageValidator();

  public user?: User;

  constructor(socket: SocketWrapper, roomManager: RoomManager) {
    this.socket = socket;
    this.roomManager = roomManager;
    socket.setHandler((raw) => this.handleRaw(raw));
    socket.setDisconnectHandler(() => this.handleDisconnect());
    socket.setPingHandler(() => this.handlePing());
  }

  public send<T extends ServerOutboundMessageKey>(
    type: T,
    message: ServerOutboundMessage<T>
  ) {
    this.socket.send({
      type: type as string,
      message: message,
    });
  }

  public sendPing() {
    this.socket.sendPing();
  }

  public handleRaw(raw: RawMessage): ServerResponse<any> {
    if (!raw || !raw.message || !raw.type) {
      return { ok: false };
    }
    const type = raw.type as ServerInboundMessageKey;
    const message = raw.message;

    if (!Connection.validator.validate(type, message)) {
      return { ok: false };
    }

    // 유저 정보가 없으므로 로그인은 따로 핸들링
    if (type === "login") {
      return this.handleLogin(message);
    }

    // Game > Room > User 순으로 전달
    if (this.user?.room?.game) {
      const response = this.user.room.game.handler.handle(
        type,
        this.user,
        message
      );
      if (response) return response;
    }
    if (this.user?.room) {
      const response = this.user.room.handler.handle(type, this.user, message);
      if (response) return response;
    }
    if (this.user) {
      const response = this.user.handler.handle(type, this.user, message);
      if (response) return response;
    }
    return { ok: false };
  }

  private handleLogin(
    message: ServerInboundMessage<"login">
  ): ServerResponse<"login"> {
    if (message.nickname.length > 12) {
      return { ok: false, reason: "닉네임은 최대 12글자입니다." };
    }
    if (message.nickname.trim().length === 0) {
      return { ok: false, reason: "닉네임을 공백으로 설정할 수 없습니다." };
    }

    this.user = new User(message.nickname, this);
    // console.log(`User ${message.username} has logged in!`);

    return { ok: true, result: this.user.username };
  }

  public handleDisconnect(): void {
    this.user?.disconnected();
  }

  public handlePing(): void {
    if (this.user) {
      this.user?.room?.pong(this.user);
    }
  }
}