몇가지 테스트를 위해 더미 클라이언트를 만들어 보았습니다.
코드 작성
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); }
오늘의 회고
오늘도 코드가 많은 벨로그네요 하핫 생각보다 노가다?작업이 많이 있었고 클라이언트 입장에서 생각해보니 테스트할떄 많이 도움이 되었던것 같았습니다. 굿
오늘도 화이팅!