diff options
Diffstat (limited to '')
-rw-r--r-- | src/BlockState.cpp | 46 | ||||
-rw-r--r-- | src/BlockState.h | 3 | ||||
-rw-r--r-- | src/Protocol/CMakeLists.txt | 3 | ||||
-rw-r--r-- | src/Protocol/ProtocolBlockTypePalette.cpp | 144 | ||||
-rw-r--r-- | src/Protocol/ProtocolBlockTypePalette.h | 40 |
5 files changed, 236 insertions, 0 deletions
diff --git a/src/BlockState.cpp b/src/BlockState.cpp index 4e2607bd3..8ee87c50f 100644 --- a/src/BlockState.cpp +++ b/src/BlockState.cpp @@ -83,6 +83,52 @@ BlockState::BlockState(const BlockState & aCopyFrom, const std::map<AString, ASt +bool BlockState::operator <(const BlockState & aOther) const +{ + // Fast-return this using checksum + if (mChecksum != aOther.mChecksum) + { + return (mChecksum < aOther.mChecksum); + } + + // Can fast-return this due to how comparison works + if (mState.size() != aOther.mState.size()) + { + return (mState.size() < aOther.mState.size()); + } + + auto itA = mState.begin(); + auto itOther = aOther.mState.begin(); + + // don't need to check itOther, size checks above ensure size(A) == size(O) + while (itA != mState.end()) + { + { + const auto cmp = itA->first.compare(itOther->first); + if (cmp != 0) + { + return (cmp < 0); + } + } + { + const auto cmp = itA->second.compare(itOther->second); + if (cmp != 0) + { + return (cmp < 0); + } + } + + ++itA; + ++itOther; + } + + return false; +} + + + + + bool BlockState::operator ==(const BlockState & aOther) const { // Fast-fail if the checksums differ or differrent counts: diff --git a/src/BlockState.h b/src/BlockState.h index 3b0575f0e..ab451236b 100644 --- a/src/BlockState.h +++ b/src/BlockState.h @@ -52,6 +52,9 @@ public: (it's possible to erase a key from aCopyFrom by setting it to empty string in aAdditionalKeysAndValues). */ BlockState(const BlockState & aCopyFrom, const std::map<AString, AString> & aAdditionalKeysAndValues); + /** Less-than comparison. */ + bool operator <(const BlockState & aOther) const; + /** Fast equality check. */ bool operator ==(const BlockState & aOther) const; diff --git a/src/Protocol/CMakeLists.txt b/src/Protocol/CMakeLists.txt index 127edd317..d2170fb95 100644 --- a/src/Protocol/CMakeLists.txt +++ b/src/Protocol/CMakeLists.txt @@ -1,3 +1,4 @@ +include_directories (SYSTEM "../../lib/jsoncpp/include") SET (SRCS Authenticator.cpp @@ -12,6 +13,7 @@ SET (SRCS Protocol_1_12.cpp Protocol_1_13.cpp ProtocolRecognizer.cpp + ProtocolBlockTypePalette.cpp ) SET (HDRS @@ -28,6 +30,7 @@ SET (HDRS Protocol_1_12.h Protocol_1_13.h ProtocolRecognizer.h + ProtocolBlockTypePalette.h ) if (NOT MSVC) diff --git a/src/Protocol/ProtocolBlockTypePalette.cpp b/src/Protocol/ProtocolBlockTypePalette.cpp new file mode 100644 index 000000000..86d05623b --- /dev/null +++ b/src/Protocol/ProtocolBlockTypePalette.cpp @@ -0,0 +1,144 @@ +#include "Globals.h" +#include "ProtocolBlockTypePalette.h" +#include <cstdint> +#include <sstream> +#include "json/value.h" +#include "json/reader.h" + + + + + +ProtocolBlockTypePalette::ProtocolBlockTypePalette() +{ + // empty +} + + + + + +bool ProtocolBlockTypePalette::loadFromString(const AString & aMapping) +{ + std::stringstream stream; + stream << aMapping; + + return loadFromStream(stream); +} + + + + + +bool ProtocolBlockTypePalette::loadFromStream(std::istream & aInputStream) +{ + Json::Value root; + + try + { + aInputStream >> root; + } + #if defined _DEBUG + catch (const std::exception & e) + { + LOGD(e.what()); + return false; + } + #else + catch (const std::exception &) + { + return false; + } + #endif + + if (!root.isObject() || + !root.isMember("Metadata") || + !root["Metadata"].isMember("ProtocolBlockTypePaletteVersion") || + !root.isMember("Palette") || + !root["Palette"].isArray()) + { + LOGD("Incorrect palette format."); + return false; + } + + if (root["Metadata"]["ProtocolBlockTypePaletteVersion"].asUInt() != 1) + { + LOGD("Palette format version not supported."); + return false; + } + + auto len = root["Palette"].size(); + for (decltype(len) i = 0; i < len; ++i) + { + const auto & record = root["Palette"][i]; + if (!record.isObject()) + { + LOGD("Record #%u must be a JSON object.", i); + return false; + } + + auto blocktype = record["name"].asString(); + auto id = std::stoul(record["id"].asString()); + std::map<AString, AString> state; + + if (id >= NOT_FOUND) + { + LOGD("`id` must be less than ProtocolBlockTypePalette::NOT_FOUND, but is %lu", id); + return false; + } + + if (record.isMember("props")) + { + const auto & props = record["props"]; + if (!props.isObject()) + { + LOGD("`props` key must be a JSON object."); + return false; + } + for (const auto & key: props.getMemberNames()) + { + state[key] = props[key].asString(); + } + } + + // Block type map entry already exists? + if (mIndex.count(blocktype) == 0) + { + mIndex.insert({blocktype, std::map<BlockState, UInt32>()}); + } + + const auto & result = mIndex[blocktype].insert({BlockState(state), id}); + if (result.second == false) + { + LOGINFO("Duplicate block state encountered (Current ID: %lu, other: %lu)", result.first->second, id); + } + } + return true; +} + + + + + +UInt32 ProtocolBlockTypePalette::index(const AString & aBlockTypeName, const BlockState & aBlockState) const +{ + auto a = mIndex.find(aBlockTypeName); + if (a != mIndex.end()) + { + auto b = a->second.find(aBlockState); + if (b != a->second.end()) + { + return b->second; + } + } + return NOT_FOUND; +} + + + + + +void ProtocolBlockTypePalette::clear() +{ + return mIndex.clear(); +} diff --git a/src/Protocol/ProtocolBlockTypePalette.h b/src/Protocol/ProtocolBlockTypePalette.h new file mode 100644 index 000000000..fb156cfd5 --- /dev/null +++ b/src/Protocol/ProtocolBlockTypePalette.h @@ -0,0 +1,40 @@ +#pragma once +#include <unordered_map> +#include "../BlockState.h" + + +/** Parses and holds a collection of block types and their possible states +together with their corresponding Id within the Minecraft network protocol. */ +class ProtocolBlockTypePalette +{ +public: + static const UInt32 NOT_FOUND = UINT32_MAX; + + /** Create a new empty instance. */ + ProtocolBlockTypePalette(); + + /** Loads the palette from a string. + See loadFromStream() for further details. */ + bool loadFromString(const AString & aMapping); + + /** Loads the palette from an input stream. + Returns `true` on success, `false` otherwise. Sucessive calls to this method + will _add_ data to the palette. If duplicate keys are encountered, they will + be ignored and an info message logged. */ + bool loadFromStream(std::istream & aInputStream); + + /** Returns the defined index corresponding of the given aBlockTypeName and + aBlockState. + Returns ProtocolBlockTypePalette::NOT_FOUND if the tuple is not found. */ + UInt32 index(const AString & aBlockTypeName, const BlockState & aBlockState) const; + + /** Clears the palette. */ + void clear(); + + +protected: + + /** The palette index. Each item in the map represents a single block state + palette entry. The value is the block state ID. */ + std::unordered_map<AString, std::map<BlockState, UInt32>> mIndex; +}; |