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/palette.json b/palette.json index 05390b98..2ac7afe9 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,54 @@ "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 + }, + { + "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 } ] diff --git a/src/dirlist.cpp b/src/dirlist.cpp index 561bacb2..809e9472 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(itr->path().stem().string())) { 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/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..04a9096b 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); + 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++) { - 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..2909d283 100644 --- a/src/engine/isometric_base.hpp +++ b/src/engine/isometric_base.hpp @@ -32,138 +32,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); + BOOST_FOREACH(boost::shared_ptr Section, L->Sections) { 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--) { - 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 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 projected_x = x; + int projected_z = z; + br_blocks.transform_xz(projected_x, projected_z); - 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_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) { diff --git a/src/generate_statistics.cpp b/src/generate_statistics.cpp index f4eaf453..bdd4cf5d 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; } @@ -136,33 +135,23 @@ 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); - + BOOST_FOREACH(boost::shared_ptr Section, L->Sections) { 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->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 (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 f7adaffc..e682c6e8 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..e70138b1 100644 --- a/src/mc/level.cpp +++ b/src/mc/level.cpp @@ -6,10 +6,11 @@ #include "mc/region.hpp" #include "mc/level_info.hpp" +#include #include #include -/* +/* Legacy compound structure; For versions < 1.13 Compound() { Compound(Level) { List(Entities, TAG_Byte, 0): [ ] @@ -22,13 +23,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 } */ @@ -36,82 +497,308 @@ 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() : Y(INT8_MIN) { + } + }; + struct level_context { boost::shared_ptr Level; - + bool error; size_t error_where; const char* error_why; - Section_Compound* tmp_Section; + nbt::Int version; + std::vector sections; section_name p[64]; int pos; - level_context() : error(false), error_where(0), error_why("") - { + level_context() : error(false), version(-1), pos(0), error_where(0), error_why("") { this->Level.reset(new Level_Compound); - this->pos = 0; } }; - inline bool in_level_section(level_context* C) { + 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 { + mc::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); + } + } + + // 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 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) { + 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; + } + } + + // 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 + && C->p[2] == section_name::Sections + && C->p[3] == section_name::None; + } + + inline bool in_level_section_b(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::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 + && 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_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 + && 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; + } + + 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++] = 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; + 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(); } } 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 < 0) { + // Skip invalid height indexes; these are typically 0 - 15. + //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; + 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 || 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) { @@ -119,44 +806,60 @@ 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_a(C) || in_level_section_c(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) { + delete int_array; } 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; } } @@ -165,10 +868,15 @@ 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 || name.compare("data") == 0) { + C->sections.back().BlockStates.reset(long_array); + return; + } + } delete long_array; } - + void error_handler(level_context* C, size_t where, const char *why) { C->error = true; C->error_where = where; @@ -197,15 +905,230 @@ namespace mc { return Level; } + /** + * 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; + + // 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]; + uint32_t out_bits = ~(0xFFFFFFFF << indice_bit_count); + 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 non-truncated bit-packing. + * + * 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 + 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 element_bits = sizeof(T) * 8; + + 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 = element_bits - 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]; + int got_bits = ~(0xFFFFFFFF << got); + result = (result & got_bits) | (element << got); + } + + 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(); + + // Static expansion of dynamic palette resolver; + // 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) { + 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 { + // 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 (block_data.get() < this->palette_size && 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 + // 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 +1155,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..f43db865 100644 --- a/src/mc/level.hpp +++ b/src/mc/level.hpp @@ -3,15 +3,19 @@ #ifndef __MC_LEVEL_HPP__ #define __MC_LEVEL_HPP__ +#include + #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; @@ -35,19 +39,57 @@ 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 { + protected: + nbt::Byte Y; + boost::shared_ptr SkyLight; + 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; + bool stream_bit_packing; + + 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); + }; + + 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 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; } 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); 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; diff --git a/src/players.cpp b/src/players.cpp index e6f13f61..914ad6cc 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) {