KoreanFoodie's Study

[C++ 게임 서버] 5-2. PacketHandler 본문

Game Dev/Game Server

[C++ 게임 서버] 5-2. PacketHandler

GoldGiver 2023. 12. 15. 01:05

Rookiss 님의 '[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버' 를 들으며 배운 내용을 정리하고 있습니다. 관심이 있으신 분은 꼭 한 번 들어보시기를 추천합니다!

[C++ 게임 서버] 5-2. PacketHandler

핵심 :

1. 다양한 패킷에 대한 처리를 대응하기 위해서, 패킷별로 enum 을 만들고, 각 enum 별로 패킷을 핸들링하는 함수를 만들어 주면 switch 문으로 한번에 처리할 수 있을 것이다.

2. 가변 데이터를 넣는 경우도 Primitive Type 을 읽는 경우와 거의 비슷하다. 벡터의 크기나 갯수 정보를 넣어주기만 하면 해결된다!

이전 시간에는 Buffer Helper 클래스를 만들어 버퍼를 조금 더 용이하게 읽고 쓸 수 있도록 했다. 하지만 아직 부족하다! 일일히 임의의 값으로 버퍼의 내용을 채워 패킷을 조립하고 분해하는 것보다, 좀 더 정형화된 형태로 패킷을 주고 받을 수 있는 것이 좋다.

그래서 이번 글에서는 PacketHandler 클래스를 만들어, 패킷 사용을 좀 더 고상하게(?) 만들어 볼 것이다.

ServerPacketHandler.h

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);
};

일단 서버족의 패킷 핸들러부터 보자. S_TEST 는 일단 S(서버) 에서 보내는 테스트 패킷이라는 의미로 붙여본 enum 타입이다. 구현을 보면...

 

ServerPacketHandler.cpp

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;
}

이전에 GameServer 의 while loop 에서 버퍼를 조립했던 내용이 Make_S_TEST 안에 들어가 있다.

즉, 우리는 여러가지 종류의 패킷을 만들때, enum class 에 패킷 이름을 추가하고, 해당 타입에 맞는 함수를 복붙해서 비슷하게 만들어 주기만 하면 된다는 것을 알 수 있다! 😮

 

다만 한 가지, 이전과 다른 점이 있다. 바로 패킷에 '가변 데이터' 가 추가되었다는 것인데, 잘 보면 buffs 라는, vector<BuffData> 타입의 인자가 있는 것을 알 수 있다. 벡터 같은 경우 패킷에 어떻게 넣을 수 있을까?

사실 답은 매우 간단한데, 먼저 가변 데이터를 넣기 전, 버퍼에 가변 데이터의 갯수에 해당하는 정보를 넣고, 그 후 벡터를 순회하며 실제 데이터를 전부 넣어 주면 된다.

 

그럼 바로 이어서 ClientPakcetHandler 도 살펴보자.

ClientPacketHandler 

enum
{
	S_TEST = 1
};

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

	static void Handle_S_TEST(BYTE* buffer, int32 len);
};


/*************************************************************************************/
	Implementation
/*************************************************************************************/

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<BuffData> 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 << "BufCount : " << buffCount << endl;
	for (int32 i = 0; i < buffCount; i++)
	{
		cout << "BufInfo : " << buffs[i].buffId << " " << buffs[i].remainTime << endl;
	}
}

일단... HandlePacket 함수를 보면, 패킷의 타입에 따라 적절한 함수가 호출되어 처리가 되는 형식임을 볼 수 있다.

Handle_S_TEST 함수를 보면, 다른 부분은 거의 똑같지만 buffs 를 읽는 부분이 조금 다른 것을 볼 수 있다. 어려운 것은 아니고, 먼저 buffCount 를 읽어서, buffCount 만큼 buffData 를 뽑아내기만 하면 된다!

그리고 우리가 추출한 buffCount 는, ServerPacketHandler 에서 buffs 의 갯수라는 것을 기억해 두자 😊

Comments