2024-11-29 ~ 30 CH-6 최종 프로젝트 9 더미 클라이언트 제작

MOON·2024년 12월 11일
0

내일배움캠프 과제

목록 보기
43/48

몇가지 테스트를 위해 더미 클라이언트를 만들어 보았습니다.

설계 단계(클라이언트 입장 생각하기)

  • 현재 클라이언트는 gateway서버와 game서버 연결합니다.
  • 순서 생각하기
    • 로그인 -> 방 생성 -> 게임 참가 등등

코드 작성

Client 클래스를 만들어 좀 더 테스트 파일에 빠르게 사용할 수 있게 작성해 보았습니다.

class Client {
  constructor() {
    this.gatewayServerOption = {
      host: '0.0.0.0',
      port: 6000,
    };

    this.gameServerOption = {
      host: '0.0.0.0',
      port: 6666,
    };

    // ------------------------클라이언트 데이터--------------지속적인 추가----------------------
    this.token;
    this.userId;
    this.sequence;
    this.host = false;
    this.gameSessionId;

    // 호스트 기준
    this.inviteCode;

    this.position = new Position();
    this.rotation = new Rotation();

    // ------------------------클라이언트 데이터---------------------------------------------

    // 클라이언트 연결 설정
    this.buffer = Buffer.alloc(0);

    // 게이트 웨이 서버랑 연결된 소켓
    this.gateToClient = net.connect(this.gatewayServerOption, () => {
      console.log('Connection established');
    });
    this.gateToClient.on('data', (data) => this.onData(data));
    this.gateToClient.on('end', () => this.onEnd());
    this.gateToClient.on('error', (error) => this.onError(error));

    // 게임 서버랑 연결된 소켓
    this.gameToClient;
  }

  gameServerConnect() {
    this.gameToClient = net.connect(this.gameServerOption, () => {
      console.log('Connection established');
    });

    this.gameToClient.on('data', (data) => this.onData(data));
    this.gameToClient.on('end', () => this.onEnd());
    this.gameToClient.on('error', (error) => this.onError(error));
  }

  sendPacket(packetType, data, clientType) {
    const packet = serializer(packetType, data, this.sequence++);

    switch (clientType) {
      case CLIENTTYPE.GATECLIENT:
        {
          this.gateToClient.write(packet);
        }
        break;
      case CLIENTTYPE.GAMECLIENT:
        {
          this.gameToClient.write(packet);
        }
        break;
    }
  }

  onData(data) {
    this.buffer = Buffer.concat([this.buffer, data]);

    while (
      this.buffer.length >=
      config.packet.typeLength + config.packet.versionLength
    ) {
      let offset = 0;
      const packetType = this.buffer.readUint16BE(offset);
      offset += config.packet.typeLength;

      const versionLength = this.buffer.readUint8(offset);
      offset += config.packet.versionLength;

      const totalHeaderLength =
        config.packet.typeLength +
        config.packet.versionLength +
        versionLength +
        config.packet.sequenceLength +
        config.packet.payloadLength;

      if (this.buffer.length < totalHeaderLength) {
        break;
      }

      const version = this.buffer
        .subarray(offset, offset + versionLength)
        .toString('utf-8');
      offset += versionLength;

      if (version !== config.client.clientVersion) {
        console.error(`Version mismatch: ${version}`);
        break;
      }

      const sequence = this.buffer.readUint32BE(offset);
      offset += config.packet.sequenceLength;

      const payloadLength = this.buffer.readUint32BE(offset);
      offset += config.packet.payloadLength;

      const totalPacketLength = totalHeaderLength + payloadLength;
      if (this.buffer.length < totalPacketLength) {
        break;
      } else {
        const payloadBuffer = this.buffer.subarray(
          offset,
          offset + payloadLength,
        );
        offset += payloadLength;

        try {
          const { payload } = parserPacket(payloadBuffer);
          this.buffer = this.buffer.subarray(offset);
          this.handlePacket(packetType, payload);
        } catch (e) {
          console.error('Error parsing packet:', e);
          this.buffer = this.buffer.subarray(offset);
        }
      }
    }
  }

  // 연결 종료 처리
  onEnd() {
    console.log('Connection closed by the server');
  }

  // 오류 처리
  onError(error) {
    console.error('Client error:', error.message);
  }

