From 00f8c3a2251833f2d16ec89861a4377029cb2401 Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Sun, 19 Jul 2020 16:29:49 +0100 Subject: Implement "caching" in ChunkDataSerializer + When sending a chunk to multiple clients, group them by protocol version and send the same data --- src/ChunkSender.cpp | 8 +- src/ClientHandle.cpp | 4 +- src/ClientHandle.h | 2 +- src/Protocol/ChunkDataSerializer.cpp | 223 ++++++++++++++--------------------- src/Protocol/ChunkDataSerializer.h | 47 ++++---- src/Protocol/Protocol.h | 2 +- src/Protocol/Protocol_1_8.h | 2 +- src/Protocol/Protocol_1_9.cpp | 32 ----- src/Protocol/Protocol_1_9.h | 4 +- 9 files changed, 122 insertions(+), 202 deletions(-) diff --git a/src/ChunkSender.cpp b/src/ChunkSender.cpp index 3c4c565a3..dc0af8647 100644 --- a/src/ChunkSender.cpp +++ b/src/ChunkSender.cpp @@ -245,13 +245,15 @@ void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkZ, std::unordered_setSendChunkData(a_ChunkX, a_ChunkZ, Data); + cChunkDataSerializer Data(a_ChunkX, a_ChunkZ, m_Data, m_BiomeMap, m_World.GetDimension()); + Data.SendToClients(a_Clients); + } + for (const auto Client : a_Clients) + { // Send block-entity packets: for (const auto & Pos : m_BlockEntities) { diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 2a2c719b6..15b7238fe 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -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: { diff --git a/src/ClientHandle.h b/src/ClientHandle.h index 5c3d61c1f..755e176dd 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -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); diff --git a/src/Protocol/ChunkDataSerializer.cpp b/src/Protocol/ChunkDataSerializer.cpp index 52782bbb7..bf8798d6f 100644 --- a/src/Protocol/ChunkDataSerializer.cpp +++ b/src/Protocol/ChunkDataSerializer.cpp @@ -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 & a_SendTo) { - Serializations::const_iterator itr = m_Serializations.find(a_Version); - if (itr != m_Serializations.end()) + std::unordered_map> ClientProtocolVersions; + + for (const auto Client : a_SendTo) { - return itr->second; + const auto ClientProtocol = static_cast(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; - - default: + switch (Entry.first) { - LOGERROR("cChunkDataSerializer::Serialize(): Unknown version: %d", a_Version); - ASSERT(!"Unknown chunk data serialization version"); - break; + 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; + } } + + LOGERROR("cChunkDataSerializer::Serialize(): Unknown version: %d", Entry.first); + ASSERT(!"Unknown chunk data serialization version"); } - 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 & 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(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 & 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(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 & 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(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 +void cChunkDataSerializer::Serialize393And401(const std::vector & 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); + CompressAndSend(Packet, a_SendTo); +} + + + + + +void cChunkDataSerializer::CompressAndSend(cByteBuffer & a_Packet, const std::vector & a_SendTo) +{ AString PacketData; - Packet.ReadAll(PacketData); - Packet.CommitRead(); + a_Packet.ReadAll(PacketData); - if (PacketData.size() >= 256) + AString ToSend; + if (!cProtocol_1_8_0::CompressPacket(PacketData, ToSend)) { - if (!cProtocol_1_9_0::CompressPacket(PacketData, a_Data)) - { - ASSERT(!"Packet compression failed."); - a_Data.clear(); - return; - } + ASSERT(!"Packet compression failed."); + return; } - else + + for (const auto Client : a_SendTo) { - cByteBuffer Buffer(20); - AString PostData; - - Buffer.WriteVarInt32(static_cast(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); } } diff --git a/src/Protocol/ChunkDataSerializer.h b/src/Protocol/ChunkDataSerializer.h index 2670a0705..69e0a7689 100644 --- a/src/Protocol/ChunkDataSerializer.h +++ b/src/Protocol/ChunkDataSerializer.h @@ -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( - const cChunkData & a_Data, - const unsigned char * a_BiomeData, - const eDimension a_Dimension + 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 & a_SendTo); protected: - using Serializations = std::map; + void Serialize47 (const std::vector & a_SendTo); // Release 1.8 + void Serialize107(const std::vector & a_SendTo); // Release 1.9 + void Serialize110(const std::vector & a_SendTo); // Release 1.9.4 + + template + void Serialize393And401(const std::vector & 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 & 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 } ; diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h index 817d0bef0..8636c350f 100644 --- a/src/Protocol/Protocol.h +++ b/src/Protocol/Protocol.h @@ -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; diff --git a/src/Protocol/Protocol_1_8.h b/src/Protocol/Protocol_1_8.h index e7686577f..005cabfe3 100644 --- a/src/Protocol/Protocol_1_8.h +++ b/src/Protocol/Protocol_1_8.h @@ -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; diff --git a/src/Protocol/Protocol_1_9.cpp b/src/Protocol/Protocol_1_9.cpp index 90e770134..73b499009 100644 --- a/src/Protocol/Protocol_1_9.cpp +++ b/src/Protocol/Protocol_1_9.cpp @@ -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? diff --git a/src/Protocol/Protocol_1_9.h b/src/Protocol/Protocol_1_9.h index cb96f6962..2debb9e13 100644 --- a/src/Protocol/Protocol_1_9.h +++ b/src/Protocol/Protocol_1_9.h @@ -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; -- cgit v1.2.3