Add ProtocolBlockTypePalette (#4391)
This commit is contained in:
parent
70d0b46b60
commit
d1c95742dd
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -63,3 +63,6 @@
|
||||
path = lib/fmt
|
||||
url = https://github.com/fmtlib/fmt.git
|
||||
ignore = dirty
|
||||
[submodule "Tools/BlockTypePaletteGenerator/lib/lunajson"]
|
||||
path = Tools/BlockTypePaletteGenerator/lib/lunajson
|
||||
url = https://github.com/grafi-tt/lunajson.git
|
||||
|
1
Server/Protocol/1.13/base.btp.json
Normal file
1
Server/Protocol/1.13/base.btp.json
Normal file
File diff suppressed because one or more lines are too long
91
Tools/BlockTypePaletteGenerator/Generator.lua
Normal file
91
Tools/BlockTypePaletteGenerator/Generator.lua
Normal file
@ -0,0 +1,91 @@
|
||||
-- lib/lunajson/src/ is not in default Lua package paths
|
||||
package.path = 'lib/lunajson/src/?.lua;' .. package.path;
|
||||
|
||||
|
||||
--- Prints usage instructions to stdout.
|
||||
-- If the optional `message` is passed, output is prepended by message _and_
|
||||
-- redirected to stderr.
|
||||
function usage(message)
|
||||
if message then
|
||||
io.output(io.stderr);
|
||||
io.write(message, "\n\n");
|
||||
end
|
||||
io.write(
|
||||
"Usage: lua Generator.lua INPUTFILE OUTPUTFILE\n"..
|
||||
"Converts the Minecraft blocks.json report format to the cuberite "..
|
||||
"block type palette format.\n"..
|
||||
"\n"..
|
||||
"INPUTFILE and OUTPUTFILE must point to a valid path. INPUTFILE must "..
|
||||
"be readable and OUTPUTFILE must be writable. Either can be replaced "..
|
||||
"with `-` (dash character) to point to standard-input or -output.\n");
|
||||
os.exit(message and 1 or 0);
|
||||
end
|
||||
|
||||
|
||||
-- Test whether the script is run in a path where it can load it's libraries
|
||||
if not pcall(function() require("lunajson.decoder") end) then
|
||||
usage("Could not load required libraries, please run `Generator.lua` "..
|
||||
"within its directory and make sure to run `git submodule update`.");
|
||||
end
|
||||
|
||||
|
||||
-- Check/Prepare CLI arguments
|
||||
local inpath, outpath = ...;
|
||||
io.input(io.stdin);
|
||||
io.output(io.stdout);
|
||||
|
||||
if select("#", ...) ~= 2 then
|
||||
usage("Incorrect number of arguments.");
|
||||
end
|
||||
|
||||
if inpath ~= "-" then
|
||||
local handle, err = io.open(inpath, "r");
|
||||
io.input(handle or usage(err));
|
||||
end
|
||||
|
||||
if outpath ~= "-" then
|
||||
local handle, err = io.open(outpath, "w");
|
||||
io.output(handle or usage(err));
|
||||
end
|
||||
|
||||
|
||||
-- Main program starts here
|
||||
local decode = (require("lunajson.decoder"))();
|
||||
local encode = (require("lunajson.encoder"))();
|
||||
|
||||
local input = decode(io.input():read("*a"));
|
||||
local registry = {};
|
||||
local max_id = -1;
|
||||
|
||||
|
||||
for blockname, blockdata in pairs(input) do
|
||||
for i = 1, #(blockdata.states or {}) do
|
||||
local state = blockdata.states[i];
|
||||
assert(registry[state.id + 1] == nil, "Ensure no duplicate IDs");
|
||||
|
||||
-- needed in the end to verify we got no holes in the array:
|
||||
max_id = math.max(max_id, state.id);
|
||||
|
||||
registry[state.id + 1] = {
|
||||
id = assert(state.id, "id is required."),
|
||||
name = assert(blockname, "Block type name is required."),
|
||||
-- default = state.default or nil, -- may need this later
|
||||
props = state.properties,
|
||||
};
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- The following assertion is not necessary by the current spec, but is required
|
||||
-- by how lunajson distinguishes objects from arrays. Also if this fails, it is
|
||||
-- _very_ likely that the input file is faulty.
|
||||
assert(#registry == max_id + 1, "Ensure that registry has contiguous keys");
|
||||
|
||||
local out = {
|
||||
Metadata = {
|
||||
ProtocolBlockTypePaletteVersion = 1
|
||||
},
|
||||
Palette = registry
|
||||
};
|
||||
|
||||
io.write(encode(out), "\n");
|
63
Tools/BlockTypePaletteGenerator/Readme.md
Normal file
63
Tools/BlockTypePaletteGenerator/Readme.md
Normal file
@ -0,0 +1,63 @@
|
||||
This generator crafts an intermediate index format to be read by cuberite
|
||||
|
||||
# Running
|
||||
|
||||
Run `lua ./Generator.lua`, pass `blocks.json` as first argument to the script
|
||||
and the desired output location as 2nd argument.
|
||||
|
||||
Make sure to run the Generator from within its directory (`cd` into the path
|
||||
where `Generator.lua` is.)
|
||||
|
||||
## Examples
|
||||
|
||||
```bash
|
||||
SERVER=/path/to/server.jar
|
||||
java -cp "$SERVER" net.minecraft.data.Main --reports &&
|
||||
lua Generator.lua \
|
||||
generated/reports/blocks.json \
|
||||
../../Server/Protocol/1.13/ProtocolBlockTypePalette.json
|
||||
```
|
||||
|
||||
```bash
|
||||
SERVER=/path/to/server.jar
|
||||
java -cp "$SERVER" net.minecraft.data.Main --reports &&
|
||||
lua Generator.lua - -\
|
||||
< generated/reports/blocks.json \
|
||||
> ../Server/Protocol/1.13/ProtocolBlockTypePalette.json
|
||||
```
|
||||
|
||||
## Output format
|
||||
|
||||
The Format is a `JSON` document containing an object with at least two keys at
|
||||
the top level: `Metadata` and `Palette`.
|
||||
|
||||
`Metadata` contains document metadata, namely a key `"ProtocolBlockType": 1`.
|
||||
|
||||
`Palette` contains an array of objects. Each of these objects has at least the
|
||||
keys `id`, `name` and an optional `props` key that contains the individual
|
||||
properties of the current state. These properties are a KV dict of pure strings.
|
||||
|
||||
The order of the array or object elements is not significant. `id` is unique.
|
||||
|
||||
```json
|
||||
{
|
||||
"Metadata": {
|
||||
"ProtocolBlockType": 1
|
||||
},
|
||||
"Palette": [{
|
||||
"id": 0,
|
||||
"name": "minecraft:air"
|
||||
}, {
|
||||
"id": 1,
|
||||
"name": "minecraft:stone"
|
||||
}, {
|
||||
"id": 221,
|
||||
"name": "minecraft:dark_oak_leaves",
|
||||
"props": {
|
||||
"persistent": "false",
|
||||
"distance": "4"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
1
Tools/BlockTypePaletteGenerator/lib/lunajson
Submodule
1
Tools/BlockTypePaletteGenerator/lib/lunajson
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 1bdc886a9abb1be745fa7436f2a043b0455d70b8
|
@ -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:
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
144
src/Protocol/ProtocolBlockTypePalette.cpp
Normal file
144
src/Protocol/ProtocolBlockTypePalette.cpp
Normal file
@ -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();
|
||||
}
|
40
src/Protocol/ProtocolBlockTypePalette.h
Normal file
40
src/Protocol/ProtocolBlockTypePalette.h
Normal file
@ -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;
|
||||
};
|
@ -109,8 +109,66 @@ static void testReplacing()
|
||||
|
||||
|
||||
|
||||
/** Tests the comparison operator. */
|
||||
static void testComparison()
|
||||
{
|
||||
LOGD("Testing comparison of BlockStates...");
|
||||
|
||||
// Simple property value tests
|
||||
TEST_FALSE((BlockState({{"a", "a"}}) < BlockState({{"a", "a"}})));
|
||||
TEST_FALSE((BlockState() < BlockState()));
|
||||
TEST_TRUE((BlockState() < BlockState({{"foo", "bar"}})));
|
||||
TEST_FALSE((BlockState({{"foo", "bar"}}) < BlockState()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Tests the comparison operator using crafted data to defeat the checksum. */
|
||||
static void testComparison2()
|
||||
{
|
||||
/* The following test ensures that items inserted in different order result
|
||||
in the same map. I.e. that the < operator is stable. */
|
||||
std::vector<BlockState> v;
|
||||
std::map<BlockState, bool> map1;
|
||||
std::map<BlockState, bool> map2;
|
||||
|
||||
for (int i = 0; i < 128; ++i)
|
||||
{
|
||||
v.push_back(BlockState({{std::string(1, static_cast<char>(0x1F)), std::string(1, static_cast<char>(i))}}));
|
||||
v.push_back(BlockState({{std::string(1, static_cast<char>(0x10)), std::string(1, static_cast<char>(i | 0x80))},
|
||||
{std::string(1, static_cast<char>(0x0F)), std::string(1, static_cast<char>(0x80))}}));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < v.size(); ++i)
|
||||
{
|
||||
map1[v[i]] = true;
|
||||
}
|
||||
|
||||
for (auto i = v.size(); i > 0; --i)
|
||||
{
|
||||
map2[v[i - 1]] = true;
|
||||
}
|
||||
|
||||
// Check result
|
||||
TEST_EQUAL(v.size(), 2 * 128);
|
||||
TEST_EQUAL(map1.size(), v.size());
|
||||
TEST_EQUAL(map1.size(), map2.size());
|
||||
for (const auto & item: map1)
|
||||
{
|
||||
TEST_EQUAL(map1[item.first], map2[item.first]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
IMPLEMENT_TEST_MAIN("BlockStateTest",
|
||||
testStaticCreation();
|
||||
testDynamicCreation();
|
||||
testReplacing();
|
||||
testComparison();
|
||||
testComparison2();
|
||||
)
|
||||
|
@ -19,3 +19,4 @@ add_subdirectory(Network)
|
||||
add_subdirectory(OSSupport)
|
||||
add_subdirectory(SchematicFileSerializer)
|
||||
add_subdirectory(UUID)
|
||||
add_subdirectory(ProtocolBlockTypePalette)
|
||||
|
47
tests/ProtocolBlockTypePalette/CMakeLists.txt
Normal file
47
tests/ProtocolBlockTypePalette/CMakeLists.txt
Normal file
@ -0,0 +1,47 @@
|
||||
cmake_minimum_required(VERSION 3.0.2)
|
||||
enable_testing()
|
||||
add_definitions(-DTEST_GLOBALS=1)
|
||||
|
||||
include_directories(SYSTEM "../../lib/jsoncpp/include")
|
||||
include_directories(${CMAKE_SOURCE_DIR}/src/)
|
||||
|
||||
|
||||
add_definitions(-DTEST_GLOBALS=1)
|
||||
|
||||
set (SHARED_SRCS
|
||||
${CMAKE_SOURCE_DIR}/src/Protocol/ProtocolBlockTypePalette.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/BlockState.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/StringUtils.cpp
|
||||
)
|
||||
|
||||
set (SHARED_HDRS
|
||||
../TestHelpers.h
|
||||
${CMAKE_SOURCE_DIR}/src/Protocol/ProtocolBlockTypePalette.h
|
||||
${CMAKE_SOURCE_DIR}/src/BlockState.h
|
||||
${CMAKE_SOURCE_DIR}/src/StringUtils.h
|
||||
)
|
||||
|
||||
set (SRCS
|
||||
ProtocolBlockTypePaletteTest.cpp
|
||||
)
|
||||
|
||||
file (COPY
|
||||
test.btp.json
|
||||
../../Server/Protocol/1.13/base.btp.json
|
||||
DESTINATION ./)
|
||||
|
||||
source_group("Shared" FILES ${SHARED_SRCS} ${SHARED_HDRS})
|
||||
source_group("Sources" FILES ${SRCS})
|
||||
add_executable(ProtocolBlockTypePaletteTest-exe ${SRCS} ${SHARED_SRCS} ${SHARED_HDRS})
|
||||
target_link_libraries(ProtocolBlockTypePaletteTest-exe fmt::fmt jsoncpp_lib_static)
|
||||
add_test(NAME ProtocolBlockTypePaletteTest-test COMMAND ProtocolBlockTypePaletteTest-exe)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Put the projects into solution folders (MSVC):
|
||||
set_target_properties(
|
||||
ProtocolBlockTypePaletteTest-exe
|
||||
PROPERTIES FOLDER Tests
|
||||
)
|
168
tests/ProtocolBlockTypePalette/ProtocolBlockTypePaletteTest.cpp
Normal file
168
tests/ProtocolBlockTypePalette/ProtocolBlockTypePaletteTest.cpp
Normal file
@ -0,0 +1,168 @@
|
||||
// ProtocolBlockTypePaletteTest.cpp
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <streambuf>
|
||||
#include "Globals.h"
|
||||
|
||||
#include "Protocol/ProtocolBlockTypePalette.h"
|
||||
|
||||
#include "../TestHelpers.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static void TestSuccess(void)
|
||||
{
|
||||
LOG("Test TestSuccess");
|
||||
ProtocolBlockTypePalette palette;
|
||||
|
||||
auto example = "{\"Metadata\":{\"ProtocolBlockTypePaletteVersion\":1}, \"Palette\":[{\
|
||||
\"props\": {\
|
||||
\"foo\": \"bar\"\
|
||||
}, \
|
||||
\"name\": \"b\", \
|
||||
\"id\": \"0\"\
|
||||
}]}";
|
||||
|
||||
palette.clear();
|
||||
TEST_TRUE(palette.loadFromString(example));
|
||||
TEST_EQUAL(palette.index("b", BlockState({{"foo", "bar"}})), 0);
|
||||
TEST_EQUAL(palette.index("b", BlockState({{"foo", "baz"}})), ProtocolBlockTypePalette::NOT_FOUND);
|
||||
TEST_EQUAL(palette.index("a", BlockState({{"foo", "bar"}})), ProtocolBlockTypePalette::NOT_FOUND);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static void TestErrors(void)
|
||||
{
|
||||
LOG("Test TestErrors");
|
||||
ProtocolBlockTypePalette palette;
|
||||
TEST_FALSE(palette.loadFromString(""));
|
||||
|
||||
palette.clear();
|
||||
TEST_FALSE(palette.loadFromString("[]"));
|
||||
|
||||
palette.clear();
|
||||
TEST_FALSE(palette.loadFromString("a = {}"));
|
||||
|
||||
palette.clear();
|
||||
TEST_FALSE(palette.loadFromString("{x = 1}")); // Lua style
|
||||
|
||||
palette.clear();
|
||||
TEST_FALSE(palette.loadFromString("$#^%&"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static void TestComplex1(void)
|
||||
{
|
||||
LOG("Test TestComplex1");
|
||||
ProtocolBlockTypePalette palette;
|
||||
auto str = "{\"Metadata\":{\"ProtocolBlockTypePaletteVersion\":1}, \"Palette\":[{\
|
||||
\"props\": {\
|
||||
\"foo\": \"bar\", \
|
||||
\"moo\": \"baz\"\
|
||||
}, \
|
||||
\"id\": \"0\", \
|
||||
\"name\": \"b\"\
|
||||
}, {\
|
||||
\"props\": {\
|
||||
\"foo\": \"baz\", \
|
||||
\"moo\": \"bar\"\
|
||||
}, \
|
||||
\"id\": \"1\", \
|
||||
\"name\": \"b\"\
|
||||
}, {\
|
||||
\"props\": {\
|
||||
\"foo\": \"baz\", \
|
||||
\"moo\": \"bar\"\
|
||||
}, \
|
||||
\"id\": \"1001\", \
|
||||
\"name\": \"b\"\
|
||||
}]}";
|
||||
TEST_TRUE(palette.loadFromString(str)); // This should print info message about duplicate ID
|
||||
TEST_EQUAL(palette.index("b", BlockState({{"foo","bar"}})), ProtocolBlockTypePalette::NOT_FOUND);
|
||||
TEST_EQUAL(palette.index("b", BlockState({{"foo","bar"}, {"moo","baz"}})), 0);
|
||||
TEST_EQUAL(palette.index("b", BlockState({{"foo","baz"}, {"moo","bar"}})), 1);
|
||||
TEST_EQUAL(palette.index("c", BlockState({{"foo","baz"}, {"moo","bar"}})), ProtocolBlockTypePalette::NOT_FOUND);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static void TestComplex2(void)
|
||||
{
|
||||
LOG("Test TestComplex2");
|
||||
ProtocolBlockTypePalette palette;
|
||||
auto str = "{\"Metadata\":{\"ProtocolBlockTypePaletteVersion\":1}, \"Palette\":[{\
|
||||
\"id\": \"0\", \
|
||||
\"name\": \"a\"\
|
||||
}, {\
|
||||
\"id\": \"1\", \
|
||||
\"name\": \"b\"\
|
||||
}]}";
|
||||
TEST_TRUE(palette.loadFromString(str));
|
||||
TEST_EQUAL(palette.index("a", BlockState()), 0);
|
||||
TEST_EQUAL(palette.index("b", BlockState({})), 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static void TestFile(void)
|
||||
{
|
||||
LOG("Test TestFile");
|
||||
std::ifstream f("base.btp.json");
|
||||
ProtocolBlockTypePalette palette;
|
||||
TEST_TRUE(palette.loadFromStream(f));
|
||||
|
||||
// This is a bit problematic - the only permanently fixed block Id is air...
|
||||
TEST_EQUAL(palette.index("minecraft:air", BlockState()), 0);
|
||||
TEST_NOTEQUAL(palette.index("minecraft:stone", BlockState()), ProtocolBlockTypePalette::NOT_FOUND);
|
||||
TEST_NOTEQUAL(palette.index("minecraft:dirt", BlockState()), ProtocolBlockTypePalette::NOT_FOUND);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static void TestFile2(void)
|
||||
{
|
||||
LOG("Test TestFile2");
|
||||
std::ifstream f("test.btp.json");
|
||||
ProtocolBlockTypePalette palette;
|
||||
TEST_TRUE(palette.loadFromStream(f));
|
||||
|
||||
TEST_EQUAL(palette.index("minecraft:air", BlockState({})), 0);
|
||||
TEST_EQUAL(palette.index("minecraft:stone", BlockState()), 1);
|
||||
TEST_EQUAL(palette.index("minecraft:stone", BlockState()), 1);
|
||||
TEST_EQUAL(palette.index(
|
||||
"minecraft:dark_oak_leaves",
|
||||
BlockState({{"persistent", "false"}, {"distance", "6"}})
|
||||
), 225);
|
||||
TEST_EQUAL(palette.index(
|
||||
"minecraft:dark_oak_leaves",
|
||||
BlockState({{"persistent", "false"}})
|
||||
), ProtocolBlockTypePalette::NOT_FOUND);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
IMPLEMENT_TEST_MAIN("ProtocolBlockTypePaletteTest",
|
||||
TestSuccess();
|
||||
TestErrors();
|
||||
TestComplex1();
|
||||
TestComplex2();
|
||||
TestFile();
|
||||
TestFile2();
|
||||
)
|
146
tests/ProtocolBlockTypePalette/test.btp.json
Normal file
146
tests/ProtocolBlockTypePalette/test.btp.json
Normal file
@ -0,0 +1,146 @@
|
||||
{
|
||||
"Metadata": {
|
||||
"ProtocolBlockTypePaletteVersion": 1
|
||||
},
|
||||
"Palette": [{
|
||||
"id": 0,
|
||||
"name": "minecraft:air"
|
||||
}, {
|
||||
"id": 1,
|
||||
"name": "minecraft:stone"
|
||||
}, {
|
||||
"id": 221,
|
||||
"name": "minecraft:dark_oak_leaves",
|
||||
"props": {
|
||||
"persistent": "false",
|
||||
"distance": "4"
|
||||
}
|
||||
}, {
|
||||
"id": 222,
|
||||
"name": "minecraft:dark_oak_leaves",
|
||||
"props": {
|
||||
"persistent": "true",
|
||||
"distance": "5"
|
||||
}
|
||||
}, {
|
||||
"id": 223,
|
||||
"name": "minecraft:dark_oak_leaves",
|
||||
"props": {
|
||||
"persistent": "false",
|
||||
"distance": "5"
|
||||
}
|
||||
}, {
|
||||
"id": 224,
|
||||
"name": "minecraft:dark_oak_leaves",
|
||||
"props": {
|
||||
"persistent": "true",
|
||||
"distance": "6"
|
||||
}
|
||||
}, {
|
||||
"id": 225,
|
||||
"name": "minecraft:dark_oak_leaves",
|
||||
"props": {
|
||||
"persistent": "false",
|
||||
"distance": "6"
|
||||
}
|
||||
}, {
|
||||
"id": 226,
|
||||
"name": "minecraft:dark_oak_leaves",
|
||||
"props": {
|
||||
"persistent": "true",
|
||||
"distance": "7"
|
||||
}
|
||||
}, {
|
||||
"id": 227,
|
||||
"name": "minecraft:dark_oak_leaves",
|
||||
"props": {
|
||||
"persistent": "false",
|
||||
"distance": "7"
|
||||
}
|
||||
}, {
|
||||
"id": 9988,
|
||||
"name": "minecraft:powered_rail",
|
||||
"props": {
|
||||
"powered": "true",
|
||||
"shape": "north_south"
|
||||
}
|
||||
}, {
|
||||
"id": 9989,
|
||||
"name": "minecraft:powered_rail",
|
||||
"props": {
|
||||
"powered": "true",
|
||||
"shape": "east_west"
|
||||
}
|
||||
}, {
|
||||
"id": 9990,
|
||||
"name": "minecraft:powered_rail",
|
||||
"props": {
|
||||
"powered": "true",
|
||||
"shape": "ascending_east"
|
||||
}
|
||||
}, {
|
||||
"id": 9991,
|
||||
"name": "minecraft:powered_rail",
|
||||
"props": {
|
||||
"powered": "true",
|
||||
"shape": "ascending_west"
|
||||
}
|
||||
}, {
|
||||
"id": 9992,
|
||||
"name": "minecraft:powered_rail",
|
||||
"props": {
|
||||
"powered": "true",
|
||||
"shape": "ascending_north"
|
||||
}
|
||||
}, {
|
||||
"id": 9993,
|
||||
"name": "minecraft:powered_rail",
|
||||
"props": {
|
||||
"powered": "true",
|
||||
"shape": "ascending_south"
|
||||
}
|
||||
}, {
|
||||
"id": 9994,
|
||||
"name": "minecraft:powered_rail",
|
||||
"props": {
|
||||
"powered": "false",
|
||||
"shape": "north_south"
|
||||
}
|
||||
}, {
|
||||
"id": 9995,
|
||||
"name": "minecraft:powered_rail",
|
||||
"props": {
|
||||
"powered": "false",
|
||||
"shape": "east_west"
|
||||
}
|
||||
}, {
|
||||
"id": 9996,
|
||||
"name": "minecraft:powered_rail",
|
||||
"props": {
|
||||
"powered": "false",
|
||||
"shape": "ascending_east"
|
||||
}
|
||||
}, {
|
||||
"id": 9997,
|
||||
"name": "minecraft:powered_rail",
|
||||
"props": {
|
||||
"powered": "false",
|
||||
"shape": "ascending_west"
|
||||
}
|
||||
}, {
|
||||
"id": 9998,
|
||||
"name": "minecraft:powered_rail",
|
||||
"props": {
|
||||
"powered": "false",
|
||||
"shape": "ascending_north"
|
||||
}
|
||||
}, {
|
||||
"id": 9999,
|
||||
"name": "minecraft:powered_rail",
|
||||
"props": {
|
||||
"powered": "false",
|
||||
"shape": "ascending_south"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue
Block a user