Skip to content

Instantly share code, notes, and snippets.

@ananace
Last active May 6, 2018 05:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ananace/5e0a54e444bf26445ad60d39952d7a31 to your computer and use it in GitHub Desktop.
Save ananace/5e0a54e444bf26445ad60d39952d7a31 to your computer and use it in GitHub Desktop.
Message packing example for libyojimbo, with built-in compression.
#include <yojimbo.h>
#include <miniz.h>
class PackedMessage : public yojimbo::BlockMessage
{
public:
enum { MsgType = 0x00 };
template <typename Stream>
bool Serialize(Stream& stream)
{
// You could use `serialize_int(stream, enclosedMsgType, 0, maxMessageNumber)`
serialize_bits(stream, enclosedMsgType, 8);
serialize_bits(stream, uncompressedSize, 15);
serialize_bool(stream, compressedDeflate);
return true;
}
// Up this if you need support for more than 256 different message types.
uint8_t enclosedMsgType;
uint16_t uncompressedSize;
bool compressedDeflate;
YOJIMBO_VIRTUAL_SERIALIZE_FUNCTIONS()
};
// Only the most basic error handling in here, but should be enough to show the idea
PackedMessage* PackMessage(YojimboId_t client, uint32_t msgSize, yojimbo::Message* msgToPack)
{
const uint32_t kCompressionLowerLimit = 1024, kCompressionMiddleLimit = 8192, kCompressionUpperLimit = ((1 << 15) - 1);
auto& allocator = g_pServer->GetClientAllocator(client);
uint8_t* block = (uint8_t*)YOJIMBO_ALLOCATE(allocator, msgSize);
yojimbo::WriteStream ws(block, msgSize, allocator);
if (!msgToPack->SerializeInternal(ws))
{
YOJIMBO_FREE(allocator, block);
return nullptr;
}
auto& factory = g_pServer->GetMsgFactory(client);
int blockSize = msgSize;
auto toRet = (PackedMessage*)factory.Create(PackedMessage::MsgType);
toRet->enclosedMsgType = msgToPack->GetType();
if (msgSize >= kCompressionLowerLimit && msgSize < kCompressionUpperLimit)
{
z_stream cstream;
cstream.zalloc = Z_NULL;
cstream.zfree = Z_NULL;
cstream.opaque = Z_NULL;
cstream.avail_in = (uInt)msgSize;
cstream.next_in = (Bytef *)block;
auto maxSize = deflateBound(&cstream, msgSize);
uint8_t* compressedBlock = (uint8_t*)YOJIMBO_ALLOCATE(allocator, maxSize);
cstream.avail_out = (uInt)maxSize;
cstream.next_out = (Bytef *)compressedBlock;
deflateInit(&cstream, (msgSize < kCompressionMiddleLimit ? Z_BEST_COMPRESSION : Z_BEST_SPEED));
deflate(&cstream, Z_FINISH);
deflateEnd(&cstream);
YOJIMBO_FREE(allocator, block);
toRet->uncompressedSize = msgSize;
toRet->compressedDeflate = true;
block = compressedBlock;
blockSize = cstream.total_out;
}
toRet->AttachBlock(allocator, block, blockSize);
return toRet;
}
yojimbo::Message* UnpackMessage(int client, PackedMessage* blockMsg)
{
auto& allocator = g_pServer->GetClientAllocator(client);
uint8_t* blockPtr = blockMsg->GetBlockData();
int blockSize = blockMsg->GetBlockSize();
if (blockMsg->compressedDeflate)
{
uint8_t* newBlock = (uint8_t*)YOJIMBO_ALLOCATE(allocator, blockMsg->uncompressedSize);
int newBlockSize = blockMsg->uncompressedSize;
z_stream cstream;
cstream.zalloc = Z_NULL;
cstream.zfree = Z_NULL;
cstream.opaque = Z_NULL;
cstream.avail_in = (uInt)blockSize;
cstream.next_in = (Bytef *)blockPtr;
cstream.avail_out = (uInt)newBlockSize;
cstream.next_out = (Bytef *)newBlock;
inflateInit(&cstream);
inflate(&cstream, Z_FINISH);
inflateEnd(&cstream);
blockPtr = newBlock;
blockSize = newBlockSize;
}
auto& factory = g_pServer->GetMsgFactory(client);
yojimbo::ReadStream rs(blockPtr, blockSize, allocator);
auto* msg = factory.Create(blockMsg->enclosedMsgType);
if (!msg->SerializeInternal(rs))
{
if (blockMsg->compressedDeflate)
YOJIMBO_FREE(allocator, blockPtr);
factory.Release(msg);
return nullptr;
}
if (blockMsg->compressedDeflate)
YOJIMBO_FREE(allocator, blockPtr);
return msg;
}
// The server part
void Server::SendSafe(int client, yojimbo::Message* msg)
{
yojimbo::MeasureStream ms;
msg->SerializeInternal(ms);
if (ms.GetBytesProcessed() >= m_config.connectionConfig.channel[kReliableChannel].packetBudget * 0.5f)
{
auto* packed = PackMessage(client, ms.GetBytesProcessed(), msg);
SendReliable(client, packed);
}
else
SendReliable(client, msg);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment