From 09ff17b71ea5659dc628d9d6e3f1fd308d10037a Mon Sep 17 00:00:00 2001 From: Howaner Date: Mon, 8 Sep 2014 00:36:30 +0200 Subject: Implemented packet compression. ChunkData packet needs this. --- src/Protocol/ChunkDataSerializer.cpp | 83 +++++++++++++++++++++++++++--------- src/Protocol/ChunkDataSerializer.h | 4 +- src/Protocol/Protocol125.cpp | 2 +- src/Protocol/Protocol132.cpp | 2 +- src/Protocol/Protocol17x.cpp | 26 ++++++++++- src/Protocol/Protocol18x.cpp | 34 ++++++++++----- src/Protocol/Protocol18x.h | 1 + 7 files changed, 117 insertions(+), 35 deletions(-) diff --git a/src/Protocol/ChunkDataSerializer.cpp b/src/Protocol/ChunkDataSerializer.cpp index 29e32ce32..a2e0c9ef9 100644 --- a/src/Protocol/ChunkDataSerializer.cpp +++ b/src/Protocol/ChunkDataSerializer.cpp @@ -8,6 +8,7 @@ #include "Globals.h" #include "ChunkDataSerializer.h" #include "zlib/zlib.h" +#include "ByteBuffer.h" @@ -30,7 +31,7 @@ cChunkDataSerializer::cChunkDataSerializer( -const AString & cChunkDataSerializer::Serialize(int a_Version) +const AString & cChunkDataSerializer::Serialize(int a_Version, int a_ChunkX, int a_ChunkZ) { Serializations::const_iterator itr = m_Serializations.find(a_Version); if (itr != m_Serializations.end()) @@ -43,7 +44,7 @@ const AString & cChunkDataSerializer::Serialize(int a_Version) { case RELEASE_1_2_5: Serialize29(data); break; case RELEASE_1_3_2: Serialize39(data); break; - case RELEASE_1_8_0: Serialize80(data); break; + case RELEASE_1_8_0: Serialize80(data, a_ChunkX, a_ChunkZ); break; // TODO: Other protocol versions may serialize the data differently; implement here default: @@ -176,45 +177,87 @@ void cChunkDataSerializer::Serialize39(AString & a_Data) -void cChunkDataSerializer::Serialize80(AString & a_Data) +void cChunkDataSerializer::Serialize80(AString & a_Data, int a_ChunkX, int a_ChunkZ) { // TODO: Do not copy data and then compress it; rather, compress partial blocks of data (zlib *can* stream) // Blocktypes converter (1.8 included the meta into the blocktype): - unsigned short Blocks[ARRAYCOUNT(m_BlockTypes)]; + /*unsigned short Blocks[ARRAYCOUNT(m_BlockTypes)]; for (size_t Index = 0; Index < cChunkDef::NumBlocks; Index++) { BLOCKTYPE BlockType = m_BlockTypes[Index]; NIBBLETYPE BlockMeta = m_BlockMetas[Index / 2] >> ((Index & 1) * 4) & 0x0f; Blocks[Index] = ((unsigned short)BlockType << 4) | ((unsigned short)BlockMeta); - } + }*/ const int BiomeDataSize = cChunkDef::Width * cChunkDef::Width; - const int BlockLightOffset = sizeof(Blocks); + const int MetadataOffset = sizeof(m_BlockTypes); + const int BlockLightOffset = MetadataOffset + sizeof(m_BlockMetas); const int SkyLightOffset = BlockLightOffset + sizeof(m_BlockLight); const int BiomeOffset = SkyLightOffset + sizeof(m_BlockSkyLight); const int DataSize = BiomeOffset + BiomeDataSize; // Temporary buffer for the composed data: char AllData [DataSize]; - memcpy(AllData, Blocks, sizeof(Blocks)); + memcpy(AllData, m_BlockTypes, sizeof(m_BlockTypes)); + memcpy(AllData + MetadataOffset, m_BlockMetas, sizeof(m_BlockMetas)); memcpy(AllData + BlockLightOffset, m_BlockLight, sizeof(m_BlockLight)); memcpy(AllData + SkyLightOffset, m_BlockSkyLight, sizeof(m_BlockSkyLight)); memcpy(AllData + BiomeOffset, m_BiomeData, BiomeDataSize); - // Put all those data into a_Data: - a_Data.push_back('\x01'); // "Ground-up continuous", or rather, "biome data present" flag - - // Two bitmaps; we're aways sending the full chunk with no additional data, so the bitmaps are 0xffff and 0, respectively - // Also, no endian flipping is needed because of the const values - unsigned short BitMap = 0xffff; - a_Data.append((const char *)&BitMap, sizeof(unsigned short)); - - // Write chunk size: - UInt32 ChunkSize = htonl((UInt32)DataSize); - a_Data.append((const char *)&ChunkSize, 4); - - a_Data.append(AllData, DataSize); // Chunk data + cByteBuffer Packet(512 KiB); + Packet.WriteVarInt(0x21); // Packet id (Chunk Data packet) + Packet.WriteBEInt(a_ChunkX); + Packet.WriteBEInt(a_ChunkZ); + Packet.WriteBool(true); // "Ground-up continuous", or rather, "biome data present" flag + Packet.WriteBEShort(0xffff); // We're aways sending the full chunk with no additional data, so the bitmap is 0xffff + Packet.WriteVarInt(DataSize); // Chunk size + Packet.WriteBuf(AllData, DataSize); // Chunk data + + AString PacketData; + Packet.ReadAll(PacketData); + Packet.CommitRead(); + + cByteBuffer NumberBuffer(20); + if (PacketData.size() >= 256) + { + AString PostData; + NumberBuffer.WriteVarInt(PacketData.size()); + NumberBuffer.ReadAll(PostData); + NumberBuffer.CommitRead(); + + // Compress the data: + const uLongf CompressedMaxSize = 200000; + char CompressedData[CompressedMaxSize]; + + uLongf CompressedSize = compressBound(PacketData.size()); + // Run-time check that our compile-time guess about CompressedMaxSize was enough: + ASSERT(CompressedSize <= CompressedMaxSize); + compress2((Bytef*)CompressedData, &CompressedSize, (const Bytef*)PacketData.data(), PacketData.size(), Z_DEFAULT_COMPRESSION); + + NumberBuffer.WriteVarInt(CompressedSize + PostData.size()); + NumberBuffer.WriteVarInt(PacketData.size()); + NumberBuffer.ReadAll(PostData); + NumberBuffer.CommitRead(); + + a_Data.clear(); + a_Data.resize(PostData.size() + CompressedSize); + a_Data.append(PostData.data(), PostData.size()); + a_Data.append(CompressedData, CompressedSize); + } + else + { + AString PostData; + NumberBuffer.WriteVarInt(Packet.GetUsedSpace() + 1); + NumberBuffer.WriteVarInt(0); + NumberBuffer.ReadAll(PostData); + NumberBuffer.CommitRead(); + + a_Data.clear(); + a_Data.resize(PostData.size() + PacketData.size()); + a_Data.append(PostData.data(), PostData.size()); + a_Data.append(PacketData.data(), PacketData.size()); + } } diff --git a/src/Protocol/ChunkDataSerializer.h b/src/Protocol/ChunkDataSerializer.h index 4c33aede1..3caa89d41 100644 --- a/src/Protocol/ChunkDataSerializer.h +++ b/src/Protocol/ChunkDataSerializer.h @@ -24,7 +24,7 @@ protected: void Serialize29(AString & a_Data); // Release 1.2.4 and 1.2.5 void Serialize39(AString & a_Data); // Release 1.3.1 to 1.7.10 - void Serialize80(AString & a_Data); // Release 1.8 + void Serialize80(AString & a_Data, int a_ChunkX, int a_ChunkZ); // Release 1.8 public: enum @@ -42,7 +42,7 @@ public: const unsigned char * a_BiomeData ); - const AString & Serialize(int a_Version); // Returns one of the internal m_Serializations[] + const AString & Serialize(int a_Version, int a_ChunkX, int a_ChunkZ); // Returns one of the internal m_Serializations[] } ; diff --git a/src/Protocol/Protocol125.cpp b/src/Protocol/Protocol125.cpp index a66c64309..a74be28a5 100644 --- a/src/Protocol/Protocol125.cpp +++ b/src/Protocol/Protocol125.cpp @@ -262,7 +262,7 @@ void cProtocol125::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerialize SendPreChunk(a_ChunkX, a_ChunkZ, true); // Send the chunk data: - AString Serialized = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_2_5); + AString Serialized = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_2_5, a_ChunkX, a_ChunkZ); WriteByte(PACKET_MAP_CHUNK); WriteInt (a_ChunkX); WriteInt (a_ChunkZ); diff --git a/src/Protocol/Protocol132.cpp b/src/Protocol/Protocol132.cpp index 5c58ab0ba..5fd2655b8 100644 --- a/src/Protocol/Protocol132.cpp +++ b/src/Protocol/Protocol132.cpp @@ -176,7 +176,7 @@ void cProtocol132::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerialize // Pre-chunk not used in 1.3.2. Finally. // Send the chunk data: - AString Serialized = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_3_2); + AString Serialized = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_3_2, a_ChunkX, a_ChunkZ); WriteByte(PACKET_CHUNK_DATA); WriteInt (a_ChunkX); WriteInt (a_ChunkZ); diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index b4f30b639..0bee40028 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -346,7 +346,7 @@ void cProtocol172::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerialize // 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_3_2); + const AString & ChunkData = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_3_2, a_ChunkX, a_ChunkZ); cPacketizer Pkt(*this, 0x21); // Chunk Data packet Pkt.WriteInt(a_ChunkX); @@ -1533,6 +1533,17 @@ void cProtocol172::AddReceivedData(const char * a_Data, size_t a_Size) // Write one NUL extra, so that we can detect over-reads bb.Write("\0", 1); + // 1.8 - Compressed packets + if (m_State == 3) + { + UInt32 CompressedSize; + if (!bb.ReadVarInt(CompressedSize)) + { + // Not enough data + break; + } + } + UInt32 PacketType; if (!bb.ReadVarInt(PacketType)) { @@ -2511,10 +2522,23 @@ cProtocol172::cPacketizer::~cPacketizer() // Send the packet length UInt32 PacketLen = (UInt32)m_Out.GetUsedSpace(); + if (m_Protocol.m_State == 3) + { + PacketLen += 1; + } + m_Protocol.m_OutPacketLenBuffer.WriteVarInt(PacketLen); m_Protocol.m_OutPacketLenBuffer.ReadAll(DataToSend); m_Protocol.SendData(DataToSend.data(), DataToSend.size()); m_Protocol.m_OutPacketLenBuffer.CommitRead(); + + if (m_Protocol.m_State == 3) + { + m_Protocol.m_OutPacketLenBuffer.WriteVarInt(0); + m_Protocol.m_OutPacketLenBuffer.ReadAll(DataToSend); + m_Protocol.SendData(DataToSend.data(), DataToSend.size()); + m_Protocol.m_OutPacketLenBuffer.CommitRead(); + } // Send the packet data: m_Out.ReadAll(DataToSend); diff --git a/src/Protocol/Protocol18x.cpp b/src/Protocol/Protocol18x.cpp index a3e72d4dd..bcb5c2036 100644 --- a/src/Protocol/Protocol18x.cpp +++ b/src/Protocol/Protocol18x.cpp @@ -13,6 +13,7 @@ Implements the 1.8.x protocol classes: #include "json/json.h" #include "ChunkDataSerializer.h" #include "Protocol18x.h" +#include "zlib/zlib.h" #include "../ClientHandle.h" #include "../CompositeChat.h" @@ -21,7 +22,7 @@ Implements the 1.8.x protocol classes: #include "../World.h" #include "../Entities/Player.h" -#include "Entities/Painting.h" +#include "../Entities/Painting.h" @@ -57,23 +58,38 @@ cProtocol180::cProtocol180(cClientHandle * a_Client, const AString & a_ServerAdd +void cProtocol180::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x28); // Effect packet + Pkt.WriteInt(a_EffectID); + Pkt.WritePosition(Vector3i(a_SrcX, a_SrcY, a_SrcZ)); + Pkt.WriteInt(a_Data); + Pkt.WriteBool(false); +} + + + + + void cProtocol180::SendLoginSuccess(void) { ASSERT(m_State == 2); // State: login? - // Disable compression: + // Enable compression: { cPacketizer Pkt(*this, 0x03); // Set compression packet - Pkt.WriteVarInt(-1); + Pkt.WriteVarInt(256); } + m_State = 3; // State = Game + { cPacketizer Pkt(*this, 0x02); // Login success packet Pkt.WriteString(cMojangAPI::MakeUUIDDashed(m_Client->GetUUID())); Pkt.WriteString(m_Client->GetUsername()); } - - m_State = 3; // State = Game } @@ -409,12 +425,10 @@ void cProtocol180::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerialize // 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_8_0); + const AString & ChunkData = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_8_0, a_ChunkX, a_ChunkZ); - cPacketizer Pkt(*this, 0x21); // Chunk Data packet - Pkt.WriteInt(a_ChunkX); - Pkt.WriteInt(a_ChunkZ); - Pkt.WriteBuf(ChunkData.data(), ChunkData.size()); + cCSLock Lock(m_CSPacket); + SendData(ChunkData.data(), ChunkData.size()); } diff --git a/src/Protocol/Protocol18x.h b/src/Protocol/Protocol18x.h index 5c6111958..d64742939 100644 --- a/src/Protocol/Protocol18x.h +++ b/src/Protocol/Protocol18x.h @@ -54,6 +54,7 @@ public: cProtocol180(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State); + virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; virtual void SendLoginSuccess (void) override; virtual void SendPlayerAbilities (void) override {} virtual void SendWindowClose (const cWindow & a_Window) override {} -- cgit v1.2.3