티스토리 뷰

강의 : (인프런)C++과 언리얼로 마드는 MMORPG 게임 개발 시리즈 - 게임서버

 

데이터를 저장하거나, 패킷으로 전달할 때 정수값이나 char 값은 그대로 저장할 수 있으나 동적으로 할당된 값은 그대로 저장할 수 없습니다. 예를 들어 아래의 구조체를 가진 데이터를 보내거나 저장하려고 할때, target이나 vector의 값을 저장해도 후에 복원시킬 때 제대로 된 값을 복원할 수 없습니다. 따라서 이러한 값을 byte나 배열로 만들어서 보내거나 저장할 수 있도록 하는 것을 직렬화라고 합니다.

 

class Player {
public:
    int32 hp = 0;
    int32 attack = 0;
    Player* target = nullptr;
    vector<int32> buffs;
};

 

XML vs JSON 

javascript에서 따온 형식이며, XML은 가독성이 좋을 수 있지만, 속도나 나머지 부분은 JSON이 좋습니다.

 

XML 예시

<?xml version="1.0" encoding="utf-8"?>

<PDL>
  <Pakcet name ="S_TEST" desc="테스트 용도">
    <Field name ="id" type="uint64" desc=""/>
    <Field name ="hp" type="uint32" desc=""/>
    <Field name ="attack" type="uint16" desc=""/>
    <List name="buffs" desc="">
      <Field name ="buffId" type="uint64" desc=""/>
      <Field name ="remainTime" type="float" desc=""/>
    </List>
  </Pakcet>
</PDL>

 

아래는 강의 예시이며, 고정길이데이터를 먼저 받은 후 가변길이 데이터를 처리하는 형태로 패킷을 만들어어 가고 있습니다.

#pragma pack(1)
// [ PKT_S_TEST ][ 가변 길이 데이터 ]
struct PKT_S_TEST
{
	struct BuffListItem
	{
		uint64 buffId;
		float remainTime;
	};

	uint16 packetSize;	// 공용 헤더
	uint16 pakcetID;	// 공용 헤더
	uint64 id;			// 8
	uint32 hp;			// 4
	uint16 attack;		// 2
	uint16 buffOffset;	// 가변데이터 시작 위치
	uint16 buffCount;	// 가변데이터 크기

	bool Validate() 
	{
		uint32 size = 0;
		size += sizeof(PKT_S_TEST);
		size += buffCount * sizeof(BuffListItem);
		if (size != packetSize)
			return false;
		if (buffOffset + buffCount * sizeof(BuffListItem) >  packetSize)
			return false;
		return true;
	}
	//vector<BuffData> buffs;
	//wstring name;
};
#pragma pack()

클라 패킷 직렬화 .h

더보기
#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);
};
template<typename T, typename C>
class PacketIterator
{
public:
	PacketIterator(C& container, uint16 index) : _container(container), _index(index) { }

	bool			operator!=(const PacketIterator& other) const { return _index != other._index; }
	const T&		operator*() const { return _container[_index]; }
	T&				operator*() { return _container[_index]; }
	T*				operator->() { return &_container[_index]; }
	PacketIterator& operator++() { _index++; return *this; }
	PacketIterator& operator++(int32) { PacketIterator ret = *this; ++_index; return ret; }

private:
	C&				_container;
	uint16			_index;
};

template<typename T>
class PacketList 
{
public:
	PacketList() : _data(nullptr), _count(0) { }
	PacketList(T* data, uint16 count) : _data(data), _count(count) { }

	T& operator[](uint16 index)
	{
		ASSERT_CRASH(index < _count);
		return _data[index];
	}

	uint16 Count() { return _count; }

	// ranged-base for 지원
	PacketIterator<T, PacketList<T>> begin() { return PacketIterator<T, PacketList<T>>((*this), 0); }
	PacketIterator<T, PacketList<T>> end() { return PacketIterator<T, PacketList<T>>((*this), _count); }
private:
	T*			_data;
	uint16		_count;
};

 

클라 패킷 직렬화 .cpp

더보기

 

#include "pch.h"
#include "ClientPacketHandler.h"
#include <BufferReader.h>

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

	//Echo
	PacketHeader header;
	br >> header;
	
	switch (header.id) 
	{
	case S_TEST:
		Handle_S_TEST(buffer, len);
		break;

	}
}
#pragma pack(1)
// [ PKT_S_TEST ][ 가변 길이 데이터 ]
struct PKT_S_TEST
{
	struct BuffListItem
	{
		uint64 buffId;
		float remainTime;
	};

	uint16 packetSize;	// 공용 헤더
	uint16 pakcetID;	// 공용 헤더
	uint64 id;			// 8
	uint32 hp;			// 4
	uint16 attack;		// 2
	uint16 buffOffset;	// 가변데이터 시작 위치
	uint16 buffCount;	// 가변데이터 크기

	bool Validate() 
	{
		uint32 size = 0;
		size += sizeof(PKT_S_TEST);
		if (packetSize < size)
			return false;

		size += buffCount * sizeof(BuffListItem);
		if (size != packetSize)
			return false;

		if (buffOffset + buffCount * sizeof(BuffListItem) >  packetSize)
			return false;
		return true;
	}

	using BuffsList = PacketList<PKT_S_TEST::BuffListItem>;

	BuffsList GetBuffList() {
		BYTE* data = reinterpret_cast<BYTE*>(this);
		data += buffOffset;

		return BuffsList(reinterpret_cast<PKT_S_TEST::BuffListItem*>(data), buffCount);
	}
	//vector<BuffData> buffs;
	//wstring name;
};
#pragma pack()

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

	if (len < sizeof(PKT_S_TEST))
		return;

	PKT_S_TEST* pkt = reinterpret_cast<PKT_S_TEST*>(buffer);

	//PKT_S_TEST pkt;
	//br >> pkt;
	
	if (pkt->Validate() == false)
		return;
	//cout << "ID : " << id << " HP : " << hp << " ATT : " << attack << endl;

	//vector<PKT_S_TEST::BuffListItem> buffs;
	//buffs.resize(pkt->buffCount);

	PKT_S_TEST::BuffsList buffs = pkt->GetBuffList();
	
	cout << "BuffCount : " << buffs.Count() << endl;
	for (int32 i = 0; i < buffs.Count(); i++) {
		cout << "BuffInfo : " << buffs[i].buffId << " " << buffs[i].remainTime << endl;
	}

	for(auto it = buffs.begin(); it != buffs.end(); ++ it)
		cout << "BuffInfo : " << it->buffId << " " << it->remainTime << endl;

	for(auto& buff : buffs)
		cout << "BuffInfo : " << buff.buffId << " " << buff.remainTime << endl;
}

 

 

저도 공부하면서 와... 하면서 공부했네요 ㅎㅎ

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함