diff --git a/src/meson.build b/src/meson.build index df540a0..6e5315c 100644 --- a/src/meson.build +++ b/src/meson.build @@ -20,6 +20,7 @@ feather_src = [ 'config/Properties.cpp', 'config/ServerProperties.cpp', + 'world/Chunk.cpp', 'world/World.cpp', 'world/WorldManager.cpp', 'world/RegionFile.cpp', diff --git a/src/world/Chunk.cpp b/src/world/Chunk.cpp new file mode 100644 index 0000000..12369ff --- /dev/null +++ b/src/world/Chunk.cpp @@ -0,0 +1,100 @@ +#include "Chunk.h" +#include "World.h" + +namespace Feather +{ + Chunk::Chunk(ChunkPos& pos, NBT::CompoundTag& tag) : + pos(pos) + { + using namespace NBT; + CompoundTag level = tag.GetCompound("Level"); + + Log::Trace("Loading chunk ({}, {}) from NBT", pos.x, pos.z); + + if (pos.x != level.Get("xPos") || pos.z != level.Get("zPos")) + { + Log::Error("Tried to load chunk ({}, {}) from NBT but NBT says chunk pos is ({}, {})", + pos.x, pos.z, + level.Get("xPos"), level.Get("zPos") + ); + } + + CompoundTag hmTag = level.GetCompound("Heightmaps"); + heightmaps = &hmTag; + + ListTag sectionsList = level.GetList("Sections"); + + int n = 0; + for (CompoundTag& sectData : sectionsList) + { + + //std::cout << sectData << "\n"; + + + int8_t y = sectData.Get("Y"); + if (y < 0 || y >= NUM_SECTIONS) { + Log::Trace("Skipping section {}", y); + continue; + } + + Log::Trace("Loading section {}/{} at Y {}", n + 1, NUM_SECTIONS, y); + + if (n >= NUM_SECTIONS) + { + Log::Error("Chunk ({}, {}) has more than {} sections!", pos.x, pos.z, NUM_SECTIONS); + break; + } + + // Palette + + ListTag palette = sectData.GetList("Palette"); + + for (int i = 0; i < palette.GetLength(); i++) + { + CompoundTag state = palette[i]; + const char* name = state.Get("Name").GetValue(); + + BlockState stateLookup(name); + int id; + + // TODO: handle this in IdMapper + try { + id = World::GLOBAL_PALETTE.GetID(stateLookup); + } + catch (std::out_of_range& err) { + (void)err; + Log::Warn("Chunk Section Palette maps id {} to invalid block state with name '{}'", i, name); + continue; + } + + Log::Trace(" {} -> {} {}", i, id, name); + } + + // Block States + + LongArrayTag blockStates = sectData.Get("BlockStates"); + + if (!blockStates) { + Log::Warn("Skipping section with no block states."); + continue; + } + + int len = blockStates.GetLength(); + if (len != ChunkSection::NUM_LONGS) { + Log::Warn("Section {} has {} longs, expected {}", y, len, ChunkSection::NUM_LONGS); + } + + ChunkSection section = Sections[y]; + + for (int i = 0; i < blockStates.GetLength() && i < ChunkSection::NUM_LONGS; i++) + { + section.blockStates[i] = blockStates[i]; + } + + n++; + } + + + + } +} \ No newline at end of file diff --git a/src/world/Chunk.h b/src/world/Chunk.h new file mode 100644 index 0000000..68dc1c4 --- /dev/null +++ b/src/world/Chunk.h @@ -0,0 +1,43 @@ +#pragma once + +#include "Types.h" + +#include "nbt/NBT.h" +#include "PalettedContainer.h" +#include "block/state/BlockState.h" + +#include + + +namespace Feather +{ + // 16x16x16 sections of a chunk + class ChunkSection + { + public: + // 4 bits per block + // 16^3 = 4096 blocks total + // 4096 * 4 = 16384 bits = 256x int64 + // 16 blocks per int64 + static constexpr int NUM_LONGS = 256; + + int64_t blockStates[NUM_LONGS]; + }; + + // Chunk: 16x256x16 world chunk + class Chunk + { + static constexpr int NUM_SECTIONS = 16; + + // Each chunk is made of up 16 ChunkSections + ChunkSection Sections[NUM_SECTIONS]; + + ChunkPos pos; + + // UNSERIALIZED + const NBT::CompoundTag* heightmaps; + + public: + Chunk(ChunkPos& pos, NBT::CompoundTag& tag); + }; +}; \ No newline at end of file diff --git a/src/world/RegionFile.cpp b/src/world/RegionFile.cpp index 22f6919..31db49c 100644 --- a/src/world/RegionFile.cpp +++ b/src/world/RegionFile.cpp @@ -8,15 +8,16 @@ using std::string; namespace Feather { RegionFile::RegionFile(string path) + : m_path(path) { - std::ifstream stream(path, std::ifstream::binary); - stream.read(m_header, 8KB); + m_stream = new std::ifstream(path, std::ifstream::binary); + m_stream->read(m_header, 8KB); // Populate m_offsets for (int i = 0; i < 1024; i++) { int32_t readInt = 0; - std::memcpy(&readInt, &m_header[i * 4], sizeof(int32_t)); + std::memcpy(&readInt, &m_header[i * sizeof(int32_t)], sizeof(int32_t)); // Read big endian ints. readInt = ReverseBytes(readInt); @@ -26,6 +27,7 @@ namespace Feather //int sector = GetSectorNumber(readInt); //int sectorCount = GetSectorCount(readInt); + //Log::Trace("{}: Sector [{:x}] Count:{} Num:{}", i, readInt, sectorCount, sector); // We can record which sectors are used by this region here } } diff --git a/src/world/RegionFile.h b/src/world/RegionFile.h index e956054..f8f1f61 100644 --- a/src/world/RegionFile.h +++ b/src/world/RegionFile.h @@ -1,7 +1,10 @@ #pragma once #include "Common.h" #include "Types.h" + #include +#include +#include namespace Feather { @@ -10,6 +13,11 @@ namespace Feather char m_header[8KB]; int32_t m_offsets[1KB]; + // TEMP + std::ifstream* m_stream; + + std::string m_path; + inline int GetOffsetIndex(RegionLocalPos pos) { return pos.x + pos.z * 32; } public: RegionFile(std::string path); @@ -20,8 +28,14 @@ namespace Feather // Contains data for this chunk? inline bool HasChunk(ChunkPos pos) { return GetOffset(RegionLocalPos(pos)) != 0; } + // TEMP + std::ifstream* GetChunkStream(RegionLocalPos pos) { + m_stream->seekg(GetSectorNumber(GetOffset(pos))); + return m_stream; + } + // Unpack sector num from result of GetOffset() - static int GetSectorNumber(int packedOffset) { return packedOffset >> 8; } + static int GetSectorNumber(int packedOffset) { return (packedOffset >> 8) * 4096; } // Unpack sector count from result of GetOffset() static int GetSectorCount(int packedOffset) { return packedOffset & 0xFF; } diff --git a/src/world/World.cpp b/src/world/World.cpp index 621ce56..300ea1c 100644 --- a/src/world/World.cpp +++ b/src/world/World.cpp @@ -56,19 +56,55 @@ namespace Feather m_levelData.spawnY = levelDat.Get("SpawnY"); m_levelData.spawnZ = levelDat.Get("SpawnZ"); - ChunkPos spawnChunk(BlockPos(m_levelData.spawnX, m_levelData.spawnY, m_levelData.spawnZ)); + BlockPos spawnPos(m_levelData.spawnX, m_levelData.spawnY, m_levelData.spawnZ); + ChunkPos spawnChunk(spawnPos); RegionPos regionPos(spawnChunk); + RegionLocalPos regionChunkPos(spawnChunk); stringstream regionFile; regionFile << "r." << regionPos.x << "." << regionPos.z << ".mca"; fs::path mcaFile = regionsPath / regionFile.str(); if (!CheckPath(mcaFile)) return; + Log::Trace("Spawn Chunk: Block({} {} {}) -> Chunk({} {}) -> Region({} {}) -> RegionChunk({} {})", + spawnPos.x, spawnPos.y, spawnPos.z, + spawnChunk.x, spawnChunk.z, + regionPos.x, regionPos.z, + regionChunkPos.x, regionChunkPos.z + ); + + //Log_Info("Spawn Chunk Region File: %s", mcaFile.string().c_str()); - RegionFile region(mcaFile.string()); + + //int regionOffset = region.GetOffset(regionChunkPos); - //std::cout << region << "\n"; + //int sectorNum = RegionFile::GetSectorNumber(regionOffset); + //Log::Trace("Loading chunk from offset: [{}] = {}", regionChunkPos.x + regionChunkPos.z * 32, sectorNum); + + std::ifstream* chunkStream = region.GetChunkStream(regionChunkPos); + + Log::Trace("{}", chunkStream->good()); + + char lengthBytes[5]; + chunkStream->read(lengthBytes, 5); + + int32_t length = 0; + std::memcpy(&length, lengthBytes, sizeof(int32_t)); + length = ReverseBytes(length); + + int8_t compressionType = lengthBytes[4]; + + Log::Trace("Loading chunk: Length: {}. Compression: {}", length, compressionType); + + char* chunkData = new char[length + 1]; + chunkStream->read(chunkData, length); + + m_chunkNBT = new NBT::CompoundTag(chunkData, length); + + std::cout << *m_chunkNBT << "\n"; + + m_chunk = new Chunk(spawnChunk, *m_chunkNBT); /*for (auto& f : fs::directory_iterator(regionsPath)) { diff --git a/src/world/World.h b/src/world/World.h index e49fe91..39a2c58 100644 --- a/src/world/World.h +++ b/src/world/World.h @@ -1,6 +1,8 @@ #pragma once #include "Common.h" +#include "nbt/NBT.h" +#include "Chunk.h" #include "Palette.h" #include @@ -13,6 +15,11 @@ namespace Feather public: // global palette of all block states static const GlobalPalette GLOBAL_PALETTE; + + // TEMP + NBT::CompoundTag* m_chunkNBT; + Chunk* m_chunk; + struct LevelData { int32_t spawnX, spawnY, spawnZ;