  // 패킷 처리
  handlePacket = (packetType, payload) => {
    //console.log(packetType, payload);

    switch (packetType) {
      case PACKET_TYPE.PingRequest:
        {
          const { timestamp } = payload;
          const data = { timestamp };
>
          // this.sendPacket(
          //   PACKET_TYPE.PingResponse,
          //   data,
          //   CLIENTTYPE.GAMECLIENT,
          // );
        }
        break;
      case PACKET_TYPE.ItemChangeNotification:
        {
        }
        break;
      case PACKET_TYPE.LoginResponse:
        {
          const { globalFailCode, userId, token } = payload;
          this.userId = userId;
          this.token = token;
        }
        break;
      case PACKET_TYPE.CreateRoomResponse:
        {
          const { globalFailCode, message, gameSessionId, inviteCode } =>
            payload;
          this.gameSessionId = gameSessionId;
          this.inviteCode = inviteCode;
        }
        break;
      case PACKET_TYPE.JoinRoomResponse:
        {
          const { globalFailCode, message, gameSessionId, playerInfos } =
            payload;
          this.gameSessionId = gameSessionId;
        }
        break;
      case PACKET_TYPE.JoinRoomNotification:
        {
          // Handle JoinRoomNotification
        }
        break;
      case PACKET_TYPE.SpawnInitialDataRequest:
        {
          const position = {
            x: 0,
            y: 0,
            z: 0,
          };
          const rotation = {
            x: 0,
            y: 0,
            z: 0,
          };
          const moveInfo = {
            position,
            rotation,
          };
          const ghostInfos = [
            {
              ghostId: 1,
              ghostTypeId: 1,
              moveInfo,
            },
            {
              ghostId: 2,
              ghostTypeId: 2,
              moveInfo,
            },
          ];
          const itemInfos = [
            {
              itemId: 1,
              itemTypeId: 1,
              position,
            },
            {
              itemId: 2,
              itemTypeId: 1,
              position,
            },
            {
              itemId: 3,
              itemTypeId: 1,
              position,
            },
            {
              itemId: 4,
              itemTypeId: 1,
              position,
            },
          ];
          const data = {
            ghostInfos,
            itemInfos,
          };
          this.sendPacket(
            PACKET_TYPE.SpawnInitialDataResponse,
            data,
            CLIENTTYPE.GAMECLIENT,
          );
        }
        break;
      case PACKET_TYPE.StartStageNotification:
        {
          const { globalFailCode, message, ghostInfos, itemInfos } = payload;
        }
        break;

      case PACKET_TYPE.ItemDisuseNotification:
        {
          // Handle ItemDisuseNotification
        }
        break;
      default: {
        console.log(
          `패킷타입 잘못보냈어 확인해봐 packetType: ${packetType} payload: ${payload}`,
        );
      }
    }
  };
}
export default Client;

실제 테스트 코드(더미 클라이언트)

여러 테스트 상황의 로직 작성하여 테스트

loadProtos();

const client = new Client();

const delay = (ms) => {
  return new Promise((resolve) => setTimeout(resolve, ms));
};

// -------------------------------로비서버 요청---------------------------

// 로그인 요청
// setTimeout(() => {
//   const data = {
//     id: 'test1',
//     password: '1234',
//   };

//   client.sendPacket(PACKET_TYPE.LoginRequest, data, CLIENTTYPE.GATECLIENT);
// }, 1000);

// --------------------------게임 서버 연결----------------------------

client.gameServerConnect();

// ------------------------------게임 서버 요청----------------------------

// 방 생성 요청
await delay(1000);
let data = {
  userId: '나는클라이언트1',
  token: '토큰인가',
};
client.host = true;

// 로그인 처리 후 send
client.sendPacket(PACKET_TYPE.CreateRoomRequest, data, CLIENTTYPE.GAMECLIENT);

// 방 참가 요청
// setTimeout(() => {
//   const data = {
//     userId: client.userId,
//     inviteCode: 'PF1XYBO1LB', // inviteCode는 수시로 변경해줘야 테스트 가능
//     token: client.token,
//   };

//   // 로그인 처리 후 send
//   client.sendPacket(
//     PACKET_TYPE.JoinRoomRequest,
//     data,
//     CLIENTTYPE.GAMECLIENT,
//   );
// }, 3000);

// 게임 시작 요청
await delay(1000);
data = {
  gameSessionId: client.gameSessionId,
  difficultyId: 'DIF0001',
};
client.sendPacket(PACKET_TYPE.StartStageRequest, data, CLIENTTYPE.GAMECLIENT);

//아이템 획득 요청
// await delay(1000);
// let i = 1;
// while (i < 5) {
//   console.log(i);
//   const data = {
//     itemId: i,
//     inventorySlot: i,
//   };

//   client.sendPacket(PACKET_TYPE.ItemGetRequest, data, CLIENTTYPE.GAMECLIENT);
//   i++;
//   await delay(50);
// }

// 문 상호작용 요청
await delay(1000);

for (let i = 0; i < 50; i++) {
  const data = {
    doorId: Math.floor(Math.random() * 10) + 1,
    doorState: Math.floor(Math.random() * 3) + 1,
  };

  client.sendPacket(PACKET_TYPE.DoorToggleRequest, data, CLIENTTYPE.GAMECLIENT);

  await delay(50);
}

오늘의 회고
오늘도 코드가 많은 벨로그네요 하핫 생각보다 노가다?작업이 많이 있었고 클라이언트 입장에서 생각해보니 테스트할떄 많이 도움이 되었던것 같았습니다. 굿

오늘도 화이팅!

profile
안녕하세요

0개의 댓글

관련 채용 정보