diff options
author | Mattes D <github@xoft.cz> | 2020-01-03 17:31:13 +0100 |
---|---|---|
committer | Mattes D <github@xoft.cz> | 2020-01-07 06:53:17 +0100 |
commit | 4aef80b47eb6941d7fc41e57efe147af0ece1f9b (patch) | |
tree | 4aeb7c9e8e4aa3ae2ceed1cc60155d868852c5cd /src/Protocol | |
parent | StringUtils: Added note to StringsConcat about StringJoin. (diff) | |
download | cuberite-4aef80b47eb6941d7fc41e57efe147af0ece1f9b.tar cuberite-4aef80b47eb6941d7fc41e57efe147af0ece1f9b.tar.gz cuberite-4aef80b47eb6941d7fc41e57efe147af0ece1f9b.tar.bz2 cuberite-4aef80b47eb6941d7fc41e57efe147af0ece1f9b.tar.lz cuberite-4aef80b47eb6941d7fc41e57efe147af0ece1f9b.tar.xz cuberite-4aef80b47eb6941d7fc41e57efe147af0ece1f9b.tar.zst cuberite-4aef80b47eb6941d7fc41e57efe147af0ece1f9b.zip |
Diffstat (limited to 'src/Protocol')
-rw-r--r-- | src/Protocol/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/Protocol/ChunkDataSerializer.cpp | 33 | ||||
-rw-r--r-- | src/Protocol/ChunkDataSerializer.h | 54 | ||||
-rw-r--r-- | src/Protocol/Protocol.h | 5 | ||||
-rw-r--r-- | src/Protocol/ProtocolPalettes.cpp | 107 | ||||
-rw-r--r-- | src/Protocol/ProtocolPalettes.h | 63 | ||||
-rw-r--r-- | src/Protocol/ProtocolRecognizer.cpp | 19 | ||||
-rw-r--r-- | src/Protocol/Protocol_1_13.cpp | 35 | ||||
-rw-r--r-- | src/Protocol/Protocol_1_13.h | 23 | ||||
-rw-r--r-- | src/Protocol/Protocol_1_8.cpp | 2 | ||||
-rw-r--r-- | src/Protocol/Protocol_1_9.cpp | 4 |
11 files changed, 297 insertions, 50 deletions
diff --git a/src/Protocol/CMakeLists.txt b/src/Protocol/CMakeLists.txt index 3e8d65b94..f2169ce86 100644 --- a/src/Protocol/CMakeLists.txt +++ b/src/Protocol/CMakeLists.txt @@ -12,6 +12,7 @@ SET (SRCS Protocol_1_11.cpp Protocol_1_12.cpp Protocol_1_13.cpp + ProtocolPalettes.cpp ProtocolRecognizer.cpp ) @@ -28,6 +29,7 @@ SET (HDRS Protocol_1_11.h Protocol_1_12.h Protocol_1_13.h + ProtocolPalettes.h ProtocolRecognizer.h ) diff --git a/src/Protocol/ChunkDataSerializer.cpp b/src/Protocol/ChunkDataSerializer.cpp index 23d5b19ae..ea688ebd8 100644 --- a/src/Protocol/ChunkDataSerializer.cpp +++ b/src/Protocol/ChunkDataSerializer.cpp @@ -1,10 +1,3 @@ - -// ChunkDataSerializer.cpp - -// Implements the cChunkDataSerializer class representing the object that can: -// - serialize chunk data to different protocol versions -// - cache such serialized data for multiple clients - #include "Globals.h" #include "ChunkDataSerializer.h" #include "zlib/zlib.h" @@ -52,7 +45,7 @@ cChunkDataSerializer::cChunkDataSerializer( -const AString & cChunkDataSerializer::Serialize(int a_Version, int a_ChunkX, int a_ChunkZ) +const AString & cChunkDataSerializer::Serialize(int a_Version, int a_ChunkX, int a_ChunkZ, const std::map<UInt32, UInt32> & a_BlockTypeMap) { Serializations::const_iterator itr = m_Serializations.find(a_Version); if (itr != m_Serializations.end()) @@ -63,11 +56,10 @@ const AString & cChunkDataSerializer::Serialize(int a_Version, int a_ChunkX, int AString data; switch (a_Version) { - case RELEASE_1_8_0: Serialize47(data, a_ChunkX, a_ChunkZ); break; + 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; - // TODO: Other protocol versions may serialize the data differently; implement here + case RELEASE_1_13: Serialize393(data, a_ChunkX, a_ChunkZ, a_BlockTypeMap); break; default: { @@ -442,10 +434,12 @@ void cChunkDataSerializer::Serialize110(AString & a_Data, int a_ChunkX, int a_Ch -void cChunkDataSerializer::Serialize393(AString & a_Data, int a_ChunkX, int a_ChunkZ) +void cChunkDataSerializer::Serialize393(AString & a_Data, int a_ChunkX, int a_ChunkZ, const std::map<UInt32, UInt32> & a_BlockTypeMap) { // This function returns the fully compressed packet (including packet size), not the raw packet! + ASSERT(!a_BlockTypeMap.empty()); // We need a protocol-specific translation map + // Create the packet: cByteBuffer Packet(512 KiB); Packet.WriteVarInt32(0x22); // Packet id (Chunk Data packet) @@ -489,17 +483,10 @@ void cChunkDataSerializer::Serialize393(AString & a_Data, int a_ChunkX, int a_Ch for (size_t Index = 0; Index < cChunkData::SectionBlockCount; Index++) { - UInt64 Value = a_Section.m_BlockTypes[Index]; - /* - if (Index % 2 == 0) - { - Value |= a_Section.m_BlockMetas[Index / 2] & 0x0f; - } - else - { - Value |= a_Section.m_BlockMetas[Index / 2] >> 4; - } - */ + UInt32 blockType = a_Section.m_BlockTypes[Index]; + UInt32 blockMeta = (a_Section.m_BlockMetas[Index / 2] >> ((Index % 2) * 4)) & 0x0f; + auto itr = a_BlockTypeMap.find(blockType * 16 | blockMeta); + UInt64 Value = (itr == a_BlockTypeMap.end()) ? 0 :itr->second; 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. diff --git a/src/Protocol/ChunkDataSerializer.h b/src/Protocol/ChunkDataSerializer.h index 7011d3e15..a77935258 100644 --- a/src/Protocol/ChunkDataSerializer.h +++ b/src/Protocol/ChunkDataSerializer.h @@ -1,31 +1,16 @@ - -// ChunkDataSerializer.h - -// Interfaces to the cChunkDataSerializer class representing the object that can: -// - serialize chunk data to different protocol versions -// - cache such serialized data for multiple clients +#pragma once #include "../ChunkData.h" + +/** 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. */ class cChunkDataSerializer { -protected: - const cChunkData & m_Data; - const unsigned char * m_BiomeData; - const eDimension m_Dimension; - - typedef std::map<int, AString> Serializations; - - 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 - public: enum { @@ -41,7 +26,34 @@ public: const eDimension a_Dimension ); - const AString & Serialize(int a_Version, int a_ChunkX, int a_ChunkZ); // Returns one of the internal m_Serializations[] + /** Serializes the contained chunk data into the specified protocol version. + TEMPORARY: a_BlockTypeMap is used for the 1.13+ protocols to map from BLOCKTYPE#META to NetBlockID. + a_BlockTypeMap is ignored for pre-1.13 protocols. */ + const AString & Serialize(int a_Version, int a_ChunkX, int a_ChunkZ, const std::map<UInt32, UInt32> & a_BlockTypeMap); + + +protected: + + using Serializations = std::map<int, AString>; + + + /** The data read from the chunk, to be serialized. */ + const cChunkData & m_Data; + + /** The biomes in the chunk, to be serialized. */ + const unsigned char * m_BiomeData; + + /** 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, const std::map<UInt32, UInt32> & a_BlockTypeMap); // Release 1.13 } ; diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h index 8707c3326..c71b295ad 100644 --- a/src/Protocol/Protocol.h +++ b/src/Protocol/Protocol.h @@ -57,6 +57,11 @@ public: virtual ~cProtocol() {} + /** Called after construction so that the protocol class can initialize itself. + Throws a std::exception descendant on failure; the client is kicked + with the exception's message as a result. */ + virtual void Initialize(cClientHandle & a_Client) {} + /** Logical types of outgoing packets. These values get translated to on-wire packet IDs in GetPacketID(), specific for each protocol. This is mainly useful for protocol sub-versions that re-number the packets while using mostly the same packet layout. */ diff --git a/src/Protocol/ProtocolPalettes.cpp b/src/Protocol/ProtocolPalettes.cpp new file mode 100644 index 000000000..2dc0857a9 --- /dev/null +++ b/src/Protocol/ProtocolPalettes.cpp @@ -0,0 +1,107 @@ +#include "Globals.h" +#include "ProtocolPalettes.h" +#include "../BlockTypePalette.h" + + + + + +void ProtocolPalettes::load(const AString & aProtocolFolder) +{ + auto contents = cFile::GetFolderContents(aProtocolFolder); + for (const auto & c: contents) + { + auto fullName = aProtocolFolder + cFile::PathSeparator() + c; + if (cFile::IsFolder(fullName)) + { + loadSingleVersion(c, fullName); + } + } +} + + + + + +std::shared_ptr<const BlockTypePalette> ProtocolPalettes::blockTypePalette(const AString & aProtocolVersion) const +{ + cCSLock lock(mCS); + auto itr = mPalettes.find(aProtocolVersion); + if (itr == mPalettes.end()) + { + return nullptr; + } + return itr->second.mBlockTypePalette; +} + + + + + +std::vector<AString> ProtocolPalettes::protocolVersions() const +{ + cCSLock lock(mCS); + + std::vector<AString> res; + for (const auto & p: mPalettes) + { + res.push_back(p.first); + } + return res; +} + + + + + +void ProtocolPalettes::loadSingleVersion(const AString & aProtocolVersion, const AString & aFolder) +{ + // Get the file list, sort by name + auto contents = cFile::GetFolderContents(aFolder); + std::sort(contents.begin(), contents.end()); + + // Load files into the palettes: + cCSLock lock(mCS); + auto & pal = mPalettes[aProtocolVersion]; + for (const auto & c: contents) + { + if (c.length() < 8) + { + // Name too short, can't have the ".btp.txt" etc. suffix + continue; + } + auto fnam = aFolder + cFile::PathSeparator() + c; + if (!cFile::IsFile(fnam)) + { + continue; + } + auto fileType = c.substr(c.length() - 8); + if ((fileType == ".btp.txt") || (c == "blocks.json")) + { + try + { + pal.mBlockTypePalette->loadFromString(cFile::ReadWholeFile(fnam)); + } + catch (...) + { + // Ignore silently + } + } + else if ((fileType == ".itp.txt") || (c == "items.json")) + { + // TODO: Load item type palette + } + } +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// ProtocolPalettes::Palettes: + +ProtocolPalettes::Palettes::Palettes(): + mBlockTypePalette(new BlockTypePalette) +{ +} diff --git a/src/Protocol/ProtocolPalettes.h b/src/Protocol/ProtocolPalettes.h new file mode 100644 index 000000000..fad093a04 --- /dev/null +++ b/src/Protocol/ProtocolPalettes.h @@ -0,0 +1,63 @@ +#pragma once + +#include "../OSSupport/CriticalSection.h" + + + + + +// fwd: +class BlockTypePalette; + + + + + +/** Loads the protocol-specific palettes on startup and provides them to the individual protocol +instances when they are created. +Uses the data in the $/Server/Protocol folder. Each protocol version has a subfolder there, +containing possibly multiple palette files. All the files are loaded in sequence (alpha-sorted), +into the palette corresponding to the file's extension (*.btp.txt -> BlockTypePalette). +Provides thread safety for the data properly. */ +class ProtocolPalettes +{ +public: + + /** Loads all the per-protocol palettes. + aProtocolFolder is the folder that contains a subfolder for each protocol version; + each subfolder contains the protocol-specific palettes (as in $/Server/Protocol) + If a protocol version is already loaded, yet present in the folder, the newly loaded data is merged + into the current data. + Always succeeds (even when there are no palettes). */ + void load(const AString & aProtocolFolder); + + /** Returns the BlockTypePalette for the specified protocol. + Returns nullptr if no such palette has been loaded. */ + std::shared_ptr<const BlockTypePalette> blockTypePalette(const AString & aProtocolVersion) const; + + /** Returns the version names of all protocols that have been loaded. */ + std::vector<AString> protocolVersions() const; + + +protected: + + /** Container for all palettes for a single protocol. */ + struct Palettes + { + std::shared_ptr<BlockTypePalette> mBlockTypePalette; + // TODO: ItemTypePalette + + Palettes(); + }; + + + /** The CS protecting all members against multithreaded access. */ + mutable cCriticalSection mCS; + + /** The map of protocol version -> all its palettes. */ + std::map<AString, Palettes> mPalettes; + + + /** Loads all the palettes from the specified folder into mPalettes under the aProtocolVersion key. */ + void loadSingleVersion(const AString & aProtocolVersion, const AString & aFolder); +}; diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp index 6ad0a095d..17b42dae9 100644 --- a/src/Protocol/ProtocolRecognizer.cpp +++ b/src/Protocol/ProtocolRecognizer.cpp @@ -1038,7 +1038,24 @@ bool cProtocolRecognizer::TryRecognizeProtocol(void) // Not enough bytes for the packet, keep waiting return false; } - return TryRecognizeLengthedProtocol(PacketLen - ReadSoFar); + if (!TryRecognizeLengthedProtocol(PacketLen - ReadSoFar)) + { + return false; + } + + // The protocol has been recognized, initialize it: + ASSERT(m_Protocol != nullptr); + try + { + m_Protocol->Initialize(*m_Client); + } + catch (const std::exception & exc) + { + m_Client->Kick(exc.what()); + m_Protocol.reset(); + return false; + } + return true; } diff --git a/src/Protocol/Protocol_1_13.cpp b/src/Protocol/Protocol_1_13.cpp index f84e8d1bd..e2cd72693 100644 --- a/src/Protocol/Protocol_1_13.cpp +++ b/src/Protocol/Protocol_1_13.cpp @@ -11,6 +11,7 @@ Implements the 1.13 protocol classes: #include "ProtocolRecognizer.h" #include "ChunkDataSerializer.h" #include "Packetizer.h" +#include "ProtocolPalettes.h" #include "../Entities/Boat.h" #include "../Entities/Minecart.h" @@ -21,9 +22,11 @@ Implements the 1.13 protocol classes: #include "../Entities/FireworkEntity.h" #include "../Entities/SplashPotionEntity.h" +#include "../BlockTypePalette.h" +#include "../ClientHandle.h" #include "../Root.h" #include "../Server.h" -#include "../ClientHandle.h" + #include "../Bindings/PluginManager.h" @@ -69,6 +72,25 @@ cProtocol_1_13::cProtocol_1_13(cClientHandle * a_Client, const AString & a_Serve +void cProtocol_1_13::Initialize(cClientHandle & a_Client) +{ + // Get the palettes; fail if not available: + auto paletteVersion = this->GetPaletteVersion(); + m_BlockTypePalette = cRoot::Get()->GetProtocolPalettes().blockTypePalette(paletteVersion); + if (m_BlockTypePalette == nullptr) + { + throw std::runtime_error(Printf("This server doesn't support protocol %s.", paletteVersion)); + } + + // Process the palette into the temporary BLOCKTYPE -> NetBlockID map: + auto upg = cRoot::Get()->GetUpgradeBlockTypePalette(); + m_BlockTypeMap = m_BlockTypePalette->createTransformMapWithFallback(upg, 0); +} + + + + + UInt32 cProtocol_1_13::GetPacketID(ePacketType a_PacketType) { switch (a_PacketType) @@ -132,6 +154,15 @@ UInt32 cProtocol_1_13::GetPacketID(ePacketType a_PacketType) +AString cProtocol_1_13::GetPaletteVersion() const +{ + return "1.13"; +} + + + + + bool cProtocol_1_13::HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType) { if (m_State != 3) @@ -292,7 +323,7 @@ void cProtocol_1_13::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSeriali { ASSERT(m_State == 3); // In game mode? - const AString & ChunkData = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_13, a_ChunkX, a_ChunkZ); + const AString & ChunkData = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_13, a_ChunkX, a_ChunkZ, m_BlockTypeMap); cCSLock Lock(m_CSPacket); SendData(ChunkData.data(), ChunkData.size()); } diff --git a/src/Protocol/Protocol_1_13.h b/src/Protocol/Protocol_1_13.h index 6eace0567..34827501c 100644 --- a/src/Protocol/Protocol_1_13.h +++ b/src/Protocol/Protocol_1_13.h @@ -20,6 +20,13 @@ Declares the 1.13 protocol classes: +// fwd: +class BlockTypePalette; + + + + + class cProtocol_1_13 : public cProtocol_1_12_2 { @@ -28,8 +35,24 @@ class cProtocol_1_13 : public: cProtocol_1_13(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State); + virtual void Initialize(cClientHandle & a_Client) override; + + protected: + /** The palette used to transform internal block type palette into the protocol-specific ID. */ + std::shared_ptr<const BlockTypePalette> m_BlockTypePalette; + + /** Temporary hack for initial 1.13+ support while keeping BLOCKTYPE data: + Map of the BLOCKTYPE#META to the protocol-specific NetBlockID. */ + std::map<UInt32, UInt32> m_BlockTypeMap; + + + /** Returns the string identifying the palettes' version, such as "1.13" or "1.14.4". + The palettes for that version are loaded into m_BlockTypePalette and m_ItemTypePalette. */ + virtual AString GetPaletteVersion() const; + + // Outgoing packet type translation: virtual UInt32 GetPacketID(ePacketType a_PacketType) override; // Packet receiving: diff --git a/src/Protocol/Protocol_1_8.cpp b/src/Protocol/Protocol_1_8.cpp index 72fbb4678..00eaf4284 100644 --- a/src/Protocol/Protocol_1_8.cpp +++ b/src/Protocol/Protocol_1_8.cpp @@ -307,7 +307,7 @@ void cProtocol_1_8_0::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerial // 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, a_ChunkX, a_ChunkZ); + const AString & ChunkData = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_8_0, a_ChunkX, a_ChunkZ, {}); cCSLock Lock(m_CSPacket); SendData(ChunkData.data(), ChunkData.size()); diff --git a/src/Protocol/Protocol_1_9.cpp b/src/Protocol/Protocol_1_9.cpp index 4bf358567..fa5215102 100644 --- a/src/Protocol/Protocol_1_9.cpp +++ b/src/Protocol/Protocol_1_9.cpp @@ -351,7 +351,7 @@ void cProtocol_1_9_0::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerial // 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); + const AString & ChunkData = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_9_0, a_ChunkX, a_ChunkZ, {}); cCSLock Lock(m_CSPacket); SendData(ChunkData.data(), ChunkData.size()); @@ -4513,7 +4513,7 @@ void cProtocol_1_9_4::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerial // 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); + const AString & ChunkData = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_9_4, a_ChunkX, a_ChunkZ, {}); cCSLock Lock(m_CSPacket); SendData(ChunkData.data(), ChunkData.size()); |