Begin implementation of chunk loading

This commit is contained in:
DankParrot 2020-08-12 20:15:51 -07:00
parent 9689e7996f
commit dc6677672a
7 changed files with 210 additions and 7 deletions

View File

@ -20,6 +20,7 @@ feather_src = [
'config/Properties.cpp',
'config/ServerProperties.cpp',
'world/Chunk.cpp',
'world/World.cpp',
'world/WorldManager.cpp',
'world/RegionFile.cpp',

100
src/world/Chunk.cpp Normal file
View File

@ -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<int>("xPos") || pos.z != level.Get<int>("zPos"))
{
Log::Error("Tried to load chunk ({}, {}) from NBT but NBT says chunk pos is ({}, {})",
pos.x, pos.z,
level.Get<int>("xPos"), level.Get<int>("zPos")
);
}
CompoundTag hmTag = level.GetCompound("Heightmaps");
heightmaps = &hmTag;
ListTag<CompoundTag> sectionsList = level.GetList<CompoundTag>("Sections");
int n = 0;
for (CompoundTag& sectData : sectionsList)
{
//std::cout << sectData << "\n";
int8_t y = sectData.Get<int8_t>("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<CompoundTag> palette = sectData.GetList<CompoundTag>("Palette");
for (int i = 0; i < palette.GetLength(); i++)
{
CompoundTag state = palette[i];
const char* name = state.Get<StringTag>("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<LongArrayTag>("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++;
}
}
}

43
src/world/Chunk.h Normal file
View File

@ -0,0 +1,43 @@
#pragma once
#include "Types.h"
#include "nbt/NBT.h"
#include "PalettedContainer.h"
#include "block/state/BlockState.h"
#include <cstdint>
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);
};
};

View File

@ -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
}
}

View File

@ -1,7 +1,10 @@
#pragma once
#include "Common.h"
#include "Types.h"
#include <string>
#include <iostream>
#include <fstream>
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; }

View File

@ -56,19 +56,55 @@ namespace Feather
m_levelData.spawnY = levelDat.Get<int32_t>("SpawnY");
m_levelData.spawnZ = levelDat.Get<int32_t>("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))
{

View File

@ -1,6 +1,8 @@
#pragma once
#include "Common.h"
#include "nbt/NBT.h"
#include "Chunk.h"
#include "Palette.h"
#include <cstdint>
@ -13,6 +15,11 @@ namespace Feather
public:
// global palette of all block states
static const GlobalPalette<BlockState> GLOBAL_PALETTE;
// TEMP
NBT::CompoundTag* m_chunkNBT;
Chunk* m_chunk;
struct LevelData
{
int32_t spawnX, spawnY, spawnZ;