diff options
author | Mattes D <github@xoft.cz> | 2019-12-20 22:10:24 +0100 |
---|---|---|
committer | Mattes D <github@xoft.cz> | 2019-12-28 22:43:35 +0100 |
commit | 2d6f6a574d8dd6a94dc13cb51628626d99c1853a (patch) | |
tree | 450b60da6dba1b8f7d603f6d3f2323235a0d83b7 /src | |
parent | Moved ProtocolBlockTypePalette functionality into BlockTypePalette. (diff) | |
download | cuberite-2d6f6a574d8dd6a94dc13cb51628626d99c1853a.tar cuberite-2d6f6a574d8dd6a94dc13cb51628626d99c1853a.tar.gz cuberite-2d6f6a574d8dd6a94dc13cb51628626d99c1853a.tar.bz2 cuberite-2d6f6a574d8dd6a94dc13cb51628626d99c1853a.tar.lz cuberite-2d6f6a574d8dd6a94dc13cb51628626d99c1853a.tar.xz cuberite-2d6f6a574d8dd6a94dc13cb51628626d99c1853a.tar.zst cuberite-2d6f6a574d8dd6a94dc13cb51628626d99c1853a.zip |
Diffstat (limited to '')
-rw-r--r-- | src/BlockTypePalette.cpp | 168 | ||||
-rw-r--r-- | src/BlockTypePalette.h | 45 | ||||
-rw-r--r-- | src/Stopwatch.h | 6 |
3 files changed, 174 insertions, 45 deletions
diff --git a/src/BlockTypePalette.cpp b/src/BlockTypePalette.cpp index 92269db9d..b8f962f5d 100644 --- a/src/BlockTypePalette.cpp +++ b/src/BlockTypePalette.cpp @@ -123,7 +123,18 @@ std::map<UInt32, UInt32> BlockTypePalette::createTransformMapWithFallback(const void BlockTypePalette::loadFromString(const AString & aString) { - // TODO: Detect format (Json vs Lua) + 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); } @@ -143,55 +154,144 @@ void BlockTypePalette::loadFromJsonString(const AString & aJsonPalette) throw LoadFailedException(errs); } - // Check the JSON's metadata + version: - if (!root.isObject() || - !root.isMember("Metadata") || - !root["Metadata"].isMember("ProtocolBlockTypePaletteVersion") || - !root.isMember("Palette") || - !root["Palette"].isArray()) - { - throw LoadFailedException("Incorrect palette format, wrong or missing metadata."); - } - auto version = root["Metadata"]["ProtocolBlockTypePaletteVersion"].asUInt(); - if (version != 1) + // Sanity-check the JSON's structure: + if (!root.isObject()) { - throw(Printf("Palette format version %d not supported.", version)); + throw LoadFailedException("Incorrect palette format, expected an object at root."); } // Load the palette: - auto len = root["Palette"].size(); - for (decltype(len) i = 0; i < len; ++i) + for (auto itr = root.begin(), end = root.end(); itr != end; ++itr) { - const auto & record = root["Palette"][i]; - if (!record.isObject()) + const auto & blockTypeName = itr.name(); + const auto & states = (*itr)["states"]; + if (states == Json::Value()) { - throw LoadFailedException(Printf("Palette record #%u is not a JSON object.", i)); + throw LoadFailedException(Printf("Missing \"states\" for block type \"%s\"", blockTypeName)); } + for (const auto & state: states) + { + auto id = static_cast<UInt32>(std::stoul(state["id"].asString())); + std::map<AString, AString> 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); + } + } +} - auto blockTypeName = record["name"].asString(); - auto id = static_cast<UInt32>(std::stoul(record["id"].asString())); - std::map<AString, AString> state; - if (record.isMember("props")) + + + +void BlockTypePalette::loadFromTsv(const AString & aTsvPalette, bool aIsUpgrade) +{ + auto lines = StringSplitAndTrim(aTsvPalette, "\n"); + + // Parse the header: + int fileVersion = 0; + AString commonPrefix; + auto numLines = lines.size(); + for (size_t idx = 1; idx < numLines; ++idx) + { + const auto & line = lines[idx]; + if (line.empty()) { - const auto & props = record["props"]; - if (!props.isObject()) + // End of headers, erase them from lines[] and go parse the data + lines.erase(lines.begin(), lines.begin() + static_cast<AStringVector::difference_type>(idx) + 1); + break; + } + auto s = StringSplit(line, "\t"); + if (s.size() != 2) + { + throw LoadFailedException(Printf("Invalid header format on line %u", idx + 1)); + } + if (s[0] == "FileVersion") + { + try { - throw LoadFailedException(Printf("Palette record #%u: \"props\" value is not a JSON object.", i)); + fileVersion = std::stoi(s[1]); } - for (const auto & key: props.getMemberNames()) + catch (const std::exception & exc) { - state[key] = props[key].asString(); + throw LoadFailedException(Printf("Invalid file version: \"%d\" (%s)", s[1], exc.what())); } } - BlockState blockState(state); + else if (s[0] == "CommonPrefix") + { + commonPrefix = s[1]; + } + } + if (fileVersion != 1) + { + throw LoadFailedException(Printf("Unknown file version (%d), only version 1 is supported", fileVersion)); + } - // Insert / update in the maps: - mNumberToBlock[id] = {blockTypeName, blockState}; - mBlockToNumber[blockTypeName][blockState] = id; - if (id > mMaxIndex) + // Parse the data: + size_t minSplit = aIsUpgrade ? 3 : 2; + for (const auto & line: lines) + { + auto s = StringSplit(line, "\t"); + auto numSplit = s.size(); + if (numSplit < minSplit) + { + throw LoadFailedException(Printf("Not enough values on data line: \"%s\"", line)); + } + UInt32 id; + try + { + id = static_cast<UInt32>(std::stoi(s[0])); + } + catch (const std::exception & exc) + { + throw LoadFailedException(Printf("Invalid block ID: \"%s\" (%s)", s[0], exc.what())); + } + size_t idx = 1; + if (aIsUpgrade) + { + id = id * 16; + try + { + id = id + static_cast<UInt32>(Clamp(std::stoi(s[1]), 0, 15)); + } + catch (const std::exception & exc) + { + throw LoadFailedException(Printf("Invalid block meta: \"%s\" (%s)", s[1], exc.what())); + } + idx = 2; + } + const auto & blockTypeName = s[idx]; + idx += 1; + std::map<AString, AString> state; + while (idx + 1 < numSplit) { - mMaxIndex = id; + state[s[idx]] = s[idx + 1]; + idx += 2; } - } // for i - Palette[] + addMapping(id, commonPrefix + blockTypeName, state); + } +} + + + + + +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 index 2d0985f08..3b6cb9ec0 100644 --- a/src/BlockTypePalette.h +++ b/src/BlockTypePalette.h @@ -15,7 +15,25 @@ The object itself provides no thread safety, users of this class need to handle 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. */ +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 +<value1><tab><value2><tab><value3><tab>...<valueN><eol>, 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 <key><tab><value><eol> 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: +<index><tab><blockTypeName><tab><state1Name><tab><state1Value><tab><state2Name> ... <eol> +For the UpgradeBlockTypePalette TSV file of version 1, the data is in the format: +<blockType><tab><blockMeta><tab><blockTypeName><tab><state1Name><tab><state1Value><tab><state2Name> ... <eol> +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: @@ -78,10 +96,11 @@ public: std::map<UInt32, UInt32> 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). + 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". - Currently handles only JSON representation, expected to handle also Lua representation in the future. */ + Auto-detects the string format (json / tsv, normal / upgrade palette) and calls the appropriate load function. */ void loadFromString(const AString & aString); @@ -100,9 +119,21 @@ protected: UInt32 mMaxIndex; - /** Loads the palette from the JSON representation. - Throws a LoadFailedException if the loading fails hard (bad string format). - 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". */ + /** 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/Stopwatch.h b/src/Stopwatch.h index b7c8e50c7..7676b3856 100644 --- a/src/Stopwatch.h +++ b/src/Stopwatch.h @@ -24,10 +24,8 @@ public: ~cStopwatch() { - #ifdef _DEBUG - auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - m_StartTime).count(); - LOGD("Stopwatch: %s took %.03f sec", m_Name.c_str(), static_cast<double>(duration) / 1000); - #endif // _DEBUG + auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - m_StartTime).count(); + LOG("Stopwatch: %s took %.03f sec", m_Name, static_cast<double>(duration) / 1000); } protected: |