KoreanFoodie's Study
[C++ 게임 서버] 5-2. PacketHandler 본문
[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 의 갯수라는 것을 기억해 두자 😊
'Game Dev > Game Server' 카테고리의 다른 글
[C++ 게임 서버] 5-4. 패킷 직렬화 #1 (0) | 2023.12.15 |
---|---|
[C++ 게임 서버] 5-3. Unicode (0) | 2023.12.15 |
[C++ 게임 서버] 5-1. BufferHelper (0) | 2023.12.14 |
[C++ 게임 서버] 4-10. PacketSession (0) | 2023.12.14 |
[C++ 게임 서버] 4-9. SendBuffer Pooling (0) | 2023.12.14 |