From 742e27ad2f037205285e475be487ec9ed874ca91 Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Tue, 8 Sep 2020 09:46:16 +0100 Subject: Remove unused Temporary namespace - Remove unused temporary mapping generation at startup --- src/Bindings/BlockState.cpp | 213 +++++++++++++++++++++ src/Bindings/BlockState.h | 93 +++++++++ src/Bindings/BlockTypePalette.cpp | 380 +++++++++++++++++++++++++++++++++++++ src/Bindings/BlockTypePalette.h | 138 ++++++++++++++ src/Bindings/BlockTypeRegistry.cpp | 240 +++++++++++++++++++++++ src/Bindings/BlockTypeRegistry.h | 216 +++++++++++++++++++++ src/BlockState.cpp | 213 --------------------- src/BlockState.h | 93 --------- src/BlockTypePalette.cpp | 380 ------------------------------------- src/BlockTypePalette.h | 138 -------------- src/BlockTypeRegistry.cpp | 240 ----------------------- src/BlockTypeRegistry.h | 216 --------------------- src/CMakeLists.txt | 8 - src/PalettedBlockArea.h | 2 +- 14 files changed, 1281 insertions(+), 1289 deletions(-) create mode 100644 src/Bindings/BlockState.cpp create mode 100644 src/Bindings/BlockState.h create mode 100644 src/Bindings/BlockTypePalette.cpp create mode 100644 src/Bindings/BlockTypePalette.h create mode 100644 src/Bindings/BlockTypeRegistry.cpp create mode 100644 src/Bindings/BlockTypeRegistry.h delete mode 100644 src/BlockState.cpp delete mode 100644 src/BlockState.h delete mode 100644 src/BlockTypePalette.cpp delete mode 100644 src/BlockTypePalette.h delete mode 100644 src/BlockTypeRegistry.cpp delete mode 100644 src/BlockTypeRegistry.h (limited to 'src') diff --git a/src/Bindings/BlockState.cpp b/src/Bindings/BlockState.cpp new file mode 100644 index 000000000..8ee87c50f --- /dev/null +++ b/src/Bindings/BlockState.cpp @@ -0,0 +1,213 @@ +#include "Globals.h" +#include "BlockState.h" + + + + + +BlockState::BlockState(): + mChecksum(initializeChecksum()) +{ + // Nothing needed yet +} + + + + + +BlockState::BlockState(const AString & aKey, const AString & aValue): + mState({{aKey, aValue}}), + mChecksum(initializeChecksum()) +{ +} + + + + + +BlockState::BlockState(std::initializer_list> aKeysAndValues): + mState(aKeysAndValues), + mChecksum(initializeChecksum()) +{ +} + + + + + +BlockState::BlockState(const std::map & aKeysAndValues): + mState(aKeysAndValues), + mChecksum(initializeChecksum()) +{ +} + + + + + +BlockState::BlockState(std::map && aKeysAndValues): + mState(std::move(aKeysAndValues)), + mChecksum(initializeChecksum()) +{ +} + + + + + +BlockState::BlockState(const BlockState & aCopyFrom, std::initializer_list> aAdditionalKeysAndValues): + mState(aCopyFrom.mState) +{ + for (const auto & kav: aAdditionalKeysAndValues) + { + mState[kav.first] = kav.second; + } + mChecksum = initializeChecksum(); +} + + + + + +BlockState::BlockState(const BlockState & aCopyFrom, const std::map & aAdditionalKeysAndValues): + mState(aCopyFrom.mState) +{ + for (const auto & kav: aAdditionalKeysAndValues) + { + mState[kav.first] = kav.second; + } + mChecksum = initializeChecksum(); +} + + + + + +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: + if ((mChecksum != aOther.mChecksum) || (mState.size() != aOther.mState.size())) + { + return false; + } + + // Slow-check everything if the checksums match: + return std::equal(mState.begin(), mState.end(), aOther.mState.begin()); +} + + + + + +const AString & BlockState::value(const AString & aKey) const +{ + auto itr = mState.find(aKey); + if (itr == mState.end()) + { + static AString empty; + return empty; + } + return itr->second; +} + + + + + +UInt32 BlockState::initializeChecksum() +{ + removeEmptyKeys(); + + // Calculate the checksum as a XOR of all mState keys' and values' checksums + // This way we don't depend on the std::map's ordering + UInt32 res = 0; + for (const auto & kv: mState) + { + auto partial = partialChecksum(kv.first) ^ partialChecksum(kv.second); + res = res ^ partial; + } + return res; +} + + + + + +void BlockState::removeEmptyKeys() +{ + for (auto itr = mState.begin(); itr != mState.end();) + { + if (itr->second.empty()) + { + itr = mState.erase(itr); + } + else + { + ++itr; + } + } +} + + + + + +UInt32 BlockState::partialChecksum(const AString & aString) +{ + UInt32 shift = 0; + UInt32 res = 0; + for (auto ch: aString) + { + UInt32 v = static_cast(ch); + v = v << shift; + shift = (shift + 1) % 24; + res = res ^ v; + } + return res; +} diff --git a/src/Bindings/BlockState.h b/src/Bindings/BlockState.h new file mode 100644 index 000000000..ab451236b --- /dev/null +++ b/src/Bindings/BlockState.h @@ -0,0 +1,93 @@ +#pragma once + +#include + + + + + +/** Represents the state of a single block (previously known as "block meta"). +The state consists of a map of string -> string, plus a mechanism for fast equality checks between two BlockState instances. +Once a BlockState instance is created, it is then immutable - there's no way of changing it, only by creating a (modified) copy. +A BlockState instance can be created from hard-coded data or from dynamic data: + BlockState bs({{"key1", "value1"}, {key2", "value2"}}); // Hard-coded + - or - + std::map map({{"key1", "value1"}, {key2", "value2"}}); + map["key3"] = "value3"; + BlockState bs(map); // From dynamic data +*/ +class BlockState +{ +public: + + /** Creates a new instance with an empty map. */ + BlockState(); + + /** Creates a new instance consisting of a single key-value pair. + If the value is empty, it is not stored wihin the map. */ + BlockState(const AString & aKey, const AString & aValue); + + /** Creates a new instance initialized with several (hard-coded) key-value pairs. + Any key with an empty value is not stored within the map. */ + BlockState(std::initializer_list> aKeysAndValues); + + /** Creates a new instance initialized with several (dynamic) key-value pairs. + Makes a copy of aKeysAndValues for this object. + Any key with an empty value is not stored within the map. */ + BlockState(const std::map & aKeysAndValues); + + /** Creates a new instance initialized with several (dynamic) key-value pairs. + Any key with an empty value is not stored within the map. */ + BlockState(std::map && aKeysAndValues); + + /** Creates a copy of the specified BlockState with the (hard-coded) additional keys and values added to it. + Any key in aAdditionalKeysAndValues that is already present in aCopyFrom is overwritten with the aAdditionalKeysAndValues' one. + Any key with an empty value is not stored in the map. + (it's possible to erase a key from aCopyFrom by setting it to empty string in aAdditionalKeysAndValues). */ + BlockState(const BlockState & aCopyFrom, std::initializer_list> aAdditionalKeysAndValues); + + /** Creates a copy of the specified BlockState with the (dynamic) additional keys and values added to it. + Any key in aAdditionalKeysAndValues that is already present in aCopyFrom is overwritten with the aAdditionalKeysAndValues' one. + Any key with an empty value is not stored in the map. + (it's possible to erase a key from aCopyFrom by setting it to empty string in aAdditionalKeysAndValues). */ + BlockState(const BlockState & aCopyFrom, const std::map & aAdditionalKeysAndValues); + + /** Less-than comparison. */ + bool operator <(const BlockState & aOther) const; + + /** Fast equality check. */ + bool operator ==(const BlockState & aOther) const; + + /** Fast inequality check. */ + bool operator !=(const BlockState & aOther) const + { + return !(operator ==(aOther)); + } + + /** Returns the value at the specified key. + If the key is not present, returns an empty string. */ + const AString & value(const AString & aKey) const; + + +protected: + + /** The state, represented as a string->string map. */ + std::map mState; + + /** The checksum used for the fast equality check. + This is calculated upon creation. */ + UInt32 mChecksum; + + + /** Normalizes mState and calculates the checksum from it. + Removes all the empty values from mState. + Used only from constructors. */ + UInt32 initializeChecksum(); + + /** Removes all the keys from mState that have an empty value. */ + void removeEmptyKeys(); + + /** Calculates the partial checksum of a single string. + Used from within initializeChecksum(). */ + UInt32 partialChecksum(const AString & aString); +}; diff --git a/src/Bindings/BlockTypePalette.cpp b/src/Bindings/BlockTypePalette.cpp new file mode 100644 index 000000000..7759505cf --- /dev/null +++ b/src/Bindings/BlockTypePalette.cpp @@ -0,0 +1,380 @@ +#include "Globals.h" +#include "BlockTypePalette.h" +#include "json/value.h" +#include "JsonUtils.h" + + + + + +/** Returns the index into aString >= aStartIdx at which the next separator occurs. +Separator is one of \t, \n or \r. +Returns AString::npos if no such separator. */ +static size_t findNextSeparator(const AString & aString, size_t aStartIdx = 0) +{ + for (size_t i = aStartIdx, len = aString.length(); i < len; ++i) + { + switch (aString[i]) + { + case '\t': + case '\n': + case '\r': + { + return i; + } + } + } + return AString::npos; +} + + + + + +BlockTypePalette::BlockTypePalette(): + mMaxIndex(0) +{ +} + + + + + +UInt32 BlockTypePalette::index(const AString & aBlockTypeName, const BlockState & aBlockState) +{ + auto idx = maybeIndex(aBlockTypeName, aBlockState); + if (idx.second) + { + return idx.first; + } + + // Not found, append: + auto index = mMaxIndex++; + mBlockToNumber[aBlockTypeName][aBlockState] = index; + mNumberToBlock[index] = {aBlockTypeName, aBlockState}; + return index; +} + + + + + +std::pair BlockTypePalette::maybeIndex(const AString & aBlockTypeName, const BlockState & aBlockState) const +{ + auto itr1 = mBlockToNumber.find(aBlockTypeName); + if (itr1 == mBlockToNumber.end()) + { + return {0, false}; + } + auto itr2 = itr1->second.find(aBlockState); + if (itr2 == itr1->second.end()) + { + return {0, false}; + } + return {itr2->second, true}; +} + + + + + +UInt32 BlockTypePalette::count() const +{ + return static_cast(mNumberToBlock.size()); +} + + + + + +const std::pair & BlockTypePalette::entry(UInt32 aIndex) const +{ + auto itr = mNumberToBlock.find(aIndex); + if (itr == mNumberToBlock.end()) + { + throw NoSuchIndexException(aIndex); + } + return itr->second; +} + + + + + +std::map BlockTypePalette::createTransformMapAddMissing(const BlockTypePalette & aFrom) +{ + std::map res; + for (const auto & fromEntry: aFrom.mNumberToBlock) + { + auto fromIndex = fromEntry.first; + const auto & blockTypeName = fromEntry.second.first; + const auto & blockState = fromEntry.second.second; + res[fromIndex] = index(blockTypeName, blockState); + } + return res; +} + + + + + +std::map BlockTypePalette::createTransformMapWithFallback(const BlockTypePalette & aFrom, UInt32 aFallbackIndex) const +{ + std::map res; + for (const auto & fromEntry: aFrom.mNumberToBlock) + { + auto fromIndex = fromEntry.first; + const auto & blockTypeName = fromEntry.second.first; + const auto & blockState = fromEntry.second.second; + auto thisIndex = maybeIndex(blockTypeName, blockState); + if (thisIndex.second) + { + // The entry was found in this + res[fromIndex] = thisIndex.first; + } + else + { + // The entry was NOT found in this, replace with fallback: + res[fromIndex] = aFallbackIndex; + } + } + return res; +} + + + + + +void BlockTypePalette::loadFromString(const AString & aString) +{ + static const AString hdrTsvRegular = "BlockTypePalette"; + static const AString hdrTsvUpgrade = "UpgradeBlockTypePalette"; + + // Detect format by checking the header line (none -> JSON): + if (aString.substr(0, hdrTsvRegular.length()) == hdrTsvRegular) + { + return loadFromTsv(aString, false); + } + else if (aString.substr(0, hdrTsvUpgrade.length()) == hdrTsvUpgrade) + { + return loadFromTsv(aString, true); + } + return loadFromJsonString(aString); +} + + + + + +void BlockTypePalette::loadFromJsonString(const AString & aJsonPalette) +{ + // Parse the string into JSON object: + Json::Value root; + std::string errs; + if (!JsonUtils::ParseString(aJsonPalette, root, &errs)) + { + throw LoadFailedException(errs); + } + + // Sanity-check the JSON's structure: + if (!root.isObject()) + { + throw LoadFailedException("Incorrect palette format, expected an object at root."); + } + + // Load the palette: + for (auto itr = root.begin(), end = root.end(); itr != end; ++itr) + { + const auto & blockTypeName = itr.name(); + const auto & states = (*itr)["states"]; + if (states == Json::Value()) + { + throw LoadFailedException(Printf("Missing \"states\" for block type \"%s\"", blockTypeName)); + } + for (const auto & state: states) + { + auto id = static_cast(std::stoul(state["id"].asString())); + std::map props; + if (state.isMember("properties")) + { + const auto & properties = state["properties"]; + if (!properties.isObject()) + { + throw LoadFailedException(Printf("Member \"properties\" is not a JSON object (block type \"%s\", id %u).", blockTypeName, id)); + } + for (const auto & key: properties.getMemberNames()) + { + props[key] = properties[key].asString(); + } + } + addMapping(id, blockTypeName, props); + } + } +} + + + + + +void BlockTypePalette::loadFromTsv(const AString & aTsvPalette, bool aIsUpgrade) +{ + static const AString hdrTsvRegular = "BlockTypePalette"; + static const AString hdrTsvUpgrade = "UpgradeBlockTypePalette"; + + // Check the file signature: + auto idx = findNextSeparator(aTsvPalette); + if ((idx == AString::npos) || (aTsvPalette[idx] == '\t')) + { + throw LoadFailedException("Invalid signature"); + } + auto signature = aTsvPalette.substr(0, idx); + bool isUpgrade = (signature == hdrTsvUpgrade); + if (!isUpgrade && (signature != hdrTsvRegular)) + { + throw LoadFailedException("Unknown signature"); + } + if (aTsvPalette[idx] == '\r') // CR of the CRLF pair, skip the LF: + { + idx += 1; + } + + // Parse the header: + bool hasHadVersion = false; + AString commonPrefix; + int line = 2; + auto len = aTsvPalette.length(); + while (true) + { + auto keyStart = idx + 1; + auto keyEnd = findNextSeparator(aTsvPalette, idx + 1); + if (keyEnd == AString::npos) + { + throw LoadFailedException(Printf("Invalid header key format on line %u", line)); + } + if (keyEnd == idx + 1) // Empty line, end of headers + { + if (aTsvPalette[keyEnd] == '\r') // CR of the CRLF pair, skip the LF: + { + ++keyEnd; + } + idx = keyEnd; + ++line; + break; + } + auto valueEnd = findNextSeparator(aTsvPalette, keyEnd + 1); + if ((valueEnd == AString::npos) || (aTsvPalette[valueEnd] == '\t')) + { + throw LoadFailedException(Printf("Invalid header value format on line %u", line)); + } + auto key = aTsvPalette.substr(keyStart, keyEnd - keyStart); + if (key == "FileVersion") + { + unsigned version = 0; + auto value = aTsvPalette.substr(keyEnd + 1, valueEnd - keyEnd - 1); + if (!StringToInteger(value, version)) + { + throw LoadFailedException("Invalid FileVersion value"); + } + else if (version != 1) + { + throw LoadFailedException(Printf("Unknown FileVersion: %u. Only version 1 is supported.", version)); + } + hasHadVersion = true; + } + else if (key == "CommonPrefix") + { + commonPrefix = aTsvPalette.substr(keyEnd + 1, valueEnd - keyEnd - 1); + } + idx = valueEnd; + if (aTsvPalette[idx] == '\r') // CR of the CRLF pair, skip the LF: + { + ++idx; + } + ++line; + } + if (!hasHadVersion) + { + throw LoadFailedException("No FileVersion value"); + } + + // Parse the data: + while (idx + 1 < len) + { + auto lineStart = idx + 1; + auto idEnd = findNextSeparator(aTsvPalette, lineStart); + if ((idEnd == AString::npos) || (aTsvPalette[idEnd] != '\t')) + { + throw LoadFailedException(Printf("Incomplete data on line %u (id)", line)); + } + UInt32 id; + if (!StringToInteger(aTsvPalette.substr(lineStart, idEnd - lineStart), id)) + { + throw LoadFailedException(Printf("Failed to parse id on line %u", line)); + } + size_t metaEnd = idEnd; + if (isUpgrade) + { + metaEnd = findNextSeparator(aTsvPalette, idEnd + 1); + if ((metaEnd == AString::npos) || (aTsvPalette[metaEnd] != '\t')) + { + throw LoadFailedException(Printf("Incomplete data on line %u (meta)", line)); + } + UInt32 meta = 0; + if (!StringToInteger(aTsvPalette.substr(idEnd + 1, metaEnd - idEnd - 1), meta)) + { + throw LoadFailedException(Printf("Failed to parse meta on line %u", line)); + } + if (meta > 15) + { + throw LoadFailedException(Printf("Invalid meta value on line %u: %u", line, meta)); + } + id = (id * 16) | meta; + } + auto blockTypeEnd = findNextSeparator(aTsvPalette, metaEnd + 1); + if (blockTypeEnd == AString::npos) + { + throw LoadFailedException(Printf("Incomplete data on line %u (blockTypeName)", line)); + } + auto blockTypeName = aTsvPalette.substr(metaEnd + 1, blockTypeEnd - metaEnd - 1); + auto blockStateEnd = blockTypeEnd; + AStringMap blockState; + while (aTsvPalette[blockStateEnd] == '\t') + { + auto keyEnd = findNextSeparator(aTsvPalette, blockStateEnd + 1); + if ((keyEnd == AString::npos) || (aTsvPalette[keyEnd] != '\t')) + { + throw LoadFailedException(Printf("Incomplete data on line %u (blockState key)", line)); + } + auto valueEnd = findNextSeparator(aTsvPalette, keyEnd + 1); + if (valueEnd == AString::npos) + { + throw LoadFailedException(Printf("Incomplete data on line %u (blockState value)", line)); + } + auto key = aTsvPalette.substr(blockStateEnd + 1, keyEnd - blockStateEnd - 1); + auto value = aTsvPalette.substr(keyEnd + 1, valueEnd - keyEnd - 1); + blockState[key] = value; + blockStateEnd = valueEnd; + } + addMapping(id, commonPrefix + blockTypeName, std::move(blockState)); + ++line; + if (aTsvPalette[blockStateEnd] == '\r') // CR of the CRLF pair, skip the LF: + { + ++blockStateEnd; + } + idx = blockStateEnd; + } +} + + + + + +void BlockTypePalette::addMapping(UInt32 aID, const AString & aBlockTypeName, const BlockState & aBlockState) +{ + mNumberToBlock[aID] = {aBlockTypeName, aBlockState}; + mBlockToNumber[aBlockTypeName][aBlockState] = aID; + if (aID > mMaxIndex) + { + mMaxIndex = aID; + } +} diff --git a/src/Bindings/BlockTypePalette.h b/src/Bindings/BlockTypePalette.h new file mode 100644 index 000000000..2aade422b --- /dev/null +++ b/src/Bindings/BlockTypePalette.h @@ -0,0 +1,138 @@ +#pragma once + +#include +#include "BlockState.h" + + + + + +/** Holds a palette that maps between block type + state and numbers. +Used primarily by PalettedBlockArea to map from stringular block representation to numeric, +and by protocols to map from stringular block representation to protocol-numeric. +The object itself provides no thread safety, users of this class need to handle locking, if required. +Note that the palette itself doesn't support erasing; +to erase, create a new instance and re-add only the wanted items. + +Internally, the object uses two synced maps, one for each translation direction. + +The palette can be loaded from a string (file). The loader supports either the blocks.json file exported by +the vanilla server itself (https://wiki.vg/Data_Generators), or a processed text file generated by +our tool $/Tools/BlockTypePaletteGenerator/, or a hand-written text file describing the upgrade from +1.12 BlockType + BlockMeta to 1.13 string representations. +The text file is a TSV (tab-separated values), which basically means the data is generally structured as +..., where eol is the platform's CR / CRLF / LF lineend. +The file starts with a single value on the first line, "BlockTypePalette" or "UpgradeBlockTypePalette", which +is used to detect the file format. The following lines are "headers", simple entries +that contain the metadata about the file. "FileVersion" is a compulsory key, "CommonPrefix" is supported, others +are ignored. +The headers are followed by an empty line (that signalizes the end of headers) and then the actual data. +For regular BlockTypePalette TSV file of version 1, the data is in the format: + ... +For the UpgradeBlockTypePalette TSV file of version 1, the data is in the format: + ... +If a CommonPrefix header is present, its value is pre-pended to each blockTypeName loaded (thus allowing +the file to be overall smaller). */ +class BlockTypePalette +{ +public: + + /** Exception that is thrown if requiesting an index not present in the palette. */ + class NoSuchIndexException: + public std::runtime_error + { + using Super = std::runtime_error; + + public: + NoSuchIndexException(UInt32 aIndex): + Super(Printf("No such palette index: %u", aIndex)) + { + } + }; + + + /** Exception that is thrown when loading the palette fails hard (bad format). */ + class LoadFailedException: + public std::runtime_error + { + using Super = std::runtime_error; + + public: + LoadFailedException(const AString & aReason): + Super(aReason) + { + } + }; + + + + /** Create a new empty instance. */ + BlockTypePalette(); + + /** Returns the index of the specified block type name and state. + If the combination is not found, it is added to the palette and the new index is returned. */ + UInt32 index(const AString & aBlockTypeName, const BlockState & aBlockState); + + /** Returns the of the specified block type name and state, if it exists. + If the combination is not found, returns . */ + std::pair maybeIndex(const AString & aBlockTypeName, const BlockState & aBlockState) const; + + /** Returns the total number of entries in the palette. */ + UInt32 count() const; + + /** Returns the blockspec represented by the specified palette index. + If the index is not valid, throws a NoSuchIndexException. */ + const std::pair & entry(UInt32 aIndex) const; + + /** Returns an index-transform map from aFrom to this (this.entry(idx) == aFrom.entry(res[idx])). + Entries from aFrom that are not present in this are added. + Used when pasting two areas, to transform the src palette to dst palette. */ + std::map createTransformMapAddMissing(const BlockTypePalette & aFrom); + + /** Returns an index-transform map from aFrom to this (this.entry(idx) == aFrom.entry(res[idx])). + Entries from aFrom that are not present in this are assigned the fallback index. + Used for protocol block type mapping. */ + std::map createTransformMapWithFallback(const BlockTypePalette & aFrom, UInt32 aFallbackIndex) const; + + /** Loads the palette from the string representation. + Throws a LoadFailedException if the loading fails hard (bad string format); + but still a part of the data may already be loaded at that point. + If the string specifies duplicate entries (either to already existing entries, or to itself), + the duplicates replace the current values silently (this allows us to chain multiple files as "overrides". + Auto-detects the string format (json / tsv, normal / upgrade palette) and calls the appropriate load function. */ + void loadFromString(const AString & aString); + + +protected: + + /** The mapping from numeric to stringular representation. + mNumberToBlock[index] = {"blockTypeName", blockState}. */ + std::map> mNumberToBlock; + + /** The mapping from stringular to numeric representation. + mStringToNumber["blockTypeName"][blockState] = index. */ + std::unordered_map> mBlockToNumber; + + /** The maximum index ever used in the maps. + Used when adding new entries through the index() call. */ + UInt32 mMaxIndex; + + + /** Loads the palette from the JSON representation, https://wiki.vg/Data_Generators + Throws a LoadFailedException if the loading fails hard (bad string format); + but still a part of the data may already be loaded at that point. + See also: loadFromString(). */ + void loadFromJsonString(const AString & aJsonPalette); + + /** Loads the palette from the regular or upgrade TSV representation. + aIsUpgrade specifies whether the format is an upgrade TSV (true) or a regular one (false) + Throws a LoadFailedException if the loading fails hard (bad string format); + but still a part of the data may already be loaded at that point. + See also: loadFromString(). */ + void loadFromTsv(const AString & aTsvPalette, bool aIsUpgrade); + + /** Adds a mapping between the numeric and stringular representation into both maps, + updates the mMaxIndex, if appropriate. + Silently overwrites any previous mapping for the ID, if present, but keeps the old string->id mapping. */ + void addMapping(UInt32 aID, const AString & aBlockTypeName, const BlockState & aBlockState); +}; diff --git a/src/Bindings/BlockTypeRegistry.cpp b/src/Bindings/BlockTypeRegistry.cpp new file mode 100644 index 000000000..491e03593 --- /dev/null +++ b/src/Bindings/BlockTypeRegistry.cpp @@ -0,0 +1,240 @@ + +#include "Globals.h" +#include "BlockTypeRegistry.h" + + + + +//////////////////////////////////////////////////////////////////////////////// +// BlockInfo: + +BlockInfo::BlockInfo( + const AString & aPluginName, + const AString & aBlockTypeName, + std::shared_ptr aHandler, + const std::map & aHints, + const std::map & aHintCallbacks +): + m_PluginName(aPluginName), + m_BlockTypeName(aBlockTypeName), + m_Handler(std::move(aHandler)), + m_Hints(aHints), + m_HintCallbacks(aHintCallbacks) +{ +} + + + + + +AString BlockInfo::hintValue( + const AString & aHintName, + const BlockState & aBlockState +) +{ + // Search the hint callbacks first: + auto itrC = m_HintCallbacks.find(aHintName); + if (itrC != m_HintCallbacks.end()) + { + // Hint callback found, use it: + return itrC->second(m_BlockTypeName, aBlockState); + } + + // Search the static hints: + auto itr = m_Hints.find(aHintName); + if (itr != m_Hints.end()) + { + // Hint found, use it: + return itr->second; + } + + // Nothing found, return empty string: + return AString(); +} + + + + + +void BlockInfo::setHint(const AString & aHintKey, const AString & aHintValue) +{ + m_Hints[aHintKey] = aHintValue; + + // Warn if the hint is already provided by a callback (aHintValue will be ignored when evaluating the hint): + auto itrC = m_HintCallbacks.find(aHintKey); + if (itrC != m_HintCallbacks.end()) + { + LOGINFO("Setting a static hint %s for block type %s, but there's already a callback for that hint. The static hint will be ignored.", + aHintKey.c_str(), m_BlockTypeName.c_str() + ); + } +} + + + + + +void BlockInfo::removeHint(const AString & aHintKey) +{ + m_Hints.erase(aHintKey); +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// BlockTypeRegistry: + +void BlockTypeRegistry::registerBlockType( + const AString & aPluginName, + const AString & aBlockTypeName, + std::shared_ptr aHandler, + const std::map & aHints, + const std::map & aHintCallbacks +) +{ + auto blockInfo = std::make_shared( + aPluginName, aBlockTypeName, std::move(aHandler), aHints, aHintCallbacks + ); + + // Check previous registrations: + cCSLock lock(m_CSRegistry); + auto itr = m_Registry.find(aBlockTypeName); + if (itr != m_Registry.end()) + { + if (itr->second->pluginName() != aPluginName) + { + throw AlreadyRegisteredException(itr->second, blockInfo); + } + } + + // Store the registration: + m_Registry[aBlockTypeName] = blockInfo; +} + + + + + +std::shared_ptr BlockTypeRegistry::blockInfo(const AString & aBlockTypeName) +{ + cCSLock lock(m_CSRegistry); + auto itr = m_Registry.find(aBlockTypeName); + if (itr == m_Registry.end()) + { + return nullptr; + } + return itr->second; +} + + + + + +void BlockTypeRegistry::removeAllByPlugin(const AString & aPluginName) +{ + cCSLock lock(m_CSRegistry); + for (auto itr = m_Registry.begin(); itr != m_Registry.end();) + { + if (itr->second->pluginName() == aPluginName) + { + itr = m_Registry.erase(itr); + } + else + { + ++itr; + } + } +} + + + + + +void BlockTypeRegistry::setBlockTypeHint( + const AString & aBlockTypeName, + const AString & aHintKey, + const AString & aHintValue +) +{ + cCSLock lock(m_CSRegistry); + auto blockInfo = m_Registry.find(aBlockTypeName); + if (blockInfo == m_Registry.end()) + { + throw NotRegisteredException(aBlockTypeName, aHintKey, aHintValue); + } + blockInfo->second->setHint(aHintKey, aHintValue); +} + + + + + +void BlockTypeRegistry::removeBlockTypeHint( + const AString & aBlockTypeName, + const AString & aHintKey +) +{ + cCSLock lock(m_CSRegistry); + auto blockInfo = m_Registry.find(aBlockTypeName); + if (blockInfo == m_Registry.end()) + { + return; + } + blockInfo->second->removeHint(aHintKey); +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// BlockTypeRegistry::AlreadyRegisteredException: + +BlockTypeRegistry::AlreadyRegisteredException::AlreadyRegisteredException( + const std::shared_ptr & aPreviousRegistration, + const std::shared_ptr & aNewRegistration +) : + Super(message(aPreviousRegistration, aNewRegistration)), + m_PreviousRegistration(aPreviousRegistration), + m_NewRegistration(aNewRegistration) +{ +} + + + + + +AString BlockTypeRegistry::AlreadyRegisteredException::message( + const std::shared_ptr & aPreviousRegistration, + const std::shared_ptr & aNewRegistration +) +{ + return Printf("Attempting to register BlockTypeName %s from plugin %s, while it is already registered in plugin %s", + aNewRegistration->blockTypeName().c_str(), + aNewRegistration->pluginName().c_str(), + aPreviousRegistration->pluginName().c_str() + ); +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// BlockTypeRegistry::NotRegisteredException: + +BlockTypeRegistry::NotRegisteredException::NotRegisteredException( + const AString & aBlockTypeName, + const AString & aHintKey, + const AString & aHintValue +): + Super(Printf( + "Attempting to set a hint of nonexistent BlockTypeName.\n\tBlockTypeName = %s\n\tHintKey = %s\n\tHintValue = %s", + aBlockTypeName.c_str(), + aHintKey.c_str(), + aHintValue.c_str() + )) +{ +} diff --git a/src/Bindings/BlockTypeRegistry.h b/src/Bindings/BlockTypeRegistry.h new file mode 100644 index 000000000..3a85ee510 --- /dev/null +++ b/src/Bindings/BlockTypeRegistry.h @@ -0,0 +1,216 @@ +#pragma once + + + + + +#include +#include + + + + + +// fwd: +class cBlockHandler; +class BlockState; + + + + + +/** Complete information about a single block type. +The BlockTypeRegistry uses this structure to store the registered information. */ +class BlockInfo +{ +public: + + /** Callback is used to query block hints dynamically, based on the current BlockState. + Useful for example for redstone lamps that can be turned on or off. */ + using HintCallback = std::function; + + + /** Creates a new instance with the specified BlockTypeName and handler / hints / callbacks. + aPluginName specifies the name of the plugin to associate with the block type (to allow unload / reload). */ + BlockInfo( + const AString & aPluginName, + const AString & aBlockTypeName, + std::shared_ptr aHandler, + const std::map & aHints = std::map(), + const std::map & aHintCallbacks = std::map() + ); + + + /** Retrieves the value associated with the specified hint for this specific BlockTypeName and BlockState. + Queries hint callbacks first, then static hints if a callback doesn't exist. + Returns an empty string if hint not found at all. */ + AString hintValue( + const AString & aHintName, + const BlockState & aBlockState + ); + + // Simple getters: + const AString & pluginName() const { return m_PluginName; } + const AString & blockTypeName() const { return m_BlockTypeName; } + std::shared_ptr handler() const { return m_Handler; } + + /** Sets (creates or updates) a static hint. + Hints provided by callbacks are unaffected by this - callbacks are "higher priority", they overwrite anything set here. + Logs an info message if the hint is already provided by a hint callback. */ + void setHint(const AString & aHintKey, const AString & aHintValue); + + /** Removes a hint. + Silently ignored if the hint hasn't been previously set. */ + void removeHint(const AString & aHintKey); + + +private: + + /** The name of the plugin that registered the block. */ + AString m_PluginName; + + /** The name of the block type, such as "minecraft:redstone_lamp" */ + AString m_BlockTypeName; + + /** The callbacks to call for various interaction. */ + std::shared_ptr m_Handler; + + /** Optional static hints for any subsystem to use, such as "IsSnowable" -> "1". + Hint callbacks are of higher priority than m_Hints - if a hint is provided by a m_HintCallback, its value in m_Hints is ignored. */ + std::map m_Hints; + + /** The callbacks for dynamic evaluation of hints, such as "LightValue" -> function(BlockTypeName, BlockState). + Hint callbacks are of higher priority than m_Hints - if a hint is provided by a m_HintCallback, its value in m_Hints is ignored. */ + std::map m_HintCallbacks; +}; + + + + + +/** Stores information on all known block types. +Can dynamically add and remove block types. +Block types are identified using BlockTypeName. +Supports unregistering and re-registering the same type by the same plugin. +Stores the name of the plugin that registered the type, for better plugin error messages ("already registered in X") +and so that we can unload and reload plugins. */ +class BlockTypeRegistry +{ +public: + // fwd: + class AlreadyRegisteredException; + class NotRegisteredException; + + + /** Creates an empty new instance of the block type registry */ + BlockTypeRegistry() = default; + + /** Registers the specified block type. + If the block type already exists and the plugin is the same, updates the registration. + If the block type already exists and the plugin is different, throws an AlreadyRegisteredException. */ + void registerBlockType( + const AString & aPluginName, + const AString & aBlockTypeName, + std::shared_ptr aHandler, + const std::map & aHints = std::map(), + const std::map & aHintCallbacks = std::map() + ); + + /** Returns the registration information for the specified BlockTypeName. + Returns nullptr if BlockTypeName not found. */ + std::shared_ptr blockInfo(const AString & aBlockTypeName); + + /** Removes all registrations done by the specified plugin. */ + void removeAllByPlugin(const AString & aPluginName); + + /** Sets (adds or overwrites) a single Hint value for a BlockType. + Throws NotRegisteredException if the BlockTypeName is not registered. */ + void setBlockTypeHint( + const AString & aBlockTypeName, + const AString & aHintKey, + const AString & aHintValue + ); + + /** Removes a previously registered single Hint value for a BlockType. + Throws NotRegisteredException if the BlockTypeName is not registered. + Silently ignored if the Hint hasn't been previously set. */ + void removeBlockTypeHint( + const AString & aBlockTypeName, + const AString & aHintKey + ); + + +private: + + /** The actual block type registry. + Maps the BlockTypeName to the BlockInfo instance. */ + std::map> m_Registry; + + /** The CS that protects m_Registry against multithreaded access. */ + cCriticalSection m_CSRegistry; +}; + + + + + +/** The exception thrown from BlockTypeRegistry::registerBlockType() if the same block type is being registered from a different plugin. */ +class BlockTypeRegistry::AlreadyRegisteredException: public std::runtime_error +{ + using Super = std::runtime_error; + +public: + + /** Creates a new instance of the exception that provides info on both the original registration and the newly attempted + registration that caused the failure. */ + AlreadyRegisteredException( + const std::shared_ptr & aPreviousRegistration, + const std::shared_ptr & aNewRegistration + ); + + // Simple getters: + std::shared_ptr previousRegistration() const { return m_PreviousRegistration; } + std::shared_ptr newRegistration() const { return m_NewRegistration; } + + +private: + + std::shared_ptr m_PreviousRegistration; + std::shared_ptr m_NewRegistration; + + + /** Returns the general exception message formatted by the two registrations. + The output is used when logging. */ + static AString message( + const std::shared_ptr & aPreviousRegistration, + const std::shared_ptr & aNewRegistration + ); +}; + + + + + +/** The exception thrown from BlockTypeRegistry::setBlockTypeHint() if the block type has not been registered before. */ +class BlockTypeRegistry::NotRegisteredException: public std::runtime_error +{ + using Super = std::runtime_error; + +public: + + /** Creates a new instance of the exception that provides info on both the original registration and the newly attempted + registration that caused the failure. */ + NotRegisteredException( + const AString & aBlockTypeName, + const AString & aHintKey, + const AString & aHintValue + ); + + // Simple getters: + const AString & blockTypeName() const { return m_BlockTypeName; } + + +private: + + const AString m_BlockTypeName; +}; diff --git a/src/BlockState.cpp b/src/BlockState.cpp deleted file mode 100644 index 8ee87c50f..000000000 --- a/src/BlockState.cpp +++ /dev/null @@ -1,213 +0,0 @@ -#include "Globals.h" -#include "BlockState.h" - - - - - -BlockState::BlockState(): - mChecksum(initializeChecksum()) -{ - // Nothing needed yet -} - - - - - -BlockState::BlockState(const AString & aKey, const AString & aValue): - mState({{aKey, aValue}}), - mChecksum(initializeChecksum()) -{ -} - - - - - -BlockState::BlockState(std::initializer_list> aKeysAndValues): - mState(aKeysAndValues), - mChecksum(initializeChecksum()) -{ -} - - - - - -BlockState::BlockState(const std::map & aKeysAndValues): - mState(aKeysAndValues), - mChecksum(initializeChecksum()) -{ -} - - - - - -BlockState::BlockState(std::map && aKeysAndValues): - mState(std::move(aKeysAndValues)), - mChecksum(initializeChecksum()) -{ -} - - - - - -BlockState::BlockState(const BlockState & aCopyFrom, std::initializer_list> aAdditionalKeysAndValues): - mState(aCopyFrom.mState) -{ - for (const auto & kav: aAdditionalKeysAndValues) - { - mState[kav.first] = kav.second; - } - mChecksum = initializeChecksum(); -} - - - - - -BlockState::BlockState(const BlockState & aCopyFrom, const std::map & aAdditionalKeysAndValues): - mState(aCopyFrom.mState) -{ - for (const auto & kav: aAdditionalKeysAndValues) - { - mState[kav.first] = kav.second; - } - mChecksum = initializeChecksum(); -} - - - - - -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: - if ((mChecksum != aOther.mChecksum) || (mState.size() != aOther.mState.size())) - { - return false; - } - - // Slow-check everything if the checksums match: - return std::equal(mState.begin(), mState.end(), aOther.mState.begin()); -} - - - - - -const AString & BlockState::value(const AString & aKey) const -{ - auto itr = mState.find(aKey); - if (itr == mState.end()) - { - static AString empty; - return empty; - } - return itr->second; -} - - - - - -UInt32 BlockState::initializeChecksum() -{ - removeEmptyKeys(); - - // Calculate the checksum as a XOR of all mState keys' and values' checksums - // This way we don't depend on the std::map's ordering - UInt32 res = 0; - for (const auto & kv: mState) - { - auto partial = partialChecksum(kv.first) ^ partialChecksum(kv.second); - res = res ^ partial; - } - return res; -} - - - - - -void BlockState::removeEmptyKeys() -{ - for (auto itr = mState.begin(); itr != mState.end();) - { - if (itr->second.empty()) - { - itr = mState.erase(itr); - } - else - { - ++itr; - } - } -} - - - - - -UInt32 BlockState::partialChecksum(const AString & aString) -{ - UInt32 shift = 0; - UInt32 res = 0; - for (auto ch: aString) - { - UInt32 v = static_cast(ch); - v = v << shift; - shift = (shift + 1) % 24; - res = res ^ v; - } - return res; -} diff --git a/src/BlockState.h b/src/BlockState.h deleted file mode 100644 index ab451236b..000000000 --- a/src/BlockState.h +++ /dev/null @@ -1,93 +0,0 @@ -#pragma once - -#include - - - - - -/** Represents the state of a single block (previously known as "block meta"). -The state consists of a map of string -> string, plus a mechanism for fast equality checks between two BlockState instances. -Once a BlockState instance is created, it is then immutable - there's no way of changing it, only by creating a (modified) copy. -A BlockState instance can be created from hard-coded data or from dynamic data: - BlockState bs({{"key1", "value1"}, {key2", "value2"}}); // Hard-coded - - or - - std::map map({{"key1", "value1"}, {key2", "value2"}}); - map["key3"] = "value3"; - BlockState bs(map); // From dynamic data -*/ -class BlockState -{ -public: - - /** Creates a new instance with an empty map. */ - BlockState(); - - /** Creates a new instance consisting of a single key-value pair. - If the value is empty, it is not stored wihin the map. */ - BlockState(const AString & aKey, const AString & aValue); - - /** Creates a new instance initialized with several (hard-coded) key-value pairs. - Any key with an empty value is not stored within the map. */ - BlockState(std::initializer_list> aKeysAndValues); - - /** Creates a new instance initialized with several (dynamic) key-value pairs. - Makes a copy of aKeysAndValues for this object. - Any key with an empty value is not stored within the map. */ - BlockState(const std::map & aKeysAndValues); - - /** Creates a new instance initialized with several (dynamic) key-value pairs. - Any key with an empty value is not stored within the map. */ - BlockState(std::map && aKeysAndValues); - - /** Creates a copy of the specified BlockState with the (hard-coded) additional keys and values added to it. - Any key in aAdditionalKeysAndValues that is already present in aCopyFrom is overwritten with the aAdditionalKeysAndValues' one. - Any key with an empty value is not stored in the map. - (it's possible to erase a key from aCopyFrom by setting it to empty string in aAdditionalKeysAndValues). */ - BlockState(const BlockState & aCopyFrom, std::initializer_list> aAdditionalKeysAndValues); - - /** Creates a copy of the specified BlockState with the (dynamic) additional keys and values added to it. - Any key in aAdditionalKeysAndValues that is already present in aCopyFrom is overwritten with the aAdditionalKeysAndValues' one. - Any key with an empty value is not stored in the map. - (it's possible to erase a key from aCopyFrom by setting it to empty string in aAdditionalKeysAndValues). */ - BlockState(const BlockState & aCopyFrom, const std::map & aAdditionalKeysAndValues); - - /** Less-than comparison. */ - bool operator <(const BlockState & aOther) const; - - /** Fast equality check. */ - bool operator ==(const BlockState & aOther) const; - - /** Fast inequality check. */ - bool operator !=(const BlockState & aOther) const - { - return !(operator ==(aOther)); - } - - /** Returns the value at the specified key. - If the key is not present, returns an empty string. */ - const AString & value(const AString & aKey) const; - - -protected: - - /** The state, represented as a string->string map. */ - std::map mState; - - /** The checksum used for the fast equality check. - This is calculated upon creation. */ - UInt32 mChecksum; - - - /** Normalizes mState and calculates the checksum from it. - Removes all the empty values from mState. - Used only from constructors. */ - UInt32 initializeChecksum(); - - /** Removes all the keys from mState that have an empty value. */ - void removeEmptyKeys(); - - /** Calculates the partial checksum of a single string. - Used from within initializeChecksum(). */ - UInt32 partialChecksum(const AString & aString); -}; diff --git a/src/BlockTypePalette.cpp b/src/BlockTypePalette.cpp deleted file mode 100644 index 7759505cf..000000000 --- a/src/BlockTypePalette.cpp +++ /dev/null @@ -1,380 +0,0 @@ -#include "Globals.h" -#include "BlockTypePalette.h" -#include "json/value.h" -#include "JsonUtils.h" - - - - - -/** Returns the index into aString >= aStartIdx at which the next separator occurs. -Separator is one of \t, \n or \r. -Returns AString::npos if no such separator. */ -static size_t findNextSeparator(const AString & aString, size_t aStartIdx = 0) -{ - for (size_t i = aStartIdx, len = aString.length(); i < len; ++i) - { - switch (aString[i]) - { - case '\t': - case '\n': - case '\r': - { - return i; - } - } - } - return AString::npos; -} - - - - - -BlockTypePalette::BlockTypePalette(): - mMaxIndex(0) -{ -} - - - - - -UInt32 BlockTypePalette::index(const AString & aBlockTypeName, const BlockState & aBlockState) -{ - auto idx = maybeIndex(aBlockTypeName, aBlockState); - if (idx.second) - { - return idx.first; - } - - // Not found, append: - auto index = mMaxIndex++; - mBlockToNumber[aBlockTypeName][aBlockState] = index; - mNumberToBlock[index] = {aBlockTypeName, aBlockState}; - return index; -} - - - - - -std::pair BlockTypePalette::maybeIndex(const AString & aBlockTypeName, const BlockState & aBlockState) const -{ - auto itr1 = mBlockToNumber.find(aBlockTypeName); - if (itr1 == mBlockToNumber.end()) - { - return {0, false}; - } - auto itr2 = itr1->second.find(aBlockState); - if (itr2 == itr1->second.end()) - { - return {0, false}; - } - return {itr2->second, true}; -} - - - - - -UInt32 BlockTypePalette::count() const -{ - return static_cast(mNumberToBlock.size()); -} - - - - - -const std::pair & BlockTypePalette::entry(UInt32 aIndex) const -{ - auto itr = mNumberToBlock.find(aIndex); - if (itr == mNumberToBlock.end()) - { - throw NoSuchIndexException(aIndex); - } - return itr->second; -} - - - - - -std::map BlockTypePalette::createTransformMapAddMissing(const BlockTypePalette & aFrom) -{ - std::map res; - for (const auto & fromEntry: aFrom.mNumberToBlock) - { - auto fromIndex = fromEntry.first; - const auto & blockTypeName = fromEntry.second.first; - const auto & blockState = fromEntry.second.second; - res[fromIndex] = index(blockTypeName, blockState); - } - return res; -} - - - - - -std::map BlockTypePalette::createTransformMapWithFallback(const BlockTypePalette & aFrom, UInt32 aFallbackIndex) const -{ - std::map res; - for (const auto & fromEntry: aFrom.mNumberToBlock) - { - auto fromIndex = fromEntry.first; - const auto & blockTypeName = fromEntry.second.first; - const auto & blockState = fromEntry.second.second; - auto thisIndex = maybeIndex(blockTypeName, blockState); - if (thisIndex.second) - { - // The entry was found in this - res[fromIndex] = thisIndex.first; - } - else - { - // The entry was NOT found in this, replace with fallback: - res[fromIndex] = aFallbackIndex; - } - } - return res; -} - - - - - -void BlockTypePalette::loadFromString(const AString & aString) -{ - static const AString hdrTsvRegular = "BlockTypePalette"; - static const AString hdrTsvUpgrade = "UpgradeBlockTypePalette"; - - // Detect format by checking the header line (none -> JSON): - if (aString.substr(0, hdrTsvRegular.length()) == hdrTsvRegular) - { - return loadFromTsv(aString, false); - } - else if (aString.substr(0, hdrTsvUpgrade.length()) == hdrTsvUpgrade) - { - return loadFromTsv(aString, true); - } - return loadFromJsonString(aString); -} - - - - - -void BlockTypePalette::loadFromJsonString(const AString & aJsonPalette) -{ - // Parse the string into JSON object: - Json::Value root; - std::string errs; - if (!JsonUtils::ParseString(aJsonPalette, root, &errs)) - { - throw LoadFailedException(errs); - } - - // Sanity-check the JSON's structure: - if (!root.isObject()) - { - throw LoadFailedException("Incorrect palette format, expected an object at root."); - } - - // Load the palette: - for (auto itr = root.begin(), end = root.end(); itr != end; ++itr) - { - const auto & blockTypeName = itr.name(); - const auto & states = (*itr)["states"]; - if (states == Json::Value()) - { - throw LoadFailedException(Printf("Missing \"states\" for block type \"%s\"", blockTypeName)); - } - for (const auto & state: states) - { - auto id = static_cast(std::stoul(state["id"].asString())); - std::map props; - if (state.isMember("properties")) - { - const auto & properties = state["properties"]; - if (!properties.isObject()) - { - throw LoadFailedException(Printf("Member \"properties\" is not a JSON object (block type \"%s\", id %u).", blockTypeName, id)); - } - for (const auto & key: properties.getMemberNames()) - { - props[key] = properties[key].asString(); - } - } - addMapping(id, blockTypeName, props); - } - } -} - - - - - -void BlockTypePalette::loadFromTsv(const AString & aTsvPalette, bool aIsUpgrade) -{ - static const AString hdrTsvRegular = "BlockTypePalette"; - static const AString hdrTsvUpgrade = "UpgradeBlockTypePalette"; - - // Check the file signature: - auto idx = findNextSeparator(aTsvPalette); - if ((idx == AString::npos) || (aTsvPalette[idx] == '\t')) - { - throw LoadFailedException("Invalid signature"); - } - auto signature = aTsvPalette.substr(0, idx); - bool isUpgrade = (signature == hdrTsvUpgrade); - if (!isUpgrade && (signature != hdrTsvRegular)) - { - throw LoadFailedException("Unknown signature"); - } - if (aTsvPalette[idx] == '\r') // CR of the CRLF pair, skip the LF: - { - idx += 1; - } - - // Parse the header: - bool hasHadVersion = false; - AString commonPrefix; - int line = 2; - auto len = aTsvPalette.length(); - while (true) - { - auto keyStart = idx + 1; - auto keyEnd = findNextSeparator(aTsvPalette, idx + 1); - if (keyEnd == AString::npos) - { - throw LoadFailedException(Printf("Invalid header key format on line %u", line)); - } - if (keyEnd == idx + 1) // Empty line, end of headers - { - if (aTsvPalette[keyEnd] == '\r') // CR of the CRLF pair, skip the LF: - { - ++keyEnd; - } - idx = keyEnd; - ++line; - break; - } - auto valueEnd = findNextSeparator(aTsvPalette, keyEnd + 1); - if ((valueEnd == AString::npos) || (aTsvPalette[valueEnd] == '\t')) - { - throw LoadFailedException(Printf("Invalid header value format on line %u", line)); - } - auto key = aTsvPalette.substr(keyStart, keyEnd - keyStart); - if (key == "FileVersion") - { - unsigned version = 0; - auto value = aTsvPalette.substr(keyEnd + 1, valueEnd - keyEnd - 1); - if (!StringToInteger(value, version)) - { - throw LoadFailedException("Invalid FileVersion value"); - } - else if (version != 1) - { - throw LoadFailedException(Printf("Unknown FileVersion: %u. Only version 1 is supported.", version)); - } - hasHadVersion = true; - } - else if (key == "CommonPrefix") - { - commonPrefix = aTsvPalette.substr(keyEnd + 1, valueEnd - keyEnd - 1); - } - idx = valueEnd; - if (aTsvPalette[idx] == '\r') // CR of the CRLF pair, skip the LF: - { - ++idx; - } - ++line; - } - if (!hasHadVersion) - { - throw LoadFailedException("No FileVersion value"); - } - - // Parse the data: - while (idx + 1 < len) - { - auto lineStart = idx + 1; - auto idEnd = findNextSeparator(aTsvPalette, lineStart); - if ((idEnd == AString::npos) || (aTsvPalette[idEnd] != '\t')) - { - throw LoadFailedException(Printf("Incomplete data on line %u (id)", line)); - } - UInt32 id; - if (!StringToInteger(aTsvPalette.substr(lineStart, idEnd - lineStart), id)) - { - throw LoadFailedException(Printf("Failed to parse id on line %u", line)); - } - size_t metaEnd = idEnd; - if (isUpgrade) - { - metaEnd = findNextSeparator(aTsvPalette, idEnd + 1); - if ((metaEnd == AString::npos) || (aTsvPalette[metaEnd] != '\t')) - { - throw LoadFailedException(Printf("Incomplete data on line %u (meta)", line)); - } - UInt32 meta = 0; - if (!StringToInteger(aTsvPalette.substr(idEnd + 1, metaEnd - idEnd - 1), meta)) - { - throw LoadFailedException(Printf("Failed to parse meta on line %u", line)); - } - if (meta > 15) - { - throw LoadFailedException(Printf("Invalid meta value on line %u: %u", line, meta)); - } - id = (id * 16) | meta; - } - auto blockTypeEnd = findNextSeparator(aTsvPalette, metaEnd + 1); - if (blockTypeEnd == AString::npos) - { - throw LoadFailedException(Printf("Incomplete data on line %u (blockTypeName)", line)); - } - auto blockTypeName = aTsvPalette.substr(metaEnd + 1, blockTypeEnd - metaEnd - 1); - auto blockStateEnd = blockTypeEnd; - AStringMap blockState; - while (aTsvPalette[blockStateEnd] == '\t') - { - auto keyEnd = findNextSeparator(aTsvPalette, blockStateEnd + 1); - if ((keyEnd == AString::npos) || (aTsvPalette[keyEnd] != '\t')) - { - throw LoadFailedException(Printf("Incomplete data on line %u (blockState key)", line)); - } - auto valueEnd = findNextSeparator(aTsvPalette, keyEnd + 1); - if (valueEnd == AString::npos) - { - throw LoadFailedException(Printf("Incomplete data on line %u (blockState value)", line)); - } - auto key = aTsvPalette.substr(blockStateEnd + 1, keyEnd - blockStateEnd - 1); - auto value = aTsvPalette.substr(keyEnd + 1, valueEnd - keyEnd - 1); - blockState[key] = value; - blockStateEnd = valueEnd; - } - addMapping(id, commonPrefix + blockTypeName, std::move(blockState)); - ++line; - if (aTsvPalette[blockStateEnd] == '\r') // CR of the CRLF pair, skip the LF: - { - ++blockStateEnd; - } - idx = blockStateEnd; - } -} - - - - - -void BlockTypePalette::addMapping(UInt32 aID, const AString & aBlockTypeName, const BlockState & aBlockState) -{ - mNumberToBlock[aID] = {aBlockTypeName, aBlockState}; - mBlockToNumber[aBlockTypeName][aBlockState] = aID; - if (aID > mMaxIndex) - { - mMaxIndex = aID; - } -} diff --git a/src/BlockTypePalette.h b/src/BlockTypePalette.h deleted file mode 100644 index 2aade422b..000000000 --- a/src/BlockTypePalette.h +++ /dev/null @@ -1,138 +0,0 @@ -#pragma once - -#include -#include "BlockState.h" - - - - - -/** Holds a palette that maps between block type + state and numbers. -Used primarily by PalettedBlockArea to map from stringular block representation to numeric, -and by protocols to map from stringular block representation to protocol-numeric. -The object itself provides no thread safety, users of this class need to handle locking, if required. -Note that the palette itself doesn't support erasing; -to erase, create a new instance and re-add only the wanted items. - -Internally, the object uses two synced maps, one for each translation direction. - -The palette can be loaded from a string (file). The loader supports either the blocks.json file exported by -the vanilla server itself (https://wiki.vg/Data_Generators), or a processed text file generated by -our tool $/Tools/BlockTypePaletteGenerator/, or a hand-written text file describing the upgrade from -1.12 BlockType + BlockMeta to 1.13 string representations. -The text file is a TSV (tab-separated values), which basically means the data is generally structured as -..., where eol is the platform's CR / CRLF / LF lineend. -The file starts with a single value on the first line, "BlockTypePalette" or "UpgradeBlockTypePalette", which -is used to detect the file format. The following lines are "headers", simple entries -that contain the metadata about the file. "FileVersion" is a compulsory key, "CommonPrefix" is supported, others -are ignored. -The headers are followed by an empty line (that signalizes the end of headers) and then the actual data. -For regular BlockTypePalette TSV file of version 1, the data is in the format: - ... -For the UpgradeBlockTypePalette TSV file of version 1, the data is in the format: - ... -If a CommonPrefix header is present, its value is pre-pended to each blockTypeName loaded (thus allowing -the file to be overall smaller). */ -class BlockTypePalette -{ -public: - - /** Exception that is thrown if requiesting an index not present in the palette. */ - class NoSuchIndexException: - public std::runtime_error - { - using Super = std::runtime_error; - - public: - NoSuchIndexException(UInt32 aIndex): - Super(Printf("No such palette index: %u", aIndex)) - { - } - }; - - - /** Exception that is thrown when loading the palette fails hard (bad format). */ - class LoadFailedException: - public std::runtime_error - { - using Super = std::runtime_error; - - public: - LoadFailedException(const AString & aReason): - Super(aReason) - { - } - }; - - - - /** Create a new empty instance. */ - BlockTypePalette(); - - /** Returns the index of the specified block type name and state. - If the combination is not found, it is added to the palette and the new index is returned. */ - UInt32 index(const AString & aBlockTypeName, const BlockState & aBlockState); - - /** Returns the of the specified block type name and state, if it exists. - If the combination is not found, returns . */ - std::pair maybeIndex(const AString & aBlockTypeName, const BlockState & aBlockState) const; - - /** Returns the total number of entries in the palette. */ - UInt32 count() const; - - /** Returns the blockspec represented by the specified palette index. - If the index is not valid, throws a NoSuchIndexException. */ - const std::pair & entry(UInt32 aIndex) const; - - /** Returns an index-transform map from aFrom to this (this.entry(idx) == aFrom.entry(res[idx])). - Entries from aFrom that are not present in this are added. - Used when pasting two areas, to transform the src palette to dst palette. */ - std::map createTransformMapAddMissing(const BlockTypePalette & aFrom); - - /** Returns an index-transform map from aFrom to this (this.entry(idx) == aFrom.entry(res[idx])). - Entries from aFrom that are not present in this are assigned the fallback index. - Used for protocol block type mapping. */ - std::map createTransformMapWithFallback(const BlockTypePalette & aFrom, UInt32 aFallbackIndex) const; - - /** Loads the palette from the string representation. - Throws a LoadFailedException if the loading fails hard (bad string format); - but still a part of the data may already be loaded at that point. - If the string specifies duplicate entries (either to already existing entries, or to itself), - the duplicates replace the current values silently (this allows us to chain multiple files as "overrides". - Auto-detects the string format (json / tsv, normal / upgrade palette) and calls the appropriate load function. */ - void loadFromString(const AString & aString); - - -protected: - - /** The mapping from numeric to stringular representation. - mNumberToBlock[index] = {"blockTypeName", blockState}. */ - std::map> mNumberToBlock; - - /** The mapping from stringular to numeric representation. - mStringToNumber["blockTypeName"][blockState] = index. */ - std::unordered_map> mBlockToNumber; - - /** The maximum index ever used in the maps. - Used when adding new entries through the index() call. */ - UInt32 mMaxIndex; - - - /** Loads the palette from the JSON representation, https://wiki.vg/Data_Generators - Throws a LoadFailedException if the loading fails hard (bad string format); - but still a part of the data may already be loaded at that point. - See also: loadFromString(). */ - void loadFromJsonString(const AString & aJsonPalette); - - /** Loads the palette from the regular or upgrade TSV representation. - aIsUpgrade specifies whether the format is an upgrade TSV (true) or a regular one (false) - Throws a LoadFailedException if the loading fails hard (bad string format); - but still a part of the data may already be loaded at that point. - See also: loadFromString(). */ - void loadFromTsv(const AString & aTsvPalette, bool aIsUpgrade); - - /** Adds a mapping between the numeric and stringular representation into both maps, - updates the mMaxIndex, if appropriate. - Silently overwrites any previous mapping for the ID, if present, but keeps the old string->id mapping. */ - void addMapping(UInt32 aID, const AString & aBlockTypeName, const BlockState & aBlockState); -}; diff --git a/src/BlockTypeRegistry.cpp b/src/BlockTypeRegistry.cpp deleted file mode 100644 index 491e03593..000000000 --- a/src/BlockTypeRegistry.cpp +++ /dev/null @@ -1,240 +0,0 @@ - -#include "Globals.h" -#include "BlockTypeRegistry.h" - - - - -//////////////////////////////////////////////////////////////////////////////// -// BlockInfo: - -BlockInfo::BlockInfo( - const AString & aPluginName, - const AString & aBlockTypeName, - std::shared_ptr aHandler, - const std::map & aHints, - const std::map & aHintCallbacks -): - m_PluginName(aPluginName), - m_BlockTypeName(aBlockTypeName), - m_Handler(std::move(aHandler)), - m_Hints(aHints), - m_HintCallbacks(aHintCallbacks) -{ -} - - - - - -AString BlockInfo::hintValue( - const AString & aHintName, - const BlockState & aBlockState -) -{ - // Search the hint callbacks first: - auto itrC = m_HintCallbacks.find(aHintName); - if (itrC != m_HintCallbacks.end()) - { - // Hint callback found, use it: - return itrC->second(m_BlockTypeName, aBlockState); - } - - // Search the static hints: - auto itr = m_Hints.find(aHintName); - if (itr != m_Hints.end()) - { - // Hint found, use it: - return itr->second; - } - - // Nothing found, return empty string: - return AString(); -} - - - - - -void BlockInfo::setHint(const AString & aHintKey, const AString & aHintValue) -{ - m_Hints[aHintKey] = aHintValue; - - // Warn if the hint is already provided by a callback (aHintValue will be ignored when evaluating the hint): - auto itrC = m_HintCallbacks.find(aHintKey); - if (itrC != m_HintCallbacks.end()) - { - LOGINFO("Setting a static hint %s for block type %s, but there's already a callback for that hint. The static hint will be ignored.", - aHintKey.c_str(), m_BlockTypeName.c_str() - ); - } -} - - - - - -void BlockInfo::removeHint(const AString & aHintKey) -{ - m_Hints.erase(aHintKey); -} - - - - - -//////////////////////////////////////////////////////////////////////////////// -// BlockTypeRegistry: - -void BlockTypeRegistry::registerBlockType( - const AString & aPluginName, - const AString & aBlockTypeName, - std::shared_ptr aHandler, - const std::map & aHints, - const std::map & aHintCallbacks -) -{ - auto blockInfo = std::make_shared( - aPluginName, aBlockTypeName, std::move(aHandler), aHints, aHintCallbacks - ); - - // Check previous registrations: - cCSLock lock(m_CSRegistry); - auto itr = m_Registry.find(aBlockTypeName); - if (itr != m_Registry.end()) - { - if (itr->second->pluginName() != aPluginName) - { - throw AlreadyRegisteredException(itr->second, blockInfo); - } - } - - // Store the registration: - m_Registry[aBlockTypeName] = blockInfo; -} - - - - - -std::shared_ptr BlockTypeRegistry::blockInfo(const AString & aBlockTypeName) -{ - cCSLock lock(m_CSRegistry); - auto itr = m_Registry.find(aBlockTypeName); - if (itr == m_Registry.end()) - { - return nullptr; - } - return itr->second; -} - - - - - -void BlockTypeRegistry::removeAllByPlugin(const AString & aPluginName) -{ - cCSLock lock(m_CSRegistry); - for (auto itr = m_Registry.begin(); itr != m_Registry.end();) - { - if (itr->second->pluginName() == aPluginName) - { - itr = m_Registry.erase(itr); - } - else - { - ++itr; - } - } -} - - - - - -void BlockTypeRegistry::setBlockTypeHint( - const AString & aBlockTypeName, - const AString & aHintKey, - const AString & aHintValue -) -{ - cCSLock lock(m_CSRegistry); - auto blockInfo = m_Registry.find(aBlockTypeName); - if (blockInfo == m_Registry.end()) - { - throw NotRegisteredException(aBlockTypeName, aHintKey, aHintValue); - } - blockInfo->second->setHint(aHintKey, aHintValue); -} - - - - - -void BlockTypeRegistry::removeBlockTypeHint( - const AString & aBlockTypeName, - const AString & aHintKey -) -{ - cCSLock lock(m_CSRegistry); - auto blockInfo = m_Registry.find(aBlockTypeName); - if (blockInfo == m_Registry.end()) - { - return; - } - blockInfo->second->removeHint(aHintKey); -} - - - - - -//////////////////////////////////////////////////////////////////////////////// -// BlockTypeRegistry::AlreadyRegisteredException: - -BlockTypeRegistry::AlreadyRegisteredException::AlreadyRegisteredException( - const std::shared_ptr & aPreviousRegistration, - const std::shared_ptr & aNewRegistration -) : - Super(message(aPreviousRegistration, aNewRegistration)), - m_PreviousRegistration(aPreviousRegistration), - m_NewRegistration(aNewRegistration) -{ -} - - - - - -AString BlockTypeRegistry::AlreadyRegisteredException::message( - const std::shared_ptr & aPreviousRegistration, - const std::shared_ptr & aNewRegistration -) -{ - return Printf("Attempting to register BlockTypeName %s from plugin %s, while it is already registered in plugin %s", - aNewRegistration->blockTypeName().c_str(), - aNewRegistration->pluginName().c_str(), - aPreviousRegistration->pluginName().c_str() - ); -} - - - - - -//////////////////////////////////////////////////////////////////////////////// -// BlockTypeRegistry::NotRegisteredException: - -BlockTypeRegistry::NotRegisteredException::NotRegisteredException( - const AString & aBlockTypeName, - const AString & aHintKey, - const AString & aHintValue -): - Super(Printf( - "Attempting to set a hint of nonexistent BlockTypeName.\n\tBlockTypeName = %s\n\tHintKey = %s\n\tHintValue = %s", - aBlockTypeName.c_str(), - aHintKey.c_str(), - aHintValue.c_str() - )) -{ -} diff --git a/src/BlockTypeRegistry.h b/src/BlockTypeRegistry.h deleted file mode 100644 index 3a85ee510..000000000 --- a/src/BlockTypeRegistry.h +++ /dev/null @@ -1,216 +0,0 @@ -#pragma once - - - - - -#include -#include - - - - - -// fwd: -class cBlockHandler; -class BlockState; - - - - - -/** Complete information about a single block type. -The BlockTypeRegistry uses this structure to store the registered information. */ -class BlockInfo -{ -public: - - /** Callback is used to query block hints dynamically, based on the current BlockState. - Useful for example for redstone lamps that can be turned on or off. */ - using HintCallback = std::function; - - - /** Creates a new instance with the specified BlockTypeName and handler / hints / callbacks. - aPluginName specifies the name of the plugin to associate with the block type (to allow unload / reload). */ - BlockInfo( - const AString & aPluginName, - const AString & aBlockTypeName, - std::shared_ptr aHandler, - const std::map & aHints = std::map(), - const std::map & aHintCallbacks = std::map() - ); - - - /** Retrieves the value associated with the specified hint for this specific BlockTypeName and BlockState. - Queries hint callbacks first, then static hints if a callback doesn't exist. - Returns an empty string if hint not found at all. */ - AString hintValue( - const AString & aHintName, - const BlockState & aBlockState - ); - - // Simple getters: - const AString & pluginName() const { return m_PluginName; } - const AString & blockTypeName() const { return m_BlockTypeName; } - std::shared_ptr handler() const { return m_Handler; } - - /** Sets (creates or updates) a static hint. - Hints provided by callbacks are unaffected by this - callbacks are "higher priority", they overwrite anything set here. - Logs an info message if the hint is already provided by a hint callback. */ - void setHint(const AString & aHintKey, const AString & aHintValue); - - /** Removes a hint. - Silently ignored if the hint hasn't been previously set. */ - void removeHint(const AString & aHintKey); - - -private: - - /** The name of the plugin that registered the block. */ - AString m_PluginName; - - /** The name of the block type, such as "minecraft:redstone_lamp" */ - AString m_BlockTypeName; - - /** The callbacks to call for various interaction. */ - std::shared_ptr m_Handler; - - /** Optional static hints for any subsystem to use, such as "IsSnowable" -> "1". - Hint callbacks are of higher priority than m_Hints - if a hint is provided by a m_HintCallback, its value in m_Hints is ignored. */ - std::map m_Hints; - - /** The callbacks for dynamic evaluation of hints, such as "LightValue" -> function(BlockTypeName, BlockState). - Hint callbacks are of higher priority than m_Hints - if a hint is provided by a m_HintCallback, its value in m_Hints is ignored. */ - std::map m_HintCallbacks; -}; - - - - - -/** Stores information on all known block types. -Can dynamically add and remove block types. -Block types are identified using BlockTypeName. -Supports unregistering and re-registering the same type by the same plugin. -Stores the name of the plugin that registered the type, for better plugin error messages ("already registered in X") -and so that we can unload and reload plugins. */ -class BlockTypeRegistry -{ -public: - // fwd: - class AlreadyRegisteredException; - class NotRegisteredException; - - - /** Creates an empty new instance of the block type registry */ - BlockTypeRegistry() = default; - - /** Registers the specified block type. - If the block type already exists and the plugin is the same, updates the registration. - If the block type already exists and the plugin is different, throws an AlreadyRegisteredException. */ - void registerBlockType( - const AString & aPluginName, - const AString & aBlockTypeName, - std::shared_ptr aHandler, - const std::map & aHints = std::map(), - const std::map & aHintCallbacks = std::map() - ); - - /** Returns the registration information for the specified BlockTypeName. - Returns nullptr if BlockTypeName not found. */ - std::shared_ptr blockInfo(const AString & aBlockTypeName); - - /** Removes all registrations done by the specified plugin. */ - void removeAllByPlugin(const AString & aPluginName); - - /** Sets (adds or overwrites) a single Hint value for a BlockType. - Throws NotRegisteredException if the BlockTypeName is not registered. */ - void setBlockTypeHint( - const AString & aBlockTypeName, - const AString & aHintKey, - const AString & aHintValue - ); - - /** Removes a previously registered single Hint value for a BlockType. - Throws NotRegisteredException if the BlockTypeName is not registered. - Silently ignored if the Hint hasn't been previously set. */ - void removeBlockTypeHint( - const AString & aBlockTypeName, - const AString & aHintKey - ); - - -private: - - /** The actual block type registry. - Maps the BlockTypeName to the BlockInfo instance. */ - std::map> m_Registry; - - /** The CS that protects m_Registry against multithreaded access. */ - cCriticalSection m_CSRegistry; -}; - - - - - -/** The exception thrown from BlockTypeRegistry::registerBlockType() if the same block type is being registered from a different plugin. */ -class BlockTypeRegistry::AlreadyRegisteredException: public std::runtime_error -{ - using Super = std::runtime_error; - -public: - - /** Creates a new instance of the exception that provides info on both the original registration and the newly attempted - registration that caused the failure. */ - AlreadyRegisteredException( - const std::shared_ptr & aPreviousRegistration, - const std::shared_ptr & aNewRegistration - ); - - // Simple getters: - std::shared_ptr previousRegistration() const { return m_PreviousRegistration; } - std::shared_ptr newRegistration() const { return m_NewRegistration; } - - -private: - - std::shared_ptr m_PreviousRegistration; - std::shared_ptr m_NewRegistration; - - - /** Returns the general exception message formatted by the two registrations. - The output is used when logging. */ - static AString message( - const std::shared_ptr & aPreviousRegistration, - const std::shared_ptr & aNewRegistration - ); -}; - - - - - -/** The exception thrown from BlockTypeRegistry::setBlockTypeHint() if the block type has not been registered before. */ -class BlockTypeRegistry::NotRegisteredException: public std::runtime_error -{ - using Super = std::runtime_error; - -public: - - /** Creates a new instance of the exception that provides info on both the original registration and the newly attempted - registration that caused the failure. */ - NotRegisteredException( - const AString & aBlockTypeName, - const AString & aHintKey, - const AString & aHintValue - ); - - // Simple getters: - const AString & blockTypeName() const { return m_BlockTypeName; } - - -private: - - const AString m_BlockTypeName; -}; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 69ea7dbd1..a3d34eb53 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,10 +6,7 @@ target_sources( BiomeDef.cpp BlockArea.cpp BlockInfo.cpp - BlockState.cpp BlockType.cpp - BlockTypePalette.cpp - BlockTypeRegistry.cpp BrewingRecipes.cpp Broadcaster.cpp BoundingBox.cpp @@ -53,7 +50,6 @@ target_sources( MonsterConfig.cpp NetherPortalScanner.cpp OverridesSettingsRepository.cpp - PalettedBlockArea.cpp ProbabDistrib.cpp RankManager.cpp RCONServer.cpp @@ -76,11 +72,8 @@ target_sources( BlockArea.h BlockInServerPluginInterface.h BlockInfo.h - BlockState.h BlockTracer.h BlockType.h - BlockTypePalette.h - BlockTypeRegistry.h BrewingRecipes.h BoundingBox.h BuildInfo.h.cmake @@ -135,7 +128,6 @@ target_sources( NetherPortalScanner.h OpaqueWorld.h OverridesSettingsRepository.h - PalettedBlockArea.h ProbabDistrib.h RankManager.h RCONServer.h diff --git a/src/PalettedBlockArea.h b/src/PalettedBlockArea.h index 3866d405b..5ded7bcb4 100644 --- a/src/PalettedBlockArea.h +++ b/src/PalettedBlockArea.h @@ -6,7 +6,7 @@ #include -#include "BlockTypePalette.h" +#include "Bindings/BlockTypePalette.h" #include "Cuboid.h" -- cgit v1.2.3