hello, in this topic you will find the necessary plugins for sending/receiving and processing packets between server-client and client-server using the bitsery serialization library.
Information:
-In the current system, the package size is fixed (there are exceptions), the size of the package created from the data serialized with Bitsery is variable. so you will need to develop a mechanism for this!
-you need to write a serialization function for each structure
-use fixed size data types
Let's get started!
download and Add to extern/include folder or [Hidden Content]
server side
Packet.h add after #pragma once
#include <bitsery/bitsery.h>
#include <bitsery/adapter/buffer.h>
#include <bitsery/traits/vector.h>
#include <bitsery/traits/string.h>
#include <vector>
namespace bitsery_packet {
using Buffer = std::vector<uint8_t>;
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
template<typename T>
struct PacketSerializer {
static Buffer serialize(const T& data) {
Buffer buffer;
auto writtenSize = bitsery::quickSerialization<OutputAdapter>(buffer, data);
buffer.resize(writtenSize);
return buffer;
}
static bool deserialize(const Buffer& buffer, T& data) {
auto state = bitsery::quickDeserialization<InputAdapter>(
InputAdapter(buffer.begin(), buffer.size()), data);
return state.first == bitsery::ReaderError::NoError && state.second;
}
static bool deserialize(const void* buffer_data, size_t size, T& data) {
Buffer tempBuffer(static_cast<const uint8_t*>(buffer_data),
static_cast<const uint8_t*>(buffer_data) + size);
auto state = bitsery::quickDeserialization<InputAdapter>(
InputAdapter(tempBuffer.begin(), tempBuffer.size()), data);
return state.first == bitsery::ReaderError::NoError && state.second;
}
};
}
desc.h
find:
BYTE GetP2PChannel() const { return m_bP2PChannel; }
add below:
template<typename T>
void BitseryPacket(const T& packet_data) {
auto serialized_buffer = bitsery_packet::PacketSerializer<T>::serialize(packet_data);
// Header (uint8_t (1)) + payload size (uint16_t(2)) + data for size information
std::vector<char> final_buffer;
final_buffer.reserve(1 + 2 + serialized_buffer.size());
//header
final_buffer.push_back(static_cast<char>(*(reinterpret_cast<const uint8_t*>(&packet_data))));
//payload size
uint16_t size = static_cast<uint16_t>(serialized_buffer.size());
final_buffer.push_back(size & 0xFF);
final_buffer.push_back((size >> 8) & 0xFF);
//serialized data
final_buffer.insert(final_buffer.end(),
serialized_buffer.begin(),
serialized_buffer.end());
#if defined(_DEBUG)
sys_log(0, "Sending bitsery packet, header %d, buffer size : %d", *(reinterpret_cast<const uint8_t*>(&packet_data)), final_buffer.size());
#endif
Packet(final_buffer.data(), final_buffer.size());
}
client side:
EterLib/NetStream.h
find:
#include "NetAddress.h"
add below:
#include <bitsery/bitsery.h>
#include <bitsery/adapter/buffer.h>
#include <bitsery/traits/vector.h>
#include <bitsery/traits/string.h>
#include <vector>
#include <algorithm>
namespace bitsery_packet {
using Buffer = std::vector<uint8_t>;
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
template<typename T>
struct PacketSerializer {
static Buffer serialize(const T& data) {
Buffer buffer;
auto writtenSize = bitsery::quickSerialization<OutputAdapter>(buffer, data);
buffer.resize(writtenSize);
return buffer;
}
static bool deserialize(const Buffer& buffer, T& data) {
auto state = bitsery::quickDeserialization<InputAdapter>(
InputAdapter(buffer.begin(), buffer.size()), data);
return state.first == bitsery::ReaderError::NoError && state.second;
}
static bool deserialize(const void* buffer_data, size_t size, T& data) {
Buffer tempBuffer(static_cast<const uint8_t*>(buffer_data),
static_cast<const uint8_t*>(buffer_data) + size);
auto state = bitsery::quickDeserialization<InputAdapter>(
InputAdapter(tempBuffer.begin(), tempBuffer.size()), data);
return state.first == bitsery::ReaderError::NoError && state.second;
}
};
}
find:
bool AutoRecv(T& c_pvData) {
return Recv(sizeof(T), &c_pvData);
}
add below:
template <typename T>
bool SendBitseryPacket(const T& data) {
auto buffer = bitsery_packet::template PacketSerializer<T>::serialize(data);
return Send(static_cast<int>(buffer.size()),
reinterpret_cast<const char*>(buffer.data()));
}
template<typename T>
bool BitseryAutoRecv(T& packet_data) {
uint8_t received_header;
if (!Recv(1, &received_header)) {
TraceError("Failed to read header");
return false;
}
uint16_t payload_size;
if (!Recv(sizeof(payload_size), &payload_size)) {
TraceError("Failed to read size information");
return false;
}
std::vector<uint8_t> buffer(payload_size);
if (!Recv(payload_size, buffer.data())) {
TraceError("Data could not be read. Expected: %d, Present: %d",
payload_size, GetRecvBufferSize());
return false;
}
// 5. Deserialization
if (!bitsery_packet::PacketSerializer<T>::deserialize(buffer, packet_data)) {
TraceError("Deserialization failed");
return false;
}
return true;
}
sample package sending and receiving for tem drop and character point
packet.h (server and client)
find:
typedef struct packet_points
{
uint8_t header;
uint16_t points[POINT_MAX_NUM];
} TPacketGCPoints;
add below:
template<typename S>
void serialize(S& serializer, TPacketGCPoints& packet) {
serializer.value1b(packet.header);
for (int i = 0; i < POINT_MAX_NUM; ++i) {
serializer.value2b(packet.points[i]);
}
}
find:
typedef struct command_item_drop //HEADER_CG_ITEM_DROP
{
uint8_t header;
TItemPos Cell;
uint32_t gold;
#if defined(ENABLE_CHEQUE_SYSTEM) && !defined(DISABLE_CHEQUE_DROP)
uint32_t cheque;
#endif
} TPacketCGItemDrop;
add below:
template<typename S>
void serialize(S& serializer, TPacketCGItemDrop& packet) {
serializer.value1b(packet.header);
serializer.object(packet.Cell);
serializer.value4b(packet.gold);
#if defined(ENABLE_CHEQUE_SYSTEM) && !defined(DISABLE_CHEQUE_DROP)
serializer.value4b(packet.cheque);
#endif
}
char.cpp
find in void CHARACTER::PointsPacket():
GetDesc()->Packet(pack);
change:
GetDesc()->BitseryPacket(pack);
input_main.cpp
find:
void CInputMain::ItemDrop(LPCHARACTER ch, const char * data)
change function:
void CInputMain::ItemDrop(LPCHARACTER ch, const char * data)
{
TPacketCGItemDrop pinfo;
if (bitsery_packet::PacketSerializer<TPacketCGItemDrop>::deserialize(data, sizeof(TPacketCGItemDrop), pinfo))
{
if (!ch)
return;
#if defined(ENABLE_CHEQUE_SYSTEM) && !defined(DISABLE_CHEQUE_DROP)
if (pinfo.cheque > 0)
{
ch->DropCheque(pinfo->cheque);
return;
}
#endif
if (pinfo.gold > 0)
ch->DropGold(pinfo.gold);
else
ch->DropItem(pinfo.Cell);
}
else
{
sys_err("packet deserialization failed");
}
}
userinterface/PythonNetworkStreamPhaseGameItem.cpp
find in bool CPythonNetworkStream::SendItemDropPacket():
if (!Send(itemDropPacket))
change:
if (!SendBitseryPacket(itemDropPacket))
userinterface/PythonNetworkStreamPhaseLoading.cpp
find in bool CPythonNetworkStream::__RecvPlayerPoints():
if (!AutoRecv(PointsPacket))
change:
if (!BitseryAutoRecv(PointsPacket))