From 390967a155afda80efe687c4b327cab92021c037 Mon Sep 17 00:00:00 2001 From: Evildeeds Date: Mon, 21 Dec 2020 00:33:42 +0100 Subject: [PATCH 01/16] Merged NBT array types into a single template --- src/nbt/types.hpp | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/src/nbt/types.hpp b/src/nbt/types.hpp index 12e85fcd..4e3cad6c 100644 --- a/src/nbt/types.hpp +++ b/src/nbt/types.hpp @@ -12,31 +12,19 @@ namespace nbt { typedef std::string String; typedef float Float; typedef double Double; - - struct ByteArray { - Int length; - Byte *values; - ~ByteArray() { - delete [] values; - } - }; - struct IntArray { + template + struct Array { Int length; - Int *values; - ~IntArray() { + T *values; + ~Array() { delete [] values; } }; - - struct LongArray { - Int length; - Long *values; - ~LongArray() { - delete [] values; - } - }; + typedef Array ByteArray; + typedef Array IntArray; + typedef Array LongArray; struct stack_entry { Byte type; From 9f118be5936b95ab2146b9a072b0b1e842ad141b Mon Sep 17 00:00:00 2001 From: Evildeeds Date: Tue, 22 Dec 2020 23:06:46 +0100 Subject: [PATCH 02/16] Refactorization of block lookup Removed dead code --- src/engine/block_rotation.cpp | 32 +- src/engine/block_rotation.hpp | 8 +- src/engine/flat_base.hpp | 176 ++++------ src/engine/functions.cpp | 29 -- src/engine/functions.hpp | 68 ---- src/engine/isometric_base.hpp | 175 ++++------ src/generate_statistics.cpp | 43 +-- src/main.cpp | 1 - src/mc/blocks.cpp | 6 +- src/mc/blocks.hpp | 18 ++ src/mc/level.cpp | 584 +++++++++++++++++++++++++++++++++- src/mc/level.hpp | 4 + 12 files changed, 737 insertions(+), 407 deletions(-) diff --git a/src/engine/block_rotation.cpp b/src/engine/block_rotation.cpp index fcca68cd..18485fed 100644 --- a/src/engine/block_rotation.cpp +++ b/src/engine/block_rotation.cpp @@ -1,18 +1,10 @@ #include "engine/block_rotation.hpp" -block_rotation::block_rotation( - int rotation, - boost::shared_ptr array) - : x(0), z(0), rotation(rotation), array(array) +block_rotation::block_rotation(int rotation) + : rotation(rotation) { } -void block_rotation::set_xz(int x, int z) { - transform_xz(x, z); - this->x = x; - this->z = z; -} - void block_rotation::transform_xz(int& x, int& z) { int t = x; @@ -31,23 +23,3 @@ void block_rotation::transform_xz(int& x, int& z) { break; }; } - -/** - */ -int block_rotation::get8(int y, int d) { - int p = ((y * 16 + z) * 16 + x); - if (!(p >= 0 && p < array->length)) return d; - return array->values[p] & 0xff; -} - -/** - * Data values are packed two by two and the position LSB decides which - * half-byte contains the requested block data value. - */ -int block_rotation::get4(int y, int d) { - int tmp = (y * 16 + z) * 16 + x; - int p = tmp >> 1; - int b = tmp & 0x1; - if (!(p >= 0 && p < array->length)) return d; - return (array->values[p] >> (b * 4)) & 0xf; -} diff --git a/src/engine/block_rotation.hpp b/src/engine/block_rotation.hpp index d1167c7c..5a4d28f7 100644 --- a/src/engine/block_rotation.hpp +++ b/src/engine/block_rotation.hpp @@ -10,17 +10,13 @@ class block_rotation { public: - block_rotation(int rotation, boost::shared_ptr array); + block_rotation(int rotation); - void set_xz(int x, int z); void transform_xz(int& x, int& z); - int get8(int y, int d=-1); - int get4(int y, int d=-1); private: - int x, z; int rotation; - boost::shared_ptr array; + }; #endif /* _ENGINE_BLOCK_ROTATION_HPP */ diff --git a/src/engine/flat_base.hpp b/src/engine/flat_base.hpp index aa0f19d2..f9327b6f 100644 --- a/src/engine/flat_base.hpp +++ b/src/engine/flat_base.hpp @@ -36,147 +36,83 @@ class flat_base : public engine_base { blocked[i] = false; } - // block type - BOOST_REVERSE_FOREACH(mc::Section_Compound Section, L->Sections) { - block_rotation br_blocks(s.rotation, Section.Blocks); - block_rotation br_data(s.rotation, Section.Data); - //block_rotation br_block_light(s.rotation, Section.BlockLight); - //block_rotation br_sky_light(s.rotation, Section.SkyLight); + block_rotation br_blocks(s.rotation); for (int y = 15; y >= 0; y--) { int abs_y = (Section.Y * 16) + y; for (int z = 0; z < mc::MapZ; z++) { for (int x = 0; x < mc::MapX; x++) { - unsigned int blocked_position = x * iw + z; + size_t blocked_position = x * iw + z; if (blocked[blocked_position]) { continue; } - br_blocks.set_xz(x, z); - br_data.set_xz(x, z); - //br_block_light.set_xz(x, z); - //br_sky_light.set_xz(x, z); - - // do incremental color fill until color is opaque - int block_type = br_blocks.get8(y); - int block_data = br_data.get4(y); + int projected_x = x; + int projected_z = z; + br_blocks.transform_xz(projected_x, projected_z); - mc::MaterialMode mode; - color top; - color side; - - boost::optional material = mc::get_material_legacy(block_type, block_data); - if (material) { - mc::MaterialT *m = material.get(); - if (!m->enabled) { + mc::BlockT block; + if (Section.get_block(block, projected_x, projected_z, y)) { + if (!block.material->enabled) { continue; } - mode = m->mode; - top = m->top; - side = m->side; - } else { - mode = mc::MaterialMode::Block; - top = mc::SharedDefaultColor; - side = mc::SharedDefaultColor; - } - blocked[blocked_position] = top.is_opaque(); - - /*int block_light = br_block_light.get4(y + 1); - int sky_light = br_sky_light.get4(y + 1, 15);*/ - - //apply_shading(s, block_light, sky_light, 0, abs_y, top); - - point p(x, abs_y, z); - - pos_t px; - pos_t py; - - flat_base::project_position(p, px, py); - - int log_rotation; - - switch(mode) { - case mc::MaterialMode::Block: - render_block(oper, block_type, px, py, top, side); - render_halfblock(oper, block_type, px, py, top, side); - break; - case mc::MaterialMode::HalfBlock: - render_halfblock(oper, block_type, px, py, top, side); - break; - case mc::MaterialMode::TorchBlock: - render_torchblock(oper, block_type, px, py, top, side); - break; - case mc::MaterialMode::LargeFlowerBlock: - // Check if the requested block is the top block - if(block_data & 0x08) { - // Small sanity check - if(y > 0 && br_blocks.get8(y-1) == block_type) { - // Minecraft currently doesn't set the lower bits to the - // corresponding type so we have to do this here. - block_data = br_data.get4(y-1) & 0x07; - top = mc::get_color_legacy(block_type, block_data); - side = mc::get_side_color_legacy(block_type, block_data); + blocked[blocked_position] = block.material->top.is_opaque(); + + point p(x, abs_y, z); + pos_t px = 0; + pos_t py = 0; + flat_base::project_position(p, px, py); + + // FIXME; this should not be used by any renderer - deprecated! + // There is no flat engine that cares about this. + int block_type = -1; + + switch(block.material->mode) { + case mc::MaterialMode::Block: + case mc::MaterialMode::LegacyLeaves: + render_block(oper, block_type, px, py, block.material->top, block.material->side); + render_halfblock(oper, block_type, px, py, block.material->top, block.material->side); + break; + case mc::MaterialMode::HalfBlock: + case mc::MaterialMode::LegacySlab: + render_halfblock(oper, block_type, px, py, block.material->top, block.material->side); + break; + case mc::MaterialMode::TorchBlock: + render_torchblock(oper, block_type, px, py, block.material->top, block.material->side); + break; + case mc::MaterialMode::LargeFlowerBlock: + if (block.properties.is_top) { + render_block(oper, block_type, px, py, block.material->top, block.material->side); + render_halfblock(oper, block_type, px, py, block.material->top, block.material->side); } else { - // Top block not placed on a correct bottom block. - // The expected LargeFlower multi block structure is invalid, skip it. - continue; + render_block(oper, block_type, px, py, block.material->side, block.material->side); + render_halfblock(oper, block_type, px, py, block.material->side, block.material->side); } - } else { - // Force the top of the lower block to also be the side color. - top = side; - } - render_block(oper, block_type, px, py, top, side); - render_halfblock(oper, block_type, px, py, top, side); - break; - case mc::MaterialMode::LogBlock: - // Log blocks are just a regular block that may differ in orientation. Top - // color is considered the inner material. Because some bits of metadata are - // used both for variant and rotation state, block type needs to be fetched again. - log_rotation = (block_data & 0x0C) >> 2; - switch(log_rotation) { - case 0: - // Up/down - top = mc::get_color_legacy(block_type, block_data & 0x3); - side = mc::get_side_color_legacy(block_type, block_data & 0x3); break; - case 1: - // East/west - case 2: - // North/south - // TODO: Actually implement render rotation, for now simply swap top and side. - side = mc::get_color_legacy(block_type, block_data & 0x3); - top = mc::get_side_color_legacy(block_type, block_data & 0x3); - break; - case 3: - // Only sides, thus no top color. - side = mc::get_side_color_legacy(block_type, block_data & 0x3); - top = side; + case mc::MaterialMode::LogBlock: + switch(block.properties.orientation) { + case mc::BlockOrientation::UpDown: + render_block(oper, block_type, px, py, block.material->top, block.material->side); + render_halfblock(oper, block_type, px, py, block.material->top, block.material->side); + break; + case mc::BlockOrientation::EastWest: + case mc::BlockOrientation::NorthSouth: + render_block(oper, block_type, px, py, block.material->side, block.material->top); + render_halfblock(oper, block_type, px, py, block.material->side, block.material->top); + break; + case mc::BlockOrientation::OnlySides: + render_block(oper, block_type, px, py, block.material->side, block.material->side); + render_halfblock(oper, block_type, px, py, block.material->side, block.material->side); + break; + case mc::BlockOrientation::Invalid: + break; + } break; } - render_block(oper, block_type, px, py, top, side); - render_halfblock(oper, block_type, px, py, top, side); - break; - case mc::MaterialMode::LegacySlab: - // Legacy slab is just a half block; but is sometimes a full block. - // The first legacy id is the full block version. - if (block_type == material.get()->legacy_ids[0]) { - render_block(oper, block_type, px, py, top, side); - } - render_halfblock(oper, block_type, px, py, top, side); - break; - case mc::MaterialMode::LegacyLeaves: - // Legacy leaves is just a regular block; however some bits of the metadata - // are used for other block states, only the two first bits are used for block - // type therefore we need to re-fetch the block type now. - top = mc::get_color_legacy(block_type, block_data & 0x3); - side = mc::get_side_color_legacy(block_type, block_data & 0x3); - render_block(oper, block_type, px, py, top, side); - render_halfblock(oper, block_type, px, py, top, side); - break; } } } diff --git a/src/engine/functions.cpp b/src/engine/functions.cpp index 7f0f18ad..0148e55b 100644 --- a/src/engine/functions.cpp +++ b/src/engine/functions.cpp @@ -1,30 +1 @@ #include "engine/functions.hpp" - -void apply_shading( - const engine_settings& s, - int block_light, - int sky_light, - int height_map, - int y, - color &c) -{ - if(s.night) { - c.darken(0x6 * (16 - block_light)); - } - else if (sky_light != -1 && y != s.top) { - c.darken(0x6 * (16 - std::max(sky_light, block_light))); - } - - //c.darken((mc::MapY - y)); - - // in heightmap mode, brightness = height - if (s.heightmap) { - c.b = y*2; - c.g = y*2; - c.r = y*2; - c.a = 0xff; - } - else if (s.striped_terrain && y % 2 == 0) { - c.darken(0xf); - } -} diff --git a/src/engine/functions.hpp b/src/engine/functions.hpp index abbf8380..000e4418 100644 --- a/src/engine/functions.hpp +++ b/src/engine/functions.hpp @@ -1,72 +1,4 @@ #ifndef _ENGINE_FUNCTIONS_HPP #define _ENGINE_FUNCTIONS_HPP -#include "engine/block_rotation.hpp" -#include "engine/engine_settings.hpp" -#include "image/color.hpp" -#include "mc/blocks.hpp" - -/** - * Shared functions between engines. - */ - -/** - * Apply shading to specified block and color. - **/ -void apply_shading( - const engine_settings& engine_s, - int bl, int sl, int hm, int y, color &c); - -inline bool is_open(mc::MaterialT*& m) { - if (!m) { - return false; - } - - return m->top.is_transparent(); -} - -inline bool is_open(int bt) { - if (bt == -1) { - return false; - } - - switch(bt) { - case mc::LegacyBlocks::Air: return true; - case mc::LegacyBlocks::Leaves: return true; - default: return false; - } -} - -// this functions is currently unused -inline bool cave_ignore_block(int y, int bt, block_rotation& b_r, bool &cave_initial) { - if (cave_initial) { - if (!is_open(bt)) { - cave_initial = false; - return true; - } - - return true; - } - - if (!is_open(bt) && is_open(b_r.get8(y + 1))) { - return false; - } - - return true; -} - -// this functions is currently unused -inline bool hell_ignore_block(int y, int bt, block_rotation& b_r, bool &hell_initial) { - if (hell_initial) { - if (is_open(bt)) { - hell_initial = false; - return false; - } - - return true; - } - - return false; -} - #endif /* _ENGINE_FUNCTIONS_HPP */ diff --git a/src/engine/isometric_base.hpp b/src/engine/isometric_base.hpp index c640530d..a32376b6 100644 --- a/src/engine/isometric_base.hpp +++ b/src/engine/isometric_base.hpp @@ -33,137 +33,82 @@ class isometric_base : public engine_base { oper->set_limits(image_width + 1, image_height); BOOST_FOREACH(mc::Section_Compound Section, L->Sections) { - block_rotation br_blocks(s.rotation, Section.Blocks); - block_rotation br_data(s.rotation, Section.Data); - //block_rotation br_block_light(s.rotation, Section.BlockLight); - //block_rotation br_sky_light(s.rotation, Section.SkyLight); + block_rotation br_blocks(s.rotation); for (int y = 0; y < 16; y++) { int abs_y = (16 * Section.Y) + y; for (int z = 0; z < mc::MapZ; z++) { for (int x = mc::MapX - 1; x >= 0; x--) { - br_blocks.set_xz(x, z); - br_data.set_xz(x, z); - //br_block_light.set_xz(x, z); - //br_sky_light.set_xz(x, z); + int projected_x = x; + int projected_z = z; + br_blocks.transform_xz(projected_x, projected_z); - int block_type = br_blocks.get8(y); - - point p(x, abs_y, z); - - pos_t px = 0; - pos_t py = 0; - - engine_base::project_position(p, px, py); - - int block_data = br_data.get4(y); - - mc::MaterialMode mode; - color top; - color side; - - boost::optional material = mc::get_material_legacy(block_type, block_data); - if (material) { - mc::MaterialT *m = material.get(); - if (!m->enabled) { + mc::BlockT block; + if (Section.get_block(block, projected_x, projected_z, y)) { + if (!block.material->enabled) { continue; } - mode = m->mode; - top = m->top; - side = m->side; - } else { - mode = mc::MaterialMode::Block; - top = mc::SharedDefaultColor; - side = mc::SharedDefaultColor; - } - int log_rotation; - - //int block_light = br_block_light.get4(y + 1); - //int sky_light = br_sky_light.get4(y + 1); - - //apply_shading(s, block_light, sky_light, 0, y, top); - //apply_shading(s, 0, 0, 0, y, side); - - switch(mode) { - case mc::MaterialMode::Block: - render_block(oper, block_type, px, py, top, side); - render_halfblock(oper, block_type, px, py, top, side); - break; - case mc::MaterialMode::HalfBlock: - render_halfblock(oper, block_type, px, py, top, side); - break; - case mc::MaterialMode::TorchBlock: - render_torchblock(oper, block_type, px, py, top, side); - break; - case mc::MaterialMode::LargeFlowerBlock: - // Check if the requested block is the top block - if(block_data & 0x08) { - // Small sanity check - if(y > 0 && br_blocks.get8(y-1) == block_type) { - // Minecraft currently doesn't set the lower bits to the - // corresponding type so we have to do this here. - block_data = br_data.get4(y-1) & 0x07; - top = mc::get_color_legacy(block_type, block_data); - side = mc::get_side_color_legacy(block_type, block_data); - } else { - // Top block not placed on a correct bottom block. - // The expected LargeFlower multi block structure is invalid, skip it. - continue; - } + point p(x, abs_y, z); + pos_t px = 0; + pos_t py = 0; + engine_base::project_position(p, px, py); + + // FIXME; this should not be used by any renderer - deprecated! + // The fatiso engine cares; there is a special handler for grass + // so try to countine passing this along for blocks with known + // leagacy id(s). + int block_type; + if (block.material->legacy_ids.size() > 0) { + block_type = block.material->legacy_ids[0]; } else { - // Force the top of the lower block to also be the side color. - top = side; + block_type = -1; } - render_block(oper, block_type, px, py, top, side); - render_halfblock(oper, block_type, px, py, top, side); - break; - case mc::MaterialMode::LogBlock: - // Log blocks are just a regular block that may differ in orientation. Top - // color is considered the inner material. Because some bits of metadata are - // used both for variant and rotation state, block type needs to be fetched again. - log_rotation = (block_data & 0x0C) >> 2; - switch(log_rotation) { - case 0: - // Up/down - top = mc::get_color_legacy(block_type, block_data & 0x3); - side = mc::get_side_color_legacy(block_type, block_data & 0x3); + + switch(block.material->mode) { + case mc::MaterialMode::Block: + case mc::MaterialMode::LegacyLeaves: + render_block(oper, block_type, px, py, block.material->top, block.material->side); + render_halfblock(oper, block_type, px, py, block.material->top, block.material->side); break; - case 1: - // East/west - case 2: - // North/south - // TODO: Actually implement render rotation, for now simply swap top and side. - side = mc::get_color_legacy(block_type, block_data & 0x3); - top = mc::get_side_color_legacy(block_type, block_data & 0x3); + case mc::MaterialMode::HalfBlock: + case mc::MaterialMode::LegacySlab: + render_halfblock(oper, block_type, px, py, block.material->top, block.material->side); break; - case 3: - // Only sides, thus no top color. - side = mc::get_side_color_legacy(block_type, block_data & 0x3); - top = side; + case mc::MaterialMode::TorchBlock: + render_torchblock(oper, block_type, px, py, block.material->top, block.material->side); + break; + case mc::MaterialMode::LargeFlowerBlock: + if (block.properties.is_top) { + render_block(oper, block_type, px, py, block.material->top, block.material->side); + render_halfblock(oper, block_type, px, py, block.material->top, block.material->side); + } else { + render_block(oper, block_type, px, py, block.material->side, block.material->side); + render_halfblock(oper, block_type, px, py, block.material->side, block.material->side); + } + break; + case mc::MaterialMode::LogBlock: + switch(block.properties.orientation) { + case mc::BlockOrientation::UpDown: + render_block(oper, block_type, px, py, block.material->top, block.material->side); + render_halfblock(oper, block_type, px, py, block.material->top, block.material->side); + break; + case mc::BlockOrientation::EastWest: + case mc::BlockOrientation::NorthSouth: + // TODO: Actually implement render rotation, for now simply swap top and side. + render_block(oper, block_type, px, py, block.material->side, block.material->top); + render_halfblock(oper, block_type, px, py, block.material->side, block.material->top); + break; + case mc::BlockOrientation::OnlySides: + render_block(oper, block_type, px, py, block.material->side, block.material->side); + render_halfblock(oper, block_type, px, py, block.material->side, block.material->side); + break; + case mc::BlockOrientation::Invalid: + break; + } break; } - render_block(oper, block_type, px, py, top, side); - render_halfblock(oper, block_type, px, py, top, side); - break; - case mc::MaterialMode::LegacySlab: - // Legacy slab is just a half block; but is sometimes a full block. - // The first legacy id is the full block version. - if (block_type == material.get()->legacy_ids[0]) { - render_block(oper, block_type, px, py, top, side); - } - render_halfblock(oper, block_type, px, py, top, side); - break; - case mc::MaterialMode::LegacyLeaves: - // Legacy leaves is just a regular block; however some bits of the metadata - // are used for other block states, only the two first bits are used for block - // type therefore we need to re-fetch the block type now. - top = mc::get_color_legacy(block_type, block_data & 0x3); - side = mc::get_side_color_legacy(block_type, block_data & 0x3); - render_block(oper, block_type, px, py, top, side); - render_halfblock(oper, block_type, px, py, top, side); - break; } } } diff --git a/src/generate_statistics.cpp b/src/generate_statistics.cpp index f4eaf453..a58bc0a1 100644 --- a/src/generate_statistics.cpp +++ b/src/generate_statistics.cpp @@ -9,7 +9,6 @@ #include "players.hpp" #include "main_utils.hpp" -#include "engine/block_rotation.hpp" #include @@ -50,9 +49,9 @@ bool generate_statistics( AltitudeGraph *_stat = new AltitudeGraph(s); mc::MaterialT *materials = mc::MaterialTable.data(); - long statistics[mc::MaterialTable.size()]; + boost::shared_ptr statistics(new long[mc::MaterialTable.size()]); - for (int i = 0; i < mc::MaterialTable.size(); i++) { + for (size_t i = 0; i < mc::MaterialTable.size(); i++) { statistics[i] = 0; } @@ -137,32 +136,22 @@ bool generate_statistics( boost::shared_ptr L = level_data.get_level(); BOOST_FOREACH(mc::Section_Compound Section, L->Sections) { - block_rotation br_blocks(0, Section.Blocks); - block_rotation br_data(0, Section.Data); - for (int y = 15; y >= 0; y--) { - int abs_y = (Section.Y * 16) + y; - - for (int z = 0; z < mc::MapZ; z++) { - for (int x = 0; x < mc::MapX; x++) { - br_blocks.set_xz(x, z); - br_data.set_xz(x, z); - - int block_type = br_blocks.get8(y); - int block_data = br_data.get4(y); - boost::optional material = mc::get_material_legacy(block_type, block_data); - - if (material) { - if (material.get()->enabled) { - size_t index = material.get() - materials; - statistics[index] += 1; - } - if(graph_block && material.get() == graph_block.get()) { - _stat->registerBloc(material.get(), abs_y); - } - } + int abs_y = (Section.Y * 16) + y; + for (int z = 0; z < mc::MapZ; z++) { + for (int x = 0; x < mc::MapX; x++) { + mc::BlockT block; + if (Section.get_block(block, x, z, y)) { + if (block.material->enabled) { + size_t index = block.material - materials; + statistics[index] += 1; + } + if(graph_block && block.material == graph_block.get()) { + _stat->registerBloc(block.material, abs_y); } + } } + } } } @@ -211,7 +200,7 @@ bool generate_statistics( } stats << "[BLOCKS]" << endl; - for (int i = 0; i < mc::MaterialTable.size(); i++) { + for (size_t i = 0; i < mc::MaterialTable.size(); i++) { if (statistics[i] > 0) { std::string full_name = mc::MaterialTable[i].mc_namespace + ":" + mc::MaterialTable[i].name; stats << std::left << setw(32) << full_name << " " << statistics[i] << endl; diff --git a/src/main.cpp b/src/main.cpp index 6dccc7ea..32f2ef15 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -53,7 +53,6 @@ inline void cout_end() { cout << hex << std::setw(2) << setfill('0') << static_cast(END_BYTE) << flush; } - int do_help(ostream& out) { out << "This program was made possible because of the work and inspiration by ZomBuster and Firemark" << endl; out << "" << endl; diff --git a/src/mc/blocks.cpp b/src/mc/blocks.cpp index 36464613..21c6a401 100644 --- a/src/mc/blocks.cpp +++ b/src/mc/blocks.cpp @@ -1,10 +1,10 @@ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. -#include -#include -#include #include + +#include + #include "blocks.hpp" namespace mc { diff --git a/src/mc/blocks.hpp b/src/mc/blocks.hpp index 7161671f..33ffd742 100644 --- a/src/mc/blocks.hpp +++ b/src/mc/blocks.hpp @@ -117,6 +117,24 @@ namespace mc { extern std::map MaterialMap; extern std::vector> MaterialPaletteLegacy; + enum BlockOrientation { + Invalid, + UpDown, + EastWest, + NorthSouth, + OnlySides + }; + + typedef struct { + BlockOrientation orientation = BlockOrientation::Invalid; + bool is_top = false; + } BlockPropertiesT; + + typedef struct { + MaterialT *material; + BlockPropertiesT properties; + } BlockT; + inline boost::optional get_material_legacy(int material, int data) { // Legacy lookup needs to aggressivly fallback to the first metadata // variant when possible, this is because some blocks have either only diff --git a/src/mc/level.cpp b/src/mc/level.cpp index 7bff488e..23d2974b 100644 --- a/src/mc/level.cpp +++ b/src/mc/level.cpp @@ -9,7 +9,7 @@ #include #include -/* +/* Legacy compound structure; For versions < 1.13 Compound() { Compound(Level) { List(Entities, TAG_Byte, 0): [ ] @@ -22,13 +22,473 @@ Compound() { IntArray(HeightMap): (256 ints) List(Sections, TAG_Compound, 1): [ Compound() { + ByteArray(Data): (2048 bytes) ByteArray(SkyLight): (2048 bytes) ByteArray(BlockLight): (2048 bytes) Byte(Y): 0x0 - ByteArray(BlockStates): (4096 bytes) + ByteArray(Blocks): (4096 bytes) } ] } + // Beta 1.3 (19132) added a version tag. + Int(DataVersion): 1343 +} +*/ + +/* Versions >= 1.13 +Compound() { + Compound(Level) { + String(Status): full + Int(zPos): -32 + Long(LastUpdate): 7452163 + IntArray(Biomes): (1024 ints) + Long(InhabitedTime): 1112 + Int(xPos): -32 + Compound(Heightmaps) { + LongArray(OCEAN_FLOOR): (37 longs) + LongArray(MOTION_BLOCKING_NO_LEAVES): (37 longs) + LongArray(MOTION_BLOCKING): (37 longs) + LongArray(WORLD_SURFACE): (37 longs) + } + List(TileEntities, TAG_End, 0): [ ] + List(Entities, TAG_Compound, 1): [ ] + Byte(isLightOn): 0x1 + List(TileTicks, TAG_End, 0): [ ] + List(Sections, TAG_Compound, 9): [ + Compound() { + Byte(Y): 0xffffffff + } + Compound() { + LongArray(BlockStates): (342 longs) + List(Palette, TAG_Compound, 22): [ + Compound() { + String(Name): minecraft:air + } + Compound() { + String(Name): minecraft:bedrock + } + Compound() { + String(Name): minecraft:stone + } + Compound() { + String(Name): minecraft:gravel + } + Compound() { + String(Name): minecraft:coal_ore + } + Compound() { + String(Name): minecraft:granite + } + Compound() { + Compound(Properties) { + String(lit): false + } + String(Name): minecraft:redstone_ore + } + Compound() { + Compound(Properties) { + String(level): 0 + } + String(Name): minecraft:lava + } + Compound() { + String(Name): minecraft:iron_ore + } + Compound() { + String(Name): minecraft:andesite + } + Compound() { + String(Name): minecraft:emerald_ore + } + Compound() { + String(Name): minecraft:cave_air + } + Compound() { + Compound(Properties) { + String(shape): north_south + } + String(Name): minecraft:rail + } + Compound() { + Compound(Properties) { + String(east): false + String(waterlogged): false + String(south): false + String(north): false + String(west): true + } + String(Name): minecraft:oak_fence + } + Compound() { + Compound(Properties) { + String(east): true + String(waterlogged): false + String(south): false + String(north): false + String(west): false + } + String(Name): minecraft:oak_fence + } + Compound() { + String(Name): minecraft:diorite + } + Compound() { + String(Name): minecraft:diamond_ore + } + Compound() { + String(Name): minecraft:oak_planks + } + Compound() { + String(Name): minecraft:cobweb + } + Compound() { + String(Name): minecraft:dirt + } + Compound() { + String(Name): minecraft:lapis_ore + } + Compound() { + String(Name): minecraft:infested_stone + } + ] + Byte(Y): 0x0 + ByteArray(BlockLight): (2048 bytes) + } + Compound() { + LongArray(BlockStates): (342 longs) + List(Palette, TAG_Compound, 21): [ + Compound() { + String(Name): minecraft:air + } + Compound() { + String(Name): minecraft:stone + } + Compound() { + String(Name): minecraft:andesite + } + Compound() { + String(Name): minecraft:diorite + } + Compound() { + String(Name): minecraft:coal_ore + } + Compound() { + String(Name): minecraft:dirt + } + Compound() { + String(Name): minecraft:infested_stone + } + Compound() { + String(Name): minecraft:emerald_ore + } + Compound() { + String(Name): minecraft:gold_ore + } + Compound() { + String(Name): minecraft:iron_ore + } + Compound() { + String(Name): minecraft:granite + } + Compound() { + String(Name): minecraft:gravel + } + Compound() { + Compound(Properties) { + String(level): 0 + } + String(Name): minecraft:water + } + Compound() { + String(Name): minecraft:cave_air + } + Compound() { + String(Name): minecraft:oak_planks + } + Compound() { + Compound(Properties) { + String(east): false + String(waterlogged): false + String(south): false + String(north): true + String(west): false + } + String(Name): minecraft:oak_fence + } + Compound() { + Compound(Properties) { + String(east): false + String(waterlogged): false + String(south): true + String(north): false + String(west): false + } + String(Name): minecraft:oak_fence + } + Compound() { + Compound(Properties) { + String(shape): north_south + } + String(Name): minecraft:rail + } + Compound() { + Compound(Properties) { + String(east): false + String(waterlogged): false + String(south): false + String(north): false + String(west): true + } + String(Name): minecraft:oak_fence + } + Compound() { + Compound(Properties) { + String(east): false + String(waterlogged): false + String(south): false + String(north): false + String(west): false + } + String(Name): minecraft:oak_fence + } + Compound() { + String(Name): minecraft:cobweb + } + ] + Byte(Y): 0x1 + ByteArray(BlockLight): (2048 bytes) + } + Compound() { + LongArray(BlockStates): (342 longs) + List(Palette, TAG_Compound, 18): [ + Compound() { + String(Name): minecraft:air + } + Compound() { + String(Name): minecraft:cave_air + } + Compound() { + String(Name): minecraft:stone + } + Compound() { + String(Name): minecraft:oak_planks + } + Compound() { + Compound(Properties) { + String(east): false + String(waterlogged): false + String(south): false + String(north): false + String(west): false + } + String(Name): minecraft:oak_fence + } + Compound() { + Compound(Properties) { + String(east): false + String(waterlogged): false + String(south): false + String(north): true + String(west): false + } + String(Name): minecraft:oak_fence + } + Compound() { + Compound(Properties) { + String(east): false + String(waterlogged): false + String(south): true + String(north): false + String(west): false + } + String(Name): minecraft:oak_fence + } + Compound() { + String(Name): minecraft:granite + } + Compound() { + Compound(Properties) { + String(east): false + String(waterlogged): false + String(south): false + String(north): false + String(west): true + } + String(Name): minecraft:oak_fence + } + Compound() { + Compound(Properties) { + String(east): true + String(waterlogged): false + String(south): false + String(north): false + String(west): false + } + String(Name): minecraft:oak_fence + } + Compound() { + String(Name): minecraft:iron_ore + } + Compound() { + String(Name): minecraft:dirt + } + Compound() { + String(Name): minecraft:gravel + } + Compound() { + String(Name): minecraft:cobweb + } + Compound() { + String(Name): minecraft:infested_stone + } + Compound() { + String(Name): minecraft:andesite + } + Compound() { + String(Name): minecraft:coal_ore + } + Compound() { + String(Name): minecraft:diorite + } + ] + Byte(Y): 0x2 + ByteArray(BlockLight): (2048 bytes) + } + Compound() { + LongArray(BlockStates): (256 longs) + List(Palette, TAG_Compound, 11): [ + Compound() { + String(Name): minecraft:air + } + Compound() { + String(Name): minecraft:stone + } + Compound() { + String(Name): minecraft:infested_stone + } + Compound() { + String(Name): minecraft:cave_air + } + Compound() { + String(Name): minecraft:iron_ore + } + Compound() { + String(Name): minecraft:dirt + } + Compound() { + String(Name): minecraft:granite + } + Compound() { + String(Name): minecraft:coal_ore + } + Compound() { + String(Name): minecraft:andesite + } + Compound() { + String(Name): minecraft:gravel + } + Compound() { + String(Name): minecraft:diorite + } + ] + Byte(Y): 0x3 + } + Compound() { + LongArray(BlockStates): (256 longs) + List(Palette, TAG_Compound, 8): [ + Compound() { + String(Name): minecraft:air + } + Compound() { + String(Name): minecraft:andesite + } + Compound() { + String(Name): minecraft:stone + } + Compound() { + String(Name): minecraft:granite + } + Compound() { + String(Name): minecraft:diorite + } + Compound() { + String(Name): minecraft:coal_ore + } + Compound() { + String(Name): minecraft:dirt + } + Compound() { + String(Name): minecraft:cave_air + } + ] + Byte(Y): 0x4 + } + Compound() { + LongArray(BlockStates): (256 longs) + List(Palette, TAG_Compound, 8): [ + Compound() { + String(Name): minecraft:air + } + Compound() { + String(Name): minecraft:stone + } + Compound() { + String(Name): minecraft:coal_ore + } + Compound() { + String(Name): minecraft:dirt + } + Compound() { + Compound(Properties) { + String(snowy): false + } + String(Name): minecraft:grass_block + } + Compound() { + String(Name): minecraft:dandelion + } + Compound() { + Compound(Properties) { + String(snowy): true + } + String(Name): minecraft:grass_block + } + Compound() { + Compound(Properties) { + String(layers): 1 + } + String(Name): minecraft:snow + } + ] + ByteArray(SkyLight): (2048 bytes) + Byte(Y): 0x5 + } + Compound() { + LongArray(BlockStates): (256 longs) + List(Palette, TAG_Compound, 2): [ + Compound() { + String(Name): minecraft:air + } + Compound() { + Compound(Properties) { + String(layers): 1 + } + String(Name): minecraft:snow + } + ] + ByteArray(SkyLight): (2048 bytes) + Byte(Y): 0x6 + } + Compound() { + ByteArray(SkyLight): (2048 bytes) + Byte(Y): 0x7 + } + ] + List(PostProcessing, TAG_List, 10): [ ] + Compound(Structures) { } + List(LiquidTicks, TAG_End, 0): [ ] + } + Int(DataVersion): 2580 } */ @@ -41,7 +501,7 @@ namespace mc { struct level_context { boost::shared_ptr Level; - + bool error; size_t error_where; const char* error_why; @@ -135,6 +595,7 @@ namespace mc { } void register_int_array(level_context* C, nbt::String name, nbt::IntArray* int_array) { + delete int_array; } void register_byte_array(level_context* C, nbt::String name, nbt::ByteArray* byte_array) { @@ -168,7 +629,7 @@ namespace mc { delete long_array; } - + void error_handler(level_context* C, size_t where, const char *why) { C->error = true; C->error_where = where; @@ -197,15 +658,122 @@ namespace mc { return Level; } + // T here will typically be nbt::Byte or nbt::long + // indice_bit_count may not exceed 32 since the return type is int. + template + inline boost::optional get_indice(int x, int z, int y, boost::shared_ptr> &arr) { + size_t indice_index = (y * 16 + z) * 16 + x; + + // Compiler should catch that this is static and pre-compute it, + // specifically it will resolve statically since all arguments + // are sourced from template parameters. + size_t indice_count_in_element = static_cast(floor(static_cast(sizeof(T)*8) / static_cast(indice_bit_count))); + + size_t index_in_element = indice_index % indice_count_in_element; + size_t element_index = (indice_index - index_in_element) / indice_count_in_element; + + if (arr->length < 0 || element_index >= static_cast(arr->length)) + return boost::optional(); + + T element = arr->values[element_index]; + int out_bits = ~(0xFFFFFFFF << indice_bit_count); + return boost::optional((element >> (indice_bit_count * index_in_element)) & out_bits); + } + + bool Section_Compound::get_block(BlockT& block, int x, int z, int y) { + boost::optional lower_block_type; + boost::optional block_type = get_indice(x, z, y, this->Blocks); + // Data values are packed two by two and the position LSB decides which + // half-byte contains the requested block data value. + boost::optional block_data = get_indice(x, z, y, this->Data); + + if (block_type && block_data) { + boost::optional material = get_material_legacy(block_type.get(), block_data.get()); + if (material) { + block.properties.orientation = BlockOrientation::Invalid; + block.properties.is_top = false; + switch(material.get()->mode) { + case MaterialMode::LargeFlowerBlock: + block.properties.is_top = block_data.get() & 0x08; + if(block.properties.is_top) { + // y-coordinate must be positive, non-zero for top-blocks + // since it is required to look at the bottom block as well, + // Minecraft simply doesn't set the lower metadata bits for + // the corresponding type on the top-block. As a technical + // limitation this imply that bottom blocks of a chunk + // cannot be large flower top-blocks, and those should be + // bedrock anyway. + if(y > 0) { + lower_block_type = get_indice(x, z, y-1, this->Blocks); + if (lower_block_type && lower_block_type.get() == block_type.get()) { + block_data = get_indice(x, z, y-1, this->Data); + if (block_data) { + material = get_material_legacy(block_type.get(), block_data.get() & 0x7); + } + } + } else { + // Top block not placed on a correct bottom block. + // The expected LargeFlower multi block structure is invalid. + material = boost::optional(); + } + } + break; + case MaterialMode::LogBlock: + switch((block_data.get() & 0xC) >> 2) { + case 0: + block.properties.orientation = BlockOrientation::UpDown; + break; + case 1: + block.properties.orientation = BlockOrientation::EastWest; + break; + case 2: + block.properties.orientation = BlockOrientation::NorthSouth; + break; + case 3: + block.properties.orientation = BlockOrientation::OnlySides; + break; + default: + block.properties.orientation = BlockOrientation::Invalid; + break; + } + // Since metadata may result in the first sub-palette entry beign + // returned, re-fetch with corrected metadata, however this will + // only function correctly if all blocks in the same legacy + // sub-palette has the same MaterialMode. Which is one of the + // limitation of the legacy palette. + material = get_material_legacy(block_type.get(), block_data.get() & 0x3); + break; + case MaterialMode::LegacyLeaves: + // Since metadata may result in the first sub-palette entry beign + // returned, re-fetch with corrected metadata, however this will + // only function correctly if all blocks in the same legacy + // sub-palette has the same MaterialMode. Which is one of the + // limitation of the legacy palette. + material = get_material_legacy(block_type.get(), block_data.get() & 0x3); + break; + default: + // Normal lookup should be correct and no + // special attributes should be needed. + break; + } + } + if (material) { + block.material = material.get(); + return true; + } + } + return false; + } + /** * might throw invalid_file if the file is not gramatically correct */ void level::read(dynamic_buffer& buffer) { level_context context; - + nbt::Parser parser(&context); - + parser.register_byte_array = register_byte_array; parser.register_int_array = register_int_array; parser.register_long_array = register_long_array; @@ -232,11 +800,11 @@ namespace mc { std::string chunk_data = oss.str(); parser.parse_buffer(buffer.get(), len); - + if (context.error) { throw invalid_file(context.error_why); } - + if (!context.Level) { throw invalid_file("not a level data file"); } diff --git a/src/mc/level.hpp b/src/mc/level.hpp index c1bc33a7..a3fabc2e 100644 --- a/src/mc/level.hpp +++ b/src/mc/level.hpp @@ -6,12 +6,14 @@ #include #include #include +#include #include "mc/dynamic_buffer.hpp" #include "mc/utils.hpp" #include "mc/marker.hpp" #include "nbt/types.hpp" +#include "mc/blocks.hpp" namespace mc { namespace fs = boost::filesystem; @@ -41,6 +43,8 @@ namespace mc { boost::shared_ptr SkyLight; boost::shared_ptr BlockLight; nbt::Byte Y; + + bool get_block(BlockT &block, int x, int y, int z); }; struct Level_Compound { From 347d595e5c9f2bc660928121e1251185cd952906 Mon Sep 17 00:00:00 2001 From: Evildeeds Date: Sun, 27 Dec 2020 13:14:00 +0100 Subject: [PATCH 03/16] Added support for reading 1.13 maps --- src/engine/flat_base.hpp | 6 +- src/engine/isometric_base.hpp | 6 +- src/generate_statistics.cpp | 6 +- src/mc/level.cpp | 293 +++++++++++++++++++++++++++++----- src/mc/level.hpp | 55 ++++++- 5 files changed, 313 insertions(+), 53 deletions(-) diff --git a/src/engine/flat_base.hpp b/src/engine/flat_base.hpp index f9327b6f..04a9096b 100644 --- a/src/engine/flat_base.hpp +++ b/src/engine/flat_base.hpp @@ -36,11 +36,11 @@ class flat_base : public engine_base { blocked[i] = false; } - BOOST_REVERSE_FOREACH(mc::Section_Compound Section, L->Sections) { + BOOST_REVERSE_FOREACH(boost::shared_ptr Section, L->Sections) { block_rotation br_blocks(s.rotation); for (int y = 15; y >= 0; y--) { - int abs_y = (Section.Y * 16) + y; + int abs_y = (Section->get_y() * 16) + y; for (int z = 0; z < mc::MapZ; z++) { for (int x = 0; x < mc::MapX; x++) { @@ -55,7 +55,7 @@ class flat_base : public engine_base { br_blocks.transform_xz(projected_x, projected_z); mc::BlockT block; - if (Section.get_block(block, projected_x, projected_z, y)) { + if (Section->get_block(block, projected_x, projected_z, y)) { if (!block.material->enabled) { continue; } diff --git a/src/engine/isometric_base.hpp b/src/engine/isometric_base.hpp index a32376b6..b0182e6d 100644 --- a/src/engine/isometric_base.hpp +++ b/src/engine/isometric_base.hpp @@ -32,11 +32,11 @@ class isometric_base : public engine_base { oper->set_limits(image_width + 1, image_height); - BOOST_FOREACH(mc::Section_Compound Section, L->Sections) { + BOOST_FOREACH(boost::shared_ptr Section, L->Sections) { block_rotation br_blocks(s.rotation); for (int y = 0; y < 16; y++) { - int abs_y = (16 * Section.Y) + y; + int abs_y = (16 * Section->get_y()) + y; for (int z = 0; z < mc::MapZ; z++) { for (int x = mc::MapX - 1; x >= 0; x--) { @@ -45,7 +45,7 @@ class isometric_base : public engine_base { br_blocks.transform_xz(projected_x, projected_z); mc::BlockT block; - if (Section.get_block(block, projected_x, projected_z, y)) { + if (Section->get_block(block, projected_x, projected_z, y)) { if (!block.material->enabled) { continue; } diff --git a/src/generate_statistics.cpp b/src/generate_statistics.cpp index a58bc0a1..bdd4cf5d 100644 --- a/src/generate_statistics.cpp +++ b/src/generate_statistics.cpp @@ -135,13 +135,13 @@ bool generate_statistics( } boost::shared_ptr L = level_data.get_level(); - BOOST_FOREACH(mc::Section_Compound Section, L->Sections) { + BOOST_FOREACH(boost::shared_ptr Section, L->Sections) { for (int y = 15; y >= 0; y--) { - int abs_y = (Section.Y * 16) + y; + int abs_y = (Section->get_y() * 16) + y; for (int z = 0; z < mc::MapZ; z++) { for (int x = 0; x < mc::MapX; x++) { mc::BlockT block; - if (Section.get_block(block, x, z, y)) { + if (Section->get_block(block, x, z, y)) { if (block.material->enabled) { size_t index = block.material - materials; statistics[index] += 1; diff --git a/src/mc/level.cpp b/src/mc/level.cpp index 23d2974b..b7d44d50 100644 --- a/src/mc/level.cpp +++ b/src/mc/level.cpp @@ -6,6 +6,7 @@ #include "mc/region.hpp" #include "mc/level_info.hpp" +#include #include #include @@ -496,9 +497,36 @@ namespace mc { enum section_name { Level, Sections, + Palette, + Properties, None }; + struct context_section { + // shared + nbt::Byte Y; + boost::shared_ptr SkyLight; + + // legacy + boost::shared_ptr Blocks; + boost::shared_ptr Data; + boost::shared_ptr BlockLight; + + // modern + boost::shared_ptr BlockStates; + std::vector Palette; + std::vector> PaletteProperties; + + // Constructor for emplacing + context_section() { + // Minecraft seems to keep an + // invalid entry first; which + // has Y set to -1, so default + // to -1 for invalid here as well. + this->Y = -1; + } + }; + struct level_context { boost::shared_ptr Level; @@ -506,7 +534,8 @@ namespace mc { size_t error_where; const char* error_why; - Section_Compound* tmp_Section; + nbt::Int Version; + std::vector sections; section_name p[64]; int pos; @@ -518,60 +547,192 @@ namespace mc { } }; + Modern_Section_Compound::Modern_Section_Compound( + nbt::Byte Y, + boost::shared_ptr BlockStates, + boost::shared_ptr SkyLight, + std::vector &Palette, + std::vector> &PaletteProperties + ) : Section_Compound(Y, SkyLight), BlockStates(BlockStates) { + palette_size = Palette.size() < PaletteProperties.size() ? Palette.size() : PaletteProperties.size(); + BlockPalette.reset(new boost::optional[palette_size]); + for(size_t i = 0; i < palette_size; i++) { + std::map::iterator material_it = mc::MaterialMap.find(Palette[i]); + if (material_it == mc::MaterialMap.end()) { + std::cout << "unknown material: " << Palette[i] << std::endl; + BlockPalette[i] = boost::optional(); + } else { + BlockT block; + block.material = material_it->second; + + std::map::iterator property_it; + switch (block.material->mode) { + case mc::MaterialMode::LargeFlowerBlock: + property_it = PaletteProperties[i].find("half"); + if (property_it == PaletteProperties[i].end()) { + block.properties.is_top = false; + } else { + // "lower" or "upper" + block.properties.is_top = property_it->second.compare("upper") == 0; + } + break; + case mc::MaterialMode::LogBlock: + property_it = PaletteProperties[i].find("axis"); + if (property_it == PaletteProperties[i].end()) { + block.properties.orientation = mc::BlockOrientation::UpDown; + } else { + // "x", "y" or "z" + if (property_it->second.compare("x") == 0) { + block.properties.orientation = mc::BlockOrientation::EastWest; + } else if (property_it->second.compare("y") == 0) { + block.properties.orientation = mc::BlockOrientation::UpDown; + } else if (property_it->second.compare("z") == 0) { + block.properties.orientation = mc::BlockOrientation::NorthSouth; + } else { + block.properties.orientation = mc::BlockOrientation::Invalid; + } + } + break; + } + + /* Debug properties + std::pair map_pair; + BOOST_FOREACH(map_pair, PaletteProperties[i]) { + std::cout << ">> " << map_pair.first << " " << map_pair.second << std::endl; + } + */ + + BlockPalette[i] = boost::optional(block); + } + } + } + inline bool in_level_section(level_context* C) { return C->pos == 4 - && C->p[0] == None - && C->p[1] == Level - && C->p[2] == Sections - && C->p[3] == None; + && C->p[0] == section_name::None + && C->p[1] == section_name::Level + && C->p[2] == section_name::Sections + && C->p[3] == section_name::None; + } + + inline bool in_palette_section(level_context* C) { + return C->pos == 6 + && C->p[0] == section_name::None + && C->p[1] == section_name::Level + && C->p[2] == section_name::Sections + && C->p[3] == section_name::None + && C->p[4] == section_name::Palette + && C->p[5] == section_name::None; + } + + inline bool in_palette_properties(level_context* C) { + return C->pos == 7 + && C->p[0] == section_name::None + && C->p[1] == section_name::Level + && C->p[2] == section_name::Sections + && C->p[3] == section_name::None + && C->p[4] == section_name::Palette + && C->p[5] == section_name::None + && C->p[6] == section_name::Properties; } void begin_compound(level_context* C, nbt::String name) { if (name.compare("Level") == 0) { - C->p[C->pos++] = Level; + C->p[C->pos++] = section_name::Level; return; + } else if(in_palette_section(C)) { + if(name.compare("Properties") == 0) { + C->p[C->pos++] = section_name::Properties; + return; + } } - C->p[C->pos++] = None; + C->p[C->pos++] = section_name::None; if (in_level_section(C)) { - C->tmp_Section = new Section_Compound; - C->tmp_Section->Y = 0; + C->sections.emplace_back(); + } else if(in_palette_section(C)) { + C->sections.back().PaletteProperties.emplace_back(); } } void end_compound(level_context* C, nbt::String name) { - if (in_level_section(C)) { - if (!C->tmp_Section->Data) { - std::cout << "missing Data" << std::endl; + --C->pos; + // Construct level objects after parsing all data from region + // file to ensure that version information is available. + if (C->pos == 0) { + BOOST_FOREACH(context_section &ctx_section, C->sections) { + + if (ctx_section.Y == -1) { + continue; } - if (!C->tmp_Section->SkyLight) { + // Legacy or no version + if (C->Version < 1519) { + if (!ctx_section.Data) { + std::cout << "missing Data" << std::endl; + } + + if (!ctx_section.SkyLight) { std::cout << "missing SkyLight" << std::endl; - } + } - if (!C->tmp_Section->BlockLight) { + if (!ctx_section.BlockLight) { std::cout << "missing BlockLight" << std::endl; - } + } - if (!C->tmp_Section->Blocks) { + if (!ctx_section.Blocks) { std::cout << "missing Blocks" << std::endl; - } + } - C->Level->Sections.push_back(C->tmp_Section); - C->tmp_Section = NULL; - } + boost::shared_ptr s(new Legacy_Section_Compound( + ctx_section.Y, + ctx_section.Blocks, + ctx_section.Data, + ctx_section.SkyLight, + ctx_section.BlockLight + )); + C->Level->Sections.push_back(s); + } else { + // Not only may a section be truncated but + // their individual fields may as well. + if (!ctx_section.BlockStates) { + //std::cout << "missing BlockStates" << std::endl; + } - --C->pos; + if (!ctx_section.SkyLight) { + //std::cout << "missing SkyLight" << std::endl; + } + + if (ctx_section.Palette.size() != ctx_section.PaletteProperties.size()) { + //std::cout << "bad palette sizes" << std::endl; + } + + boost::shared_ptr s(new Modern_Section_Compound( + ctx_section.Y, + ctx_section.BlockStates, + ctx_section.SkyLight, + ctx_section.Palette, + ctx_section.PaletteProperties + )); + C->Level->Sections.push_back(s); + } + } + } } void begin_list(level_context* C, nbt::String name, nbt::Byte type, nbt::Int count) { if (name.compare("Sections") == 0) { - C->p[C->pos++] = Sections; + C->p[C->pos++] = section_name::Sections; return; + } else if(in_level_section(C)) { + if (name.compare("Palette") == 0) { + C->p[C->pos++] = section_name::Palette; + return; + } } - C->p[C->pos++] = None; + C->p[C->pos++] = section_name::None; } void end_list(level_context* C, nbt::String name) { @@ -579,19 +740,35 @@ namespace mc { } void register_string(level_context* C, nbt::String name, nbt::String value) { + if (in_palette_section(C)) { + if (name.compare("Name") == 0) { + C->sections.back().Palette.push_back(value); + } + } else if(in_palette_properties(C)) { + std::pair::iterator, bool> const& result = + C->sections.back().PaletteProperties.back().insert( + std::map::value_type(name, value) + ); + if (!result.second) { + std::cout << "duplicated palette property: " << name << std::endl; + } + } } void register_byte(level_context* C, nbt::String name, nbt::Byte value) { - if (in_level_section(C)) - { + if (in_level_section(C)) { if (name.compare("Y") == 0) { - C->tmp_Section->Y = value; - return; + C->sections.back().Y = value; } } } void register_int(level_context* C, nbt::String name, nbt::Int i) { + if (C->pos == 1 && C->p[0] == section_name::None) { + if (name.compare("DataVersion") == 0) { + C->Version = i; + } + } } void register_int_array(level_context* C, nbt::String name, nbt::IntArray* int_array) { @@ -599,25 +776,24 @@ namespace mc { } void register_byte_array(level_context* C, nbt::String name, nbt::ByteArray* byte_array) { - if (in_level_section(C)) - { + if (in_level_section(C)) { if (name.compare("Data") == 0) { - C->tmp_Section->Data.reset(byte_array); + C->sections.back().Data.reset(byte_array); return; } if (name.compare("SkyLight") == 0) { - C->tmp_Section->SkyLight.reset(byte_array); + C->sections.back().SkyLight.reset(byte_array); return; } if (name.compare("BlockLight") == 0) { - C->tmp_Section->BlockLight.reset(byte_array); + C->sections.back().BlockLight.reset(byte_array); return; } if (name.compare("Blocks") == 0) { - C->tmp_Section->Blocks.reset(byte_array); + C->sections.back().Blocks.reset(byte_array); return; } } @@ -626,7 +802,12 @@ namespace mc { } void register_long_array(level_context* C, nbt::String name, nbt::LongArray* long_array) { - + if (in_level_section(C)) { + if (name.compare("BlockStates") == 0) { + C->sections.back().BlockStates.reset(long_array); + return; + } + } delete long_array; } @@ -680,7 +861,47 @@ namespace mc { return boost::optional((element >> (indice_bit_count * index_in_element)) & out_bits); } - bool Section_Compound::get_block(BlockT& block, int x, int z, int y) { + bool Modern_Section_Compound::get_block(BlockT& block, int x, int z, int y) { + if (this->BlockStates) { + boost::optional block_data; + + // Static expansion of dynamic palette resolver; + // each section (16**3) contains 4096 unique blocks wihch yeilds max 2**12. + if (this->palette_size <= 16) { + block_data = get_indice(x, z, y, this->BlockStates); + } else if (this->palette_size <= 32) { + block_data = get_indice(x, z, y, this->BlockStates); + } else if (this->palette_size <= 64) { + block_data = get_indice(x, z, y, this->BlockStates); + } else if (this->palette_size <= 128) { + block_data = get_indice(x, z, y, this->BlockStates); + } else if (this->palette_size <= 256) { + block_data = get_indice(x, z, y, this->BlockStates); + } else if (this->palette_size <= 512) { + block_data = get_indice(x, z, y, this->BlockStates); + } else if (this->palette_size <= 1024) { + block_data = get_indice(x, z, y, this->BlockStates); + } else if (this->palette_size <= 2048) { + block_data = get_indice(x, z, y, this->BlockStates); + } else if (this->palette_size <= 4096) { + block_data = get_indice(x, z, y, this->BlockStates); + } else { + // For this to happen the map format must change as each segment only can + // contain a maximum of 4096 unique blocks. + std::cout << "chunk segment has a larger palette then expected" << std::endl; + } + + if (block_data) { + if (this->BlockPalette[block_data.get()]) { + block = this->BlockPalette[block_data.get()].get(); + return true; + } + } + } + return false; + } + + bool Legacy_Section_Compound::get_block(BlockT& block, int x, int z, int y) { boost::optional lower_block_type; boost::optional block_type = get_indice(x, z, y, this->Blocks); // Data values are packed two by two and the position LSB decides which diff --git a/src/mc/level.hpp b/src/mc/level.hpp index a3fabc2e..63a34752 100644 --- a/src/mc/level.hpp +++ b/src/mc/level.hpp @@ -3,6 +3,8 @@ #ifndef __MC_LEVEL_HPP__ #define __MC_LEVEL_HPP__ +#include + #include #include #include @@ -37,21 +39,58 @@ namespace mc { } }; - struct Section_Compound { - boost::shared_ptr Blocks; - boost::shared_ptr Data; - boost::shared_ptr SkyLight; - boost::shared_ptr BlockLight; - nbt::Byte Y; + class Section_Compound { + private: + nbt::Byte Y; + boost::shared_ptr SkyLight; + + protected: + Section_Compound(nbt::Byte Y, boost::shared_ptr SkyLight) : Y(Y), SkyLight(SkyLight) {}; + + public: + virtual bool get_block(BlockT &block, int x, int y, int z) = 0; + nbt::Byte get_y() { return this->Y; } + }; + + class Modern_Section_Compound : public Section_Compound { + private: + size_t palette_size; + boost::shared_ptr BlockStates; + boost::shared_ptr[]> BlockPalette; + + public: + Modern_Section_Compound( + nbt::Byte Y, + boost::shared_ptr BlockStates, + boost::shared_ptr SkyLight, + std::vector &Palette, + std::vector> &PaletteProperties + ); + bool get_block(BlockT &block, int x, int y, int z); + }; - bool get_block(BlockT &block, int x, int y, int z); + class Legacy_Section_Compound : public Section_Compound { + private: + boost::shared_ptr Blocks; + boost::shared_ptr Data; + boost::shared_ptr BlockLight; + + public: + Legacy_Section_Compound( + nbt::Byte Y, + boost::shared_ptr Blocks, + boost::shared_ptr Data, + boost::shared_ptr SkyLight, + boost::shared_ptr BlockLight + ) : Section_Compound(Y, SkyLight), Blocks(Blocks), Data(Data), BlockLight(BlockLight) {}; + bool get_block(BlockT &block, int x, int y, int z); }; struct Level_Compound { nbt::Int xPos; nbt::Int zPos; boost::shared_ptr HeightMap; - boost::ptr_vector Sections; + std::vector> Sections; }; class level From e63a8d977c9ee248a19c54497529010405d75381 Mon Sep 17 00:00:00 2001 From: Evildeeds Date: Sun, 27 Dec 2020 15:08:47 +0100 Subject: [PATCH 04/16] Added a few missing colors and new blocks which are common --- palette.json | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 85 insertions(+), 2 deletions(-) diff --git a/palette.json b/palette.json index 05390b98..55cc73ba 100644 --- a/palette.json +++ b/palette.json @@ -7,6 +7,13 @@ "darken": false, "legacy_id": 0 }, + { + "namespace": "minecraft", + "material": "cave_air", + "mode": "block", + "top_color": [255, 255, 255, 0], + "darken": false + }, { "namespace": "minecraft", "material": "stone", @@ -627,9 +634,18 @@ "material": "grass", "mode": "block", "top_color": [144, 188, 39, 255], - "side_color": [144, 188, 39, 255], "darken": false, - "legacy_id": 31 + "legacy_id": 31, + "legacy_meta": 0 + }, + { + "namespace": "minecraft", + "material": "fern", + "mode": "block", + "top_color": [120, 164, 13, 255], + "darken": false, + "legacy_id": 31, + "legacy_meta": 2 }, { "namespace": "minecraft", @@ -896,6 +912,24 @@ "legacy_id": 38, "legacy_meta": 8 }, + { + "namespace": "minecraft", + "material": "cornflower", + "mode": "block", + "top_color": [18, 136, 204, 245], + "darken": true, + "legacy_id": 38, + "legacy_meta": 9 + }, + { + "namespace": "minecraft", + "material": "lily_of_the_valley", + "mode": "block", + "top_color": [231, 231, 231, 255], + "darken": true, + "legacy_id": 38, + "legacy_meta": 10 + }, { "namespace": "minecraft", "material": "brown_mushroom", @@ -1055,6 +1089,13 @@ "top_color": [255, 225, 96, 208], "darken": false }, + { + "namespace": "minecraft", + "material": "lantern", + "mode": "torch_block", + "top_color": [255, 225, 96, 208], + "darken": false + }, { "namespace": "minecraft", "material": "fire", @@ -1154,6 +1195,7 @@ "namespace": "minecraft", "material": "oak_door", "mode": "block", + "top_color": [157, 128, 79, 255], "darken": false, "legacy_id": 64 }, @@ -1201,6 +1243,7 @@ "namespace": "minecraft", "material": "iron_door", "mode": "block", + "top_color": [217, 217, 217, 255], "darken": false, "legacy_id": 71 }, @@ -2953,6 +2996,7 @@ "namespace": "minecraft", "material": "spruce_door", "mode": "block", + "top_color": [102, 77, 46, 255], "darken": false, "legacy_id": 193 }, @@ -2960,6 +3004,7 @@ "namespace": "minecraft", "material": "birch_door", "mode": "block", + "top_color": [193, 177, 122, 255], "darken": false, "legacy_id": 194 }, @@ -2967,6 +3012,7 @@ "namespace": "minecraft", "material": "jungle_door", "mode": "block", + "top_color": [152, 109, 76, 255], "darken": false, "legacy_id": 195 }, @@ -2974,6 +3020,7 @@ "namespace": "minecraft", "material": "acacia_door", "mode": "block", + "top_color": [168, 91, 50, 255], "darken": false, "legacy_id": 196 }, @@ -2981,6 +3028,7 @@ "namespace": "minecraft", "material": "dark_oak_door", "mode": "block", + "top_color": [60, 39, 18, 255], "darken": false, "legacy_id": 197 }, @@ -3647,5 +3695,40 @@ "top_color": [98, 74, 98, 255], "darken": true, "legacy_id": 255 + }, + { + "namespace": "minecraft", + "material": "seagrass", + "mode": "block", + "top_color": [9, 161, 62, 64], + "darken": false + }, + { + "namespace": "minecraft", + "material": "tall_seagrass", + "mode": "block", + "top_color": [9, 161, 62, 64], + "darken": false + }, + { + "namespace": "minecraft", + "material": "sweet_berry_bush", + "mode": "block", + "top_color": [120, 164, 13, 255], + "darken": false + }, + { + "namespace": "minecraft", + "material": "bamboo", + "mode": "block", + "top_color": [45, 125, 15, 255], + "darken": false + }, + { + "namespace": "minecraft", + "material": "bamboo_sapling", + "top_color": [58, 111, 38, 255], + "mode": "block", + "darken": false } ] From eebe005cde824347a84233338ce7c5c9c37aa58b Mon Sep 17 00:00:00 2001 From: Evildeeds Date: Mon, 28 Dec 2020 03:43:50 +0100 Subject: [PATCH 05/16] Support optimal bit-packed chunk segments; this also fixes a related crash --- src/mc/level.cpp | 140 ++++++++++++++++++++++++++++++++++++++--------- src/mc/level.hpp | 1 + 2 files changed, 114 insertions(+), 27 deletions(-) diff --git a/src/mc/level.cpp b/src/mc/level.cpp index b7d44d50..c6c263cf 100644 --- a/src/mc/level.cpp +++ b/src/mc/level.cpp @@ -605,6 +605,28 @@ namespace mc { BlockPalette[i] = boost::optional(block); } } + + // check bit packing method of array; check if the array fits exactly 4096 indices for + // bit counts that don't divide 64 (size of long) then it is an optimal bit stream rather + // then the expected truncated bit-packing. (as for why it is sometimes used I have no idea) + if (palette_size > 16 && palette_size <= 32 && BlockStates->length == 320) { + stream_bit_packing = true; + } else if(palette_size > 32 && palette_size <= 64 && BlockStates->length == 384) { + stream_bit_packing = true; + } else if(palette_size > 64 && palette_size <= 128 && BlockStates->length == 448) { + stream_bit_packing = true; + } else if(palette_size > 256 && palette_size <= 512 && BlockStates->length == 576) { + stream_bit_packing = true; + } else if(palette_size > 512 && palette_size <= 1024 && BlockStates->length == 640) { + stream_bit_packing = true; + } else if(palette_size > 1024 && palette_size <= 2048 && BlockStates->length == 704) { + stream_bit_packing = true; + } else if(palette_size > 2048 && palette_size <= 4096 && BlockStates->length == 768) { + stream_bit_packing = true; + } else { + // slack bits, if any, are assumed to be unused. + stream_bit_packing = false; + } } inline bool in_level_section(level_context* C) { @@ -705,7 +727,7 @@ namespace mc { } if (ctx_section.Palette.size() != ctx_section.PaletteProperties.size()) { - //std::cout << "bad palette sizes" << std::endl; + std::cout << "bad palette sizes" << std::endl; } boost::shared_ptr s(new Modern_Section_Compound( @@ -839,8 +861,14 @@ namespace mc { return Level; } - // T here will typically be nbt::Byte or nbt::long - // indice_bit_count may not exceed 32 since the return type is int. + /** + * Get the bit slice (indice) for the given coordinates in the provided array. + * + * This method assumes truncated bit-packing. + * + * T here will typically be nbt::Byte or nbt::long + * indice_bit_count is the number of bits in the slice and may not exceed 32 since the return type is int. + */ template inline boost::optional get_indice(int x, int z, int y, boost::shared_ptr> &arr) { size_t indice_index = (y * 16 + z) * 16 + x; @@ -861,38 +889,96 @@ namespace mc { return boost::optional((element >> (indice_bit_count * index_in_element)) & out_bits); } + /** + * Get the bit slice (indice) for the given coordinates in the provided array. + * + * This method assumes optimal bit-packing. + * + * T here will typically be nbt::Byte or nbt::long + * indice_bit_count is the number of bits in the slice and may not exceed 32 since the return type is int. + */ + template + inline boost::optional get_stream_indice(int x, int z, int y, boost::shared_ptr> &arr) { + size_t indice_index = (y * 16 + z) * 16 + x; + + size_t bits_into_stream = indice_index * indice_bit_count; + + size_t bits_in_element = bits_into_stream % 64; + size_t element_index = (bits_into_stream - bits_in_element) / 64; + + if (arr->length < 0 || element_index >= static_cast(arr->length)) + return boost::optional(); + + T element = arr->values[element_index]; + int result = element >> bits_in_element; + size_t got = 64 - bits_in_element; + if (got < indice_bit_count) { + if (element_index + 1 >= static_cast(arr->length)) + return boost::optional(); + element = arr->values[element_index + 1]; + result = result | (element << got); + } + + int out_bits = ~(0xFFFFFFFF << indice_bit_count); + return boost::optional(result & out_bits); + } + bool Modern_Section_Compound::get_block(BlockT& block, int x, int z, int y) { if (this->BlockStates) { - boost::optional block_data; + boost::optional block_data = boost::optional(); // Static expansion of dynamic palette resolver; - // each section (16**3) contains 4096 unique blocks wihch yeilds max 2**12. - if (this->palette_size <= 16) { - block_data = get_indice(x, z, y, this->BlockStates); - } else if (this->palette_size <= 32) { - block_data = get_indice(x, z, y, this->BlockStates); - } else if (this->palette_size <= 64) { - block_data = get_indice(x, z, y, this->BlockStates); - } else if (this->palette_size <= 128) { - block_data = get_indice(x, z, y, this->BlockStates); - } else if (this->palette_size <= 256) { - block_data = get_indice(x, z, y, this->BlockStates); - } else if (this->palette_size <= 512) { - block_data = get_indice(x, z, y, this->BlockStates); - } else if (this->palette_size <= 1024) { - block_data = get_indice(x, z, y, this->BlockStates); - } else if (this->palette_size <= 2048) { - block_data = get_indice(x, z, y, this->BlockStates); - } else if (this->palette_size <= 4096) { - block_data = get_indice(x, z, y, this->BlockStates); + // each section (16**3) contains 4096 unique blocks wihch yields max 2**12. + if(this->stream_bit_packing) { + // Special case bit-packing. + if (this->palette_size > 16 && this->palette_size <= 32) { + block_data = get_stream_indice(x, z, y, this->BlockStates); + } else if (this->palette_size > 32 && this->palette_size <= 64) { + block_data = get_stream_indice(x, z, y, this->BlockStates); + } else if (this->palette_size > 64 && this->palette_size <= 128) { + block_data = get_stream_indice(x, z, y, this->BlockStates); + } else if (this->palette_size > 256 && this->palette_size <= 512) { + block_data = get_stream_indice(x, z, y, this->BlockStates); + } else if (this->palette_size > 512 && this->palette_size <= 1024) { + block_data = get_stream_indice(x, z, y, this->BlockStates); + } else if (this->palette_size > 1024 && this->palette_size <= 2048) { + block_data = get_stream_indice(x, z, y, this->BlockStates); + } else if (this->palette_size > 2048 && this->palette_size <= 4096) { + block_data = get_stream_indice(x, z, y, this->BlockStates); + } else { + // If this happens check the constructor logic when + // stream_bit_packing decision is made. + std::cout << "chunk segment uses unexpected bit-packing" << std::endl; + } } else { - // For this to happen the map format must change as each segment only can - // contain a maximum of 4096 unique blocks. - std::cout << "chunk segment has a larger palette then expected" << std::endl; + // This is the common bit-packing method. + if (this->palette_size <= 16) { + block_data = get_indice(x, z, y, this->BlockStates); + } else if (this->palette_size <= 32) { + block_data = get_indice(x, z, y, this->BlockStates); + } else if (this->palette_size <= 64) { + block_data = get_indice(x, z, y, this->BlockStates); + } else if (this->palette_size <= 128) { + block_data = get_indice(x, z, y, this->BlockStates); + } else if (this->palette_size <= 256) { + block_data = get_indice(x, z, y, this->BlockStates); + } else if (this->palette_size <= 512) { + block_data = get_indice(x, z, y, this->BlockStates); + } else if (this->palette_size <= 1024) { + block_data = get_indice(x, z, y, this->BlockStates); + } else if (this->palette_size <= 2048) { + block_data = get_indice(x, z, y, this->BlockStates); + } else if (this->palette_size <= 4096) { + block_data = get_indice(x, z, y, this->BlockStates); + } else { + // For this to happen the map format must change as each segment only can + // contain a maximum of 4096 unique blocks. + std::cout << "chunk segment has a larger palette then expected" << std::endl; + } } if (block_data) { - if (this->BlockPalette[block_data.get()]) { + if (block_data.get() < this->palette_size && this->BlockPalette[block_data.get()]) { block = this->BlockPalette[block_data.get()].get(); return true; } diff --git a/src/mc/level.hpp b/src/mc/level.hpp index 63a34752..1bd6845c 100644 --- a/src/mc/level.hpp +++ b/src/mc/level.hpp @@ -57,6 +57,7 @@ namespace mc { size_t palette_size; boost::shared_ptr BlockStates; boost::shared_ptr[]> BlockPalette; + bool stream_bit_packing; public: Modern_Section_Compound( From 63f0faa509663befa3202bc844fa785d2722dd82 Mon Sep 17 00:00:00 2001 From: Evildeeds Date: Mon, 28 Dec 2020 03:48:37 +0100 Subject: [PATCH 06/16] Added kelp --- palette.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/palette.json b/palette.json index 55cc73ba..2ac7afe9 100644 --- a/palette.json +++ b/palette.json @@ -3730,5 +3730,19 @@ "top_color": [58, 111, 38, 255], "mode": "block", "darken": false + }, + { + "namespace": "minecraft", + "material": "kelp", + "mode": "block", + "top_color": [9, 161, 62, 64], + "darken": false + }, + { + "namespace": "minecraft", + "material": "kelp_plant", + "mode": "block", + "top_color": [9, 161, 62, 64], + "darken": false } ] From 56cf862a433924e17bb8c55e5acf06f1dfcfdf23 Mon Sep 17 00:00:00 2001 From: Evildeeds Date: Mon, 28 Dec 2020 10:24:11 +0100 Subject: [PATCH 07/16] Support reading player db from new maps --- src/generate_map.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/generate_map.cpp b/src/generate_map.cpp index 2a1165bd..56fa27fc 100644 --- a/src/generate_map.cpp +++ b/src/generate_map.cpp @@ -462,7 +462,14 @@ bool generate_map( } if (s.show_players) { - load_players(out, world_path / "players", players, s.show_players_set); + + // Try both legacy and modern player dbs. + fs::path full_path = world_path / "playerdata"; + if (!fs::is_directory(full_path)) { + full_path = full_path = world_path / "players"; + } + + load_players(out, full_path, players, s.show_players_set); } if (s.show_signs) { From 462237bf8c21658b36cae9171b96eb5241fa3c28 Mon Sep 17 00:00:00 2001 From: Evildeeds Date: Thu, 31 Dec 2020 17:00:59 +0100 Subject: [PATCH 08/16] Corrected new indice lookup template --- src/mc/level.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/mc/level.cpp b/src/mc/level.cpp index c6c263cf..ac8af429 100644 --- a/src/mc/level.cpp +++ b/src/mc/level.cpp @@ -902,16 +902,17 @@ namespace mc { size_t indice_index = (y * 16 + z) * 16 + x; size_t bits_into_stream = indice_index * indice_bit_count; + size_t element_bits = sizeof(T) * 8; - size_t bits_in_element = bits_into_stream % 64; - size_t element_index = (bits_into_stream - bits_in_element) / 64; + size_t bits_in_element = bits_into_stream % element_bits; + size_t element_index = (bits_into_stream - bits_in_element) / element_bits; if (arr->length < 0 || element_index >= static_cast(arr->length)) return boost::optional(); T element = arr->values[element_index]; int result = element >> bits_in_element; - size_t got = 64 - bits_in_element; + size_t got = element_bits - bits_in_element; if (got < indice_bit_count) { if (element_index + 1 >= static_cast(arr->length)) return boost::optional(); From 1ec0c1486a10832be63b0a9ba7257d354e5ee25c Mon Sep 17 00:00:00 2001 From: Evildeeds Date: Sun, 3 Jan 2021 12:02:42 +0100 Subject: [PATCH 09/16] Filter playerdb for dat-files to avoid picking up bakups --- CMakeLists.txt | 2 +- README.md | 2 +- src/players.cpp | 23 ++++++++++++++++++----- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c387f0bf..b29ef503 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,7 +28,7 @@ find_package(ZLIB REQUIRED) find_package(PNG 1.2 REQUIRED) find_package(Freetype 2 REQUIRED) find_package(Threads REQUIRED) -find_package(Boost 1.46 COMPONENTS thread filesystem system REQUIRED) +find_package(Boost 1.46 COMPONENTS thread filesystem system regex REQUIRED) include_directories(${CMAKE_SOURCE_DIR}/src) include_directories(${ZLIB_INCLUDE_DIR}) diff --git a/README.md b/README.md index d42e5278..a5012cd4 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Requirements * libz (?) * libpng (>= 1.2) * libfreetype (>= 2) - * libboost (thread, filesystem, system and test) (>= 1.46) + * libboost (thread, filesystem, system, regex and test) (>= 1.46) Features -------- diff --git a/src/players.cpp b/src/players.cpp index ed84284d..ff03cc9a 100644 --- a/src/players.cpp +++ b/src/players.cpp @@ -1,6 +1,8 @@ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. +#include + #include "players.hpp" namespace fs = boost::filesystem; @@ -30,7 +32,7 @@ void register_double(player *p, std::string name, nbt::Double value) { case 1: p->yPos = value; break; case 2: p->zPos = value; break; } - + p->pos_c++; } } @@ -54,20 +56,31 @@ players_db::players_db(fs::path path, std::set set) { } +const boost::regex player_extension( "[^\\.]*\\.dat" ); + void players_db::read(std::vector& players) const { fs::path full_path = fs::system_complete( path ); - + if (!fs::is_directory(full_path)) { throw players_db_exception("database does not exist"); } - + fs::directory_iterator end_iter; - - for ( fs::directory_iterator dir_itr( full_path ); + + for (fs::directory_iterator dir_itr(full_path); dir_itr != end_iter; ++dir_itr ) { + // Player files are simply dat-files; filter for them + // to avoid picking up dat_old-files or any other + // unexpeced files. + boost::smatch match; + if (!boost::regex_match(dir_itr->path().filename().string(), match, player_extension)) { + continue; + } + + // player files have always been .dat; TODO filter that since .dat_old is used.. sometimes? player p(dir_itr->path()); if (filter_set.size() > 0) { From 95f2a0913dd2db400f696c128516ae82ab043f89 Mon Sep 17 00:00:00 2001 From: Evildeeds Date: Sun, 3 Jan 2021 14:52:43 +0100 Subject: [PATCH 10/16] Fixed an issue with legacy rendering where the version field was uninitalized. Fixed access specifier for common fields. --- src/mc/level.cpp | 4 +++- src/mc/level.hpp | 4 +--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mc/level.cpp b/src/mc/level.cpp index ac8af429..70ff0115 100644 --- a/src/mc/level.cpp +++ b/src/mc/level.cpp @@ -542,6 +542,7 @@ namespace mc { level_context() : error(false), error_where(0), error_why("") { + this->Version = -1; this->Level.reset(new Level_Compound); this->pos = 0; } @@ -685,7 +686,8 @@ namespace mc { if (C->pos == 0) { BOOST_FOREACH(context_section &ctx_section, C->sections) { - if (ctx_section.Y == -1) { + if (ctx_section.Y < 0) { + // Skip invalid height indexes; these are typically 0 - 15. continue; } diff --git a/src/mc/level.hpp b/src/mc/level.hpp index 1bd6845c..f43db865 100644 --- a/src/mc/level.hpp +++ b/src/mc/level.hpp @@ -40,11 +40,9 @@ namespace mc { }; class Section_Compound { - private: + protected: nbt::Byte Y; boost::shared_ptr SkyLight; - - protected: Section_Compound(nbt::Byte Y, boost::shared_ptr SkyLight) : Y(Y), SkyLight(SkyLight) {}; public: From 681dff94926e26e51463739c43aebe129679b210 Mon Sep 17 00:00:00 2001 From: Evildeeds Date: Mon, 4 Jan 2021 23:48:07 +0100 Subject: [PATCH 11/16] Fixed missing bitmask causing minor spectral noise when chunk segment uses stream packing --- src/mc/level.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mc/level.cpp b/src/mc/level.cpp index 70ff0115..b7b0b8c6 100644 --- a/src/mc/level.cpp +++ b/src/mc/level.cpp @@ -919,7 +919,8 @@ namespace mc { if (element_index + 1 >= static_cast(arr->length)) return boost::optional(); element = arr->values[element_index + 1]; - result = result | (element << got); + int got_bits = ~(0xFFFFFFFF << got); + result = (result & got_bits) | (element << got); } int out_bits = ~(0xFFFFFFFF << indice_bit_count); From 0582ce3aacc24ef2a6801c8b5cd93fe4b06520f6 Mon Sep 17 00:00:00 2001 From: Evildeeds Date: Sun, 11 Apr 2021 15:54:40 +0200 Subject: [PATCH 12/16] Avoid processing entity lists --- src/mc/region_iterator.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/mc/region_iterator.cpp b/src/mc/region_iterator.cpp index d030bd50..a2c91c89 100644 --- a/src/mc/region_iterator.cpp +++ b/src/mc/region_iterator.cpp @@ -6,6 +6,8 @@ bool dir_filter(const std::string& name) { if (name.compare(0, 3, "DIM") == 0) return false; if (name.compare("players") == 0) return false; + if (name.compare("poi") == 0) return false; + if (name.compare("entities") == 0) return false; return true; } @@ -21,7 +23,7 @@ namespace mc { : root(path), lister(new dirlist(path)) { } - + bool region_iterator::has_next() { if (!lister->has_next(dir_filter, file_filter)) { @@ -32,7 +34,7 @@ namespace mc { current_region.reset(new region(next)); return true; } - + region_ptr region_iterator::next() { return current_region; } From 525de2916ffad0b35a454958c0be6a5e97074eea Mon Sep 17 00:00:00 2001 From: Evildeeds Date: Mon, 7 Feb 2022 23:19:24 +0100 Subject: [PATCH 13/16] Minor code cleanup --- src/dirlist.cpp | 12 ++++++------ src/engine/isometric_base.hpp | 3 +-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/dirlist.cpp b/src/dirlist.cpp index 27a042e1..ea9b6d63 100644 --- a/src/dirlist.cpp +++ b/src/dirlist.cpp @@ -15,16 +15,16 @@ bool dirlist::has_next(dir_filter_func dir_filter, file_filter_func file_filter) if (directories.empty()) { return false; } - + // work until you find any files while (!directories.empty()) { fs::path dir_path = directories.front(); directories.pop(); - + if (!fs::is_directory(dir_path)) { continue; } - + fs::directory_iterator end_itr; for ( fs::directory_iterator itr(dir_path); @@ -35,19 +35,19 @@ bool dirlist::has_next(dir_filter_func dir_filter, file_filter_func file_filter) if (!dir_filter(fs::basename(itr->path()))) { continue; } - + directories.push(itr->path()); } else if (fs::is_regular_file(itr->status())) { if (!file_filter(path_string(itr->path().filename()))) { continue; } - + files.push(itr->path()); } } } - + return !files.empty(); } diff --git a/src/engine/isometric_base.hpp b/src/engine/isometric_base.hpp index b0182e6d..2909d283 100644 --- a/src/engine/isometric_base.hpp +++ b/src/engine/isometric_base.hpp @@ -32,9 +32,8 @@ class isometric_base : public engine_base { oper->set_limits(image_width + 1, image_height); + block_rotation br_blocks(s.rotation); BOOST_FOREACH(boost::shared_ptr Section, L->Sections) { - block_rotation br_blocks(s.rotation); - for (int y = 0; y < 16; y++) { int abs_y = (16 * Section->get_y()) + y; From 92be553eea77a72f54c5127495fb15f6c4eebb4b Mon Sep 17 00:00:00 2001 From: Evildeeds Date: Mon, 7 Feb 2022 23:21:21 +0100 Subject: [PATCH 14/16] Try harder to find map data and disable the height check --- src/mc/level.cpp | 102 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 73 insertions(+), 29 deletions(-) diff --git a/src/mc/level.cpp b/src/mc/level.cpp index b7b0b8c6..a46241ab 100644 --- a/src/mc/level.cpp +++ b/src/mc/level.cpp @@ -518,12 +518,7 @@ namespace mc { std::vector> PaletteProperties; // Constructor for emplacing - context_section() { - // Minecraft seems to keep an - // invalid entry first; which - // has Y set to -1, so default - // to -1 for invalid here as well. - this->Y = -1; + context_section() : Y(INT8_MIN) { } }; @@ -534,17 +529,14 @@ namespace mc { size_t error_where; const char* error_why; - nbt::Int Version; + nbt::Int version; std::vector sections; section_name p[64]; int pos; - level_context() : error(false), error_where(0), error_why("") - { - this->Version = -1; + level_context() : error(false), version(-1), pos(0), error_where(0), error_why("") { this->Level.reset(new Level_Compound); - this->pos = 0; } }; @@ -563,7 +555,7 @@ namespace mc { std::cout << "unknown material: " << Palette[i] << std::endl; BlockPalette[i] = boost::optional(); } else { - BlockT block; + mc::BlockT block; block.material = material_it->second; std::map::iterator property_it; @@ -603,13 +595,13 @@ namespace mc { } */ - BlockPalette[i] = boost::optional(block); + BlockPalette[i] = boost::optional(block); } } // check bit packing method of array; check if the array fits exactly 4096 indices for - // bit counts that don't divide 64 (size of long) then it is an optimal bit stream rather - // then the expected truncated bit-packing. (as for why it is sometimes used I have no idea) + // bit counts that don't divide 64 (size of long) then it is an proper bit stream rather + // then the truncated bit-packing. if (palette_size > 16 && palette_size <= 32 && BlockStates->length == 320) { stream_bit_packing = true; } else if(palette_size > 32 && palette_size <= 64 && BlockStates->length == 384) { @@ -630,7 +622,8 @@ namespace mc { } } - inline bool in_level_section(level_context* C) { + // Originally a level compond object existed which was later removed + inline bool in_level_section_a(level_context* C) { return C->pos == 4 && C->p[0] == section_name::None && C->p[1] == section_name::Level @@ -638,7 +631,27 @@ namespace mc { && C->p[3] == section_name::None; } - inline bool in_palette_section(level_context* C) { + inline bool in_level_section_b(level_context* C) { + return C->pos == 4 + && C->p[0] == section_name::None + && C->p[1] == section_name::Sections + && C->p[2] == section_name::None + && C->p[3] == section_name::None; + } + + // Y value did not move + inline bool in_level_section_c(level_context* C) { + return C->pos == 3 + && C->p[0] == section_name::None + && C->p[1] == section_name::Sections + && C->p[2] == section_name::None; + } + + inline bool in_level_section(level_context* C) { + return in_level_section_a(C) || in_level_section_b(C); + } + + inline bool in_palette_section_a(level_context* C) { return C->pos == 6 && C->p[0] == section_name::None && C->p[1] == section_name::Level @@ -648,7 +661,21 @@ namespace mc { && C->p[5] == section_name::None; } - inline bool in_palette_properties(level_context* C) { + inline bool in_palette_section_b(level_context* C) { + return C->pos == 6 + && C->p[0] == section_name::None + && C->p[1] == section_name::Sections + && C->p[2] == section_name::None + && C->p[3] == section_name::None + && C->p[4] == section_name::Palette + && C->p[5] == section_name::None; + } + + inline bool in_palette_section(level_context* C) { + return in_palette_section_a(C) || in_palette_section_b(C); + } + + inline bool in_palette_properties_a(level_context* C) { return C->pos == 7 && C->p[0] == section_name::None && C->p[1] == section_name::Level @@ -659,6 +686,21 @@ namespace mc { && C->p[6] == section_name::Properties; } + inline bool in_palette_properties_b(level_context* C) { + return C->pos == 7 + && C->p[0] == section_name::None + && C->p[1] == section_name::Sections + && C->p[2] == section_name::None + && C->p[3] == section_name::None + && C->p[4] == section_name::Palette + && C->p[5] == section_name::None + && C->p[6] == section_name::Properties; + } + + inline bool in_palette_properties(level_context* C) { + return in_palette_properties_a(C) || in_palette_properties_b(C); + } + void begin_compound(level_context* C, nbt::String name) { if (name.compare("Level") == 0) { C->p[C->pos++] = section_name::Level; @@ -688,11 +730,11 @@ namespace mc { if (ctx_section.Y < 0) { // Skip invalid height indexes; these are typically 0 - 15. - continue; + //continue; } // Legacy or no version - if (C->Version < 1519) { + if (C->version < 1519) { if (!ctx_section.Data) { std::cout << "missing Data" << std::endl; } @@ -746,11 +788,11 @@ namespace mc { } void begin_list(level_context* C, nbt::String name, nbt::Byte type, nbt::Int count) { - if (name.compare("Sections") == 0) { + if (name.compare("Sections") == 0 || name.compare("sections") == 0) { C->p[C->pos++] = section_name::Sections; return; } else if(in_level_section(C)) { - if (name.compare("Palette") == 0) { + if (name.compare("Palette") == 0 || name.compare("palette") == 0) { C->p[C->pos++] = section_name::Palette; return; } @@ -780,7 +822,7 @@ namespace mc { } void register_byte(level_context* C, nbt::String name, nbt::Byte value) { - if (in_level_section(C)) { + if (in_level_section_a(C) || in_level_section_c(C)) { if (name.compare("Y") == 0) { C->sections.back().Y = value; } @@ -790,7 +832,7 @@ namespace mc { void register_int(level_context* C, nbt::String name, nbt::Int i) { if (C->pos == 1 && C->p[0] == section_name::None) { if (name.compare("DataVersion") == 0) { - C->Version = i; + C->version = i; } } } @@ -827,7 +869,7 @@ namespace mc { void register_long_array(level_context* C, nbt::String name, nbt::LongArray* long_array) { if (in_level_section(C)) { - if (name.compare("BlockStates") == 0) { + if (name.compare("BlockStates") == 0 || name.compare("data") == 0) { C->sections.back().BlockStates.reset(long_array); return; } @@ -878,7 +920,8 @@ namespace mc { // Compiler should catch that this is static and pre-compute it, // specifically it will resolve statically since all arguments // are sourced from template parameters. - size_t indice_count_in_element = static_cast(floor(static_cast(sizeof(T)*8) / static_cast(indice_bit_count))); + size_t indice_count_in_element = + static_cast(floor(static_cast(sizeof(T)*8) / static_cast(indice_bit_count))); size_t index_in_element = indice_index % indice_count_in_element; size_t element_index = (indice_index - index_in_element) / indice_count_in_element; @@ -894,9 +937,9 @@ namespace mc { /** * Get the bit slice (indice) for the given coordinates in the provided array. * - * This method assumes optimal bit-packing. + * This method assumes non-truncated bit-packing. * - * T here will typically be nbt::Byte or nbt::long + * T here will typically be nbt::long * indice_bit_count is the number of bits in the slice and may not exceed 32 since the return type is int. */ template @@ -932,7 +975,8 @@ namespace mc { boost::optional block_data = boost::optional(); // Static expansion of dynamic palette resolver; - // each section (16**3) contains 4096 unique blocks wihch yields max 2**12. + // each section (16**3) contains 4096 unique blocks wihch yields max 2**12 + // and the minimum is 4 bits regardless of the number of entries in the palette. if(this->stream_bit_packing) { // Special case bit-packing. if (this->palette_size > 16 && this->palette_size <= 32) { From 487a8f219331f9a85342dc9c6af1d535b10be686 Mon Sep 17 00:00:00 2001 From: Evildeeds Date: Tue, 8 Feb 2022 19:27:35 +0100 Subject: [PATCH 15/16] Possible crash fix related to nbt string buffer --- src/nbt/nbt.hpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/nbt/nbt.hpp b/src/nbt/nbt.hpp index 611d0041..fed0ef20 100644 --- a/src/nbt/nbt.hpp +++ b/src/nbt/nbt.hpp @@ -333,9 +333,11 @@ namespace nbt { inline String read_string(input_buffer_ptr file) { Short s = read_short(file); nbt_assert_error(exc_env, file, s >= 0, "String specified with invalid length < 0"); - uint8_t *str = new uint8_t[s + 1]; - assert_error_c(exc_env, file, file->read(str, s) == s, "Buffer to short to read String", delete str); - String so((const char*)str, s); + size_t count = static_cast(s); + uint8_t *str = new uint8_t[count + 1]; + nbt_assert_error(exc_env, file, str, "Unable to allocate string buffer"); + assert_error_c(exc_env, file, file->read(str, count) == count, "Buffer to short to read String", delete [] str); + String so(reinterpret_cast(str), count); delete [] str; return so; } @@ -736,7 +738,7 @@ namespace nbt { parse(file); } - void parse_file(const char *path) + void parse_file(const char* path) { input_buffer_ptr file(new gzfile_buffer(path)); parse(file); From b2fc37f9e7c6998c65f82ec313723902d199e45f Mon Sep 17 00:00:00 2001 From: Evildeeds Date: Wed, 9 Feb 2022 00:04:00 +0100 Subject: [PATCH 16/16] Enforced unsigned data-streams Fixed Y-index crash from empty section stack --- src/mc/level.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/mc/level.cpp b/src/mc/level.cpp index a46241ab..e70138b1 100644 --- a/src/mc/level.cpp +++ b/src/mc/level.cpp @@ -714,7 +714,7 @@ namespace mc { C->p[C->pos++] = section_name::None; - if (in_level_section(C)) { + if (in_level_section_a(C) || in_level_section_c(C)) { C->sections.emplace_back(); } else if(in_palette_section(C)) { C->sections.back().PaletteProperties.emplace_back(); @@ -914,7 +914,7 @@ namespace mc { * indice_bit_count is the number of bits in the slice and may not exceed 32 since the return type is int. */ template - inline boost::optional get_indice(int x, int z, int y, boost::shared_ptr> &arr) { + inline boost::optional get_indice(int x, int z, int y, boost::shared_ptr> &arr) { size_t indice_index = (y * 16 + z) * 16 + x; // Compiler should catch that this is static and pre-compute it, @@ -927,11 +927,11 @@ namespace mc { size_t element_index = (indice_index - index_in_element) / indice_count_in_element; if (arr->length < 0 || element_index >= static_cast(arr->length)) - return boost::optional(); + return boost::optional(); T element = arr->values[element_index]; - int out_bits = ~(0xFFFFFFFF << indice_bit_count); - return boost::optional((element >> (indice_bit_count * index_in_element)) & out_bits); + uint32_t out_bits = ~(0xFFFFFFFF << indice_bit_count); + return boost::optional((element >> (indice_bit_count * index_in_element)) & out_bits); } /** @@ -943,7 +943,7 @@ namespace mc { * indice_bit_count is the number of bits in the slice and may not exceed 32 since the return type is int. */ template - inline boost::optional get_stream_indice(int x, int z, int y, boost::shared_ptr> &arr) { + inline boost::optional get_stream_indice(int x, int z, int y, boost::shared_ptr> &arr) { size_t indice_index = (y * 16 + z) * 16 + x; size_t bits_into_stream = indice_index * indice_bit_count; @@ -953,26 +953,26 @@ namespace mc { size_t element_index = (bits_into_stream - bits_in_element) / element_bits; if (arr->length < 0 || element_index >= static_cast(arr->length)) - return boost::optional(); + return boost::optional(); T element = arr->values[element_index]; int result = element >> bits_in_element; size_t got = element_bits - bits_in_element; if (got < indice_bit_count) { if (element_index + 1 >= static_cast(arr->length)) - return boost::optional(); + return boost::optional(); element = arr->values[element_index + 1]; int got_bits = ~(0xFFFFFFFF << got); result = (result & got_bits) | (element << got); } - int out_bits = ~(0xFFFFFFFF << indice_bit_count); - return boost::optional(result & out_bits); + uint32_t out_bits = ~(0xFFFFFFFF << indice_bit_count); + return boost::optional(result & out_bits); } bool Modern_Section_Compound::get_block(BlockT& block, int x, int z, int y) { if (this->BlockStates) { - boost::optional block_data = boost::optional(); + boost::optional block_data = boost::optional(); // Static expansion of dynamic palette resolver; // each section (16**3) contains 4096 unique blocks wihch yields max 2**12 @@ -1036,11 +1036,11 @@ namespace mc { } bool Legacy_Section_Compound::get_block(BlockT& block, int x, int z, int y) { - boost::optional lower_block_type; - boost::optional block_type = get_indice(x, z, y, this->Blocks); + boost::optional lower_block_type; + boost::optional block_type = get_indice(x, z, y, this->Blocks); // Data values are packed two by two and the position LSB decides which // half-byte contains the requested block data value. - boost::optional block_data = get_indice(x, z, y, this->Data); + boost::optional block_data = get_indice(x, z, y, this->Data); if (block_type && block_data) { boost::optional material = get_material_legacy(block_type.get(), block_data.get());