Skip to content

Commit

Permalink
Creates files for version compatibility functions
Browse files Browse the repository at this point in the history
  • Loading branch information
spoutn1k committed Nov 13, 2021
1 parent fe55715 commit e4440a7
Show file tree
Hide file tree
Showing 9 changed files with 306 additions and 229 deletions.
3 changes: 3 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ SET(SOURCES ${SOURCES}
section.cpp
settings.cpp
worldloader.cpp
chunk_format_versions/assert.cpp
chunk_format_versions/get_section.cpp
chunk_format_versions/section_format.cpp
VERSION)

ADD_LIBRARY(mcmap_core STATIC ${SOURCES})
Expand Down
59 changes: 4 additions & 55 deletions src/chunk.cpp
Original file line number Diff line number Diff line change
@@ -1,74 +1,23 @@
#include "./chunk.h"
#include "./chunk_format_versions/assert.hpp"
#include "./chunk_format_versions/get_section.hpp"
#include <compat.hpp>
#include <functional>

namespace mcmap {

namespace versions {
namespace assert_versions {
#ifdef SNAPSHOT_SUPPORT
bool v2844(const nbt::NBT &chunk) {
// Snapshot 21w43a
return chunk.contains("sections") // No sections mean no blocks
&& chunk.contains("Status") // Ensure the status is `full`
&& chunk["Status"].get<nbt::NBT::tag_string_t>() == "full";
}

bool v2840(const nbt::NBT &chunk) {
// Snapshot 21w42a
return chunk.contains("Level") && // Level data is required
chunk["Level"].contains("Sections") // No sections mean no blocks
&& chunk["Level"].contains("Status") // Ensure the status is `full`
&& chunk["Level"]["Status"].get<nbt::NBT::tag_string_t>() == "full";
}
#endif

bool v1976(const nbt::NBT &chunk) {
// From 1.14 onwards
return chunk.contains("Level") // Level data is required
&& chunk["Level"].contains("Sections") // No sections mean no blocks
&& chunk["Level"].contains("Status") // Ensure the status is `full`
&& chunk["Level"]["Status"].get<nbt::NBT::tag_string_t>() == "full";
}

bool v1628(const nbt::NBT &chunk) {
// From 1.13 onwards
return chunk.contains("Level") // Level data is required
&& chunk["Level"].contains("Sections") // No sections mean no blocks
&& chunk["Level"].contains("Status") // Ensure the status is `full`
&& chunk["Level"]["Status"].get<nbt::NBT::tag_string_t>() ==
"postprocessed";
}

bool catchall(const nbt::NBT &chunk) {
logger::deep_debug("Unsupported DataVersion: {}\n",
chunk["DataVersion"].get<int>());
return false;
}
} // namespace assert_versions

namespace sections_versions {
nbt::NBT v2844(const nbt::NBT &chunk) { return chunk["sections"]; }
nbt::NBT v1628(const nbt::NBT &chunk) { return chunk["Level"]["Sections"]; }
nbt::NBT catchall(const nbt::NBT &chunk) {
logger::deep_debug("Unsupported DataVersion: {}\n",
chunk["DataVersion"].get<int>());
return nbt::NBT(std::vector<nbt::NBT>());
}
} // namespace sections_versions

std::map<int, std::function<bool(const nbt::NBT &)>> assert = {
{2844, assert_versions::v2844},
#ifdef SNAPSHOT_SUPPORT
{2844, assert_versions::v2844}, {2840, assert_versions::v2840},
{2840, assert_versions::v2840},
#endif
{1976, assert_versions::v1976}, {1628, assert_versions::v1628},
{0, assert_versions::catchall},
};

std::map<int, std::function<nbt::NBT(const nbt::NBT &)>> sections = {
#ifdef SNAPSHOT_SUPPORT
{2844, sections_versions::v2844},
#endif
{1628, sections_versions::v1628},
{0, sections_versions::catchall},
};
Expand Down
47 changes: 47 additions & 0 deletions src/chunk_format_versions/assert.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include "./assert.hpp"

namespace mcmap {
namespace versions {
namespace assert_versions {
bool v2844(const nbt::NBT &chunk) {
// Snapshot 21w43a
return chunk.contains("sections") // No sections mean no blocks
&& chunk.contains("Status") // Ensure the status is `full`
&& chunk["Status"].get<nbt::NBT::tag_string_t>() == "full";
}

#ifdef SNAPSHOT_SUPPORT
bool v2840(const nbt::NBT &chunk) {
// Snapshot 21w42a
return chunk.contains("Level") && // Level data is required
chunk["Level"].contains("Sections") // No sections mean no blocks
&& chunk["Level"].contains("Status") // Ensure the status is `full`
&& chunk["Level"]["Status"].get<nbt::NBT::tag_string_t>() == "full";
}
#endif

bool v1976(const nbt::NBT &chunk) {
// From 1.14 onwards
return chunk.contains("Level") // Level data is required
&& chunk["Level"].contains("Sections") // No sections mean no blocks
&& chunk["Level"].contains("Status") // Ensure the status is `full`
&& chunk["Level"]["Status"].get<nbt::NBT::tag_string_t>() == "full";
}

bool v1628(const nbt::NBT &chunk) {
// From 1.13 onwards
return chunk.contains("Level") // Level data is required
&& chunk["Level"].contains("Sections") // No sections mean no blocks
&& chunk["Level"].contains("Status") // Ensure the status is `full`
&& chunk["Level"]["Status"].get<nbt::NBT::tag_string_t>() ==
"postprocessed";
}

bool catchall(const nbt::NBT &chunk) {
logger::deep_debug("Unsupported DataVersion: {}\n",
chunk["DataVersion"].get<int>());
return false;
}
} // namespace assert_versions
} // namespace versions
} // namespace mcmap
19 changes: 19 additions & 0 deletions src/chunk_format_versions/assert.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#include <nbt/nbt.hpp>

namespace mcmap {
namespace versions {
namespace assert_versions {
bool v2844(const nbt::NBT &chunk);

#ifdef SNAPSHOT_SUPPORT
bool v2840(const nbt::NBT &chunk);
#endif

bool v1976(const nbt::NBT &chunk);

bool v1628(const nbt::NBT &chunk);

bool catchall(const nbt::NBT &chunk);
} // namespace assert_versions
} // namespace versions
} // namespace mcmap
15 changes: 15 additions & 0 deletions src/chunk_format_versions/get_section.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include "get_section.hpp"

namespace mcmap {
namespace versions {
namespace sections_versions {
nbt::NBT v2844(const nbt::NBT &chunk) { return chunk["sections"]; }
nbt::NBT v1628(const nbt::NBT &chunk) { return chunk["Level"]["Sections"]; }
nbt::NBT catchall(const nbt::NBT &chunk) {
logger::deep_debug("Unsupported DataVersion: {}\n",
chunk["DataVersion"].get<int>());
return nbt::NBT(std::vector<nbt::NBT>());
}
} // namespace sections_versions
} // namespace versions
} // namespace mcmap
11 changes: 11 additions & 0 deletions src/chunk_format_versions/get_section.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include <nbt/nbt.hpp>

namespace mcmap {
namespace versions {
namespace sections_versions {
nbt::NBT v2844(const nbt::NBT &);
nbt::NBT v1628(const nbt::NBT &);
nbt::NBT catchall(const nbt::NBT &);
} // namespace sections_versions
} // namespace versions
} // namespace mcmap
176 changes: 176 additions & 0 deletions src/chunk_format_versions/section_format.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
#include "section_format.hpp"

namespace mcmap {
namespace versions {
namespace block_states_versions {
void post116(const uint8_t index_length,
const std::vector<int64_t> *blockStates,
Section::block_array &buffer) {
// NEW in 1.16, longs are padded by 0s when a block cannot fit, so no more
// overflow to deal with !

for (uint16_t index = 0; index < 4096; index++) {
// Determine how many indexes each long holds
const uint8_t blocksPerLong = 64 / index_length;

// Calculate where in the long array is the long containing the right index.
const uint16_t longIndex = index / blocksPerLong;

// Once we located a long, we have to know where in the 64 bits
// the relevant block is located.
const uint8_t padding = (index - longIndex * blocksPerLong) * index_length;

// Bring the data to the first bits of the long, then extract it by bitwise
// comparison
const uint16_t blockIndex = ((*blockStates)[longIndex] >> padding) &
((uint64_t(1) << index_length) - 1);

buffer[index] = blockIndex;
}
}

void pre116(const uint8_t index_length, const std::vector<int64_t> *blockStates,
Section::block_array &buffer) {
// The `BlockStates` array contains data on the section's blocks. You have to
// extract it by understanfing its structure.
//
// Although it is a array of long values, one must see it as an array of block
// indexes, whose element size depends on the size of the Palette. This
// routine locates the necessary long, extracts the block with bit
// comparisons.
//
// The length of a block index has to be coded on the minimal possible size,
// which is the logarithm in base2 of the size of the palette, or 4 if the
// logarithm is smaller.

for (uint16_t index = 0; index < 4096; index++) {

// We skip the `position` first blocks, of length `size`, then divide by 64
// to get the number of longs to skip from the array
const uint16_t skip_longs = (index * index_length) >> 6;

// Once we located the data in a long, we have to know where in the 64 bits
// it is located. This is the remaining of the previous operation
const int8_t padding = (index * index_length) & 63;

// Sometimes the data of an index does not fit entirely into a long, so we
// check if there is overflow
const int8_t overflow =
(padding + index_length > 64 ? padding + index_length - 64 : 0);

// This complicated expression extracts the necessary bits from the current
// long.
//
// Lets say we need the following bits in a long (not to scale):
// 10011100111001110011100
// ^^^^^
// We do this by shifting (>>) the data by padding, to get the relevant bits
// on the end of the long:
// ???????????????10011100
// ^^^^^
// We then apply a mask to get only the relevant bits:
// ???????????????10011100
// 00000000000000000011111 &
// 00000000000000000011100 <- result
//
// The mask is made at the size of the data, using the formula (1 << n) - 1,
// the resulting bitset is of the following shape: 0...01...1 with n 1s.
//
// If there is an overflow, the mask size is reduced, as not to catch noise
// from the padding (ie the interrogation points earlier) that appear on
// ARM32.
uint16_t lower_data = ((*blockStates)[skip_longs] >> padding) &
((uint64_t(1) << (index_length - overflow)) - 1);

if (overflow > 0) {
// The exact same process is used to catch the overflow from the next long
const uint16_t upper_data =
((*blockStates)[skip_longs + 1]) & ((uint64_t(1) << overflow) - 1);
// We then associate both values to create the final value
lower_data = lower_data | (upper_data << (index_length - overflow));
}

// lower_data now contains the index in the palette
buffer[index] = lower_data;
}
}
} // namespace block_states_versions

namespace init_versions {
void v1628(Section *target, const nbt::NBT &raw_section) {
if (raw_section.contains("BlockStates") && raw_section.contains("Palette")) {
target->palette =
*raw_section["Palette"].get<const std::vector<nbt::NBT> *>();
const nbt::NBT::tag_long_array_t *blockStates =
raw_section["BlockStates"].get<const nbt::NBT::tag_long_array_t *>();

// Remove the air that is default-constructed
target->colors.clear();
// Anticipate the color input from the palette's size
target->colors.reserve(target->palette.size());

// The length in bits of a block is the log2 of the palette's size or 4,
// whichever is greatest. Ranges from 4 to 12.
const uint8_t blockBitLength =
std::max(uint8_t(ceil(log2(target->palette.size()))), uint8_t(4));

// Parse the blockstates for block info
block_states_versions::pre116(blockBitLength, blockStates, target->blocks);
}
}

void v2534(Section *target, const nbt::NBT &raw_section) {
if (raw_section.contains("BlockStates") && raw_section.contains("Palette")) {
target->palette =
*raw_section["Palette"].get<const std::vector<nbt::NBT> *>();
const nbt::NBT::tag_long_array_t *blockStates =
raw_section["BlockStates"].get<const nbt::NBT::tag_long_array_t *>();

// Remove the air that is default-constructed
target->colors.clear();
// Anticipate the color input from the palette's size
target->colors.reserve(target->palette.size());

// The length in bits of a block is the log2 of the palette's size or 4,
// whichever is greatest. Ranges from 4 to 12.
const uint8_t blockBitLength =
std::max(uint8_t(ceil(log2(target->palette.size()))), uint8_t(4));

// Parse the blockstates for block info
block_states_versions::post116(blockBitLength, blockStates, target->blocks);
}
}

#ifdef SNAPSHOT_SUPPORT
void v2840(Section *target, const nbt::NBT &raw_section) {
if (raw_section.contains("block_states") &&
raw_section["block_states"].contains("data") &&
raw_section["block_states"].contains("palette")) {
target->palette = *raw_section["block_states"]["palette"]
.get<const std::vector<nbt::NBT> *>();
const nbt::NBT::tag_long_array_t *blockStates =
raw_section["block_states"]["data"]
.get<const nbt::NBT::tag_long_array_t *>();

// Remove the air that is default-constructed
target->colors.clear();
// Anticipate the color input from the palette's size
target->colors.reserve(target->palette.size());

// The length in bits of a block is the log2 of the palette's size or 4,
// whichever is greatest. Ranges from 4 to 12.
const uint8_t blockBitLength =
std::max(uint8_t(ceil(log2(target->palette.size()))), uint8_t(4));

// Parse the blockstates for block info
block_states_versions::post116(blockBitLength, blockStates, target->blocks);
}
}
#endif

void catchall(Section *, const nbt::NBT &) {
logger::deep_debug("Unsupported DataVersion\n");
}
} // namespace init_versions
} // namespace versions
} // namespace mcmap
26 changes: 26 additions & 0 deletions src/chunk_format_versions/section_format.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#include "../section.h"
#include <stdint.h>

namespace mcmap {
namespace versions {
namespace block_states_versions {
void post116(const uint8_t, const std::vector<int64_t> *,
Section::block_array &);

void pre116(const uint8_t, const std::vector<int64_t> *,
Section::block_array &);
} // namespace block_states_versions

namespace init_versions {
void v1628(Section *, const nbt::NBT &);

void v2534(Section *, const nbt::NBT &);

#ifdef SNAPSHOT_SUPPORT
void v2840(Section *, const nbt::NBT &);
#endif

void catchall(Section *, const nbt::NBT &);
} // namespace init_versions
} // namespace versions
} // namespace mcmap
Loading

0 comments on commit e4440a7

Please sign in to comment.