message.ts 4.04 KB
import {
  Boolean,
  Number,
  String,
  Literal,
  Array,
  Tuple,
  Record,
  Union,
  Static,
  Optional,
} from "runtypes";
import {
  Role,
  RoomDescription,
  RoomDescriptionRecord,
  RoomInfo,
  RoomInfoRecord,
} from "./dataType";

// 서버로 들어오는 메세지 타입을 정의합니다.
// 'result' 속성은 서버 요청 결과에만 포함되는 특별한 속성입니다.
export class ServerInboundMessageRecordMap {
  // 로그인을 시도합니다.
  login = Record({ nickname: String, result: String });

  // 방 목록을 요청합니다.
  roomList = Record({
    result: Array(RoomDescriptionRecord),
  });

  // 방을 만듭니다.
  createRoom = Record({
    name: String,
    result: RoomInfoRecord,
  });

  // 방에 접속합니다.
  joinRoom = Record({
    uuid: String,
    result: RoomInfoRecord,
  });

  // 방에서 나갑니다.
  leaveRoom = Record({});

  // 채팅을 보냅니다.
  chat = Record({
    message: String,
  });

  // 준비합니다.
  ready = Record({
    ready: Boolean,
  });

  // 방장이 게임을 시작합니다.
  // TODO: 주의! 아래 필드는 디버그 용도로만 사용됩니다. 추후에 준비 화면에서 공개적으로 설정하는 것으로 구현해야 합니다.
  startGame = Record({
    maxRound: Optional(Number),
    roundDuration: Optional(Number),
    roundTerm: Optional(Number),
  });

  // drawer가 단어를 선택합니다.
  chooseWord = Record({
    word: String,
  });

  // 브러시 정보를 변경합니다.
  setBrush = Record({
    size: Number,
    color: String,
    drawing: Boolean,
  });

  // 브러시를 이동합니다.
  moveBrush = Record({
    x: Number,
    y: Number,
  });

  // 핑을 보냅니다.
  ping = Record({});
}

type ServerInboundMessageMap = {
  [Key in keyof ServerInboundMessageRecordMap]: Static<
    ServerInboundMessageRecordMap[Key]
  >;
};

// 서버에서 나가는 메세지 타입을 정의합니다.
interface ServerOutboundMessageMap {
  // 방에 접속 중인 유저 목록이 업데이트 되었습니다.
  updateRoomUser: {
    state: "added" | "updated" | "removed";
    user: {
      username: string;
      nickname: string;
      admin: boolean;
      ready: boolean;
    };
  };

  // 다른 유저가 채팅을 보냈습니다.
  chat: {
    sender: string;
    message: string;
  };

  // 라운드가 시작되었습니다.
  startRound: {
    round: number;
    duration: number;
    roles: {
      username: string;
      nickname: string;
      role: Role;
    }[];
  };

  // drawer에게 선택할 수 있는 단어가 주어졌습니다.
  wordSet: {
    words: string[];
  };

  // 이번 라운드의 단어가 선택되었습니다.
  wordChosen: {
    length: number;
  };

  // 라운드 타이머 정보를 동기화합니다.
  timer: {
    state: "started" | "stopped";
    time: number;
  };

  // 라운드가 종료되었습니다.
  finishRound: {
    answer: string;
  };

  // 역할이 변경되었습니다.
  role: {
    username: string;
    role: Role;
  };

  // 보낸 단어가 정답 처리 되었습니다.
  answerAccepted: {
    answer: string;
  };

  // 게임이 종료되었습니다.
  finishGame: {};

  // 브러시 정보가 변경되었습니다.
  setBrush: {
    size: number;
    color: string;
    drawing: boolean;
  };

  // 브러시가 이동되었습니다.
  moveBrush: {
    x: number;
    y: number;
  };

  // 핑을 보냅니다.
  ping: {};
}

export interface RawMessage {
  type: string;
  message: any;
}

export type ServerInboundMessageKey = keyof ServerInboundMessageMap;

export type ServerInboundMessage<Key extends ServerInboundMessageKey> = Omit<
  ServerInboundMessageMap[Key],
  "result"
>;

export interface ServerResponse<Key extends ServerInboundMessageKey> {
  ok: boolean;
  reason?: string;
  result?: "result" extends keyof ServerInboundMessageMap[Key]
    ? ServerInboundMessageMap[Key]["result"]
    : never;
}

export type ServerOutboundMessage<Key extends keyof ServerOutboundMessageMap> =
  ServerOutboundMessageMap[Key];

export type ServerOutboundMessageKey = keyof ServerOutboundMessageMap;