summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/BlockState.cpp46
-rw-r--r--src/BlockState.h3
-rw-r--r--src/Protocol/CMakeLists.txt3
-rw-r--r--src/Protocol/ProtocolBlockTypePalette.cpp144
-rw-r--r--src/Protocol/ProtocolBlockTypePalette.h40
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;
+};