diff --git a/core/include/vf/Anvil.h b/core/include/vf/Anvil.h new file mode 100644 index 0000000..94db0fc --- /dev/null +++ b/core/include/vf/Anvil.h @@ -0,0 +1,89 @@ +#pragma once + +#include +#include +#include +#include "Block.h" +#include "Chunk.h" +#include "Codec.h" +#include "World.h" + +namespace vf { + +constexpr int anvilRegionSpan = 32; // chunk columns per region side (X and Z) +constexpr int32_t heightNone = -2147483647 - 1; + +// Fixed-width index packer: N palette indices of `bitWidth` bits packed into +// 64-bit words, LSB-first, padded so no index straddles a word boundary +// (the modern Anvil layout). Round trips exactly for any width in 1..64. +class BitPacker { +public: + static int bitWidthForPalette(std::size_t paletteSize); + static std::vector pack(const std::vector& values, int bitWidth); + static std::vector unpack(const std::vector& words, int bitWidth, + std::size_t count); +}; + +// One 16^3 section: the distinct blocks it uses plus bit-packed indices into +// that palette. +struct AnvilSection { + int sectionY = 0; + std::vector palette; + int bitWidth = 1; + std::vector packed; + + void encodeFrom(const Chunk& chunk); + void decodeTo(Chunk& chunk) const; +}; + +// One chunk column inside a region: its stacked sections plus a heightmap of +// the highest non-air world-Y per (x,z) column. +struct AnvilColumn { + int localX = 0; + int localZ = 0; + std::vector sections; + std::vector heightmap; + + const AnvilSection* section(int sectionY) const; +}; + +// A region v5 blob: a 32x32 tile of chunk columns, each column compressed via +// the Codec. Chunks are stored by their world chunk coordinate. +class AnvilRegion { +public: + AnvilRegion() = default; + AnvilRegion(int regionX, int regionZ) : regionX(regionX), regionZ(regionZ) {} + + int rx() const { return regionX; } + int rz() const { return regionZ; } + + void putChunk(const Vec3i& chunkCoord, const Chunk& chunk); + bool hasChunk(const Vec3i& chunkCoord) const; + bool getChunk(const Vec3i& chunkCoord, Chunk& out) const; + + std::size_t columnCount() const { return columns.size(); } + std::size_t sectionCount() const; + int32_t heightAt(int localX, int localZ, int x, int z) const; + + ByteBuffer serialize() const; + static bool deserialize(const uint8_t* data, std::size_t size, AnvilRegion& out); + + const std::vector& allColumns() const { return columns; } + +private: + AnvilColumn& columnFor(int localX, int localZ); + const AnvilColumn* findColumn(int localX, int localZ) const; + + int regionX = 0; + int regionZ = 0; + std::vector columns; +}; + +// Splits a world into region v5 tiles by chunk-column position. +std::vector anvilRegionsFromWorld(const World& world); + +// One blob holding every region of a world, and its inverse. +ByteBuffer anvilSerializeWorld(const World& world); +bool anvilDeserializeWorld(const uint8_t* data, std::size_t size, World& out); + +} // namespace vf