상세 컨텐츠

본문 제목

PacketHandler

똑똑한 개발/C++ 게임개발

by 성댕쓰 2023. 5. 6. 00:27

본문

이전 시간에 만든 Packet을 쓰거나 읽을 때 필요한 공통적인 부분을 모듈화 해보자

 

ServerPacketHandler.h, ServerPacketHandler.cpp

#pragma once
enum
{
	S_TEST = 1 
}; 

struct BuffData
{
	uint64 buffId;
	float remainTime;
};

class ServerPacketHandler
{
public:
	static void HandlePacket(BYTE* buffer, int32 len);

	static SendBufferRef Make_S_TEST(uint64 id, uint32 hp, uint16 attack, vector<BuffData> buffs);
};
#include "pch.h"
#include "ServerPacketHandler.h"
#include "BufferReader.h"
#include "BufferWriter.h"

void ServerPacketHandler::HandlePacket(BYTE* buffer, int32 len)
{
	BufferReader br(buffer, len);
	
	PacketHeader header;
	br.Peek(&header);

	switch (header.id)
	{
	default:
		break;
	}
}

SendBufferRef ServerPacketHandler::Make_S_TEST(uint64 id, uint32 hp, uint16 attack, vector<BuffData> buffs)
{
	SendBufferRef sendBuffer = GSendBufferManager->Open(4096);
	
	BufferWriter bw(sendBuffer->Buffer(), sendBuffer->AllocSize());

	PacketHeader* header = bw.Reserve<PacketHeader>();
	// id(uint64), 체력(uint32), 공격력(uint16)
	bw << id << hp << attack;

	// 가변 데이터
	bw << (uint16)buffs.size();
	for (BuffData& buff : buffs)
	{
		bw << buff.buffId << buff.remainTime;
	}

	header->size = bw.WriteSize();
	header->id = S_TEST; // 1 : Test Msg

	sendBuffer->Close(bw.WriteSize());

	return sendBuffer;
}

 

ClientPacketHandler.h, ClientPacketHandler.cpp

#pragma once

enum
{
	S_TEST = 1
};

class ClientPacketHandler
{
public:
	static void HandlePacket(BYTE* buffer, int32 len);

	static void Handle_S_TEST(BYTE* buffer, int32 len);
};
#include "pch.h"
#include "ClientPacketHandler.h"
#include "BufferReader.h"

void ClientPacketHandler::HandlePacket(BYTE* buffer, int32 len)
{
	BufferReader br(buffer, len);

	PacketHeader header;
	br >> header;

	switch (header.id)
	{
	case S_TEST:
		Handle_S_TEST(buffer, len);
		break;
	}
}

// 패킷 설계 TEMP
struct BuffData
{
	uint64 buffId;
	float remainTime;
};

struct S_TEST
{
	uint64 id;
	uint32 hp;
	uint16 attack;
	// 가변 데이터
	// 1) 문자열 (ex. name)
	// 2) 그냥 바이트 배열 (ex. 길드 이미지)
	// 3) 일반 리스트
	vector<int64> buffs;
};

void ClientPacketHandler::Handle_S_TEST(BYTE* buffer, int32 len)
{
	BufferReader br(buffer, len);

	PacketHeader header;
	br >> header;

	uint64 id;
	uint32 hp;
	uint16 attack;
	br >> id >> hp >> attack;

	cout << "ID: " << id << " HP : " << hp << " ATT : " << attack << endl;

	vector<BuffData> buffs;
	uint16 buffCount;
	br >> buffCount;

	buffs.resize(buffCount);
	for (int32 i = 0; i < buffCount; i++)
	{
		br >> buffs[i].buffId >> buffs[i].remainTime;
	}

	cout << "BuffCount : " << buffCount << endl;
	for (int32 i = 0; i < buffCount; i++)
	{
		cout << "BufInfo : " << buffs[i].buffId << " " << buffs[i].remainTime << endl;
	}
}

 

GameSession.cpp

void GameSession::OnRecvPacket(BYTE* buffer, int32 len)
{
	ServerPacketHandler::HandlePacket(buffer, len);
}

 

GameServer.cpp

...

while (true)
{
    vector<BuffData> buffs{ BuffData{100, 1.5f}, BuffData{200, 2.3f}, BuffData{300, 0.7f} };
    SendBufferRef sendBuffer = ServerPacketHandler::Make_S_TEST(1001, 100, 10, buffs);

    GSessionManager.Broadcast(sendBuffer);

    this_thread::sleep_for(250ms);
}

...

 

DummyClient.cpp

...

virtual void OnRecvPacket(BYTE* buffer, int32 len) override
{
    ClientPacketHandler::HandlePacket(buffer, len);
}

...

 

BufferWriter.h

class BufferWriter
{
	...
    
    // 삭제 start
    template<typename T>
	BufferWriter& operator<<(const T& src);
    // 삭제 end
}

...

 // 삭제 start
template<typename T>
BufferWriter& BufferWriter::operator<<(const T& src)
{
	*reinterpret_cast<T*>(&_buffer[_pos]) = src;
	_pos += sizeof(T);
	return *this;
}
 // 삭제 end

template<typename T>
BufferWriter& BufferWriter::operator<<(T&& src)
{
	using DataType = std::remove_reference_t<T>;
	*reinterpret_cast<DataType*>(&_buffer[_pos]) = std::forward<DataType>(src);
	_pos += sizeof(T);
	return *this;
}

 

1. 기존 방법 안좋은 점

- Write 한 순서대로 Read 해야 함.

- Packet 지금 1 종류임.

 

2. 보편참조가 일어나는 조건은?

- rvalue 파라메터로 받는 template 함수, 같은 파라메터 이지만 lvalue로 받는 template 함수 있을 때, 전자 template 함수만 쓰이게 됨.

- 보편참조 함수에 lvalue 들어오면 "const T&" 로 rvalue 이면 "T&&"로 들어온다.

 

3. BufferWriter operator<<에 컴파일 에러가 생기는 이유와 해결방?
- 보편참조 함수에 uint64 들어옴 > 함수 안에서 const uint64& (= T값)의 포인터를 추출하려고 시도하기 때문임.

- 레퍼런스를 뺀 나머지만 이용하여 연산하면 해결됨.

- std::remove_reference_t<T> 는 T에 레퍼런스가 있으면 떼줌.

 

참조 : [C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버 대시보드 - 인프런 | 강의 (inflearn.com)

'똑똑한 개발 > C++ 게임개발' 카테고리의 다른 글

Unicode  (0) 2023.05.07
Buffer Helpers  (0) 2023.04.19
Packet Session  (0) 2023.04.16
SendBuffer Pooling  (0) 2023.04.05
SendBuffer  (0) 2021.12.21

관련글 더보기

댓글 영역