Begin implementation of chunk loading
This commit is contained in:
parent
9689e7996f
commit
dc6677672a
|
@ -20,6 +20,7 @@ feather_src = [
|
||||||
'config/Properties.cpp',
|
'config/Properties.cpp',
|
||||||
'config/ServerProperties.cpp',
|
'config/ServerProperties.cpp',
|
||||||
|
|
||||||
|
'world/Chunk.cpp',
|
||||||
'world/World.cpp',
|
'world/World.cpp',
|
||||||
'world/WorldManager.cpp',
|
'world/WorldManager.cpp',
|
||||||
'world/RegionFile.cpp',
|
'world/RegionFile.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<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++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
};
|
||||||
|
};
|
|
@ -8,15 +8,16 @@ using std::string;
|
||||||
namespace Feather
|
namespace Feather
|
||||||
{
|
{
|
||||||
RegionFile::RegionFile(string path)
|
RegionFile::RegionFile(string path)
|
||||||
|
: m_path(path)
|
||||||
{
|
{
|
||||||
std::ifstream stream(path, std::ifstream::binary);
|
m_stream = new std::ifstream(path, std::ifstream::binary);
|
||||||
stream.read(m_header, 8KB);
|
m_stream->read(m_header, 8KB);
|
||||||
|
|
||||||
// Populate m_offsets
|
// Populate m_offsets
|
||||||
for (int i = 0; i < 1024; i++)
|
for (int i = 0; i < 1024; i++)
|
||||||
{
|
{
|
||||||
int32_t readInt = 0;
|
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.
|
// Read big endian ints.
|
||||||
readInt = ReverseBytes(readInt);
|
readInt = ReverseBytes(readInt);
|
||||||
|
@ -26,6 +27,7 @@ namespace Feather
|
||||||
|
|
||||||
//int sector = GetSectorNumber(readInt);
|
//int sector = GetSectorNumber(readInt);
|
||||||
//int sectorCount = GetSectorCount(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
|
// We can record which sectors are used by this region here
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
#include "Types.h"
|
#include "Types.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
namespace Feather
|
namespace Feather
|
||||||
{
|
{
|
||||||
|
@ -10,6 +13,11 @@ namespace Feather
|
||||||
char m_header[8KB];
|
char m_header[8KB];
|
||||||
int32_t m_offsets[1KB];
|
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; }
|
inline int GetOffsetIndex(RegionLocalPos pos) { return pos.x + pos.z * 32; }
|
||||||
public:
|
public:
|
||||||
RegionFile(std::string path);
|
RegionFile(std::string path);
|
||||||
|
@ -20,8 +28,14 @@ namespace Feather
|
||||||
// Contains data for this chunk?
|
// Contains data for this chunk?
|
||||||
inline bool HasChunk(ChunkPos pos) { return GetOffset(RegionLocalPos(pos)) != 0; }
|
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()
|
// 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()
|
// Unpack sector count from result of GetOffset()
|
||||||
static int GetSectorCount(int packedOffset) { return packedOffset & 0xFF; }
|
static int GetSectorCount(int packedOffset) { return packedOffset & 0xFF; }
|
||||||
|
|
|
@ -56,19 +56,55 @@ namespace Feather
|
||||||
m_levelData.spawnY = levelDat.Get<int32_t>("SpawnY");
|
m_levelData.spawnY = levelDat.Get<int32_t>("SpawnY");
|
||||||
m_levelData.spawnZ = levelDat.Get<int32_t>("SpawnZ");
|
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);
|
RegionPos regionPos(spawnChunk);
|
||||||
|
RegionLocalPos regionChunkPos(spawnChunk);
|
||||||
|
|
||||||
stringstream regionFile;
|
stringstream regionFile;
|
||||||
regionFile << "r." << regionPos.x << "." << regionPos.z << ".mca";
|
regionFile << "r." << regionPos.x << "." << regionPos.z << ".mca";
|
||||||
|
|
||||||
fs::path mcaFile = regionsPath / regionFile.str();
|
fs::path mcaFile = regionsPath / regionFile.str();
|
||||||
if (!CheckPath(mcaFile)) return;
|
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());
|
//Log_Info("Spawn Chunk Region File: %s", mcaFile.string().c_str());
|
||||||
|
|
||||||
RegionFile region(mcaFile.string());
|
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))
|
/*for (auto& f : fs::directory_iterator(regionsPath))
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
|
#include "nbt/NBT.h"
|
||||||
|
#include "Chunk.h"
|
||||||
#include "Palette.h"
|
#include "Palette.h"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
@ -13,6 +15,11 @@ namespace Feather
|
||||||
public:
|
public:
|
||||||
// global palette of all block states
|
// global palette of all block states
|
||||||
static const GlobalPalette<BlockState> GLOBAL_PALETTE;
|
static const GlobalPalette<BlockState> GLOBAL_PALETTE;
|
||||||
|
|
||||||
|
// TEMP
|
||||||
|
NBT::CompoundTag* m_chunkNBT;
|
||||||
|
Chunk* m_chunk;
|
||||||
|
|
||||||
struct LevelData
|
struct LevelData
|
||||||
{
|
{
|
||||||
int32_t spawnX, spawnY, spawnZ;
|
int32_t spawnX, spawnY, spawnZ;
|
||||||
|
|
Loading…
Reference in New Issue