From 52c34caaa64902dc77c948787d90fde4acd64e53 Mon Sep 17 00:00:00 2001 From: DaniD3v Date: Sun, 25 Aug 2024 12:17:58 +0200 Subject: [PATCH] Added `ChunkBlocks` abstraction for `ChunkData` ChunkBlocks makes it easy & safer to access blocks of a chunk. It also makes it possible to consistently handle the heightmap updates in 1 place. --- .../src/client/play/c_chunk_data.rs | 4 +- pumpkin-world/src/chunk.rs | 113 +++++++++++++++--- 2 files changed, 97 insertions(+), 20 deletions(-) diff --git a/pumpkin-protocol/src/client/play/c_chunk_data.rs b/pumpkin-protocol/src/client/play/c_chunk_data.rs index 87eef9972..0f4ea20ff 100644 --- a/pumpkin-protocol/src/client/play/c_chunk_data.rs +++ b/pumpkin-protocol/src/client/play/c_chunk_data.rs @@ -16,13 +16,13 @@ impl<'a> ClientPacket for CChunkData<'a> { buf.put_i32(self.0.position.z); let heightmap_nbt = - fastnbt::to_bytes_with_opts(&self.0.heightmaps, fastnbt::SerOpts::network_nbt()) + fastnbt::to_bytes_with_opts(&self.0.blocks.heightmap, fastnbt::SerOpts::network_nbt()) .unwrap(); // Heightmaps buf.put_slice(&heightmap_nbt); let mut data_buf = ByteBuffer::empty(); - self.0.blocks.chunks(16 * 16 * 16).for_each(|chunk| { + self.0.blocks.iter_subchunks().for_each(|chunk| { let block_count = chunk .iter() .dedup() diff --git a/pumpkin-world/src/chunk.rs b/pumpkin-world/src/chunk.rs index 951d975cd..f0a27f5f3 100644 --- a/pumpkin-world/src/chunk.rs +++ b/pumpkin-world/src/chunk.rs @@ -1,12 +1,13 @@ use std::cmp::max; use std::collections::HashMap; +use std::ops::Index; use fastnbt::LongArray; use serde::{Deserialize, Serialize}; use crate::{ block::block_registry::BlockId, - coordinates::{ChunkCoordinates, ChunkRelativeBlockCoordinates}, + coordinates::{ChunkCoordinates, ChunkRelativeBlockCoordinates, Height}, level::WorldError, WORLD_HEIGHT, }; @@ -16,9 +17,19 @@ const SUBCHUNK_VOLUME: usize = CHUNK_AREA * 16; const CHUNK_VOLUME: usize = CHUNK_AREA * WORLD_HEIGHT; pub struct ChunkData { - pub blocks: Box<[BlockId; CHUNK_VOLUME]>, + pub blocks: ChunkBlocks, pub position: ChunkCoordinates, - pub heightmaps: ChunkHeightmaps, +} + +pub struct ChunkBlocks { + // TODO make this a Vec that doesn't store the upper layers that only contain air + + // The packet relies on this ordering -> leave it like this for performance + /// Ordering: yzx (y being the most significant) + blocks: Box<[BlockId; CHUNK_VOLUME]>, + + /// See `https://minecraft.fandom.com/wiki/Heightmap` for more info + pub heightmap: ChunkHeightmaps, } #[derive(Deserialize, Debug, Clone)] @@ -59,6 +70,74 @@ struct ChunkNbt { heightmaps: ChunkHeightmaps, } +// TODO @DaniD3v implement once Fre is done with heightmaps +// impl Default for ChunkBlocks { +// fn default() -> Self { +// Self { +// blocks: Box::new([BlockId::default(); CHUNK_VOLUME]), +// heightmap: todo!(), +// } +// } +// } + +impl ChunkBlocks { + pub fn empty_with_heightmap(heightmap: ChunkHeightmaps) -> Self { + Self { + blocks: Box::new([BlockId::default(); CHUNK_VOLUME]), + heightmap, + } + } + + /// Sets the given block in the chunk, returning the old block + pub fn set_block( + &mut self, + position: ChunkRelativeBlockCoordinates, + block: BlockId, + ) -> BlockId { + // TODO @LUK_ESC? update the heightmap + self.set_block_no_heightmap_update(position, block) + } + + /// Sets the given block in the chunk, returning the old block + /// Contrary to `set_block` this does not update the heightmap. + /// + /// Only use this if you know you don't need to update the heightmap + /// or if you manually set the heightmap in `empty_with_heightmap` + pub fn set_block_no_heightmap_update( + &mut self, + position: ChunkRelativeBlockCoordinates, + block: BlockId, + ) -> BlockId { + std::mem::replace(&mut self.blocks[Self::convert_index(position)], block) + } + + pub fn iter_subchunks(&self) -> impl Iterator { + self.blocks + .chunks(SUBCHUNK_VOLUME) + .map(|subchunk| subchunk.try_into().unwrap()) + } + + fn convert_index(index: ChunkRelativeBlockCoordinates) -> usize { + // % works for negative numbers as intended. + index.y.get_absolute() as usize * CHUNK_AREA + *index.z as usize * 16 + *index.x as usize + } + + #[allow(dead_code)] + fn calculate_heightmap(&self) -> ChunkHeightmaps { + // figure out how LongArray is formatted + // figure out how to find out if block is motion blocking + todo!() + } +} + +impl Index for ChunkBlocks { + type Output = BlockId; + + fn index(&self, index: ChunkRelativeBlockCoordinates) -> &Self::Output { + &self.blocks[Self::convert_index(index)] + } +} + impl ChunkData { pub fn from_bytes(chunk_data: Vec, at: ChunkCoordinates) -> Result { let chunk_data = match fastnbt::from_bytes::(chunk_data.as_slice()) { @@ -67,7 +146,7 @@ impl ChunkData { }; // this needs to be boxed, otherwise it will cause a stack-overflow - let mut blocks = Box::new([BlockId::default(); CHUNK_VOLUME]); + let mut blocks = ChunkBlocks::empty_with_heightmap(chunk_data.heightmaps); let mut block_index = 0; // which block we're currently at for section in chunk_data.sections.into_iter() { @@ -107,7 +186,18 @@ impl ChunkData { let index = (block >> (i * block_bit_size)) & mask; let block = palette[index as usize]; - blocks[block_index] = block; + // TODO allow indexing blocks directly so we can just use block_index and save some time? + // this is fine because we initalized the heightmap of `blocks` + // from the cached value in the world file + blocks.set_block_no_heightmap_update( + ChunkRelativeBlockCoordinates { + z: ((block_index % CHUNK_AREA) / 16).into(), + y: Height::from_absolute((block_index / CHUNK_AREA) as u16), + x: (block_index % 16).into(), + }, + block, + ); + block_index += 1; // if `SUBCHUNK_VOLUME `is not divisible by `blocks_in_pallete` the block_data @@ -122,19 +212,6 @@ impl ChunkData { Ok(ChunkData { blocks, position: at, - heightmaps: chunk_data.heightmaps, }) } - /// Sets the given block in the chunk, returning the old block - pub fn set_block( - &mut self, - at: ChunkRelativeBlockCoordinates, - block_id: BlockId, - ) -> Result { - Ok(std::mem::replace( - &mut self.blocks - [at.y.get_absolute() as usize * CHUNK_AREA + (*at.z * 16 + *at.x) as usize], - block_id, - )) - } }