2024-11-26 CH-6 최종 프로젝트 8 담당 로직 구현 (ghost)

MOON·2024년 12월 2일
0

내일배움캠프 과제

목록 보기
41/48

어제 서버팀에서도 회의를 진행하면서 담당을 각자 맡게 되었습니다. 저는 ghost관련 로직이 재밌어보여 ghost관련 서버 로직을 구현해 보고싶다하여 감사하게도 제가 맡게 되었습니다.

1. 패킷 명세 확인

추가된 패킷은 현재 고스트 상태 변화 패킷이랑 고스트 피격 패킷이 있습니다. + 귀신 특수 상태 변화 패킷(특정 귀신마다의 특수 상태를 나타내기 위한 패킷입니다.)

고스트 상태 변화 패킷

클라이언트에서 서버로 요청
C2S_GhostStateChangeRequest
{
  GhostStateInfo ghostStateInfo = 1;
}
서버에서 클라이언트 통지
S2C_GhostStateChangeNotification
{
  GhostStateInfo ghostStateInfo = 1;
} 

해당 고스트의 상태를 클라이언트가(호스트만 해당) 알려주면 그걸 다른 클라이언트도 상태 동기화를 위해 서버에서 Notification을 보내주는 형태입니다.

고스트 피격 패킷

C2S_GhostAttackedRequest
{
  uint32 ghostId = 1;
  string userId = 2;
}

사실 이 패킷은 현재 사용되는 패킷은 아닙니다. 이후 플레이어가 고스트에게 무언가 피격을 할 것을 대비해 그냥 만들어 둔 패킷입니다...이후 삭제하거나 수정 할 것 같네요

고스트 특수 상태 변화 패킷

C2S_GhostSpecialStateRequest
{
uint32 ghostId = 1;
uint32 specialStateType = 2;

bool isOn = 3;
}

S2C_GhostSpecialStateNotification
{
uint32 ghostId = 1;
uint32 specialStateType = 2;

bool isOn = 3;
}

2. 로직 작성

Ghost 클래스

class Ghost {
  constructor(id, ghostTypeId, position, rotation, state = 0) {
    this.id = id;
    this.ghostTypeId = ghostTypeId;
    this.position = new Position(position.x, position.y, position.z);
    this.rotation = new Rotation(rotation.x, rotation.y, rotation.z);
    this.state = state;
  }

  /**
   * 귀신의 상태변경 함수입니다.
   * @param {*} state
   */
  setState(state) {
    this.state = state;
  }

  /**
   * 귀신 타입에 따른 공격 함수입니다.
   * @param {*} user 공격 당할 플레이어(유저)
   */
  attack(user) {
    switch (this.ghostTypeId) {
      case GHOST_TYPE_ID.SMILINGGENTLEMAN:
        {
          user.character.life--;
        }
        break;
      case GHOST_TYPE_ID.MASSAGER:
        {
        }
        break;
      case GHOST_TYPE_ID.NAUGHTYBOY:
        {
        }
        break;
      case GHOST_TYPE_ID.DARKHAND:
        {
        }
        break;
      case GHOST_TYPE_ID.GRIMREAPER:
        {
          user.character.life = 0;
        }
        break;
    }
  }
}

먼저 고스트 클래스에 상태 변환 메서드와 공격 메서드를 추가하였습니다.

상태 변화 요청 패킷

export const ghostStateChangeRequestHandler = ({ socket, payload }) => {
  try {
    const { ghostStateInfo } = payload;
    const { ghostId, ghostState } = ghostStateInfo;
    // user 검증
    const user = getUserById(socket.userId);
    if (!user) {
      throw new CustomError(ErrorCodesMaps.USER_NOT_FOUND);
    }

    // 게임 세션 검증
    const gameSession = getGameSessionById(user.gameId);
    if (!gameSession) {
      throw new CustomError(ErrorCodesMaps.GAME_NOT_FOUND);
    }

    ghostStateChangeNotification(gameSession, ghostId, ghostState);
  } catch (e) {
    handleError(e);
  }
};

ghostStateChangeNotification

/**
 * 고스트의 상태변화 통지를 알리는 함수입니다. (호스트 제외)
 * @param {*} gameSession
 * @param {*} ghostId
 * @param {*} ghostState
 */
export const ghostStateChangeNotification = (
  gameSession,
  ghostId,
  ghostState,
) => {
  // 고스트 검증
  const ghost = gameSession.getGhost(ghostId);
  if (!ghost) {
    throw new CustomError(ErrorCodesMaps.GHOST_NOT_FOUND);
  }
  ghost.setState(ghostState);

  const data = {
    ghostId,
    ghostState,
  };

  // 호스트 제외 packet 전송
  gameSession.users.forEach((user) => {
    if (gameSession.hostId === user.id) {
      return;
    }
    const packet = serializer(
      PACKET_TYPE.GhostStateChangeNotification,
      data,
      user.socket.sequence++,
    );
    user.socket.write(packet);
  });

귀신의 특수 상태 변화 패킷

export const ghostSpecialStateRequestHandler = ({ socket, payload }) => {
  try {
    const { ghostId, specialStateType, isOn } = payload;
    // user 검증
    const user = getUserById(socket.userId);
    if (!user) {
      throw new CustomError(ErrorCodesMaps.USER_NOT_FOUND);
    }

    // 게임 세션 검증
    const gameSession = getGameSessionById(user.gameId);
    if (!gameSession) {
      throw new CustomError(ErrorCodesMaps.GAME_NOT_FOUND);
    }

    // 추후 특수 상태에 따른 변화 로직이 있으면 추가 TODO
    // switch(specialStateType) {

    // }

    ghostSpecialStateNotification(gameSession, payload);
  } catch (e) {
    handleError(e);
  }
};

ghostSpecialStateNotification

/**
 * 귀신의 특수상태 통지를 알리는 함수입니다. (호스트 제외)
 * @param {*} gameSession
 * @param {*} payload
 */
export const ghostSpecialStateNotification = (gameSession, payload) => {
  const { ghostId, specialStateType, isOn } = payload;

  // 고스트 검증
  const ghost = gameSession.getGhost(ghostId);
  if (!ghost) {
    throw new CustomError(ErrorCodesMaps.GHOST_NOT_FOUND);
  }

  const data = {
    ghostId,
    specialStateType,
    isOn,
  };

  // 호스트 제외 packet 전송
  gameSession.users.forEach((user) => {
    if (gameSession.hostId === user.id) {
      return;
    }

    const packet = serializer(
      PACKET_TYPE.GhostSpecialStateNotification,
      data,
      user.socket.sequence++,
    );
    user.socket.write(packet);
  });
};

오늘의 회고
일단 맡은 일은 어느정도 구상은 끝이 났고 여기서 추가할 것들 추가하고 나머지 귀신들의 특징들을 파악하면서 로직을 수정해야 될 것 같습니다. 생각보다 할게 별로 없었습니다. 하핫..뭐지ㅋㅋㅋ

오늘도 화이팅!

profile
안녕하세요

0개의 댓글

관련 채용 정보