1
0

Implement "caching" in ChunkDataSerializer

+ When sending a chunk to multiple clients, group them by protocol version and send the same data
This commit is contained in:
Tiger Wang 2020-07-19 16:29:49 +01:00
parent bedddfffbc
commit 00f8c3a225
9 changed files with 123 additions and 203 deletions

View File

@ -245,13 +245,15 @@ void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkZ, std::unordered_set<cCli
{
return;
}
cChunkDataSerializer Data(m_Data, m_BiomeMap, m_World.GetDimension());
{
// Send:
cChunkDataSerializer Data(a_ChunkX, a_ChunkZ, m_Data, m_BiomeMap, m_World.GetDimension());
Data.SendToClients(a_Clients);
}
for (const auto Client : a_Clients)
{
// Send:
Client->SendChunkData(a_ChunkX, a_ChunkZ, Data);
// Send block-entity packets:
for (const auto & Pos : m_BlockEntities)
{

View File

@ -2461,7 +2461,7 @@ void cClientHandle::SendChatSystem(const cCompositeChat & a_Message)
void cClientHandle::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
void cClientHandle::SendChunkData(int a_ChunkX, int a_ChunkZ, const std::string_view a_ChunkData)
{
ASSERT(m_Player != nullptr);
@ -2490,7 +2490,7 @@ void cClientHandle::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializ
return;
}
m_Protocol->SendChunkData(a_ChunkX, a_ChunkZ, a_Serializer);
m_Protocol->SendChunkData(a_ChunkData);
// Add the chunk to the list of chunks sent to the player:
{

View File

@ -157,7 +157,7 @@ public: // tolua_export
void SendChatAboveActionBar (const cCompositeChat & a_Message);
void SendChatSystem (const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData = "");
void SendChatSystem (const cCompositeChat & a_Message);
void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer);
void SendChunkData (int a_ChunkX, int a_ChunkZ, const std::string_view a_ChunkData);
void SendCollectEntity (const cEntity & a_Collected, const cEntity & a_Collector, unsigned a_Count);
void SendDestroyEntity (const cEntity & a_Entity);
void SendDetachEntity (const cEntity & a_Entity, const cEntity & a_PreviousVehicle);

View File

@ -4,9 +4,11 @@
#include "Protocol_1_8.h"
#include "Protocol_1_9.h"
#include "../ByteBuffer.h"
#include "../ClientHandle.h"
#include "Palettes/Upgrade.h"
#include "Palettes/Palette_1_13.h"
#include "Palettes/Palette_1_13_1.h"
@ -34,10 +36,14 @@ void ForEachSection(const cChunkData & a_Data, Func a_Func)
// cChunkDataSerializer:
cChunkDataSerializer::cChunkDataSerializer(
int a_ChunkX,
int a_ChunkZ,
const cChunkData & a_Data,
const unsigned char * a_BiomeData,
const eDimension a_Dimension
):
) :
m_ChunkX(a_ChunkX),
m_ChunkZ(a_ChunkZ),
m_Data(a_Data),
m_BiomeData(a_BiomeData),
m_Dimension(a_Dimension)
@ -48,49 +54,74 @@ cChunkDataSerializer::cChunkDataSerializer(
const AString & cChunkDataSerializer::Serialize(int a_Version, int a_ChunkX, int a_ChunkZ)
void cChunkDataSerializer::SendToClients(const std::unordered_set<cClientHandle *> & a_SendTo)
{
Serializations::const_iterator itr = m_Serializations.find(a_Version);
if (itr != m_Serializations.end())
std::unordered_map<cProtocol::Version, std::vector<cClientHandle *>> ClientProtocolVersions;
for (const auto Client : a_SendTo)
{
return itr->second;
const auto ClientProtocol = static_cast<cProtocol::Version>(Client->GetProtocolVersion());
ClientProtocolVersions[ClientProtocol].emplace_back(Client);
}
AString data;
switch (a_Version)
for (const auto & Entry : ClientProtocolVersions)
{
case RELEASE_1_8_0: Serialize47 (data, a_ChunkX, a_ChunkZ); break;
case RELEASE_1_9_0: Serialize107(data, a_ChunkX, a_ChunkZ); break;
case RELEASE_1_9_4: Serialize110(data, a_ChunkX, a_ChunkZ); break;
case RELEASE_1_13: Serialize393(data, a_ChunkX, a_ChunkZ); break;
switch (Entry.first)
{
case cProtocol::Version::Version_1_8_0:
{
Serialize47(Entry.second);
continue;
}
case cProtocol::Version::Version_1_9_0:
case cProtocol::Version::Version_1_9_1:
case cProtocol::Version::Version_1_9_2:
{
Serialize107(Entry.second);
continue;
}
case cProtocol::Version::Version_1_9_4:
case cProtocol::Version::Version_1_10_0:
case cProtocol::Version::Version_1_11_0:
case cProtocol::Version::Version_1_11_1:
case cProtocol::Version::Version_1_12:
case cProtocol::Version::Version_1_12_1:
case cProtocol::Version::Version_1_12_2:
{
Serialize110(Entry.second);
continue;
}
case cProtocol::Version::Version_1_13:
{
Serialize393And401<&Palette_1_13::FromBlock>(Entry.second); // This version didn't last very long xD
continue;
}
case cProtocol::Version::Version_1_13_1:
case cProtocol::Version::Version_1_13_2:
{
Serialize393And401<&Palette_1_13_1::FromBlock>(Entry.second);
continue;
}
}
default:
{
LOGERROR("cChunkDataSerializer::Serialize(): Unknown version: %d", a_Version);
LOGERROR("cChunkDataSerializer::Serialize(): Unknown version: %d", Entry.first);
ASSERT(!"Unknown chunk data serialization version");
break;
}
}
if (!data.empty())
{
m_Serializations[a_Version] = data;
}
return m_Serializations[a_Version];
}
void cChunkDataSerializer::Serialize47(AString & a_Data, int a_ChunkX, int a_ChunkZ)
void cChunkDataSerializer::Serialize47(const std::vector<cClientHandle *> & a_SendTo)
{
// This function returns the fully compressed packet (including packet size), not the raw packet!
// Create the packet:
cByteBuffer Packet(512 KiB);
Packet.WriteVarInt32(0x21); // Packet id (Chunk Data packet)
Packet.WriteBEInt32(a_ChunkX);
Packet.WriteBEInt32(a_ChunkZ);
Packet.WriteBEInt32(m_ChunkX);
Packet.WriteBEInt32(m_ChunkZ);
Packet.WriteBool(true); // "Ground-up continuous", or rather, "biome data present" flag
Packet.WriteBEUInt16(m_Data.GetSectionBitmask());
@ -135,48 +166,22 @@ void cChunkDataSerializer::Serialize47(AString & a_Data, int a_ChunkX, int a_Chu
// Write the biome data:
Packet.WriteBuf(m_BiomeData, BiomeDataSize);
AString PacketData;
Packet.ReadAll(PacketData);
Packet.CommitRead();
cByteBuffer Buffer(20);
if (PacketData.size() >= 256)
{
if (!cProtocol_1_8_0::CompressPacket(PacketData, a_Data))
{
ASSERT(!"Packet compression failed.");
a_Data.clear();
return;
}
}
else
{
AString PostData;
Buffer.WriteVarInt32(static_cast<UInt32>(Packet.GetUsedSpace() + 1));
Buffer.WriteVarInt32(0);
Buffer.ReadAll(PostData);
Buffer.CommitRead();
a_Data.clear();
a_Data.reserve(PostData.size() + PacketData.size());
a_Data.append(PostData.data(), PostData.size());
a_Data.append(PacketData.data(), PacketData.size());
}
CompressAndSend(Packet, a_SendTo);
}
void cChunkDataSerializer::Serialize107(AString & a_Data, int a_ChunkX, int a_ChunkZ)
void cChunkDataSerializer::Serialize107(const std::vector<cClientHandle *> & a_SendTo)
{
// This function returns the fully compressed packet (including packet size), not the raw packet!
// Create the packet:
cByteBuffer Packet(512 KiB);
Packet.WriteVarInt32(0x20); // Packet id (Chunk Data packet)
Packet.WriteBEInt32(a_ChunkX);
Packet.WriteBEInt32(a_ChunkZ);
Packet.WriteBEInt32(m_ChunkX);
Packet.WriteBEInt32(m_ChunkZ);
Packet.WriteBool(true); // "Ground-up continuous", or rather, "biome data present" flag
Packet.WriteVarInt32(m_Data.GetSectionBitmask());
// Write the chunk size:
@ -268,48 +273,22 @@ void cChunkDataSerializer::Serialize107(AString & a_Data, int a_ChunkX, int a_Ch
// Write the biome data
Packet.WriteBuf(m_BiomeData, BiomeDataSize);
AString PacketData;
Packet.ReadAll(PacketData);
Packet.CommitRead();
cByteBuffer Buffer(20);
if (PacketData.size() >= 256)
{
if (!cProtocol_1_9_0::CompressPacket(PacketData, a_Data))
{
ASSERT(!"Packet compression failed.");
a_Data.clear();
return;
}
}
else
{
AString PostData;
Buffer.WriteVarInt32(static_cast<UInt32>(Packet.GetUsedSpace() + 1));
Buffer.WriteVarInt32(0);
Buffer.ReadAll(PostData);
Buffer.CommitRead();
a_Data.clear();
a_Data.reserve(PostData.size() + PacketData.size());
a_Data.append(PostData.data(), PostData.size());
a_Data.append(PacketData.data(), PacketData.size());
}
CompressAndSend(Packet, a_SendTo);
}
void cChunkDataSerializer::Serialize110(AString & a_Data, int a_ChunkX, int a_ChunkZ)
void cChunkDataSerializer::Serialize110(const std::vector<cClientHandle *> & a_SendTo)
{
// This function returns the fully compressed packet (including packet size), not the raw packet!
// Create the packet:
cByteBuffer Packet(512 KiB);
Packet.WriteVarInt32(0x20); // Packet id (Chunk Data packet)
Packet.WriteBEInt32(a_ChunkX);
Packet.WriteBEInt32(a_ChunkZ);
Packet.WriteBEInt32(m_ChunkX);
Packet.WriteBEInt32(m_ChunkZ);
Packet.WriteBool(true); // "Ground-up continuous", or rather, "biome data present" flag
Packet.WriteVarInt32(m_Data.GetSectionBitmask());
// Write the chunk size:
@ -404,48 +383,23 @@ void cChunkDataSerializer::Serialize110(AString & a_Data, int a_ChunkX, int a_Ch
// Identify 1.9.4's tile entity list as empty
Packet.WriteBEUInt8(0);
AString PacketData;
Packet.ReadAll(PacketData);
Packet.CommitRead();
cByteBuffer Buffer(20);
if (PacketData.size() >= 256)
{
if (!cProtocol_1_9_0::CompressPacket(PacketData, a_Data))
{
ASSERT(!"Packet compression failed.");
a_Data.clear();
return;
}
}
else
{
AString PostData;
Buffer.WriteVarInt32(static_cast<UInt32>(Packet.GetUsedSpace() + 1));
Buffer.WriteVarInt32(0);
Buffer.ReadAll(PostData);
Buffer.CommitRead();
a_Data.clear();
a_Data.reserve(PostData.size() + PacketData.size());
a_Data.append(PostData.data(), PostData.size());
a_Data.append(PacketData.data(), PacketData.size());
}
CompressAndSend(Packet, a_SendTo);
}
void cChunkDataSerializer::Serialize393(AString & a_Data, int a_ChunkX, int a_ChunkZ)
template <auto Palette>
void cChunkDataSerializer::Serialize393And401(const std::vector<cClientHandle *> & a_SendTo)
{
// This function returns the fully compressed packet (including packet size), not the raw packet!
// Create the packet:
cByteBuffer Packet(512 KiB);
Packet.WriteVarInt32(0x22); // Packet id (Chunk Data packet)
Packet.WriteBEInt32(a_ChunkX);
Packet.WriteBEInt32(a_ChunkZ);
Packet.WriteBEInt32(m_ChunkX);
Packet.WriteBEInt32(m_ChunkZ);
Packet.WriteBool(true); // "Ground-up continuous", or rather, "biome data present" flag
Packet.WriteVarInt32(m_Data.GetSectionBitmask());
@ -486,7 +440,7 @@ void cChunkDataSerializer::Serialize393(AString & a_Data, int a_ChunkX, int a_Ch
{
UInt32 blockType = a_Section.m_BlockTypes[Index];
UInt32 blockMeta = (a_Section.m_BlockMetas[Index / 2] >> ((Index % 2) * 4)) & 0x0f;
UInt64 Value = Palette_1_13::FromBlock(PaletteUpgrade::FromBlock(blockType, blockMeta));
UInt64 Value = Palette(PaletteUpgrade::FromBlock(blockType, blockMeta));
Value &= Mask; // It shouldn't go out of bounds, but it's still worth being careful
// Painful part where we write data into the long array. Based off of the normal code.
@ -536,32 +490,27 @@ void cChunkDataSerializer::Serialize393(AString & a_Data, int a_ChunkX, int a_Ch
// Identify 1.9.4's tile entity list as empty
Packet.WriteVarInt32(0);
AString PacketData;
Packet.ReadAll(PacketData);
Packet.CommitRead();
CompressAndSend(Packet, a_SendTo);
}
if (PacketData.size() >= 256)
{
if (!cProtocol_1_9_0::CompressPacket(PacketData, a_Data))
void cChunkDataSerializer::CompressAndSend(cByteBuffer & a_Packet, const std::vector<cClientHandle *> & a_SendTo)
{
AString PacketData;
a_Packet.ReadAll(PacketData);
AString ToSend;
if (!cProtocol_1_8_0::CompressPacket(PacketData, ToSend))
{
ASSERT(!"Packet compression failed.");
a_Data.clear();
return;
}
}
else
for (const auto Client : a_SendTo)
{
cByteBuffer Buffer(20);
AString PostData;
Buffer.WriteVarInt32(static_cast<UInt32>(Packet.GetUsedSpace() + 1));
Buffer.WriteVarInt32(0);
Buffer.ReadAll(PostData);
Buffer.CommitRead();
a_Data.clear();
a_Data.reserve(PostData.size() + PacketData.size());
a_Data.append(PostData.data(), PostData.size());
a_Data.append(PacketData.data(), PacketData.size());
Client->SendChunkData(m_ChunkX, m_ChunkZ, ToSend);
}
}

View File

@ -7,6 +7,12 @@
class cByteBuffer;
/** Serializes one chunk's data to (possibly multiple) protocol versions.
Caches the serialized data for as long as this object lives, so that the same data can be sent to
other clients using the same protocol. */
@ -14,26 +20,31 @@ class cChunkDataSerializer
{
public:
enum
{
RELEASE_1_8_0 = 47,
RELEASE_1_9_0 = 107,
RELEASE_1_9_4 = 110,
RELEASE_1_13 = 393,
} ;
cChunkDataSerializer(
int a_ChunkX,
int a_ChunkZ,
const cChunkData & a_Data,
const unsigned char * a_BiomeData,
const eDimension a_Dimension
);
/** Serializes the contained chunk data into the specified protocol version. */
const AString & Serialize(int a_Version, int a_ChunkX, int a_ChunkZ);
/** For each client, serializes the chunk into their protocol version and sends it. */
void SendToClients(const std::unordered_set<cClientHandle *> & a_SendTo);
protected:
using Serializations = std::map<int, AString>;
void Serialize47 (const std::vector<cClientHandle *> & a_SendTo); // Release 1.8
void Serialize107(const std::vector<cClientHandle *> & a_SendTo); // Release 1.9
void Serialize110(const std::vector<cClientHandle *> & a_SendTo); // Release 1.9.4
template <auto Palette>
void Serialize393And401(const std::vector<cClientHandle *> & a_SendTo); // Release 1.13 - 1.13.1
/** Finalises the data, compresses it if required, and delivers it to all clients. */
void CompressAndSend(cByteBuffer & a_Packet, const std::vector<cClientHandle *> & a_SendTo);
/** The coordinates of the chunk to serialise. */
int m_ChunkX, m_ChunkZ;
/** The data read from the chunk, to be serialized. */
const cChunkData & m_Data;
@ -43,14 +54,6 @@ protected:
/** The dimension where the chunk resides. */
const eDimension m_Dimension;
/** The per-protocol serialized data, cached for reuse for other clients. */
Serializations m_Serializations;
void Serialize47 (AString & a_Data, int a_ChunkX, int a_ChunkZ); // Release 1.8
void Serialize107(AString & a_Data, int a_ChunkX, int a_ChunkZ); // Release 1.9
void Serialize110(AString & a_Data, int a_ChunkX, int a_ChunkZ); // Release 1.9.4
void Serialize393(AString & a_Data, int a_ChunkX, int a_ChunkZ); // Release 1.13
} ;

View File

@ -357,7 +357,7 @@ public:
virtual void SendChat (const AString & a_Message, eChatType a_Type) = 0;
virtual void SendChat (const cCompositeChat & a_Message, eChatType a_Type, bool a_ShouldUseChatPrefixes) = 0;
virtual void SendChatRaw (const AString & a_MessageRaw, eChatType a_Type) = 0;
virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) = 0;
virtual void SendChunkData (const std::string_view a_ChunkData) = 0;
virtual void SendCollectEntity (const cEntity & a_Collected, const cEntity & a_Collector, unsigned a_Count) = 0;
virtual void SendDestroyEntity (const cEntity & a_Entity) = 0;
virtual void SendDetachEntity (const cEntity & a_Entity, const cEntity & a_PreviousVehicle) = 0;

View File

@ -46,7 +46,7 @@ public:
virtual void SendChat (const AString & a_Message, eChatType a_Type) override;
virtual void SendChat (const cCompositeChat & a_Message, eChatType a_Type, bool a_ShouldUseChatPrefixes) override;
virtual void SendChatRaw (const AString & a_MessageRaw, eChatType a_Type) override;
virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
virtual void SendChunkData (const std::string_view a_ChunkData) override;
virtual void SendCollectEntity (const cEntity & a_Collected, const cEntity & a_Collector, unsigned a_Count) override;
virtual void SendDestroyEntity (const cEntity & a_Entity) override;
virtual void SendDetachEntity (const cEntity & a_Entity, const cEntity & a_PreviousVehicle) override;

View File

@ -106,22 +106,6 @@ void cProtocol_1_9_0::SendAttachEntity(const cEntity & a_Entity, const cEntity &
void cProtocol_1_9_0::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
{
ASSERT(m_State == 3); // In game mode?
// Serialize first, before creating the Packetizer (the packetizer locks a CS)
// This contains the flags and bitmasks, too
const AString & ChunkData = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_9_0, a_ChunkX, a_ChunkZ);
cCSLock Lock(m_CSPacket);
SendData(ChunkData.data(), ChunkData.size());
}
void cProtocol_1_9_0::SendDetachEntity(const cEntity & a_Entity, const cEntity & a_PreviousVehicle)
{
ASSERT(m_State == 3); // In game mode?
@ -2257,22 +2241,6 @@ cProtocol_1_9_4::cProtocol_1_9_4(cClientHandle * a_Client, const AString & a_Ser
void cProtocol_1_9_4::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
{
ASSERT(m_State == 3); // In game mode?
// Serialize first, before creating the Packetizer (the packetizer locks a CS)
// This contains the flags and bitmasks, too
const AString & ChunkData = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_9_4, a_ChunkX, a_ChunkZ);
cCSLock Lock(m_CSPacket);
SendData(ChunkData.data(), ChunkData.size());
}
void cProtocol_1_9_4::SendUpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4)
{
ASSERT(m_State == 3); // In game mode?

View File

@ -42,7 +42,6 @@ public:
/** Sending stuff to clients (alphabetically sorted): */
virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity & a_Vehicle) override;
virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
virtual void SendDetachEntity (const cEntity & a_Entity, const cEntity & a_PreviousVehicle) override;
virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override;
virtual void SendEntityMetadata (const cEntity & a_Entity) override;
@ -202,8 +201,7 @@ public:
protected:
virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override;
virtual void SendUpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override;
/** Returns 1.9.4. */
virtual Version GetProtocolVersion() override;