diff --git a/src/types.rs b/src/types.rs index 71835dd..905f0c0 100644 --- a/src/types.rs +++ b/src/types.rs @@ -19,6 +19,7 @@ pub struct BlockY(pub u8); #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct BlockZ(pub u8); +/// X, Y and Z coordinates of a block in a chunk section #[derive(Clone, Copy, PartialEq, Eq)] pub struct BlockCoords { pub x: BlockX, @@ -27,6 +28,11 @@ pub struct BlockCoords { } impl BlockCoords { + /// Computes a block's offset in various data structures + /// + /// Many chunk data structures store block and biome data in the same + /// order. [BlockCoords::offset] computes the offset at which the data + /// for the block at a given coordinate is stored. pub fn offset(&self) -> usize { use BLOCKS_PER_CHUNK as N; let x = self.x.0 as usize; @@ -69,6 +75,9 @@ impl Debug for ChunkCoords { } } +/// Generic array for data stored per chunk of a region +/// +/// Includes various convenient iteration functions. #[derive(Debug, Clone, Copy, Default)] pub struct ChunkArray(pub [[T; CHUNKS_PER_REGION]; CHUNKS_PER_REGION]); @@ -105,11 +114,14 @@ impl IndexMut for ChunkArray { } } +/// Calculate division and remainder at the same time pub trait DivRem where Self: Div, Self: Rem, { + /// Returns the result of the division and remainder operations + /// with the same inputs fn div_rem(self, rhs: Rhs) -> (>::Output, >::Output); } diff --git a/src/world/chunk.rs b/src/world/chunk.rs index 5ccb8f9..e6b2aa1 100644 --- a/src/world/chunk.rs +++ b/src/world/chunk.rs @@ -11,41 +11,62 @@ use super::{ }; use crate::types::*; +/// Chunk data structure wrapping a [de::Chunk] for convenient access to +/// block and biome data +#[derive(Debug)] pub enum Chunk<'a> { + /// Minecraft v1.18+ chunk with biome data moved into sections V1_18 { section_map: BTreeMap, PaletteSectionBiomes<'a>)>, }, + /// Minecraft v1.13+ chunk + /// + /// Block data is stored in an indexed format with variable bit width + /// (depending on the total numer of distinct block types used in a + /// section), and a palette mapping these indices to namespaced + /// block IDs V1_13 { section_map: BTreeMap>, biomes: &'a de::BiomesOld, }, + /// Original pre-1.13 chunk + /// + /// The original chunk format with fixed 8-bit numeric block IDs Old { section_map: BTreeMap>, biomes: &'a de::BiomesOld, }, + /// Unpopulated chunk without any block data Empty, } +/// Inner data structure of [SectionIter] #[derive(Debug, Clone)] enum SectionIterInner<'a> { + /// Iterator over sections of [Chunk::V1_18] V1_18 { iter: btree_map::Iter<'a, SectionY, (PaletteSection<'a>, PaletteSectionBiomes<'a>)>, }, + /// Iterator over sections of [Chunk::V1_13] V1_13 { iter: btree_map::Iter<'a, SectionY, PaletteSection<'a>>, }, + /// Iterator over sections of [Chunk::Old] Old { iter: btree_map::Iter<'a, SectionY, OldSection<'a>>, }, + /// Empty iterator over an unpopulated chunk ([Chunk::Empty]) Empty, } +/// Iterator over the sections of a [Chunk] #[derive(Debug, Clone)] pub struct SectionIter<'a> { inner: SectionIterInner<'a>, } impl<'a> Chunk<'a> { + /// Creates a new [Chunk] from a deserialized [de::Chunk] pub fn new(data: &'a de::Chunk) -> Result { let data_version = data.data_version.unwrap_or_default(); @@ -55,6 +76,7 @@ impl<'a> Chunk<'a> { } } + /// [Chunk::new] implementation for Minecraft v1.18+ chunks fn new_v1_18(data_version: u32, sections: &'a Vec) -> Result { let mut section_map = BTreeMap::new(); @@ -80,6 +102,7 @@ impl<'a> Chunk<'a> { Ok(Chunk::V1_18 { section_map }) } + /// [Chunk::new] implementation for all pre-1.18 chunk variants fn new_old(data_version: u32, level: &'a de::LevelOld) -> Result { let mut section_map_v1_13 = BTreeMap::new(); let mut section_map_old = BTreeMap::new(); @@ -131,6 +154,7 @@ impl<'a> Chunk<'a> { ) } + /// Returns an interator over the chunk's sections and their Y coordinates pub fn sections(&self) -> SectionIter { use SectionIterInner::*; SectionIter { diff --git a/src/world/section.rs b/src/world/section.rs index 265b393..78201a1 100644 --- a/src/world/section.rs +++ b/src/world/section.rs @@ -3,6 +3,11 @@ use anyhow::{bail, Context, Result}; use super::de; use crate::{resource, types::*}; +/// Determine the number of bits required for indexing into a palette of a given length +/// +/// This is basically a base-2 logarithm, with clamping to a minimum value and +/// check against a maximum value. If the result would be greater than the passed +/// `max` value, [None] is returned. fn palette_bits(len: usize, min: u8, max: u8) -> Option { let mut bits = min; while (1 << bits) < len { @@ -16,10 +21,16 @@ fn palette_bits(len: usize, min: u8, max: u8) -> Option { Some(bits) } +/// Trait for common functions of [PaletteSection] and [OldSection] pub trait Section { fn get_block_id(&self, coords: BlockCoords) -> Result<&str>; } +/// Minecraft v1.18+ section biome data +/// +/// The biome data is part of the section structure in Minecraft v1.18+, with +/// the biomes laid out as an array of indices into a palette, similar to the +/// v1.13+ block data. #[derive(Debug)] pub struct PaletteSectionBiomes<'a> { _biomes: Option<&'a fastnbt::LongArray>, @@ -28,6 +39,7 @@ pub struct PaletteSectionBiomes<'a> { } impl<'a> PaletteSectionBiomes<'a> { + /// Constructs a new [PaletteSectionBiomes] from deserialized data structures pub fn new(biomes: Option<&'a fastnbt::LongArray>, palette: &'a Vec) -> Result { let bits = palette_bits(palette.len(), 1, 6).context("Unsupported block palette size")?; @@ -47,6 +59,7 @@ impl<'a> PaletteSectionBiomes<'a> { } } +/// Minecraft v1.13+ section block data #[derive(Debug)] pub struct PaletteSection<'a> { block_states: Option<&'a fastnbt::LongArray>, @@ -56,6 +69,7 @@ pub struct PaletteSection<'a> { } impl<'a> PaletteSection<'a> { + /// Constructs a new [PaletteSection] from deserialized data structures pub fn new( data_version: u32, block_states: Option<&'a fastnbt::LongArray>, @@ -85,6 +99,7 @@ impl<'a> PaletteSection<'a> { }) } + /// Looks up the block type palette index at the given coordinates fn get_palette_index(&self, coords: BlockCoords) -> usize { let Some(block_states) = self.block_states else { return 0; @@ -127,6 +142,7 @@ impl<'a> Section for PaletteSection<'a> { } } +/// Pre-1.13 section block data #[derive(Debug)] pub struct OldSection<'a> { blocks: &'a fastnbt::ByteArray, @@ -134,6 +150,7 @@ pub struct OldSection<'a> { } impl<'a> OldSection<'a> { + /// Constructs a new [OldSection] from deserialized data structures pub fn new(blocks: &'a fastnbt::ByteArray, data: &'a fastnbt::ByteArray) -> Result { use BLOCKS_PER_CHUNK as N;