From 4aef80b47eb6941d7fc41e57efe147af0ece1f9b Mon Sep 17 00:00:00 2001 From: Mattes D Date: Fri, 3 Jan 2020 17:31:13 +0100 Subject: Added temporary block type mapping for 1.13+ protocols. --- Server/Install/UnixExecutables.list | 1 + Server/Install/WindowsExecutables.list | 1 + Server/Protocol/UpgradeBlockTypePalette.txt | 30 +++++--- src/Protocol/CMakeLists.txt | 2 + src/Protocol/ChunkDataSerializer.cpp | 33 +++------ src/Protocol/ChunkDataSerializer.h | 54 ++++++++------ src/Protocol/Protocol.h | 5 ++ src/Protocol/ProtocolPalettes.cpp | 107 ++++++++++++++++++++++++++++ src/Protocol/ProtocolPalettes.h | 63 ++++++++++++++++ src/Protocol/ProtocolRecognizer.cpp | 19 ++++- src/Protocol/Protocol_1_13.cpp | 35 ++++++++- src/Protocol/Protocol_1_13.h | 23 ++++++ src/Protocol/Protocol_1_8.cpp | 2 +- src/Protocol/Protocol_1_9.cpp | 4 +- src/Root.cpp | 61 +++++++++++----- src/Root.h | 11 +++ 16 files changed, 375 insertions(+), 76 deletions(-) create mode 100644 src/Protocol/ProtocolPalettes.cpp create mode 100644 src/Protocol/ProtocolPalettes.h diff --git a/Server/Install/UnixExecutables.list b/Server/Install/UnixExecutables.list index 2d6734684..4bf5017d4 100644 --- a/Server/Install/UnixExecutables.list +++ b/Server/Install/UnixExecutables.list @@ -1,6 +1,7 @@ Cuberite Plugins Prefabs +Protocol webadmin BACKERS brewing.txt diff --git a/Server/Install/WindowsExecutables.list b/Server/Install/WindowsExecutables.list index 66eeb4175..afd582c8f 100644 --- a/Server/Install/WindowsExecutables.list +++ b/Server/Install/WindowsExecutables.list @@ -2,6 +2,7 @@ Cuberite.exe *.dll Plugins Prefabs +Protocol webadmin BACKERS brewing.txt diff --git a/Server/Protocol/UpgradeBlockTypePalette.txt b/Server/Protocol/UpgradeBlockTypePalette.txt index a54a73c49..6373e6172 100644 --- a/Server/Protocol/UpgradeBlockTypePalette.txt +++ b/Server/Protocol/UpgradeBlockTypePalette.txt @@ -10,7 +10,7 @@ CommonPrefix minecraft: 1 4 polished_diorite 1 5 andesite 1 6 polished_andesite -2 0 grass_block +2 0 grass_block snowy false 3 0 dirt 3 1 coarse_dirt 3 2 podzol @@ -38,14 +38,26 @@ CommonPrefix minecraft: 14 0 gold_ore 15 0 iron_ore 16 0 coal_ore -17 0 oak_log -17 1 spruce_log -17 2 birch_log -17 3 jungle_log -18 0 oak_leaves -18 1 spruce_leaves -18 2 birch_leaves -18 3 jungle_leaves +17 0 oak_log axis y +17 1 spruce_log axis y +17 2 birch_log axis y +17 3 jungle_log axis y +17 4 oak_log axis x +17 5 spruce_log axis x +17 6 birch_log axis x +17 7 jungle_log axis x +17 8 oak_log axis z +17 9 spruce_log axis z +17 10 birch_log axis z +17 11 jungle_log axis z +17 12 oak_wood axis y +17 13 spruce_wood axis y +17 14 birch_wood axis y +17 15 jungle_wood axis y +18 0 oak_leaves persistent false distance 7 +18 1 spruce_leaves persistent false distance 7 +18 2 birch_leaves persistent false distance 7 +18 3 jungle_leaves persistent false distance 7 19 0 sponge 19 1 wet_sponge 20 0 glass 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 & 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 & 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 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 & a_BlockTypeMap); + + +protected: + + using Serializations = std::map; + + + /** 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 & 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 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 ProtocolPalettes::protocolVersions() const +{ + cCSLock lock(mCS); + + std::vector 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 blockTypePalette(const AString & aProtocolVersion) const; + + /** Returns the version names of all protocols that have been loaded. */ + std::vector protocolVersions() const; + + +protected: + + /** Container for all palettes for a single protocol. */ + struct Palettes + { + std::shared_ptr 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 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 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 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()); diff --git a/src/Root.cpp b/src/Root.cpp index 0869192b8..8ccf55d47 100644 --- a/src/Root.cpp +++ b/src/Root.cpp @@ -40,6 +40,7 @@ #include "Logger.h" #include "ClientHandle.h" #include "BlockTypePalette.h" +#include "Protocol/ProtocolPalettes.h" @@ -193,23 +194,7 @@ void cRoot::Start(std::unique_ptr a_OverridesRepo) // cClientHandle::FASTBREAK_PERCENTAGE = settingsRepo->GetValueSetI("AntiCheat", "FastBreakPercentage", 97) / 100.0f; cClientHandle::FASTBREAK_PERCENTAGE = 0; // AntiCheat disabled due to bugs. We will enabled it once they are fixed. See #3506. - // Load the UpgradeBlockTypePalette: - LOG("Loading UpgradeBlockTypePalette..."); - try - { - auto paletteStr = cFile::ReadWholeFile("Protocol/UpgradeBlockTypePalette.txt"); - if (paletteStr.empty()) - { - throw std::runtime_error("File is empty"); - } - m_UpgradeBlockTypePalette.reset(new BlockTypePalette); - m_UpgradeBlockTypePalette->loadFromString(paletteStr); - } - catch (const std::exception & exc) - { - LOGERROR("Failed to load the Upgrade block type palette from Protocol/UpgradeBlockTypePalette.txt: %s\nAborting", exc.what()); - throw; - } + LoadPalettes(settingsRepo->GetValueSet("Folders", "ProtocolPalettes", "Protocol")); m_MojangAPI = new cMojangAPI; bool ShouldAuthenticate = settingsRepo->GetValueSetB("Authentication", "Authenticate", true); @@ -416,6 +401,48 @@ void cRoot::LoadGlobalSettings() +void cRoot::LoadPalettes(const AString & aProtocolFolder) +{ + LOG("Loading UpgradeBlockTypePalette..."); + try + { + auto paletteStr = cFile::ReadWholeFile(aProtocolFolder + cFile::PathSeparator() + "UpgradeBlockTypePalette.txt"); + if (paletteStr.empty()) + { + throw std::runtime_error("File is empty"); + } + m_UpgradeBlockTypePalette.reset(new BlockTypePalette); + m_UpgradeBlockTypePalette->loadFromString(paletteStr); + } + catch (const std::exception & exc) + { + LOGERROR("Failed to load the Upgrade block type palette from %s/UpgradeBlockTypePalette.txt: %s\nAborting", + aProtocolFolder, exc.what() + ); + throw; + } + + // Note: This can take a lot of time in MSVC debug builds + // If impatient, edit the settings.ini: [Folders] ProtocolPalettes=DummyDir; copy only one palette to DummyDir + LOG("Loading per-protocol palettes..."); + m_ProtocolPalettes.reset(new ProtocolPalettes); + m_ProtocolPalettes->load(aProtocolFolder); + auto versions = m_ProtocolPalettes->protocolVersions(); + if (versions.empty()) + { + LOGWARNING("No per-protocol palettes were loaded"); + } + else + { + std::sort(versions.begin(), versions.end()); + LOG("Loaded palettes for protocol versions: %s", StringJoin(versions, ", ")); + } +} + + + + + void cRoot::LoadWorlds(cDeadlockDetect & a_dd, cSettingsRepositoryInterface & a_Settings, bool a_IsNewIniFile) { if (a_IsNewIniFile) diff --git a/src/Root.h b/src/Root.h index 1d3a6a347..90676a7df 100644 --- a/src/Root.h +++ b/src/Root.h @@ -29,6 +29,7 @@ class cSettingsRepositoryInterface; class cDeadlockDetect; class cUUID; class BlockTypePalette; +class ProtocolPalettes; using cPlayerListCallback = cFunctionRef; using cWorldListCallback = cFunctionRef; @@ -96,6 +97,9 @@ public: /** Returns the block type palette used for upgrading blocks from pre-1.13 data. */ const BlockTypePalette & GetUpgradeBlockTypePalette() const { return *m_UpgradeBlockTypePalette; } + /** Returns the per-protocol palettes manager. */ + ProtocolPalettes & GetProtocolPalettes() const { return *m_ProtocolPalettes; } + /** Returns the number of ticks for how long the item would fuel a furnace. Returns zero if not a fuel */ static int GetFurnaceFuelBurnTime(const cItem & a_Fuel); // tolua_export @@ -241,9 +245,16 @@ private: /** The upgrade palette for pre-1.13 blocks. */ std::unique_ptr m_UpgradeBlockTypePalette; + /** The per-protocol palettes manager. */ + std::unique_ptr m_ProtocolPalettes; + void LoadGlobalSettings(); + /** Loads the upgrade palette and the per-protocol palettes. + The aProtocolFolder is the path to the folder containing the per-protocol palettes. */ + void LoadPalettes(const AString & aProtocolFolder); + /** Loads the worlds from settings.ini, creates the worldmap */ void LoadWorlds(cDeadlockDetect & a_dd, cSettingsRepositoryInterface & a_Settings, bool a_IsNewIniFile); -- cgit v1.2.3