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.
This commit is contained in:
parent
1d1dd2ed6c
commit
52c34caaa6
@ -16,13 +16,13 @@ impl<'a> ClientPacket for CChunkData<'a> {
|
|||||||
buf.put_i32(self.0.position.z);
|
buf.put_i32(self.0.position.z);
|
||||||
|
|
||||||
let heightmap_nbt =
|
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();
|
.unwrap();
|
||||||
// Heightmaps
|
// Heightmaps
|
||||||
buf.put_slice(&heightmap_nbt);
|
buf.put_slice(&heightmap_nbt);
|
||||||
|
|
||||||
let mut data_buf = ByteBuffer::empty();
|
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
|
let block_count = chunk
|
||||||
.iter()
|
.iter()
|
||||||
.dedup()
|
.dedup()
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::ops::Index;
|
||||||
|
|
||||||
use fastnbt::LongArray;
|
use fastnbt::LongArray;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
block::block_registry::BlockId,
|
block::block_registry::BlockId,
|
||||||
coordinates::{ChunkCoordinates, ChunkRelativeBlockCoordinates},
|
coordinates::{ChunkCoordinates, ChunkRelativeBlockCoordinates, Height},
|
||||||
level::WorldError,
|
level::WorldError,
|
||||||
WORLD_HEIGHT,
|
WORLD_HEIGHT,
|
||||||
};
|
};
|
||||||
@ -16,9 +17,19 @@ const SUBCHUNK_VOLUME: usize = CHUNK_AREA * 16;
|
|||||||
const CHUNK_VOLUME: usize = CHUNK_AREA * WORLD_HEIGHT;
|
const CHUNK_VOLUME: usize = CHUNK_AREA * WORLD_HEIGHT;
|
||||||
|
|
||||||
pub struct ChunkData {
|
pub struct ChunkData {
|
||||||
pub blocks: Box<[BlockId; CHUNK_VOLUME]>,
|
pub blocks: ChunkBlocks,
|
||||||
pub position: ChunkCoordinates,
|
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)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
@ -59,6 +70,74 @@ struct ChunkNbt {
|
|||||||
heightmaps: ChunkHeightmaps,
|
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<Item = &[BlockId; SUBCHUNK_VOLUME]> {
|
||||||
|
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<ChunkRelativeBlockCoordinates> for ChunkBlocks {
|
||||||
|
type Output = BlockId;
|
||||||
|
|
||||||
|
fn index(&self, index: ChunkRelativeBlockCoordinates) -> &Self::Output {
|
||||||
|
&self.blocks[Self::convert_index(index)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ChunkData {
|
impl ChunkData {
|
||||||
pub fn from_bytes(chunk_data: Vec<u8>, at: ChunkCoordinates) -> Result<Self, WorldError> {
|
pub fn from_bytes(chunk_data: Vec<u8>, at: ChunkCoordinates) -> Result<Self, WorldError> {
|
||||||
let chunk_data = match fastnbt::from_bytes::<ChunkNbt>(chunk_data.as_slice()) {
|
let chunk_data = match fastnbt::from_bytes::<ChunkNbt>(chunk_data.as_slice()) {
|
||||||
@ -67,7 +146,7 @@ impl ChunkData {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// this needs to be boxed, otherwise it will cause a stack-overflow
|
// 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
|
let mut block_index = 0; // which block we're currently at
|
||||||
|
|
||||||
for section in chunk_data.sections.into_iter() {
|
for section in chunk_data.sections.into_iter() {
|
||||||
@ -107,7 +186,18 @@ impl ChunkData {
|
|||||||
let index = (block >> (i * block_bit_size)) & mask;
|
let index = (block >> (i * block_bit_size)) & mask;
|
||||||
let block = palette[index as usize];
|
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;
|
block_index += 1;
|
||||||
|
|
||||||
// if `SUBCHUNK_VOLUME `is not divisible by `blocks_in_pallete` the block_data
|
// if `SUBCHUNK_VOLUME `is not divisible by `blocks_in_pallete` the block_data
|
||||||
@ -122,19 +212,6 @@ impl ChunkData {
|
|||||||
Ok(ChunkData {
|
Ok(ChunkData {
|
||||||
blocks,
|
blocks,
|
||||||
position: at,
|
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<BlockId, WorldError> {
|
|
||||||
Ok(std::mem::replace(
|
|
||||||
&mut self.blocks
|
|
||||||
[at.y.get_absolute() as usize * CHUNK_AREA + (*at.z * 16 + *at.x) as usize],
|
|
||||||
block_id,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user