-
-
Save maxweisel/421ceeeee50d217a8cc33079815da289 to your computer and use it in GitHub Desktop.
Message packing example for libyojimbo, with built-in compression.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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