FeatherMC/src/world/Chunk.cpp

152 lines
3.9 KiB
C++

#include "Chunk.h"
#include "World.h"
namespace Feather
{
Chunk::Chunk(ChunkPos& pos, std::unique_ptr<NBT::CompoundTag> tag)
: pos(pos), nbt(std::move(tag))
{
using namespace NBT;
CompoundTag level = nbt->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")
);
}
// TODO: Heightmaps
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;
}
ChunkSection* section = &sections[y];
// Section Palette
ListTag<CompoundTag> palette = sectData.GetList<CompoundTag>("Palette");
for (size_t i = 0; i < palette.GetLength(); i++)
{
CompoundTag state = palette[i];
const char* name = state.Get<StringTag>("Name").GetValue();
BlockState stateLookup(name);
CompoundTag properties = state.GetCompound("Properties");
if (properties)
{
for (auto& prop : properties)
{
stateLookup.AddProperty(prop.GetName(), prop.GetValueString());
}
}
int id;
// TODO: handle this in IdMapper
try {
id = World::GLOBAL_PALETTE.GetID(stateLookup);
}
catch (std::out_of_range& err) {
(void)err;
int defaultId = World::GLOBAL_PALETTE.GetDefaultID();
section->blockStates.palette.AddMapping(i, defaultId);
Log::Warn("Chunk Section Palette maps id {} to invalid block state with name '{}'", i, name);
continue;
}
// TODO: could use Add()
section->blockStates.palette.AddMapping(i, id);
//Log::Trace(" {} -> {} {}", i, id, name);
}
// Block States
LongArrayTag blockStates = sectData.Get<LongArrayTag>("BlockStates");
if (!blockStates) {
Log::Warn("Skipping section with no block states.");
continue;
}
Assert(blockStates.GetLength() < section->blockStates.GetNumLongs());
{
auto [data, lock] = section->blockStates.data.borrow();
for (size_t i = 0; i < blockStates.GetLength(); i++)
data[i] = blockStates[i];
}
section->blockStates.SetBits(Math::CeilDiv(blockStates.GetLength() * bitsizeof(int64), ChunkSection::NUM_BLOCKS));
n++;
}
}
bool Chunk::SetBlock(const BlockPos& pos, const BlockState& state)
{
int i = GetSection(pos);
ChunkSection& section = sections[i];
Log::Debug("Chunk::SetBlock: ({}, {}, {}) -> ({}, {}, {}) Chunk Local",
pos.x, pos.y, pos.z,
pos.x & 0xF, pos.y & 0xF, pos.z & 0xF
);
int index = GetSectionIndex(pos);
Log::Debug("Chunk::SetBlock -> section {}, index {}", i, index);
//if (!section.blockStates.palette.HasValue(state))
// TODO: temp code. use BlockState instead of global ID
{
int id = World::GLOBAL_PALETTE.GetID(state);
if (!section.blockStates.palette.HasValue(id)) {
Log::Warn("Chunk::SetBlock for block that is not in section's local palette. NYI");
return false;
}
int localId = section.blockStates.palette.GetID(id);
uint prev = section.blockStates.GetAndSet(index, localId);
Log::Debug("Chunk::SetBlock changed block from {} to {}", prev, localId);
}
//section.blockStates.GetAndSet(GetSectionIndex(pos), )
return true;
}
}