From cc011481f342077cb523a2c9d2d4975552466173 Mon Sep 17 00:00:00 2001 From: Akashic Records <76927522+akashic-records-of-the-abyss@users.noreply.github.com> Date: Mon, 6 Jan 2025 17:45:02 +0000 Subject: [PATCH 1/5] [DOES NOT WORK]: Finished making the API This commit isn't stable. No writes below Y 0 are saved into the world. To make this production ready. Fixing that bug, adding more tests, and adding some examples are needed --- crates/level/Cargo.toml | 1 + crates/level/src/level/chunk.rs | 539 --------- crates/level/src/level/chunk_cache.rs | 27 - .../src/level/db_interface/bedrock_key.rs | 8 +- crates/level/src/level/db_interface/rusty.rs | 87 +- crates/level/src/level/error.rs | 80 ++ crates/level/src/level/file_interface.rs | 96 +- crates/level/src/level/level.rs | 1058 +++++++++-------- crates/level/src/level/mod.rs | 3 +- crates/level/src/level/sub_chunk.rs | 880 +++++++------- crates/level/src/level/world_block.rs | 150 ++- crates/level/src/lib.rs | 2 +- crates/level/src/types/clear_cache.rs | 110 -- crates/level/src/types/miner.rs | 29 - crates/level/src/types/mod.rs | 2 - crates/level/src/utility/miner.rs | 50 + crates/level/src/utility/mod.rs | 1 + crates/level/test_level/db/000003.log | Bin 0 -> 8242 bytes crates/level/test_level/db/000044.ldb | Bin 83098 -> 0 bytes crates/level/test_level/db/CURRENT | 2 +- crates/level/test_level/db/LOCK | 0 crates/level/test_level/db/LOG | 4 + crates/level/test_level/db/LOG.old | 4 + crates/level/test_level/db/MANIFEST-000002 | Bin 0 -> 50 bytes crates/level/test_level/db/MANIFEST-000041 | Bin 247 -> 0 bytes crates/level/tests/api_test.rs | 128 +- 26 files changed, 1326 insertions(+), 1935 deletions(-) delete mode 100644 crates/level/src/level/chunk.rs delete mode 100644 crates/level/src/level/chunk_cache.rs create mode 100644 crates/level/src/level/error.rs delete mode 100644 crates/level/src/types/clear_cache.rs delete mode 100644 crates/level/src/types/miner.rs delete mode 100644 crates/level/src/types/mod.rs create mode 100644 crates/level/src/utility/miner.rs create mode 100644 crates/level/src/utility/mod.rs create mode 100644 crates/level/test_level/db/000003.log delete mode 100644 crates/level/test_level/db/000044.ldb create mode 100644 crates/level/test_level/db/LOCK create mode 100644 crates/level/test_level/db/LOG create mode 100644 crates/level/test_level/db/LOG.old create mode 100644 crates/level/test_level/db/MANIFEST-000002 delete mode 100644 crates/level/test_level/db/MANIFEST-000041 diff --git a/crates/level/Cargo.toml b/crates/level/Cargo.toml index 3980cc10..34e8ff19 100644 --- a/crates/level/Cargo.toml +++ b/crates/level/Cargo.toml @@ -21,6 +21,7 @@ vek = "0.17" [dev-dependencies] rand = "0.8" copy_dir = "0.1.3" +anyhow = "1.0.95" [[test]] diff --git a/crates/level/src/level/chunk.rs b/crates/level/src/level/chunk.rs deleted file mode 100644 index 6a110c74..00000000 --- a/crates/level/src/level/chunk.rs +++ /dev/null @@ -1,539 +0,0 @@ -use crate::level::level::LevelModificationProvider; -use crate::level::sub_chunk::SubChunkTrait; -use crate::level::world_block::WorldBlockTrait; -use bedrockrs_shared::world::dimension::Dimension; -use std::fmt::Debug; -use thiserror::Error; -use vek::{Vec2, Vec3}; - -/// Specifies the type of filter used when filling a region in the world. -/// -/// # Type Parameters -/// - `UserBlockType`: A block type that implements the `WorldBlockTrait`. -pub enum FillFilter { - /// Fills the entire region unconditionally, overwriting all blocks. - Blanket, - - /// Replaces only the blocks that match the specified type. - /// - /// # Parameters - /// - `UserBlockType`: The block type to be replaced. - Replace(UserBlockType), - - /// Avoids overwriting blocks that match the specified type. - /// - /// # Parameters - /// - `UserBlockType`: The block type to avoid. - Avoid(UserBlockType), - - /// Uses a custom precedence function to determine whether a block should be replaced. - /// - /// # Parameters - /// - A boxed function with the following parameters: - /// - `&UserBlockType`: The current block being evaluated. - /// - `Vec3`: The local coordinates of the block within the current subchunk. - /// - `Vec2`: The world-space XZ coordinates of the chunk. - /// - `i8`: The subchunk y. - /// - /// # Returns - /// - `bool`: `true` to allow replacing the block, `false` to skip it. - Precedence(Box, Vec2, i8) -> bool>), -} - -impl FillFilter { - pub fn may_place(&self, target: &T, position: Vec3, xz: Vec2, y: i8) -> bool { - match self { - FillFilter::Blanket => true, - FillFilter::Replace(v) => v == target, - FillFilter::Avoid(v) => v != target, - FillFilter::Precedence(f) => f(target, position, xz, y), - } - } -} - -#[derive(Error, Debug)] -pub enum FillError { - #[error("Attempted to fill Subchunk {0} and got none back")] - MissingSubchunk(i8), - #[error("Attempted to read block at x: {0}, y {1}, z: {2} and got None")] - BlockIndexDidntReturn(u8, u8, u8), -} - -pub trait LevelChunkTrait: Sized -where - >::UserSubchunk: SubChunkTrait, - >::UserBlock: WorldBlockTrait, - ::UserSubChunkType: SubChunkTrait, - <>::UserSubchunk as SubChunkTrait>::BlockType: - WorldBlockTrait, -{ - type UserLevel; // = UserLevel; - type UserBlock; // = UserLevel::UserBlockType; - type UserSubchunk; // = UserLevel::UserSubChunkType; - type UserState; // = UserLevel::UserState; - type Err; - - /// Loads the chunk from the world based on the specified min and max subchunk indices, - /// XZ coordinates, and dimension. - /// - /// # Parameters - /// - `min_max`: Minimum and maximum subchunk indices. - /// - `xz`: The XZ coordinate of the chunk in the world. - /// - `dim`: The dimension where the chunk resides. - /// - `level`: The level data to load the chunk into. - /// - /// # Returns - /// - `Ok(Self)`: The loaded chunk. - /// - `Err(Self::Err)`: An error occurred during loading. - fn load_from_world( - min_max: Vec2, - xz: Vec2, - dim: Dimension, - level: &mut Self::UserLevel, - ) -> Result; - - /// Writes the chunk back into the world, optionally overriding its position and dimension. - /// - /// # Parameters - /// - `level`: The level data where the chunk will be written. - /// - `xz_override`: Optional override for the chunk's XZ coordinates. - /// - `dim_override`: Optional override for the chunk's dimension. - /// - /// # Returns - /// - `Ok(())`: Successfully written to the world. - /// - `Err(Self::Err)`: An error occurred during writing. - fn write_to_world( - self, - level: &mut Self::UserLevel, - xz_override: Option>, - dim_override: Option, - ) -> Result<(), Self::Err>; - - /// Retrieves a mutable reference to the block at the specified XZ and Y coordinates within the chunk. - /// - /// # Parameters - /// - `xz`: The XZ coordinate of the block within the chunk. - /// - `y`: The Y coordinate of the block. - /// - /// # Returns - /// - `Some(&mut Self::UserBlock)`: A mutable reference to the block if it exists. - /// - `None`: No block exists at the specified coordinates. - fn get_block_at_mut(&mut self, xz: Vec2, y: i16) -> Option<&mut Self::UserBlock>; - - /// Retrieves an immutable reference to the block at the specified XZ and Y coordinates within the chunk. - /// - /// # Parameters - /// - `xz`: The XZ coordinate of the block within the chunk. - /// - `y`: The Y coordinate of the block. - /// - /// # Returns - /// - `Some(&Self::UserBlock)`: An immutable reference to the block if it exists. - /// - `None`: No block exists at the specified coordinates. - fn get_block_at(&self, xz: Vec2, y: i16) -> Option<&Self::UserBlock>; - - /// Sets the block at the specified XZ and Y coordinates within the chunk. - /// - /// # Parameters - /// - `block`: The block to set. - /// - `xz`: The XZ coordinate of the block within the chunk. - /// - `y`: The Y coordinate of the block. - /// - /// # Returns - /// - `Ok(())`: Successfully set the block. - /// - `Err(Self::UserSubchunk::Err)`: An error occurred while setting the block. - fn set_block_at( - &mut self, - block: Self::UserBlock, - xz: Vec2, - y: i16, - ) -> Result<(), ::Err>; - - /// Returns the minimum and maximum subchunk indices for this chunk. - /// - /// # Returns - /// - `Vec2`: A vector containing the minimum and maximum subchunk indices. - fn min_max(&self) -> Vec2; - - /// Retrieves an immutable reference to the subchunk at the specified Y index. - /// - /// # Parameters - /// - `y`: The Y index of the subchunk. - /// - /// # Returns - /// - `Some(&Self::UserSubchunk)`: An immutable reference to the subchunk if it exists. - /// - `None`: No subchunk exists at the specified index. - fn get_subchunk(&self, y: i8) -> Option<&Self::UserSubchunk>; - - /// Retrieves a mutable reference to the subchunk at the specified Y index. - /// - /// # Parameters - /// - `y`: The Y index of the subchunk. - /// - /// # Returns - /// - `Some(&mut Self::UserSubchunk)`: A mutable reference to the subchunk if it exists. - /// - `None`: No subchunk exists at the specified index. - fn get_subchunk_mut(&mut self, y: i8) -> Option<&mut Self::UserSubchunk>; - - /// Gets the position of the chunk in world space as XZ coordinates. - /// - /// # Returns - /// - `Vec2`: The chunk's position in world space. - fn pos(&self) -> Vec2; -} - -#[cfg(feature = "default-impl")] -pub mod default_impl { - use super::*; - use crate::level::file_interface::RawWorldTrait; - use crate::level::level::LevelError; - use crate::level::sub_chunk::{SubChunkDecoder, SubChunkTrait}; - use crate::level::world_block::WorldBlockTrait; - use std::marker::PhantomData; - use std::mem::MaybeUninit; - use std::ops::{Deref, DerefMut}; - use std::slice::{Iter, IterMut}; - use std::vec::Vec; - - #[allow(dead_code)] - pub struct LevelChunk< - UserState, - UserSubChunkType, - UserLevelInterface: LevelModificationProvider, - > { - bounds: Vec2, - xz: Vec2, - dim: Dimension, - sections: Vec, - phantom_data: PhantomData, - _phantom_data: PhantomData, - } - - impl Deref - for LevelChunk - { - type Target = Vec; - fn deref(&self) -> &Self::Target { - &self.sections - } - } - - impl DerefMut - for LevelChunk - { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.sections - } - } - - impl< - UserState, - UserBlockType: WorldBlockTrait, - UserSubChunkType: SubChunkTrait, - UserLevelInterface: LevelModificationProvider, - > LevelChunk - { - // This function computes the true index of a subchunk in the section array. - // E.g., with a bounds of -4 to 20 subchunks in size if a raw_index of -4 is passed in it will perform - // -4 + 4 equaling 0 meaning the true index of a subchunk at y -4 is 0; this logic extends to any y level - // y 2 would do 2 + 4 meaning the true index of a subchunk at y 2 is 6 - fn true_index(min_max: Vec2, raw_index: i8) -> i8 { - #[cfg(debug_assertions)] - if raw_index < min_max.x || raw_index > min_max.y { - panic!("Y out of bounds"); - } - raw_index + min_max.x.abs() - } - - // The need for this function comes from the fact that when subchunks are in -y the y index is inverted. - // This means that 20 -9 20 is near the bottom of the subchunk were as 20 9 20 is near the top of the subchunk (this is assuming we are using world space coords). - // The Y can be thought more of as an offset from the base. - // The base in this context is 0 if the subchunk is positive y and 16 if the subchunk is negative - fn real_y(raw: i8) -> i8 { - if raw >= 0 { - raw - } else { - 16 + raw - // This looks a little confusing so ill explain. - // raw is a negative number, meaning 16 + raw is basically 16 - abs(raw) - } - } - - /// Returns an iterator over all subchunks in this chunk, ordered from low to high. - /// - /// # Returns - /// - `Iter<'_, UserSubChunkType>`: An iterator over the subchunks. - pub fn iter(&self) -> Iter<'_, UserSubChunkType> { - self.sections.iter() - } - - /// Returns a mutable iterator over all subchunks in this chunk, ordered from low to high. - /// - /// # Returns - /// - `IterMut<'_, UserSubChunkType>`: A mutable iterator over the subchunks. - pub fn iter_mut(&mut self) -> IterMut<'_, UserSubChunkType> { - self.sections.iter_mut() - } - - /// Fetches an immutable reference to the subchunk at the specified Y level. - /// - /// # Parameters - /// - `idx`: The Y level of the desired subchunk. - /// - /// # Returns - /// - `Some(&UserSubChunkType)`: An immutable reference to the subchunk if it exists. - /// - `None`: No subchunk exists at the given Y level. - pub fn get_subchunk(&self, idx: i8) -> Option<&UserSubChunkType> { - self.sections - .get(Self::true_index(self.bounds, idx) as usize) - } - - /// Fetches a mutable reference to the subchunk at the specified Y level. - /// - /// # Parameters - /// - `idx`: The Y level of the desired subchunk. - /// - /// # Returns - /// - `Some(&mut UserSubChunkType)`: A mutable reference to the subchunk if it exists. - /// - `None`: No subchunk exists at the given Y level. - pub fn get_subchunk_mut(&mut self, idx: i8) -> Option<&mut UserSubChunkType> { - self.sections - .get_mut(Self::true_index(self.bounds, idx) as usize) - } - - /// Sets the subchunk at the specified Y level. - /// - /// # Parameters - /// - `y`: The Y level where the subchunk will be set. - /// - `chnk`: The subchunk to set at the specified Y level. - pub fn set_subchunk(&mut self, y: i8, mut chnk: UserSubChunkType) { - chnk.set_y(y); - self[Self::real_y(y) as usize] = chnk; - } - - /// Creates a new chunk filled with empty subchunks. - /// - /// # Parameters - /// - `xz`: The XZ coordinates of the chunk in world space. - /// - `min_max`: The minimum and maximum subchunk indices. - /// - `dim`: The dimension where the chunk resides. - /// - `state`: The state object used to create empty subchunks. - /// - /// # Returns - /// - `Self`: A new chunk with empty subchunks. - pub fn empty( - xz: Vec2, - min_max: Vec2, - dim: Dimension, - state: &mut UserState, - ) -> Self { - let ret_subchunks = (min_max.x..min_max.y) - .map(|y| UserSubChunkType::empty(y, state)) - .collect::>(); - Self { - xz, - bounds: min_max, - sections: ret_subchunks, - dim, - phantom_data: PhantomData, - _phantom_data: PhantomData, - } - } - } - - impl< - UserState, - UserBlockType: WorldBlockTrait, - UserSubChunkType: SubChunkTrait, - UserLevelInterface: LevelModificationProvider< - UserBlockType = UserBlockType, - UserState = UserState, - UserSubChunkType = UserSubChunkType, - Error = LevelError<<::UserWorldInterface as RawWorldTrait>::Err, - <::UserSubChunkDecoder as SubChunkDecoder>::Err, - ::Err,>, - >, - > LevelChunk - where - ::UserWorldInterface: RawWorldTrait, - ::UserSubChunkDecoder: SubChunkDecoder, - ::UserBlockType: WorldBlockTrait, - ::Err: Debug, - <::UserWorldInterface as RawWorldTrait>::Err: Debug, - <::UserSubChunkDecoder as SubChunkDecoder>::Err: Debug, - { - /// Fills the entire chunk with the specified block, applying the given `FillFilter`. - /// - /// # Parameters - /// - `block`: The block to fill the chunk with. - /// - `filter`: The filter defining the conditions for filling blocks. - /// - /// # Returns - /// - `Ok(&mut Self)`: Successfully filled the chunk. - /// - `Err(FillError)`: An error occurred, such as a missing subchunk or invalid block index. - /// - /// # Errors - /// - `FillError::MissingSubchunk(y_level)`: Subchunk at the specified Y level is missing. - /// - `FillError::BlockIndexDidntReturn(x, y, z)`: Block index is invalid or did not return a block. - /// - /// # Behavior - /// Iterates through all blocks in all subchunks, applying the `FillFilter`: - /// - `Blanket`: Fills all blocks unconditionally. - /// - `Replace(mask)`: Replaces only blocks matching the given mask. - /// - `Avoid(mask)`: Fills blocks that do not match the mask. - /// - `Precedence(func)`: Uses a custom function to determine if a block should be replaced. - - pub fn fill_chunk(&mut self, block: UserBlockType, filter: FillFilter) -> Result<&mut Self, FillError> { - let pos = self.pos(); - for y_level in self.bounds.x..self.bounds.y { - let subchunk = self.get_subchunk_mut(y_level) - .ok_or(FillError::MissingSubchunk(y_level))?; - for z in 0..16u8 { - for y in 0..16u8 { - for x in 0..16u8 { - let blk = subchunk - .get_block((x, y, z).into()) - .ok_or(FillError::BlockIndexDidntReturn(x, y, z))?; - if filter.may_place(blk, (x, y, z).into(), pos, y_level) { - subchunk.set_block((x, y, z).into(), block.clone()).unwrap() - } - } - } - } - } - Ok(self) - } - } - impl< - UserState, - UserBlockType: WorldBlockTrait, - UserSubChunkType: SubChunkTrait, - UserLevelInterface: LevelModificationProvider< - UserBlockType = UserBlockType, - UserState = UserState, - UserSubChunkType = UserSubChunkType, - Error = LevelError<<::UserWorldInterface as RawWorldTrait>::Err, - <::UserSubChunkDecoder as SubChunkDecoder>::Err, - ::Err,>, - >, - > LevelChunkTrait for LevelChunk - where - ::UserWorldInterface: RawWorldTrait, - ::UserSubChunkDecoder: SubChunkDecoder, - ::UserBlockType: WorldBlockTrait, - ::Err: Debug, - <::UserWorldInterface as RawWorldTrait>::Err: Debug, - <::UserSubChunkDecoder as SubChunkDecoder>::Err: Debug, - { - type UserLevel = UserLevelInterface; - type UserBlock = UserBlockType; - type UserSubchunk = UserSubChunkType; - type UserState = UserState; - type Err = LevelError< - ::Err, - ::Err, - ::Err, - >; - - fn load_from_world( - min_max: Vec2, - xz: Vec2, - dim: Dimension, - level: &mut Self::UserLevel, - ) -> Result { - let mut subchunk_list: Vec> = (min_max.x..min_max.y) - .map(|_| MaybeUninit::uninit() ) - .collect(); - for y in min_max.x..min_max.y { - let subchunk = level.get_sub_chunk(xz, y, dim); - match subchunk { - Ok(subchunk) => { - let idx = Self::true_index(min_max, y) as usize; - subchunk_list[idx].write(subchunk); - } - Err(err) => { - for r in min_max.x..y { - // Safety: We are only dropping subchunks which came before this one meaning they have to be init - unsafe { - subchunk_list[Self::true_index(min_max, r) as usize] - .assume_init_drop(); - } - } - return Err(err); - } - } - } - // Safety: Since `MaybeUninit` is a ZST the ABI of the two types is the same - Ok(Self { - bounds: min_max, - xz, - dim, - sections: unsafe { std::mem::transmute(subchunk_list) }, - phantom_data: PhantomData, - _phantom_data: PhantomData - }) - } - - fn write_to_world( - self, - level: &mut Self::UserLevel, - xz_override: Option>, - dim_override: Option, - ) -> Result<(), Self::Err> { - for sub_chnk in self.sections { - level.set_subchunk( - xz_override.unwrap_or(self.xz), - sub_chnk.get_y(), - dim_override.unwrap_or(self.dim), - sub_chnk, - )?; - } - Ok(()) - } - - fn get_block_at_mut(&mut self, xz: Vec2, y: i16) -> Option<&mut Self::UserBlock> { - self.sections - .get_mut(Self::true_index(self.bounds, (y / 16) as i8) as usize)? - .get_block_mut((xz.x, Self::real_y((y / 16) as i8) as u8, xz.y).into()) - } - - fn get_block_at(&self, xz: Vec2, y: i16) -> Option<&Self::UserBlock> { - self.sections - .get(Self::true_index(self.bounds, (y / 16) as i8) as usize)? - .get_block((xz.x, Self::real_y((y / 16) as i8) as u8, xz.y).into()) - } - - fn set_block_at( - &mut self, - block: Self::UserBlock, - xz: Vec2, - y: i16, - ) -> Result<(), ::Err> { - self.sections - .get_mut(Self::true_index(self.bounds, (y / 16) as i8) as usize) - .unwrap() /*TODO: Figure out a way to report this error back to the user*/ - .set_block( - (xz.x, Self::real_y((y / 16) as i8) as u8, xz.y).into(), - block, - ) - } - - fn min_max(&self) -> Vec2 { - self.bounds - } - - fn get_subchunk(&self, y: i8) -> Option<&Self::UserSubchunk> { - self.sections.get(Self::true_index(self.bounds, y) as usize) - } - - fn get_subchunk_mut(&mut self, y: i8) -> Option<&mut Self::UserSubchunk> { - self.sections - .get_mut(Self::true_index(self.bounds, y) as usize) - } - - fn pos(&self) -> Vec2 { - self.xz - } - - - } -} diff --git a/crates/level/src/level/chunk_cache.rs b/crates/level/src/level/chunk_cache.rs deleted file mode 100644 index c341cd0f..00000000 --- a/crates/level/src/level/chunk_cache.rs +++ /dev/null @@ -1,27 +0,0 @@ -use bedrockrs_shared::world::dimension::Dimension; -use vek::Vec2; - -#[derive(Debug, PartialEq, Hash, Eq, Clone)] -pub struct SubchunkCacheKey { - pub xz: Vec2, - pub y: i8, - pub dim: Dimension, -} - -#[derive(Debug, PartialEq, Hash, Eq, Clone)] -pub struct ChunkCacheKey { - xz: Vec2, - sequence_id: usize, -} - -impl SubchunkCacheKey { - pub fn new(xz: Vec2, y: i8, dim: Dimension) -> Self { - Self { xz, y, dim } - } -} - -impl ChunkCacheKey { - pub fn new(xz: Vec2, sequence_id: usize) -> Self { - Self { xz, sequence_id } - } -} diff --git a/crates/level/src/level/db_interface/bedrock_key.rs b/crates/level/src/level/db_interface/bedrock_key.rs index 8f157e7a..0ed50545 100644 --- a/crates/level/src/level/db_interface/bedrock_key.rs +++ b/crates/level/src/level/db_interface/bedrock_key.rs @@ -3,7 +3,7 @@ use crate::level::db_interface::key_level::KeyTypeTag; use bedrockrs_shared::world::dimension::Dimension; use byteorder::{LittleEndian, WriteBytesExt}; use std::io::Cursor; -use vek::Vec2; +use vek::{Vec2, Vec3}; #[derive(Debug)] pub struct ChunkKey { @@ -14,12 +14,12 @@ pub struct ChunkKey { } impl ChunkKey { - pub fn new_subchunk(xz: Vec2, dim: Dimension, y_index: i8) -> Self { + pub fn new_sub_chunk(xyz: Vec3, dim: Dimension) -> Self { Self { - xz, + xz: (xyz.x, xyz.z).into(), dim, key_type: KeyTypeTag::SubChunkPrefix, - y_index: Some(y_index), + y_index: Some(xyz.y as i8), } } diff --git a/crates/level/src/level/db_interface/rusty.rs b/crates/level/src/level/db_interface/rusty.rs index defe29b6..f05ab1b8 100644 --- a/crates/level/src/level/db_interface/rusty.rs +++ b/crates/level/src/level/db_interface/rusty.rs @@ -10,7 +10,6 @@ use rusty_leveldb::compressor::NoneCompressor; use rusty_leveldb::{Compressor, CompressorList, LdbIterator, Options, Status, WriteBatch, DB}; use std::collections::HashSet; use std::io::Cursor; -use std::marker::PhantomData; use std::path::Path; use std::rc::Rc; use thiserror::Error; @@ -71,9 +70,8 @@ pub fn mcpe_options(compression_level: u8) -> Options { } const COMPRESSION_LEVEL: u8 = CompressionLevel::DefaultLevel as u8; -pub struct RustyDBInterface { +pub struct RustyDBInterface { db: DB, - phantom_data: PhantomData, } #[derive(Debug, Error)] @@ -82,9 +80,9 @@ pub enum DBError { DatabaseError(#[from] Status), } -impl RustyDBInterface { - fn build_key_batch(subchunk_batch_info: Vec, data: &mut Vec) -> WriteBatch { - let count = subchunk_batch_info +impl RustyDBInterface { + fn build_key_batch(sub_chunk_batch_info: Vec, data: &mut Vec) -> WriteBatch { + let count = sub_chunk_batch_info .iter() .map(|ele| ele.estimate_size()) .sum(); @@ -94,7 +92,7 @@ impl RustyDBInterface { let mut batch = WriteBatch::default(); - for key in subchunk_batch_info { + for key in sub_chunk_batch_info { let start = buff.position(); key.write_key(&mut buff); @@ -113,58 +111,64 @@ impl RustyDBInterface { } } -impl Drop for RustyDBInterface { +impl Drop for RustyDBInterface { fn drop(&mut self) { self.db.close().unwrap(); } } -impl RawWorldTrait for RustyDBInterface { +impl RawWorldTrait for RustyDBInterface { type Err = DBError; - type UserState = UserState; + type ConstructionInformation = (); + + fn open( + path: Box, + create_if_missing: bool, + _: &mut Self::ConstructionInformation, + ) -> Result { + let mut opts = mcpe_options(COMPRESSION_LEVEL); + opts.create_if_missing = create_if_missing; + let db = DB::open(path, opts)?; + Ok(Self { db }) + } + + fn close(&mut self) -> Result<(), Self::Err> { + self.db.close()?; + Ok(()) + } fn write_bytes_to_key( &mut self, chunk_info: ChunkKey, chunk_bytes: &[u8], - _: &mut Self::UserState, ) -> Result<(), Self::Err> { let mut batch = WriteBatch::default(); batch.put(&Self::build_key(&chunk_info), chunk_bytes); Ok(self.db.write(batch, false)?) } - fn get_bytes_from_key( - &mut self, - chunk_info: ChunkKey, - _: &mut Self::UserState, - ) -> Result>, Self::Err> { + fn get_bytes_from_key(&mut self, chunk_info: ChunkKey) -> Result>, Self::Err> { Ok(self.db.get(&Self::build_key(&chunk_info))) } - fn delete_bytes_at_key( - &mut self, - chunk_info: ChunkKey, - _: &mut Self::UserState, - ) -> Result<(), Self::Err> { + fn delete_bytes_at_key(&mut self, chunk_info: ChunkKey) -> Result<(), Self::Err> { Ok(self.db.delete(&Self::build_key(&chunk_info))?) } - fn write_subchunk_batch( + fn write_sub_chunk_batch( &mut self, - subchunk_batch_info: Vec<(ChunkKey, Vec)>, - _: &mut Self::UserState, + sub_chunk_batch_info: Vec<(ChunkKey, Vec)>, ) -> Result<(), Self::Err> { let mut data: Vec = vec![ 0; - subchunk_batch_info + sub_chunk_batch_info .iter() .map(|(info, _)| info.estimate_size()) .sum() ]; let mut buff: Cursor<&mut [u8]> = Cursor::new(&mut data); let mut batch = WriteBatch::default(); - for (key, _) in &subchunk_batch_info { + for (key, _) in &sub_chunk_batch_info { let start = buff.position(); key.write_key(&mut buff); @@ -178,13 +182,12 @@ impl RawWorldTrait for RustyDBInterface { Ok(self.db.write(batch, false)?) } - fn write_subchunk_marker_batch( + fn write_sub_chunk_marker_batch( &mut self, - subchunk_batch_info: Vec, - _: &mut Self::UserState, + sub_chunk_batch_info: Vec, ) -> Result<(), Self::Err> { let mut data: Vec = Vec::new(); - let batch = Self::build_key_batch(subchunk_batch_info, &mut data); + let batch = Self::build_key_batch(sub_chunk_batch_info, &mut data); Ok(self.db.write(batch, false)?) } @@ -195,29 +198,7 @@ impl RawWorldTrait for RustyDBInterface { key_bytes } - fn new( - path: Box, - create_if_missing: bool, - _: &mut Self::UserState, - ) -> Result { - let mut opts = mcpe_options(COMPRESSION_LEVEL); - opts.create_if_missing = create_if_missing; - let db = DB::open(path, opts)?; - Ok(Self { - db, - phantom_data: PhantomData, - }) - } - - fn close(&mut self) -> Result<(), Self::Err> { - self.db.close()?; - Ok(()) - } - - fn generated_chunks( - &mut self, - _: &mut Self::UserState, - ) -> Result)>, Self::Err> { + fn generated_chunks(&mut self) -> Result)>, Self::Err> { let mut out_set = HashSet::new(); let mut iter = self.db.new_iter()?; diff --git a/crates/level/src/level/error.rs b/crates/level/src/level/error.rs new file mode 100644 index 00000000..d73b04a7 --- /dev/null +++ b/crates/level/src/level/error.rs @@ -0,0 +1,80 @@ +use std::fmt::Debug; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum SubChunkError { + #[error("Failed To Get Layer: {0}")] + LayerError(u8), +} + +#[derive(Error, Debug)] +pub enum SubChunkSerDeError { + #[error(transparent)] + WorldError(WorldError), + #[error(transparent)] + SerDeError(SerDeError), +} + +#[derive(Error, Debug)] +pub enum SubChunkReadWriteError { + #[error(transparent)] + TranslationError(TranslationError), + #[error(transparent)] + WorldError(WorldError), + #[error(transparent)] + SerDeError(SerDeError), + #[error(transparent)] + SubChunkError(#[from] SubChunkError), +} + +#[derive(Error, Debug)] +pub enum LevelFilLError { + #[error(transparent)] + TranslationError(TranslationError), + #[error(transparent)] + WorldError(WorldError), + #[error(transparent)] + DecodeError(Decode), + #[error(transparent)] + EncoderError(Encode), + #[error(transparent)] + SubChunkError(#[from] SubChunkError), +} + +impl + LevelFilLError +{ + pub fn from_decode( + other: SubChunkReadWriteError, + ) -> Self { + match other { + SubChunkReadWriteError::TranslationError(r) => Self::TranslationError(r), + SubChunkReadWriteError::WorldError(r) => Self::WorldError(r), + SubChunkReadWriteError::SerDeError(r) => Self::DecodeError(r), + SubChunkReadWriteError::SubChunkError(r) => Self::SubChunkError(r), + } + } + + pub fn from_encode( + other: SubChunkReadWriteError, + ) -> Self { + match other { + SubChunkReadWriteError::TranslationError(r) => Self::TranslationError(r), + SubChunkReadWriteError::WorldError(r) => Self::WorldError(r), + SubChunkReadWriteError::SerDeError(r) => Self::EncoderError(r), + SubChunkReadWriteError::SubChunkError(r) => Self::SubChunkError(r), + } + } +} + +impl + From> + for SubChunkReadWriteError +{ + fn from(value: SubChunkSerDeError) -> Self { + match value { + SubChunkSerDeError::WorldError(e) => SubChunkReadWriteError::WorldError(e), + SubChunkSerDeError::SerDeError(e) => SubChunkReadWriteError::SerDeError(e), + } + } +} diff --git a/crates/level/src/level/file_interface.rs b/crates/level/src/level/file_interface.rs index 15330a30..b18f362c 100644 --- a/crates/level/src/level/file_interface.rs +++ b/crates/level/src/level/file_interface.rs @@ -3,7 +3,7 @@ use bedrockrs_shared::world::dimension::Dimension; use std::collections::HashSet; use std::ops::Range; use std::path::Path; -use vek::Vec2; +use vek::{Vec2, Vec3}; pub struct DatabaseBatchHolder { collective: Vec, @@ -32,110 +32,76 @@ impl DatabaseBatchHolder { pub trait RawWorldTrait: Sized { type Err; - type UserState; + type ConstructionInformation; + + fn open( + path: Box, + create_if_missing: bool, + information: &mut Self::ConstructionInformation, + ) -> Result; + + fn close(&mut self) -> Result<(), Self::Err>; fn write_bytes_to_key( &mut self, chunk_info: ChunkKey, chunk_bytes: &[u8], - state: &mut Self::UserState, ) -> Result<(), Self::Err>; - fn get_bytes_from_key( - &mut self, - chunk_info: ChunkKey, - state: &mut Self::UserState, - ) -> Result>, Self::Err>; + fn get_bytes_from_key(&mut self, chunk_info: ChunkKey) -> Result>, Self::Err>; - fn delete_bytes_at_key( - &mut self, - chunk_info: ChunkKey, - state: &mut Self::UserState, - ) -> Result<(), Self::Err>; + fn delete_bytes_at_key(&mut self, chunk_info: ChunkKey) -> Result<(), Self::Err>; - fn set_subchunk_raw( + fn set_sub_chunk_raw( &mut self, chunk_info: ChunkKey, chunk_bytes: &[u8], - state: &mut Self::UserState, ) -> Result<(), Self::Err> { - self.write_bytes_to_key(chunk_info, chunk_bytes, state) + self.write_bytes_to_key(chunk_info, chunk_bytes) } - fn get_subchunk_raw( - &mut self, - chunk_info: ChunkKey, - state: &mut Self::UserState, - ) -> Result>, Self::Err> { - self.get_bytes_from_key(chunk_info, state) + fn get_sub_chunk_raw(&mut self, chunk_info: ChunkKey) -> Result>, Self::Err> { + self.get_bytes_from_key(chunk_info) } - fn chunk_exists( - &mut self, - chunk_info: ChunkKey, - state: &mut Self::UserState, - ) -> Result { - Ok(self.get_bytes_from_key(chunk_info, state)?.is_some()) + fn chunk_exists(&mut self, chunk_info: ChunkKey) -> Result { + Ok(self.get_bytes_from_key(chunk_info)?.is_some()) } - fn write_subchunk_batch( + fn write_sub_chunk_batch( &mut self, - subchunk_batch_info: Vec<(ChunkKey, Vec)>, - state: &mut Self::UserState, + sub_chunk_batch_info: Vec<(ChunkKey, Vec)>, ) -> Result<(), Self::Err>; - fn write_subchunk_marker_batch( + fn write_sub_chunk_marker_batch( &mut self, - subchunk_batch_info: Vec, - state: &mut Self::UserState, + sub_chunk_batch_info: Vec, ) -> Result<(), Self::Err>; - fn mark_exist_chunk( - &mut self, - chunk_info: ChunkKey, - state: &mut Self::UserState, - ) -> Result<(), Self::Err> { - self.write_bytes_to_key(chunk_info, &[], state) + fn mark_exist_chunk(&mut self, chunk_info: ChunkKey) -> Result<(), Self::Err> { + self.write_bytes_to_key(chunk_info, &[]) } fn build_key(key: &ChunkKey) -> Vec; - fn new( - path: Box, - create_if_missing: bool, - state: &mut Self::UserState, - ) -> Result; - - fn close(&mut self) -> Result<(), Self::Err>; - - fn generated_chunks( - &mut self, - state: &mut Self::UserState, - ) -> Result)>, Self::Err>; + fn generated_chunks(&mut self) -> Result)>, Self::Err>; fn delete_chunk( &mut self, xz: Vec2, dimension: Dimension, - subchunk_range: Vec2, - state: &mut Self::UserState, + sub_chunk_range: Vec2, ) -> Result<(), Self::Err> { - for y in subchunk_range.x..=subchunk_range.y { - self.delete_subchunk(xz, dimension, y, state)? + for y in sub_chunk_range.x..=sub_chunk_range.y { + self.delete_sub_chunk((xz.x, y as i32, xz.y).into(), dimension)? } - self.delete_bytes_at_key(ChunkKey::chunk_marker(xz, dimension), state)?; + self.delete_bytes_at_key(ChunkKey::chunk_marker(xz, dimension))?; Ok(()) } - fn delete_subchunk( - &mut self, - xz: Vec2, - dimension: Dimension, - y: i8, - state: &mut Self::UserState, - ) -> Result<(), Self::Err> { - self.delete_bytes_at_key(ChunkKey::new_subchunk(xz, dimension, y), state) + fn delete_sub_chunk(&mut self, xyz: Vec3, dimension: Dimension) -> Result<(), Self::Err> { + self.delete_bytes_at_key(ChunkKey::new_sub_chunk(xyz, dimension)) } } diff --git a/crates/level/src/level/level.rs b/crates/level/src/level/level.rs index 4f4e7857..65233d64 100644 --- a/crates/level/src/level/level.rs +++ b/crates/level/src/level/level.rs @@ -1,20 +1,16 @@ -use crate::level::chunk::LevelChunkTrait; -use crate::level::chunk_cache::SubchunkCacheKey; use crate::level::db_interface::bedrock_key::ChunkKey; use crate::level::file_interface::RawWorldTrait; -use crate::level::sub_chunk::{SubChunkDecoder, SubChunkTrait}; -use crate::level::world_block::WorldBlockTrait; -use crate::level_try; -use crate::types::clear_cache::ClearCacheContainer; +use crate::level::sub_chunk::{ + SerDeStoreRef, SerDeTrait, SubChunk, SubChunkDecoder, SubChunkEncoder, SubChunkSerDe, + SubChunkTrait, SubChunkTraitExtended, SubChunkTransition, +}; +use crate::level::world_block::{BlockTransition, LevelBlock}; use bedrockrs_shared::world::dimension::Dimension; use std::collections::hash_set::Iter; use std::collections::HashSet; use std::fmt::Debug; -use std::io::Cursor; -use std::marker::PhantomData; use std::path::Path; -use thiserror::Error; -use vek::Vec2; +use vek::{Vec2, Vec3}; /// This is used when filtering chunks. /// `ChunkSelectionFilter::Dimension` is used to just check if the dimension is the same. @@ -24,7 +20,7 @@ pub enum ChunkSelectionFilter { Filter(Box) -> bool>), } -/// This is used when filtering subchunks. +/// This is used when filtering sub chunks. /// /// `SubchunkSelectionFilter::Dimension` is used to just check if the dimension is the same. /// @@ -36,7 +32,7 @@ pub enum SubchunkSelectionFilter { } impl ChunkSelectionFilter { - pub fn poll(&mut self, chunk_dim: Dimension, pos: Vec2) -> bool { + pub fn valid(&mut self, chunk_dim: Dimension, pos: Vec2) -> bool { match self { ChunkSelectionFilter::Dimension(dim) => dim == &chunk_dim, ChunkSelectionFilter::Filter(func) => func(chunk_dim, pos), @@ -44,16 +40,6 @@ impl ChunkSelectionFilter { } } -#[derive(Error, Debug)] -pub enum LevelError { - #[error(transparent)] - DatabaseError(DataBaseError), - #[error(transparent)] - SubChunkDecodeError(SubChunkDecodeError), - #[error(transparent)] - SubChunkError(SubChunkError), -} - #[derive(Debug)] pub struct LevelConfiguration { pub sub_chunk_range: Vec2, @@ -71,608 +57,666 @@ impl Default for LevelConfiguration { } } +#[derive(Debug)] +pub struct SetBlockConfig { + pub position: Vec3, + pub block: Block, + pub dim: Dimension, + pub layer: u8, +} + +#[derive(Debug)] +pub struct GetBlockConfig { + pub position: Vec3, + pub dim: Dimension, + pub layer: u8, +} + +#[derive(Debug)] +pub struct FillConfig { + pub from: Vec3, + pub to: Vec3, + pub block: Block, + pub dim: Dimension, + pub layer: u8, + pub data_version: u8, +} + #[allow(dead_code)] -pub struct Level< - UserState, - UserWorldInterface: RawWorldTrait, - UserBlockType: WorldBlockTrait, - UserSubChunkType: SubChunkTrait, - UserSubChunkDecoder: SubChunkDecoder, -> { +pub struct Level +where + ::Err: Debug, +{ db: UserWorldInterface, - state: UserState, config: LevelConfiguration, - cached_sub_chunks: ClearCacheContainer, chunk_existence: HashSet<(Dimension, Vec2)>, - _block_type_marker: PhantomData, - _decoder_marker: PhantomData, } #[allow(dead_code)] -impl< - UserState, - UserWorldInterface: RawWorldTrait, - UserBlockType: WorldBlockTrait, - UserSubChunkType: SubChunkTrait, - UserSubChunkDecoder: SubChunkDecoder, - > Level +impl Level where - ::Err: Debug, - ::Err: Debug, ::Err: Debug, { /// Simple function used to open the world - pub fn open( + pub fn open( path: Box, config: LevelConfiguration, - mut state: UserState, - ) -> Result< - Self, - LevelError, - > { - let db = level_try!(DatabaseError, { + information: &mut ConstructionInformation, + ) -> Result + where + UserWorldInterface: RawWorldTrait, + { + let db = { let val = - UserWorldInterface::new(path.clone(), config.create_db_if_missing, &mut state); + UserWorldInterface::open(path.clone(), config.create_db_if_missing, information); if let Ok(v) = val { Ok(v) } else { - UserWorldInterface::new( + UserWorldInterface::open( { let mut buff = path.into_path_buf(); buff.push("db"); buff.into_boxed_path() }, config.create_db_if_missing, - &mut state, + information, ) } - }); + }?; let mut this = Self { db, - state, config, - cached_sub_chunks: ClearCacheContainer::with_threshold(1024), chunk_existence: HashSet::new(), - _block_type_marker: PhantomData, - _decoder_marker: PhantomData, }; - this.chunk_existence = level_try!(DatabaseError, this.db.generated_chunks(&mut this.state)); + this.chunk_existence = this.db.generated_chunks()?; Ok(this) } - /// # Safety - /// This function is marked as `unsafe` because it allows the caller to bypass the caching systems. - /// If modifications are made directly to the underlying database, the cache may become desynchronized, - /// potentially leading to inconsistent. - /// - /// # When Safe to Use - /// It is safe to use this function if you can guarantee that no information held in the cache - /// will be modified or invalidated by your changes. - pub unsafe fn underlying_world_interface(&mut self) -> &mut UserWorldInterface { + /// Provides a mut ref to the underlying database implementation + pub fn underlying_world_interface(&mut self) -> &mut UserWorldInterface { &mut self.db } - pub fn remove_chunk( + /// Fills blocks into the world in the boundaries given. + /// This function works on a global scale meaning it works on/over sub chunk boundaries + /// This uses the default Encoders and Decoders. If custom ones are needed use [`Level::fill_ex`]. + pub fn fill( &mut self, - xz: Vec2, - dimension: Dimension, - ) -> Result<(), UserWorldInterface::Err> { - self.remove_chunk_ex(xz, dimension, self.config.sub_chunk_range.clone()) + config: FillConfig, + ) -> Result< + (), + LevelFilLError< + UserWorldInterface::Err, + ::Err, + ::Err, + ::Err, + >, + > { + self.fill_ex(config, &mut SubChunkSerDe, &mut SubChunkSerDe) } - pub fn remove_chunk_ex( + /// Fills blocks into the world in the boundaries given. + /// This function works on a global scale meaning it works on/over sub chunk boundaries + /// This uses custom Encoders and Decoders + pub fn fill_ex( &mut self, - xz: Vec2, - dimension: Dimension, - subchunk_range: Vec2, - ) -> Result<(), UserWorldInterface::Err> { - self.db - .delete_chunk(xz, dimension, subchunk_range, &mut self.state) + config: FillConfig, + decoder: &mut Decoder, + encoder: &mut Encoder, + ) -> Result< + (), + LevelFilLError< + UserWorldInterface::Err, + Decoder::Err, + Encoder::Err, + ::Err, + >, + > + where + Decoder::Err: Debug, + Encoder::Err: Debug, + { + // This gets the `to` and `from` into a state where `from` will always be the bottom left of the selection and `to` will be the top right + let (from, to) = bounds_left_optimized(config.to, config.from); + + let (min, local_min) = LevelBlock::block_pos_to_sub_chunk(from); + let (max, local_max) = LevelBlock::block_pos_to_sub_chunk(to); + + let block = config.block.into_transition(); + + println!("{min} {max}"); + for x in min.x..=max.x { + for z in min.z..=max.z { + for y in min.y..=max.y { + println!("{x} {y} {z}"); + if in_range(min.x, max.x, x) + && in_range(min.y, max.y, y) + && in_range(min.z, max.z, z) + { + // Since this whole sub chunk must be replaced we will just create a new one to cut down on the overhead of reading it + let pos = (x, y, z).into(); + + let data = + SubChunkTransition::full(pos, config.data_version, block.clone()); + + self.set_sub_chunk_raw(encoder, data, pos, config.dim) + .map_err(|err| LevelFilLError::from_encode(err.into()))?; + } else { + // Now we need to find where this sub chunk lies in the boundary + println!("Called"); + + let invert_x = x == max.x; + let invert_y = y == max.y; + let invert_z = z == max.z; + + let x_range = if invert_x { + 0..=local_max.x + } else { + local_min.x..=15 + }; + + let y_range = if invert_y { + 0..=local_max.y + } else { + local_min.y..=15 + }; + + let z_range = if invert_z { + 0..=local_max.z + } else { + local_min.z..=15 + }; + + // This is slightly convoluted to allow for fewer allocations, + // by generating a sub chunk directly if it didn't exist in the database + // [`Result::unwrap_or_else`] is used over [`Result::unwrap_or`] to bypass the overhead of creating the default even when it's not needed + let mut data = self + .get_sub_chunk_raw::( + (x, y, z).into(), + config.dim, + decoder, + ) + .map_err(|err| LevelFilLError::from_decode(err.into()))? + .map(|transition| { + SubChunk::decode_from_transition(transition, config.dim, &mut ()) + .unwrap() // This is a safe unwrap because `SubChunk::decode_from_translation` never fails + }) + .unwrap_or_else(|| { + { + SubChunk::empty((x, y, z).into(), config.dim).to_init() + } + }); + + for x in x_range { + for z in z_range.clone() { + for y in y_range.clone() { + data.set_block((x, y, z).into(), block.clone()) + .map_err(|err| LevelFilLError::SubChunkError(err))?; + } + } + } + + let translation = data.to_transition(&mut ()).unwrap(); // Safe because this can't fail + dbg!(&translation); + self.set_sub_chunk_raw(encoder, translation, (x, y, z).into(), config.dim) + .map_err(|err| LevelFilLError::from_encode(err.into()))?; + } + } + } + } + Ok(()) } - pub fn remove_subchunk( + /// # Warning + /// This function is incredibly expensive to call due to having to deserialize the sub chunk. Only call this for 1 off cases of getting blocks + /// + /// # Description + /// Gets a block at a specific position in the world. + /// # Info + /// This function uses the default decoder implementation. + /// If a custom Decoder is needed please use [`Level::get_block_ex`]. + pub fn get_block( &mut self, - xz: Vec2, - dimension: Dimension, - y: i8, - ) -> Result<(), UserWorldInterface::Err> { - self.db.delete_subchunk(xz, dimension, y, &mut self.state) - } - - /// Checks if a given chunk exists - pub fn chunk_exists(&mut self, xz: Vec2, dimension: Dimension) -> bool { - self.chunk_existence.contains(&(dimension, xz)) - } - - /// Must call before destruction - pub fn close( - mut self, + config: GetBlockConfig, ) -> Result< - (), - LevelError, + Option, + SubChunkReadWriteError< + UserWorldInterface::Err, + ::Err, + ::Err, + >, > { - level_try!(DatabaseError, self.flush_existence_buffer()); - level_try!(SubChunkDecodeError, self.cull()); - - // Must come after all the other closing steps - level_try!(DatabaseError, self.db.close()); - Ok(()) + self.get_block_ex::( + config.position, + config.dim, + config.layer, + &mut SubChunkSerDe, + ) } - /// Returns all chunks (in the form of its key) that exist in the world - pub fn existence_chunks(&self) -> Iter<'_, (Dimension, Vec2)> { - self.chunk_existence.iter() - } + /// # Warning + /// This function is incredibly expensive to call due to having to deserialize the sub chunk. Only call this for 1 off cases of getting blocks + /// + /// # Description + /// Gets a block at a specific position in the world. + /// # Info + /// This function uses a custom decoder. + /// If a custom layer is not needed and default decoding is fine use [`Level::get_block`]. + pub fn get_block_ex( + &mut self, + pos: Vec3, + dim: Dimension, + layer: u8, + decoder: &mut Decoder, + ) -> Result< + Option, + SubChunkReadWriteError< + UserWorldInterface::Err, + Decoder::Err, + ::Err, + >, + > + where + Decoder::Err: Debug, + { + let (sub_chunk_pos, local_pos) = LevelBlock::block_pos_to_sub_chunk(pos); + + let data = if let Some(data) = self.get_sub_chunk::( + sub_chunk_pos, + dim, + SerDeStoreRef::new(decoder, &mut ()), + )? { + data + } else { + let mut out = SubChunk::empty(sub_chunk_pos, dim); - /// Fetches all chunk keys that satisfy the filter's constraints - pub fn get_chunk_keys(&mut self, mut filter: ChunkSelectionFilter) -> Vec> { - self.chunk_existence - .iter() - .filter_map(|(chunk_dim, pos)| { - if filter.poll(chunk_dim.clone(), *pos) { - Some(*pos) - } else { - None + if layer != 0 { + for _ in 1..=layer { + out.add_sub_layer() } - }) - .collect() + } + out.set_active_layer(layer); + + out + }; + + Ok(data + .get_block(local_pos) + .cloned() + .map(|ele| BlockType::from_transition(ele))) } - /// Fetches all chunks that satisfy the filter - pub fn get_chunks< - T: LevelChunkTrait< - Self, - UserLevel = Level< - UserState, - UserWorldInterface, - UserBlockType, - UserSubChunkType, - UserSubChunkDecoder, - >, + /// # Warning + /// This function is incredibly expensive to call due to having to deserialize and serialize the sub chunk. Only call this for 1 off cases of setting blocks + /// + /// # Description + /// Sets a block at a specific position in the world. + /// # Info + /// This function uses the default decoder and encoder implementations. + /// If a custom Decoder or Encoder is needed please use [`Level::set_block_ex`]. + pub fn set_block( + &mut self, + config: SetBlockConfig, + ) -> Result< + (), + SubChunkReadWriteError< + UserWorldInterface::Err, + ::Err, + ::Err, >, + > { + self.set_block_ex::( + config.block, + config.position, + config.dim, + config.layer, + &mut SubChunkSerDe, + &mut SubChunkSerDe, + ) + } + + /// # Warning + /// This function is incredibly expensive to call due to having to deserialize and serialize the sub chunk. Only call this for 1 off cases of setting blocks + /// + /// # Description + /// Sets a block at a specific position in the world. + /// # Info + /// This function uses a custom Encoder and Decoder. + /// If a custom layer is not needed and default Encoding and Decoding is fine use [`Level::set_block`]. + pub fn set_block_ex< + BlockType: BlockTransition, + Decoder: SubChunkDecoder, + Encoder: SubChunkEncoder, >( &mut self, - mut filter: ChunkSelectionFilter, - min_max: Vec2, - ) -> Result, T::Err> + block: BlockType, + pos: Vec3, + dim: Dimension, + layer: u8, + decoder: &mut Decoder, + encoder: &mut Encoder, + ) -> Result< + (), + SubChunkReadWriteError< + UserWorldInterface::Err, + Encoder::Err, + ::Err, + >, + > where - <, - >>::UserSubchunk as SubChunkTrait>::BlockType: WorldBlockTrait, + Decoder::Err: Debug, + Encoder::Err: Debug, { - let positions: Vec<_> = self - .chunk_existence - .iter() - .filter_map(|(chunk_dim, pos)| { - if filter.poll(*chunk_dim, *pos) { - Some((*chunk_dim, *pos)) - } else { - None + let (sub_chunk_pos, local_pos) = LevelBlock::block_pos_to_sub_chunk(pos); + + let mut data = if let Ok(Some(data)) = self.get_sub_chunk::( + sub_chunk_pos, + dim, + SerDeStoreRef::new(decoder, &mut ()), + ) { + data + } else { + let mut out = SubChunk::empty(sub_chunk_pos, dim); + + if layer != 0 { + for _ in 1..=layer { + out.add_sub_layer() } - }) - .collect(); + } + out.set_active_layer(layer); - positions - .into_iter() - .map(|(dim, pos)| T::load_from_world(min_max, pos, dim, self)) - .collect() + out + }; + + data.set_block(local_pos, block.into_transition())?; + + self.set_sub_chunk::(&data, SerDeStoreRef::new(encoder, &mut ())) } - /// Fetches a subchunk at a given xyz and dimension - pub fn get_sub_chunk( + /// High level function to fetch a sub chunk that contains a specific block. + /// If the sub chunk doesn't exist this will return None + pub fn get_sub_chunk_block_position( &mut self, - xz: Vec2, - y: i8, + pos: Vec3, dim: Dimension, + config: impl SerDeTrait, ) -> Result< - UserSubChunkType, - LevelError, - > { - if self.config.rw_cache { - if let Some(chunk) = self - .cached_sub_chunks - .get(&SubchunkCacheKey::new(xz, y, dim)) - { - return Ok(chunk.state_clone(&mut self.state)); - } - } - let raw_bytes = level_try!( - DatabaseError, - self.db - .get_subchunk_raw(ChunkKey::new_subchunk(xz, dim, y), &mut self.state) - ); - let out = match raw_bytes { - None => Ok::< - (i8, Option), - LevelError< - UserWorldInterface::Err, - UserSubChunkDecoder::Err, - UserSubChunkType::Err, - >, - >((y, None)), - Some(bytes) => { - if bytes.len() < 100 { - // This happens when there is no layers - let out = (y, None); - Ok(out) - } else { - let mut bytes = Cursor::new(bytes); - let data = level_try!( - SubChunkDecodeError, - UserSubChunkDecoder::decode_bytes_as_chunk(&mut bytes, &mut self.state) - ); - let out = ( - y, - Some(level_try!( - SubChunkError, - UserSubChunkType::decode_from_raw(data, &mut self.state) - )), - ); - Ok(out) - } - } - }?; - if self.config.rw_cache { - if let Some(data) = &out.1 { - let new = data.state_clone(&mut self.state); - self.cached_sub_chunks - .insert(SubchunkCacheKey::new(xz, y, dim), new); - } - } - if let None = &out.1 { - Ok(UserSubChunkType::empty(out.0, self.state())) - } else { - Ok(out.1.unwrap()) - } + Option, + SubChunkReadWriteError, + > + where + Decoder::Err: Debug, + SubChunkType::Err: Debug, + { + let (sub_chunk_pos, _) = LevelBlock::block_pos_to_sub_chunk(pos); + self.get_sub_chunk::(sub_chunk_pos, dim, config) } - /// Sets a subchunk at the given xyz and dimension - pub fn set_sub_chunk( + /// High level function to fetch a sub chunk directly from the database. + /// If the sub chunk doesn't exist this will return None + pub fn get_sub_chunk( &mut self, - data: UserSubChunkType, - xz: Vec2, - y: i8, + pos: Vec3, dim: Dimension, + mut config: impl SerDeTrait, ) -> Result< - (), - LevelError, - > { - if self.config.rw_cache { - self.cached_sub_chunks - .insert(SubchunkCacheKey::new(xz, y, dim), data); - level_try!(SubChunkDecodeError, self.perform_flush()); - } else { - let raw = level_try!( - SubChunkDecodeError, - UserSubChunkDecoder::write_as_bytes( - level_try!(SubChunkError, data.to_raw(y, &mut self.state)), - false, - &mut self.state, - ) - ); - let key = ChunkKey::new_subchunk(xz, dim, y); - level_try!( - DatabaseError, - self.db.set_subchunk_raw(key, &raw, &mut self.state) - ); - self.handle_exist(xz, dim); - } - Ok(()) - } + Option, + SubChunkReadWriteError, + > + where + Decoder::Err: Debug, + SubChunkType::Err: Debug, + { + let ser = self.get_sub_chunk_raw::(pos, dim, config.serde())?; - /// Sets a whole chunk in the saved position of the chunk and the saved dimension. - /// `xz_override` lets the xz position be replaced if copying the chunk - /// `dim_override` lets the dimension of the chunk be changed if copying the chunk - pub fn set_chunk_ex< - UserChunkType: LevelChunkTrait< - Self, - UserLevel = Level< - UserState, - UserWorldInterface, - UserBlockType, - UserSubChunkType, - UserSubChunkDecoder, - >, - >, - >( + let ser = match ser { + None => return Ok(None), + Some(e) => e, + }; + + Ok(Some( + SubChunkType::decode_from_transition(ser, dim, config.info()) + .map_err(|ele| SubChunkReadWriteError::TranslationError(ele))?, + )) + } + pub fn get_sub_chunk_raw( &mut self, - chnk: UserChunkType, - xz_override: Option>, - dim_override: Option, - ) -> Result<(), UserChunkType::Err> + pos: Vec3, + dim: Dimension, + decoder: &mut Decoder, + ) -> Result, SubChunkSerDeError> where - <, - >>::UserSubchunk as SubChunkTrait>::BlockType: WorldBlockTrait, + Decoder::Err: Debug, { - chnk.write_to_world(self, xz_override, dim_override) + let bytes = self + .db + .get_sub_chunk_raw(ChunkKey::new_sub_chunk(pos, dim)) + .map_err(|ele| SubChunkSerDeError::WorldError(ele))?; + + let bytes = match bytes { + None => return Ok(None), + Some(e) => e, + }; + + Ok(Some( + decoder + .decode_bytes_as_sub_chunk(&mut std::io::Cursor::new(bytes), (pos.x, pos.z).into()) + .map_err(|ele| SubChunkSerDeError::SerDeError(ele))?, + )) } - /// Sets a whole chunk in the saved position of the chunk and the saved dimension. - pub fn set_chunk< - UserChunkType: LevelChunkTrait< - Self, - UserLevel = Level< - UserState, - UserWorldInterface, - UserBlockType, - UserSubChunkType, - UserSubChunkDecoder, - >, - >, + /// High level function to write a sub chunk directly into the database. + /// Writes the sub chunk at its current dimension and position. + /// If a dimension or position override is required please call [`Level::set_sub_chunk_ex`] + pub fn set_sub_chunk< + SubChunkType: SubChunkTraitExtended, + SubChunkEncoderType: SubChunkEncoder, >( &mut self, - chnk: UserChunkType, - ) -> Result<(), UserChunkType::Err> + sub_chunk: &SubChunkType, + encode_data: impl SerDeTrait< + Info = SubChunkType::TransitionInformation, + SerDe = SubChunkEncoderType, + >, + ) -> Result< + (), + SubChunkReadWriteError< + UserWorldInterface::Err, + SubChunkEncoderType::Err, + SubChunkType::Err, + >, + > where - <, - >>::UserSubchunk as SubChunkTrait>::BlockType: WorldBlockTrait, - <, - >>::UserSubchunk as SubChunkTrait>::BlockType: WorldBlockTrait, + SubChunkEncoderType::Err: Debug, + SubChunkType::Err: Debug, { - self.set_chunk_ex(chnk, None, None) + self.set_sub_chunk_ex( + sub_chunk, + encode_data, + sub_chunk.position(), + sub_chunk.dimension(), + ) } - /// Fetches a chunk from the world at the given xz and dimension and with the given bounds - /// ### Note: - /// `min_max` is the min and max subchunks not blocks - pub fn get_chunk_ex< - UserChunkType: LevelChunkTrait< - Self, - UserLevel = Self, - Err = LevelError< - UserWorldInterface::Err, - UserSubChunkDecoder::Err, - UserSubChunkType::Err, - >, - >, + /// Slightly lower level function to write a sub chunk directly into the database. + /// Writes the sub chunk at the position and dimension provided. + /// If you do not need to override this please use [`Level::set_sub_chunk`] instead + pub fn set_sub_chunk_ex< + SubChunkType: SubChunkTraitExtended, + SubChunkEncoderType: SubChunkEncoder, >( &mut self, - xz: Vec2, + sub_chunk: &SubChunkType, + mut encode_data: impl SerDeTrait< + Info = SubChunkType::TransitionInformation, + SerDe = SubChunkEncoderType, + >, + pos: Vec3, dim: Dimension, - min_max: Vec2, - ) -> Result + ) -> Result< + (), + SubChunkReadWriteError< + UserWorldInterface::Err, + SubChunkEncoderType::Err, + SubChunkType::Err, + >, + > where - <, - >>::UserSubchunk as SubChunkTrait>::BlockType: WorldBlockTrait, + SubChunkType::Err: Debug, + SubChunkEncoderType::Err: Debug, { - UserChunkType::load_from_world(min_max, xz, dim, self) + let intermediate = sub_chunk + .to_transition(encode_data.info()) + .map_err(|ele| SubChunkReadWriteError::TranslationError(ele))?; + Ok(self.set_sub_chunk_raw(encode_data.serde(), intermediate, pos, dim)?) } - /// Fetches a chunk from the world at the given xz and dimension and with the given bounds - /// ### Note: - /// `min_max` is the min and max subchunks not blocks - pub fn get_chunk< - UserChunkType: LevelChunkTrait< - Self, - UserLevel = Self, - Err = LevelError< - UserWorldInterface::Err, - UserSubChunkDecoder::Err, - UserSubChunkType::Err, - >, - >, - >( + /// Lowest level setting function for a sub chunk. + /// This function takes a transition and writes it into the database directly. + /// This is a powerful function which can lead to invalid data being written into the database + pub fn set_sub_chunk_raw( &mut self, - xz: Vec2, + encoder: &mut SubChunkEncoderType, + intermediate: SubChunkTransition, + pos: Vec3, dim: Dimension, - ) -> Result + ) -> Result<(), SubChunkSerDeError> where - <, - >>::UserSubchunk as SubChunkTrait>::BlockType: WorldBlockTrait, + SubChunkEncoderType::Err: Debug, { - self.get_chunk_ex(xz, dim, self.config.sub_chunk_range.clone()) - } + let bytes = encoder + .write_as_bytes(intermediate, false) + .map_err(|ele| SubChunkSerDeError::SerDeError(ele))?; - fn handle_exist(&mut self, xz: Vec2, dim: Dimension) { - self.chunk_existence.insert((dim, xz)); - } + self.db + .set_sub_chunk_raw(ChunkKey::new_sub_chunk(pos, dim), &bytes) + .map_err(|ele| SubChunkSerDeError::WorldError(ele))?; - fn perform_flush(&mut self) -> Result<(), UserSubChunkDecoder::Err> { - let mut batch_info: Vec<(ChunkKey, Vec)> = Vec::new(); - let mut exist_info: Vec = Vec::new(); - self.cached_sub_chunks.cull(|user_key, data| { - let raw = UserSubChunkDecoder::write_as_bytes( - data.to_raw(user_key.y, &mut self.state).unwrap(), - false, - &mut self.state, - )?; - let key = ChunkKey::new_subchunk(user_key.xz, user_key.dim, user_key.y); - - batch_info.push((key, raw)); - exist_info.push(ChunkKey::chunk_marker(user_key.xz, user_key.dim)); - Ok(()) - })?; - if !batch_info.is_empty() { - self.db - .write_subchunk_batch(batch_info, &mut self.state) - .unwrap() - } - if !exist_info.is_empty() { - self.db - .write_subchunk_marker_batch(exist_info, &mut self.state) - .unwrap() - } + self.handle_exist((pos.x, pos.z).into(), dim); Ok(()) } - fn cull(&mut self) -> Result<(), UserSubChunkDecoder::Err> { - let mut batch_info: Vec<(ChunkKey, Vec)> = Vec::new(); - let mut exist_info: Vec = Vec::new(); - self.cached_sub_chunks.clear(|user_key, data| { - let raw = UserSubChunkDecoder::write_as_bytes( - data.to_raw(user_key.y, &mut self.state).unwrap(), - false, - &mut self.state, - )?; - let key = ChunkKey::new_subchunk(user_key.xz, user_key.dim, user_key.y); - - batch_info.push((key, raw)); - exist_info.push(ChunkKey::chunk_marker(user_key.xz, user_key.dim)); - Ok(()) - })?; - if !batch_info.is_empty() { - self.db - .write_subchunk_batch(batch_info, &mut self.state) - .unwrap() - } - if !exist_info.is_empty() { - self.db - .write_subchunk_marker_batch(exist_info, &mut self.state) - .unwrap() - } - Ok(()) - } - - fn flush_existence_buffer(&mut self) -> Result<(), UserWorldInterface::Err> { - for (dim, pos) in &self.chunk_existence { - self.db - .mark_exist_chunk(ChunkKey::chunk_marker(*pos, *dim), &mut self.state)? - } - Ok(()) + /// Used to delete a whole chunk from the world. This clears the data from the database. It only removes data in the configured range of the level. + /// If clearing inside or outside the range which the level covers is needed see [`Level::remove_chunk_ex`] + pub fn remove_chunk( + &mut self, + xz: Vec2, + dimension: Dimension, + ) -> Result<(), UserWorldInterface::Err> { + self.remove_chunk_ex(xz, dimension, self.config.sub_chunk_range.clone()) } -} - -pub trait LevelModificationProvider { - type UserState; - type UserWorldInterface; - type UserBlockType; - type UserSubChunkType; - type UserSubChunkDecoder; - type Error; - fn get_sub_chunk( + /// Used to delete a chunk from the world. This clears data from the database. This removes all sub chunks in the range specified. + /// If you do not need to delete only a section of information use [`Level::remove_chunk`] + pub fn remove_chunk_ex( &mut self, xz: Vec2, - y: i8, - dim: Dimension, - ) -> Result; + dimension: Dimension, + sub_chunk_range: Vec2, + ) -> Result<(), UserWorldInterface::Err> { + self.chunk_existence.remove(&(dimension, xz)); + self.db.delete_chunk(xz, dimension, sub_chunk_range) + } - fn set_subchunk( + /// Removes a sub chunk from the database + pub fn remove_sub_chunk( &mut self, - xz: Vec2, - y: i8, - dim: Dimension, - chnk: Self::UserSubChunkType, - ) -> Result<(), Self::Error>; + xyz: Vec3, + dimension: Dimension, + ) -> Result<(), UserWorldInterface::Err> { + self.db.delete_sub_chunk(xyz, dimension) + } - fn state(&mut self) -> &mut Self::UserState; - fn chunk_exists(&mut self, xz: Vec2, dimension: Dimension) -> bool; -} + /// Checks if a chunk exists and returns the result + pub fn chunk_exists(&mut self, xz: Vec2, dimension: Dimension) -> bool { + self.chunk_existence.contains(&(dimension, xz)) + } -impl< - UserState, - UserWorldInterface: RawWorldTrait, - UserBlockType: WorldBlockTrait, - UserSubChunkType: SubChunkTrait, - UserSubChunkDecoder: SubChunkDecoder, - > LevelModificationProvider - for Level -where - ::Err: Debug, - ::Err: Debug, - ::Err: Debug, -{ - type UserState = UserState; - type UserWorldInterface = UserWorldInterface; - type UserBlockType = UserBlockType; - type UserSubChunkType = UserSubChunkType; - type UserSubChunkDecoder = UserSubChunkDecoder; - type Error = - LevelError; - - fn get_sub_chunk( - &mut self, - xz: Vec2, - y: i8, - dim: Dimension, - ) -> Result { - self.get_sub_chunk(xz, y, dim) + /// Closes the current level and saves all information to the DB + pub fn close(mut self) -> Result<(), UserWorldInterface::Err> { + self.close_internal() } - fn set_subchunk( + /// Returns all chunks (in the form of its key) that exist in the world + pub fn existence_chunks(&self) -> Iter<'_, (Dimension, Vec2)> { + self.chunk_existence.iter() + } + + /// This function WIPES all chunks that exist in the target dimension. This doesn't mean reset. This deletes them + /// This function only effects sub chunks in the current level range. If you only want to delete sections of the chunk please call [`Level::clear_ex`] + pub fn clear(&mut self, dim: Dimension) -> Result<(), UserWorldInterface::Err> { + self.clear_ex(dim, self.config.sub_chunk_range) + } + + /// This function WIPES all chunks that exist in the target dimension. This doesn't mean reset. This deletes them + /// This function + pub fn clear_ex( &mut self, - xz: Vec2, - y: i8, dim: Dimension, - chnk: Self::UserSubChunkType, - ) -> Result<(), Self::Error> { - self.set_sub_chunk(chnk, xz, y, dim) + sub_chunk_range: Vec2, + ) -> Result<(), UserWorldInterface::Err> { + self.get_chunk_keys(ChunkSelectionFilter::Dimension(dim)) + .drain(..) + .try_for_each(|ele| self.remove_chunk_ex(ele, dim, sub_chunk_range)) } - fn state(&mut self) -> &mut Self::UserState { - &mut self.state + /// Fetches all chunk keys that satisfy the filter's constraints + pub fn get_chunk_keys(&mut self, mut filter: ChunkSelectionFilter) -> Vec> { + self.chunk_existence + .iter() + .filter_map(|(chunk_dim, pos)| { + if filter.valid(chunk_dim.clone(), *pos) { + Some(*pos) + } else { + None + } + }) + .collect() + } + + // Internal functions which mainly handle DB layer interactions + fn close_internal(&mut self) -> Result<(), UserWorldInterface::Err> { + self.flush_existence_buffer()?; + + // Must come after all the other closing steps + self.db.close() + } + + fn handle_exist(&mut self, xz: Vec2, dim: Dimension) { + self.chunk_existence.insert((dim, xz)); + } + fn flush_existence_buffer(&mut self) -> Result<(), UserWorldInterface::Err> { + for (dim, pos) in &self.chunk_existence { + self.db + .mark_exist_chunk(ChunkKey::chunk_marker(*pos, *dim))? + } + Ok(()) } +} - fn chunk_exists(&mut self, xz: Vec2, dimension: Dimension) -> bool { - self.chunk_exists(xz, dimension) +impl Drop for Level +where + ::Err: Debug, +{ + fn drop(&mut self) { + self.close_internal().unwrap(); } } #[cfg(feature = "default-impl")] +#[allow(unused_imports)] pub mod default_impl { use super::*; - use crate::level::chunk::default_impl::LevelChunk; use crate::level::db_interface::rusty::RustyDBInterface; - use crate::level::sub_chunk::default_impl::{SubChunk, SubChunkDecoderImpl}; - use crate::level::world_block::default_impl::WorldBlock; - - pub struct BedrockState {} - pub type RawInterface = RustyDBInterface; - pub type BedrockWorldBlock = WorldBlock; - pub type BedrockSubChunk = SubChunk; - pub type BedrockSubChunkDecoder = SubChunkDecoderImpl; - pub type BedrockLevel = Level< - BedrockState, - RawInterface, - BedrockWorldBlock, - BedrockSubChunk, - BedrockSubChunkDecoder, - >; - pub type BedrockChunk = LevelChunk; - pub type BedrockLevelError = LevelError< - ::Err, - ::Err, - ::Err, - >; + use crate::level::sub_chunk::{SerDeStore, SubChunk, SubChunkSerDe}; + use crate::level::world_block::LevelBlock; + + pub type LevelDbInterface = RustyDBInterface; + pub type LevelDbError = ::Err; + pub type BedrockLevel = Level; } + +use crate::level::error::{LevelFilLError, SubChunkReadWriteError, SubChunkSerDeError}; +use crate::utility::miner::{bounds_left_optimized, in_range}; +#[cfg(feature = "default-impl")] +pub use default_impl::*; diff --git a/crates/level/src/level/mod.rs b/crates/level/src/level/mod.rs index 866ee024..ba143a16 100644 --- a/crates/level/src/level/mod.rs +++ b/crates/level/src/level/mod.rs @@ -1,6 +1,5 @@ -pub mod chunk; -pub mod chunk_cache; pub mod db_interface; +mod error; pub mod file_interface; pub mod level; pub mod sub_chunk; diff --git a/crates/level/src/level/sub_chunk.rs b/crates/level/src/level/sub_chunk.rs index 74ede1c2..0f6855d2 100644 --- a/crates/level/src/level/sub_chunk.rs +++ b/crates/level/src/level/sub_chunk.rs @@ -1,546 +1,504 @@ -use crate::level::world_block::WorldBlockTrait; -use std::fmt::Debug; +pub use crate::level::error::SubChunkError; +use crate::level::world_block::{BlockTransition, LevelBlock}; +use crate::utility::miner::idx_3_to_1; +use bedrockrs_shared::world::dimension::Dimension; +use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; +use nbtx::NbtError; +use std::fmt::{Debug, Formatter}; use std::io::Cursor; +use std::io::Write; +use std::mem::MaybeUninit; use thiserror::Error; -use vek::Vec3; +use vek::{Vec2, Vec3}; pub type BlockLayer = (Box<[u16; 4096]>, Vec); -/// Specifies the type of filter used when filling a region in the world. -/// -/// # Type Parameters -/// - `UserBlockType`: A block type that implements the `WorldBlockTrait`. -pub enum SubchunkFillFilter { - /// Fills the entire region unconditionally, overwriting all blocks. - Blanket, - - /// Replaces only the blocks that match the specified type. - /// - /// # Parameters - /// - `UserBlockType`: The block type to be replaced. - Replace(UserBlockType), - - /// Avoids overwriting blocks that match the specified type. - /// - /// # Parameters - /// - `UserBlockType`: The block type to avoid. - Avoid(UserBlockType), - - /// Uses a custom precedence function to determine whether a block should be replaced. - /// - /// # Parameters - /// - A boxed function with the following parameters: - /// - `&UserBlockType`: The current block being evaluated. - /// - `Vec3`: The local coordinates of the block within the current subchunk. - /// - `i8`: The subchunk y. - /// - /// # Returns - /// - `bool`: `true` to allow replacing the block, `false` to skip it. - Precedence(Box, i8) -> bool>), +#[allow(dead_code)] +pub struct SubChunkTransition { + position: Vec3, + data_version: u8, + layers: Vec>, } - -impl SubchunkFillFilter { - pub fn may_place(&self, target: &T, position: Vec3, y: i8) -> bool { - match self { - SubchunkFillFilter::Blanket => true, - SubchunkFillFilter::Replace(v) => v == target, - SubchunkFillFilter::Avoid(v) => v != target, - SubchunkFillFilter::Precedence(f) => f(target, position, y), +impl Debug for SubChunkTransition { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "SubChunkTransition(position ({}), data_version ({}), layer count ({}), ", + self.position, + self.data_version, + self.layers.len() + )?; + for l in &self.layers { + write!(f, "Layer block count ({})", l.1.len())?; } - } -} - -#[derive(Error, Debug)] -pub enum SubchunkFillError { - #[error("Attempted to read block at x: {0}, y {1}, z: {2} and got None")] - BlockIndexDidntReturn(u8, u8, u8), - - #[error(transparent)] - SubchunkError(T), -} -#[allow(dead_code)] -pub struct SubchunkTransitionalData { - y_level: i8, - data_version: u8, - layers: Vec>, + write!(f, ")") + } } -impl SubchunkTransitionalData { - pub fn new(y_level: i8, layer_count: usize) -> Self { +impl SubChunkTransition { + pub fn new(position: Vec3, layer_count: usize, version: u8) -> Self { Self { - y_level, - data_version: 9, + position, + data_version: version, layers: Vec::with_capacity(layer_count), } } - pub fn new_layer(&mut self, data: (Box<[u16; 4096]>, Vec)) { + pub fn new_layer(&mut self, data: (Box<[u16; 4096]>, Vec)) { self.layers.push(data); } + + pub fn full(position: Vec3, version: u8, block: LevelBlock) -> Self { + let mut ret = Self::new(position, 1, version); + let buffer: BlockLayer = (Box::new([0u16; 4096]), vec![block]); + ret.layers.push(buffer); + ret + } + + pub fn air(position: Vec3, version: u8) -> Self { + Self::full(position, version, LevelBlock::air()) + } } -/// The trait which any decoder implementation must implement pub trait SubChunkDecoder { type Err; - type BlockType; - type UserState; - /// This function is responsible for decoding a stream of raw bytes into the intermediate structure used for creating subchunks - fn decode_bytes_as_chunk( + /// This function is responsible for decoding a stream of raw bytes into the intermediate structure used for creating sub chunks + fn decode_bytes_as_sub_chunk( + &mut self, bytes: &mut Cursor>, - state: &mut Self::UserState, - ) -> Result, Self::Err>; + xz: Vec2, + ) -> Result; +} + +pub trait SubChunkEncoder { + type Err; /// This function is responsible /// for encoding the raw block data into a vector of bytes which the database layer can then save. fn write_as_bytes( - chunk_state: SubchunkTransitionalData, + &mut self, + chunk_state: SubChunkTransition, network: bool, - state: &mut Self::UserState, ) -> Result, Self::Err>; } -/// The main trait that any subchunk type must implement -pub trait SubChunkTrait: Sized { - type Err; - type BlockType; - type UserState; +pub struct SerDeStore { + pub serde: SerDeType, + pub information: TransitionType, +} - /// This must create a valid empty state for the subchunk. - /// This may be just an empty optional, or it may be defaulting to air - fn empty(y_index: i8, state: &mut Self::UserState) -> Self; +pub struct SerDeStoreRef<'a, 'u, SerDeType, TransitionType> { + pub serde: &'a mut SerDeType, + pub information: &'u mut TransitionType, +} - /// This must create a valid "full" state for the subchunk from the transitional data - fn decode_from_raw( - data: SubchunkTransitionalData, - state: &mut Self::UserState, - ) -> Result; +impl<'a, 'u, SerDeType, TransitionType> SerDeStoreRef<'a, 'u, SerDeType, TransitionType> { + pub fn new(serde: &'a mut SerDeType, information: &'u mut TransitionType) -> Self { + Self { serde, information } + } +} - /// This must create a transitional state from the current subchunk information - fn to_raw( - &self, - y_level: i8, - state: &mut Self::UserState, - ) -> Result, Self::Err>; - - /// Gets the block at the current position on the active layer - fn get_block(&self, xyz: Vec3) -> Option<&Self::BlockType>; - /// Gets the block at the current position on the active layer - fn get_block_mut(&mut self, xyz: Vec3) -> Option<&mut Self::BlockType>; - /// Sets the block at the current position on the active layer - fn set_block(&mut self, xyz: Vec3, block: Self::BlockType) -> Result<(), Self::Err>; - - /// Gets the active layer - fn get_active_layer(&self) -> u8; - /// Sets the active layer - fn set_active_layer(&mut self, idx: u8); - - /// This generates a new empty sublayer for the subchunk. This may only apply to bedrock, and if so, just unimplement this function - fn add_sub_layer(&mut self, state: &mut Self::UserState); - /// This returns the count of all sublayers - fn get_sub_layer_count(&self) -> usize; - - /// Gets the subchunk Y - fn get_y(&self) -> i8; - /// Sets the subchunk Y - fn set_y(&mut self, y: i8) -> i8; - - /// This is used as a replacement for the normal clone functions. It allows access to the state - fn state_clone(&self, state: &mut Self::UserState) -> Self; - - /// This returns if the subchunk is just air if so nothing is written to the database if this isn't desired behavior just always return false - fn is_empty(&self) -> bool; +pub trait SerDeTrait { + type SerDe; + type Info; + fn serde(&mut self) -> &mut Self::SerDe; + fn info(&mut self) -> &mut Self::Info; } -#[cfg(feature = "default-impl")] -pub mod default_impl { - use super::*; - use crate::level::world_block::{BlockTransitionalState, WorldBlockTrait}; - use crate::types::miner::idx_3_to_1; - use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; - use nbtx::NbtError; - use std::io::{Cursor, Write}; - use std::marker::PhantomData; - use std::mem::MaybeUninit; - use thiserror::Error; - - pub struct SubChunkDecoderImpl { - _block_marker: PhantomData, - _state_marker: PhantomData, - } - - #[derive(Debug, Error)] - pub enum SubChunkDecoderError { - #[error("Missing Subchunk Version")] - SubChunkVersion, - #[error("Unknown Subchunk Version: {0}")] - UnknownVersion(u8), - #[error("Failed To Read Layer Count")] - LayerError, - #[error("Failed To Read Y Index")] - IndexError, - #[error("Failed To Read Palette Type")] - PaletteError, - #[error("Failed To Read Index Word")] - WordError, - #[error("Failed To Read Palette Count")] - PaletteCountError, - #[error("Failed To Slice NBT")] - SliceError, - #[error("Binary Error: {0}")] - BinaryError(#[from] std::io::Error), - #[error("NBT Error: {0}")] - NBTError(#[from] NbtError), - } - - impl, UserState> SubChunkDecoder - for SubChunkDecoderImpl - { - type Err = SubChunkDecoderError; - type BlockType = UserBlockType; - type UserState = UserState; - - fn decode_bytes_as_chunk( - bytes: &mut Cursor>, - state: &mut Self::UserState, - ) -> Result, Self::Err> { - let version = bytes.read_u8()?; - if version != 8 && version != 9 { - return Err(SubChunkDecoderError::UnknownVersion(version)); - } +impl SerDeTrait for SerDeStoreRef<'_, '_, SerDeType, TransitionType> { + type SerDe = SerDeType; + type Info = TransitionType; - let storage_layer_count = bytes.read_u8()?; - let y_index = bytes.read_i8()?; - - let mut transitiondata = - SubchunkTransitionalData::new(y_index, storage_layer_count as usize); - - for _ in 0..storage_layer_count { - let palette_type = bytes.read_u8()?; - let network = palette_type & 0x1 == 1; - let bits_per_block = palette_type >> 1; - let blocks_per_word = 32 / bits_per_block; - let word_count = (4096 + (blocks_per_word as i32) - 1) / (blocks_per_word as i32); - let mask = (1 << bits_per_block) - 1; - let mut pos = 0usize; - let mut block_indices = Box::new([0u16; 4096]); - - for _ in 0..word_count { - let mut word = bytes.read_u32::()?; - for _ in 0..blocks_per_word { - let index: u16 = (word & mask) as u16; - if pos == 4096 { - break; - } - block_indices[pos] = index; - word >>= bits_per_block; - pos += 1; - } - } + fn serde(&mut self) -> &mut Self::SerDe { + self.serde + } - let palette_count = bytes.read_u32::()?; - let mut blocks = Vec::with_capacity(palette_count as usize); - for _ in 0_usize..palette_count as usize { - if network { - blocks.push(Self::BlockType::from_transition( - nbtx::from_bytes::( - bytes, - )?, - state, - )); - } else { - blocks.push(Self::BlockType::from_transition( - nbtx::from_bytes::(bytes)?, - state, - )); - } - } - transitiondata.new_layer((block_indices, blocks)); - } - Ok(transitiondata) - } + fn info(&mut self) -> &mut Self::Info { + self.information + } +} - // TODO: Handle 0, 2, 3, 4 ,5 ,6 7, also handle 1 - fn write_as_bytes( - chunk_state: SubchunkTransitionalData, - network: bool, - state: &mut Self::UserState, - ) -> Result, Self::Err> { - let mut buffer = Cursor::>::new(vec![]); - buffer.write_u8(chunk_state.data_version)?; - buffer.write_u8(chunk_state.layers.len() as u8)?; - buffer.write_i8(chunk_state.y_level)?; - for layer in chunk_state.layers { - let bits_per_block = bits_needed_to_store(layer.1.len() as u32); - buffer.write_u8(bits_per_block << (1 + (network as u8)))?; - - let mut current_word = 0u32; - let mut bits_written = 0; - layer.0.iter().try_for_each(|element| { - let element = *element as u32; - if bits_written + bits_per_block > 32 { - buffer.write_u32::(current_word)?; - current_word = 0; - bits_written = 0; - } +impl SerDeTrait for SerDeStore { + type SerDe = SerDeType; + type Info = TransitionType; - current_word = current_word + (element << bits_written); - bits_written += bits_per_block; - Ok::<(), std::io::Error>(()) - })?; - if bits_written != 0 { - buffer.write_u32::(current_word)?; - } - buffer.write_u32::(layer.1.len() as u32)?; - for blk in layer.1 { - if network { - buffer.write(&nbtx::to_net_bytes(&blk.into_transition(state))?)? - } else { - buffer.write(&nbtx::to_le_bytes(&blk.into_transition(state))?)? - }; - } - } - Ok(buffer.into_inner()) - } + fn serde(&mut self) -> &mut Self::SerDe { + &mut self.serde } - pub struct SubChunk { - blocks: Vec>, - y_index: i8, - active_layer: u8, - is_empty: bool, - _state_tag: PhantomData, + fn info(&mut self) -> &mut Self::Info { + &mut self.information } +} - impl, UserState> - SubChunk - { - pub fn force_non_empty(&mut self) { - self.is_empty = false; +impl Default for SerDeStore { + fn default() -> Self { + Self { + serde: SerDeType::default(), + information: Transition::default(), } + } +} - pub fn force_empty(&mut self) { - self.is_empty = true; - } +/// The main trait that any sub chunk type must implement +pub trait SubChunkTrait: Sized { + type TransitionInformation; + type Err; + /// This must create a valid "full" state for the sub chunk from the transitional data + fn decode_from_transition( + data: SubChunkTransition, + dimension: Dimension, + info: &mut Self::TransitionInformation, + ) -> Result; - pub fn replace(&mut self, other: Self) { - self.blocks = other.blocks; - self.y_index = other.y_index; - self.active_layer = other.active_layer; - self.is_empty = other.is_empty; - } + /// This must create a transitional state from the current sub chunk information + fn to_transition( + &self, + info: &mut Self::TransitionInformation, + ) -> Result; + + /// This returns if the sub chunk is just air if so nothing is written to the database if this isn't desired behavior just always return false + fn is_empty(&self) -> bool; +} - pub fn full(y_index: i8, block: &UserBlockType) -> Self { - let mut val = Self { - blocks: Vec::with_capacity(1), - y_index, - active_layer: 0, - is_empty: true, - _state_tag: PhantomData, - }; - val.blocks - .push(Box::new(std::array::from_fn(|_| block.clone()))); - val +/// An extension of the SubChunkTrait which must be implemented if the simple functions want to be called. Such as [`Level::write_sub_chunk`] +pub trait SubChunkTraitExtended: SubChunkTrait { + fn dimension(&self) -> Dimension; + fn position(&self) -> Vec3; +} + +#[derive(Default, Debug)] +pub struct SubChunkSerDe; + +#[derive(Debug, Error)] +pub enum SubChunkDecoderError { + #[error("Missing Subchunk Version")] + SubChunkVersion, + #[error("Unknown Subchunk Version: {0}")] + UnknownVersion(u8), + #[error("Failed To Read Layer Count")] + LayerError, + #[error("Failed To Read Y Index")] + IndexError, + #[error("Failed To Read Palette Type")] + PaletteError, + #[error("Failed To Read Index Word")] + WordError, + #[error("Failed To Read Palette Count")] + PaletteCountError, + #[error("Failed To Slice NBT")] + SliceError, + #[error("Binary Error: {0}")] + BinaryError(#[from] std::io::Error), + #[error("NBT Error: {0}")] + NBTError(#[from] NbtError), +} + +impl SubChunkDecoder for SubChunkSerDe { + type Err = SubChunkDecoderError; + + fn decode_bytes_as_sub_chunk( + &mut self, + bytes: &mut Cursor>, + xz: Vec2, + ) -> Result { + let version = bytes.read_u8()?; + if version != 8 && version != 9 { + return Err(SubChunkDecoderError::UnknownVersion(version)); } - pub fn fill( - &mut self, - block: &UserBlockType, - filter: SubchunkFillFilter, - ) -> Result<(), SubchunkFillError< as SubChunkTrait>::Err>> - { - for x in 0..16u8 { - for y in 0..16u8 { - for z in 0..16u8 { - let blk = self - .get_block((x, y, z).into()) - .ok_or(SubchunkFillError::BlockIndexDidntReturn(x, y, z))?; - if filter.may_place(blk, (x, y, z).into(), self.y_index) { - self.set_block((x, y, z).into(), block.clone()) - .map_err(|ele| SubchunkFillError::SubchunkError(ele))?; - } + let storage_layer_count = bytes.read_u8()?; + let y_index = bytes.read_i8()?; + + let mut transitiondata = SubChunkTransition::new( + Vec3::new(xz.x, y_index as i32, xz.y), + storage_layer_count as usize, + version, + ); + + for _ in 0..storage_layer_count { + let palette_type = bytes.read_u8()?; + let network = palette_type & 0x1 == 1; + let bits_per_block = palette_type >> 1; + let blocks_per_word = 32 / bits_per_block; + let word_count = (4096 + (blocks_per_word as i32) - 1) / (blocks_per_word as i32); + let mask = (1 << bits_per_block) - 1; + let mut pos = 0usize; + let mut block_indices = Box::new([0u16; 4096]); + + for _ in 0..word_count { + let mut word = bytes.read_u32::()?; + for _ in 0..blocks_per_word { + let index: u16 = (word & mask) as u16; + if pos == 4096 { + break; } + block_indices[pos] = index; + word >>= bits_per_block; + pos += 1; } } - Ok(()) + let palette_count = bytes.read_u32::()?; + let mut blocks = Vec::with_capacity(palette_count as usize); + for _ in 0_usize..palette_count as usize { + if network { + blocks.push(LevelBlock::from_transition(nbtx::from_bytes::< + nbtx::NetworkLittleEndian, + LevelBlock, + >(bytes)?)); + } else { + blocks.push(LevelBlock::from_transition(nbtx::from_bytes::< + nbtx::LittleEndian, + LevelBlock, + >(bytes)?)); + } + } + transitiondata.new_layer((block_indices, blocks)); } + Ok(transitiondata) } - #[derive(Debug, Error)] - pub enum SubChunkError { - #[error("Failed To Get Layer: {0}")] - LayerError(u8), - } - - impl, UserState> SubChunkTrait - for SubChunk - { - type Err = SubChunkError; - type BlockType = UserBlockType; - type UserState = UserState; - - fn empty(y_index: i8, state: &mut Self::UserState) -> Self { - let mut val = Self { - blocks: Vec::with_capacity(1), - y_index, - active_layer: 0, - is_empty: true, - _state_tag: PhantomData, - }; - val.blocks.push(Box::new(std::array::from_fn(|_| { - Self::BlockType::air(state) - }))); - val - } +} + +impl SubChunkEncoder for SubChunkSerDe { + type Err = SubChunkDecoderError; - fn decode_from_raw( - data: SubchunkTransitionalData, - _: &mut Self::UserState, - ) -> Result { - let mut layers: Vec; 4096]>> = (0..data.layers.len()) - .map(|_| Box::new([const { MaybeUninit::uninit() }; 4096])) - .collect(); - for (layer_index, (indices, blocks)) in data.layers.into_iter().enumerate() { - let layer: &mut Box<[MaybeUninit; 4096]> = &mut layers[layer_index]; - for whole_index in 0..4096usize { - layer[whole_index].write(Self::BlockType::from_other( - &blocks[indices[whole_index] as usize], - )); + // TODO: Handle 0, 2, 3, 4 ,5 ,6 7, also handle 1 + fn write_as_bytes( + &mut self, + chunk_state: SubChunkTransition, + network: bool, + ) -> Result, Self::Err> { + let mut buffer = Cursor::>::new(vec![]); + buffer.write_u8(chunk_state.data_version)?; + buffer.write_u8(chunk_state.layers.len() as u8)?; + buffer.write_i8(chunk_state.position.y as i8)?; + for layer in chunk_state.layers { + let bits_per_block = bits_needed_to_store(layer.1.len() as u32); + buffer.write_u8(bits_per_block << (1 + (network as u8)))?; + + let mut current_word = 0u32; + let mut bits_written = 0; + layer.0.iter().try_for_each(|element| { + let element = *element as u32; + if bits_written + bits_per_block > 32 { + buffer.write_u32::(current_word)?; + current_word = 0; + bits_written = 0; } - } - Ok(Self { - blocks: unsafe { std::mem::transmute(layers) }, - y_index: data.y_level, - active_layer: 0, - is_empty: false, - _state_tag: PhantomData, - }) - } - fn to_raw( - &self, - y_level: i8, - _: &mut Self::UserState, - ) -> Result, Self::Err> { - let mut layers: Vec> = - Vec::with_capacity(self.blocks.len()); - for layer in 0..self.blocks.len() { - layers.push(self.encode_single_layer(layer)); + current_word = current_word + (element << bits_written); + bits_written += bits_per_block; + Ok::<(), std::io::Error>(()) + })?; + if bits_written != 0 { + buffer.write_u32::(current_word)?; + } + buffer.write_u32::(layer.1.len() as u32)?; + for blk in layer.1 { + if network { + buffer.write(&nbtx::to_net_bytes(&blk.into_transition())?)? + } else { + buffer.write(&nbtx::to_le_bytes(&blk.into_transition())?)? + }; } - Ok(SubchunkTransitionalData { - layers, - data_version: 9, // TODO: Change this to be configurable - y_level, - }) } + Ok(buffer.into_inner()) + } +} - fn get_block(&self, xyz: Vec3) -> Option<&Self::BlockType> { - let layer: &[Self::BlockType; 4096] = self.blocks.get(self.active_layer as usize)?; - layer.get(idx_3_to_1::(xyz, 16u8, 16u8)) - } +#[derive(Clone, PartialEq)] +pub struct SubChunk { + blocks: Vec>, + position: Vec3, + dimension: Dimension, + active_layer: u8, + is_empty: bool, +} - fn get_block_mut(&mut self, xyz: Vec3) -> Option<&mut Self::BlockType> { - let layer: &mut [Self::BlockType; 4096] = - self.blocks.get_mut(self.active_layer as usize)?; - layer.get_mut(idx_3_to_1::(xyz, 16u8, 16u8)) - } +impl Debug for SubChunk { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "SubChunk(Blocks: (..., {} elements), position: {}, active_layer: {}, is_empty: {})", + self.blocks.len(), + self.position, + self.active_layer, + self.is_empty + ) + } +} - fn set_block(&mut self, xyz: Vec3, block: Self::BlockType) -> Result<(), Self::Err> { - let layer: &mut [Self::BlockType; 4096] = self - .blocks - .get_mut(self.active_layer as usize) - .ok_or(SubChunkError::LayerError(self.active_layer))?; - layer[idx_3_to_1::(xyz, 16u8, 16u8)] = block; - self.is_empty = false; - Ok(()) - } - fn get_active_layer(&self) -> u8 { - self.active_layer - } +impl SubChunk { + pub fn force_non_empty(&mut self) { + self.is_empty = false; + } - fn set_active_layer(&mut self, idx: u8) { - if idx as usize >= self.blocks.len() { - panic!( - "Selected subchunk index outside of valid range!, Layer Count: {}", - self.blocks.len() - ) - } - self.active_layer = idx; - } + pub fn force_empty(&mut self) { + self.is_empty = true; + } - fn add_sub_layer(&mut self, state: &mut Self::UserState) { - self.blocks.push(Box::new(std::array::from_fn(|_| { - Self::BlockType::air(state) - }))); - } + pub fn full(position: Vec3, dimension: Dimension, block: impl BlockTransition) -> Self { + let mut val = Self { + blocks: Vec::with_capacity(1), + position, + dimension, + active_layer: 0, + is_empty: false, + }; + val.blocks.push(Box::new(std::array::from_fn(|_| { + block.clone().into_transition() + }))); + val + } - fn get_sub_layer_count(&self) -> usize { - self.blocks.len() - } + pub fn empty(position: Vec3, dimension: Dimension) -> Self { + let mut val = Self { + blocks: Vec::with_capacity(1), + position, + dimension, + active_layer: 0, + is_empty: true, + }; + val.blocks + .push(Box::new(std::array::from_fn(|_| LevelBlock::air()))); + val + } - fn get_y(&self) -> i8 { - self.y_index - } + pub fn to_init(mut self) -> Self { + self.is_empty = false; + self + } - fn set_y(&mut self, y: i8) -> i8 { - self.y_index = y; - self.y_index - } + pub fn get_block(&self, xyz: Vec3) -> Option<&LevelBlock> { + let layer = self.blocks.get(self.active_layer as usize)?; + layer.get(idx_3_to_1::(xyz, 16u8, 16u8)) + } - fn state_clone(&self, _: &mut Self::UserState) -> Self { - Self { - blocks: self.blocks.clone(), - y_index: self.y_index, - active_layer: self.active_layer, - is_empty: self.is_empty, - _state_tag: PhantomData, - } - } + pub fn get_block_mut(&mut self, xyz: Vec3) -> Option<&mut LevelBlock> { + let layer = self.blocks.get_mut(self.active_layer as usize)?; + layer.get_mut(idx_3_to_1::(xyz, 16u8, 16u8)) + } - fn is_empty(&self) -> bool { - self.is_empty + pub fn set_block( + &mut self, + xyz: Vec3, + block: Block, + ) -> Result<(), SubChunkError> { + let layer = self + .blocks + .get_mut(self.active_layer as usize) + .ok_or(SubChunkError::LayerError(self.active_layer))?; + layer[idx_3_to_1::(xyz, 16u8, 16u8)] = block.into_transition(); + self.is_empty = false; + Ok(()) + } + + pub fn get_active_layer(&self) -> u8 { + self.active_layer + } + + pub fn set_active_layer(&mut self, idx: u8) { + if idx as usize >= self.blocks.len() { + panic!( + "Selected sub chunk index outside of valid range!, Layer Count: {}", + self.blocks.len() + ) } + self.active_layer = idx; + } + + pub fn add_sub_layer(&mut self) { + self.blocks + .push(Box::new(std::array::from_fn(|_| LevelBlock::air()))); + } + + pub fn get_sub_layer_count(&self) -> usize { + self.blocks.len() } - impl, UserState> - SubChunk - { - fn encode_single_layer(&self, layer_override: usize) -> BlockLayer { - let mut indices = Box::new([0u16; 4096]); - let mut unique_block_array = Vec::new(); - let layer = &self.blocks[layer_override]; - for z in 0..16u8 { - for y in 0..16u8 { - for x in 0..16u8 { - let current_block = &layer[idx_3_to_1((x, y, z).into(), 16, 16)]; - if let Some(index) = unique_block_array - .iter() - .position(|ele| ele == current_block) - { - indices[idx_3_to_1((x, y, z).into(), 16, 16)] = index as u16; - } else { - unique_block_array.push(current_block.clone()); - indices[idx_3_to_1((x, y, z).into(), 16, 16)] = - (unique_block_array.len() - 1) as u16; - } + pub fn encode_single_layer(&self, layer_override: usize) -> BlockLayer { + let mut indices = Box::new([0u16; 4096]); + let mut unique_block_array = Vec::new(); + let layer = &self.blocks[layer_override]; + for z in 0..16u8 { + for y in 0..16u8 { + for x in 0..16u8 { + let current_block = &layer[idx_3_to_1((x, y, z).into(), 16, 16)]; + if let Some(index) = unique_block_array + .iter() + .position(|ele| ele == current_block) + { + indices[idx_3_to_1((x, y, z).into(), 16, 16)] = index as u16; + } else { + unique_block_array.push(current_block.clone()); + indices[idx_3_to_1((x, y, z).into(), 16, 16)] = + (unique_block_array.len() - 1) as u16; } } } - (indices, unique_block_array) } + (indices, unique_block_array) + } +} +impl SubChunkTraitExtended for SubChunk { + fn dimension(&self) -> Dimension { + self.dimension } - impl Clone for SubChunk { - fn clone(&self) -> Self { - Self { - active_layer: self.active_layer, - _state_tag: PhantomData, - blocks: self.blocks.clone(), - is_empty: self.is_empty, - y_index: self.y_index.clone(), + fn position(&self) -> Vec3 { + self.position + } +} + +impl SubChunkTrait for SubChunk { + type TransitionInformation = (); + type Err = (); + + fn decode_from_transition( + data: SubChunkTransition, + dimension: Dimension, + _: &mut Self::TransitionInformation, + ) -> Result { + let mut layers: Vec; 4096]>> = (0..data.layers.len()) + .map(|_| Box::new([const { MaybeUninit::uninit() }; 4096])) + .collect(); + for (layer_index, (indices, blocks)) in data.layers.into_iter().enumerate() { + let layer: &mut Box<[MaybeUninit; 4096]> = &mut layers[layer_index]; + for whole_index in 0..4096usize { + layer[whole_index].write(LevelBlock::from_other( + &blocks[indices[whole_index] as usize], + )); } } + Ok(Self { + blocks: unsafe { std::mem::transmute(layers) }, + position: data.position, + dimension, + active_layer: 0, + is_empty: false, + }) + } + + fn to_transition( + &self, + _: &mut Self::TransitionInformation, + ) -> Result { + let mut layers: Vec> = Vec::with_capacity(self.blocks.len()); + for layer in 0..self.blocks.len() { + layers.push(self.encode_single_layer(layer)); + } + Ok(SubChunkTransition { + layers, + data_version: 9, // TODO: Change this to be configurable + position: self.position, + }) + } + + fn is_empty(&self) -> bool { + self.is_empty } } diff --git a/crates/level/src/level/world_block.rs b/crates/level/src/level/world_block.rs index bd14019a..85dbd03d 100644 --- a/crates/level/src/level/world_block.rs +++ b/crates/level/src/level/world_block.rs @@ -1,5 +1,6 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; +use vek::{Vec2, Vec3}; #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] #[serde(untagged)] @@ -10,95 +11,114 @@ pub enum BlockStateValue { } #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -pub struct BlockTransitionalState { - name: String, - states: HashMap, +pub struct LevelBlock { + // If the name of anything in this struct changes the world ends + pub name: String, + pub states: HashMap, } -pub trait WorldBlockTrait: Clone + PartialEq { - type UserState; - fn from_transition(value: BlockTransitionalState, state: &mut Self::UserState) -> Self; - fn into_transition(self, state: &mut Self::UserState) -> BlockTransitionalState; +pub trait BlockTransition: Clone + PartialEq { + fn from_transition(value: LevelBlock) -> Self; + fn into_transition(self) -> LevelBlock; fn get_id(&self) -> &str; - fn air(state: &mut Self::UserState) -> Self; fn from_other(other: &Self) -> Self { other.clone() } } -#[cfg(feature = "default-impl")] -pub mod default_impl { - use super::*; - use std::fmt::Formatter; - use std::marker::PhantomData; - - pub struct WorldBlock { - id: String, - pub states: HashMap, - _state_marker: PhantomData, - } - - impl WorldBlock { - pub fn new(id: String) -> Self { - Self { - id, - states: HashMap::new(), - _state_marker: PhantomData, - } +impl LevelBlock { + pub fn air() -> Self { + Self { + name: String::from("minecraft:air"), + states: HashMap::new(), } } - impl<'a, UserState> PartialEq for WorldBlock { - fn eq(&self, other: &Self) -> bool { - self.id == other.id && self.states == other.states + pub fn id(id: String) -> Self { + Self { + name: id, + states: HashMap::new(), } } - impl WorldBlockTrait for WorldBlock { - type UserState = UserState; + pub fn block_pos_to_sub_chunk(pos: Vec3) -> (Vec3, Vec3) { + let sub_chunk_y = (pos.y as f32 / 16f32).floor() as i8; + let sub_chunk_y_inside = Self::interior_position(pos.y); - fn from_transition(value: BlockTransitionalState, _: &mut Self::UserState) -> Self { - Self { - id: value.name.clone(), - states: value.states.clone(), - _state_marker: PhantomData, - } - } + let chunk_xz = Vec2::new( + (pos.x as f32 / 16f32).floor() as i32, + (pos.z as f32 / 16f32).floor() as i32, + ); - fn into_transition(self, _: &mut Self::UserState) -> BlockTransitionalState { - BlockTransitionalState { - name: self.id, - states: self.states, - } - } + let sub_chunk_xz = Vec2::new( + Self::interior_position(pos.x), + Self::interior_position(pos.z), + ); - fn get_id(&self) -> &str { - &self.id - } + ( + (chunk_xz.x, sub_chunk_y as i32, chunk_xz.y).into(), + (sub_chunk_xz.x, sub_chunk_y_inside, sub_chunk_xz.y).into(), + ) + } - fn air(_: &mut Self::UserState) -> Self { - Self { - id: String::from("minecraft:air"), - states: HashMap::new(), - _state_marker: PhantomData, - } + fn interior_position(pos: i32) -> u8 { + if pos >= 0 { + (pos % 16) as u8 + } else { + (pos % 16 + 16) as u8 } } +} - impl std::fmt::Debug for WorldBlock { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "WorldBlock(ID: {}, States: {:?})", self.id, self.states) +impl, StateType: Into>> + From<(IdType, StateType)> for LevelBlock +{ + fn from((id, states): (IdType, StateType)) -> Self { + Self { + name: id.into(), + states: states.into(), } } +} - impl std::clone::Clone for WorldBlock { - fn clone(&self) -> Self { - Self { - id: self.id.clone(), - states: self.states.clone(), - _state_marker: PhantomData, - } - } +impl BlockTransition for LevelBlock { + fn from_transition(value: LevelBlock) -> Self { + value + } + + fn into_transition(self) -> LevelBlock { + self + } + + fn get_id(&self) -> &str { + &self.name + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn position_translation_tests() { + // Test values stolen from minecraft java's debug menu + + assert_eq!( + LevelBlock::block_pos_to_sub_chunk((117, 73, -163).into()), + ((7, 4, -11).into(), (5, 9, 13).into()) + ); + assert_eq!( + LevelBlock::block_pos_to_sub_chunk((117, -53, -163).into()), + ((7, -4, -11).into(), (5, 11, 13).into()) + ); + assert_eq!( + LevelBlock::block_pos_to_sub_chunk((164, -39, -219).into()), + ((10, -3, -14).into(), (4, 9, 5).into()) + ); + assert_eq!( + LevelBlock::block_pos_to_sub_chunk((0, -3, 0).into()), + ((0, -1, 0).into(), (0, 13, 0).into()) + ); } } diff --git a/crates/level/src/lib.rs b/crates/level/src/lib.rs index 615351ff..f01b0f53 100644 --- a/crates/level/src/lib.rs +++ b/crates/level/src/lib.rs @@ -1,4 +1,4 @@ extern crate core; pub mod level; -mod types; +pub mod utility; diff --git a/crates/level/src/types/clear_cache.rs b/crates/level/src/types/clear_cache.rs deleted file mode 100644 index ef931789..00000000 --- a/crates/level/src/types/clear_cache.rs +++ /dev/null @@ -1,110 +0,0 @@ -use std::collections::HashMap; -use std::hash::Hash; - -#[derive(Debug, PartialEq)] -struct ClearCacheContainerValue { - internal: CachedValue, - sequence_id: usize, -} - -impl ClearCacheContainerValue { - pub fn new(internal: CachedValue, sequence_id: usize) -> Self { - Self { - internal, - sequence_id, - } - } -} - -pub struct ClearCacheContainer { - sequence_id: usize, - pub clear_threshold: usize, - cache_information: HashMap>, -} - -#[allow(dead_code)] -impl ClearCacheContainer { - pub fn new() -> Self { - Self { - sequence_id: 0, - clear_threshold: 2048, - cache_information: HashMap::new(), - } - } - - pub fn with_threshold(clear_threshold: usize) -> Self { - Self { - sequence_id: 0, - clear_threshold, - cache_information: HashMap::new(), - } - } - - pub fn get(&self, key: &KeyType) -> Option<&CachedValue> { - Some(&self.cache_information.get(key)?.internal) - } - - pub fn insert(&mut self, key: KeyType, val: CachedValue) { - let idx = self.next_seq(); - let _ = self - .cache_information - .insert(key, ClearCacheContainerValue::new(val, idx)); // Drops the old value - } - - pub fn is_cached(&self, key: &KeyType) -> bool { - self.get(key).map_or_else(|| false, |_| true) - } - - pub fn len(&self) -> usize { - self.cache_information.len() - } - - pub fn seq_id(&self) -> usize { - self.sequence_id - } - - fn next_seq(&mut self) -> usize { - self.sequence_id += 1; - self.sequence_id - 1 - } - - pub fn cull Result<(), E>>( - &mut self, - mut on_culled: F, - ) -> Result<(), E> { - if self.sequence_id != 0 && self.sequence_id % self.clear_threshold != 0 { - return Ok(()); - } - let max_distance = self.clear_threshold / 10; // This grabs the maximum distance the seq ID can be and still be kept in the cache - let dropped_keys = self - .cache_information - .iter() - .filter_map(|(key, val)| { - if self.sequence_id - val.sequence_id > max_distance { - Some(key.clone()) - } else { - None - } - }) - .collect::>(); - - { - for key in dropped_keys { - let information = self.cache_information.remove(&key).unwrap(); - on_culled(key, information.internal)?; - } - } - - Ok(()) - } - - pub fn clear Result<(), E>>( - &mut self, - mut on_culled: F, - ) -> Result<(), E> { - for (key, val) in self.cache_information.drain() { - on_culled(key, val.internal)?; - } - Ok(()) - } -} diff --git a/crates/level/src/types/miner.rs b/crates/level/src/types/miner.rs deleted file mode 100644 index d4cdc147..00000000 --- a/crates/level/src/types/miner.rs +++ /dev/null @@ -1,29 +0,0 @@ -use vek::Vec3; - -#[allow(dead_code)] -pub fn idx_3_to_1>(vec: Vec3, width: T, height: T) -> usize -where - usize: From, -{ - (usize::from(vec.x) - + usize::from(vec.y) * usize::from(width) - + usize::from(vec.z) * usize::from(width) * usize::from(height)) - .into() -} - -#[allow(dead_code)] -pub fn in_bounds(min: T, max: T, val: T) -> bool { - val >= min && val <= max -} - -#[macro_export] -macro_rules! level_try { - ($name:ident, $expr:expr $(,)?) => { - match $expr { - Ok(val) => val, - Err(err) => { - return Err($crate::level::level::LevelError::$name(err)); - } - } - }; -} diff --git a/crates/level/src/types/mod.rs b/crates/level/src/types/mod.rs deleted file mode 100644 index eb00f4af..00000000 --- a/crates/level/src/types/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod clear_cache; -pub mod miner; diff --git a/crates/level/src/utility/miner.rs b/crates/level/src/utility/miner.rs new file mode 100644 index 00000000..6154a389 --- /dev/null +++ b/crates/level/src/utility/miner.rs @@ -0,0 +1,50 @@ +use std::cmp::{max, min}; +use vek::Vec3; + +pub fn idx_3_to_1>(vec: Vec3, height: T, depth: T) -> usize +where + usize: From, +{ + (usize::from(vec.x) * usize::from(depth) * usize::from(height) + + usize::from(vec.z) * usize::from(height) + + usize::from(vec.y)) + .into() +} +/// Checks EXCLUSIVELY if something is in range +pub fn in_range(min: T, max: T, val: T) -> bool { + min < val && val < max +} + +pub fn bounds_left_optimized(lhs: Vec3, rhs: Vec3) -> (Vec3, Vec3) { + let smallest_new_x = min(lhs.x.clone(), rhs.x.clone()); + let smallest_new_y = min(lhs.y.clone(), rhs.y.clone()); + let smallest_new_z = min(lhs.z.clone(), rhs.z.clone()); + + let largest_new_x = max(lhs.x, rhs.x); + let largest_new_y = max(lhs.y, rhs.y); + let largest_new_z = max(lhs.z, rhs.z); + + ( + (smallest_new_x, smallest_new_y, smallest_new_z).into(), + (largest_new_x, largest_new_y, largest_new_z).into(), + ) +} + +#[cfg(test)] +mod test { + use crate::utility::miner::bounds_left_optimized; + use vek::Vec3; + + #[test] + fn bounds_left() { + assert_eq!( + bounds_left_optimized(Vec3::new(-3, -3, -3), Vec3::new(5, 5, 5)), + (Vec3::new(-3, -3, -3), Vec3::new(5, 5, 5)) + ); + + assert_eq!( + bounds_left_optimized(Vec3::new(10, -3, -3), Vec3::new(5, 5, 5)), + (Vec3::new(5, -3, -3), Vec3::new(10, 5, 5)) + ); + } +} diff --git a/crates/level/src/utility/mod.rs b/crates/level/src/utility/mod.rs new file mode 100644 index 00000000..25588127 --- /dev/null +++ b/crates/level/src/utility/mod.rs @@ -0,0 +1 @@ +pub mod miner; diff --git a/crates/level/test_level/db/000003.log b/crates/level/test_level/db/000003.log new file mode 100644 index 0000000000000000000000000000000000000000..d69b2b75cbcd9f5a9b3e29692b3e815eee1a6bbc GIT binary patch literal 8242 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 literal 0 HcmV?d00001 diff --git a/crates/level/test_level/db/000044.ldb b/crates/level/test_level/db/000044.ldb deleted file mode 100644 index 03a173832ee8a137a590e4a3fb996b7f2d9c93a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 83098 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]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 diff --git a/crates/level/tests/api_test.rs b/crates/level/tests/api_test.rs index bfc39c26..f2ec22aa 100644 --- a/crates/level/tests/api_test.rs +++ b/crates/level/tests/api_test.rs @@ -1,92 +1,82 @@ -use bedrockrs_level::level::chunk::{FillFilter, LevelChunkTrait}; -use bedrockrs_level::level::level::default_impl::*; -use bedrockrs_level::level::level::{ChunkSelectionFilter, LevelConfiguration}; -use bedrockrs_level::level::sub_chunk::SubchunkFillFilter; +use bedrockrs_level::level::level::{ + BedrockLevel, FillConfig, GetBlockConfig, LevelConfiguration, LevelDbError, SetBlockConfig, +}; +use bedrockrs_level::level::sub_chunk::{SerDeStore, SubChunk, SubChunkSerDe}; +use bedrockrs_level::level::world_block::LevelBlock; use bedrockrs_shared::world::dimension::Dimension; + +// Needed for the test only use copy_dir::copy_dir; use std::path::Path; #[cfg(feature = "default-impl")] -fn get_level_with_copy( -) -> Result> -{ - let _ = std::fs::remove_dir_all("./test_level_temp"); // If this errors its fine +fn get_level_with_copy() -> Result { + let _ = std::fs::remove_dir_all("./test_level_temp"); // If this throws an error its fine copy_dir("./test_level", "./test_level_temp").unwrap(); BedrockLevel::open( Box::from(Path::new("./test_level_temp")), LevelConfiguration::default(), - BedrockState {}, + &mut (), ) } #[cfg(feature = "default-impl")] #[test] -fn world_test( -) -> Result<(), BedrockLevelError> { - let wld_path = "./test_level/db"; - +fn world_test() -> Result<(), anyhow::Error> { println!("Loading World"); let mut level = get_level_with_copy()?; - println!("Collecting Chunks"); - let chunks = level.get_chunk_keys(ChunkSelectionFilter::Dimension(Dimension::Overworld)); - - println!("Collected {} Chunks!", chunks.len()); - - let blks = [ - BedrockWorldBlock::new("minecraft:iron_block".to_string()), - BedrockWorldBlock::new("minecraft:diamond_block".to_string()), - ]; - let len = chunks.len(); - - println!("Filling Chunks"); - for (idx, key) in chunks.into_iter().enumerate() { - let mut chunk = BedrockChunk::empty( - key, - (-4, 20).into(), + let mut sub_chunk = level + .get_sub_chunk::( + (0, 0, 0).into(), Dimension::Overworld, - &mut BedrockState {}, - ); - - for blk in &blks { - chunk - .fill_chunk( - blk.clone(), - FillFilter::Precedence(Box::new(|_, _, _, _| rand::random::() > 0.5)), - ) - .unwrap(); - } - - chunk.write_to_world(&mut level, None, None).unwrap(); - - if idx % (len / 10 + 1) == 0 { - println!("Wrote {idx} out of {len} chunks!"); - } - } - let mut chunk = level.get_chunk::((0, 0).into(), Dimension::Overworld)?; - - chunk - .fill_chunk( - BedrockWorldBlock::new("minecraft:diamond_block".into()), - FillFilter::Precedence(Box::new(|_, _, _, y| y == 0)), + SerDeStore::default(), ) - .unwrap(); - level.set_chunk(chunk)?; - - let mut chunk = level.get_chunk::((0, -1).into(), Dimension::Overworld)?; - - let subchunk = chunk.get_subchunk_mut(0).unwrap(); - subchunk - .fill( - &BedrockWorldBlock::new("minecraft:diamond_block".into()), - SubchunkFillFilter::Blanket, - ) - .unwrap(); - - level.set_chunk(chunk)?; - - level.close()?; + .expect("Read failed") + .unwrap_or_else(|| SubChunk::empty((0, 0, 0).into(), Dimension::Overworld).to_init()); + + sub_chunk + .set_block((0, 0, 0).into(), LevelBlock::id("minecraft:tnt".into())) + .expect(""); + + level + .set_sub_chunk::(&sub_chunk, SerDeStore::default()) + .expect("This should never fail"); + + let block = LevelBlock::id("minecraft:tnt".into()); + + level + .set_block(SetBlockConfig { + position: (0, -3, 0).into(), + block: block.clone(), + dim: Dimension::Overworld, + layer: 0, + }) + .expect("Cant Fail"); + + assert_eq!( + level + .get_block::(GetBlockConfig { + position: (0, -3, 0).into(), + dim: Dimension::Overworld, + layer: 0, + }) + .expect("Cant Fail") + .unwrap(), + block + ); + + level + .fill(FillConfig { + from: (-11, -11, -11).into(), + to: (10, 10, 10).into(), + block: block.clone(), + dim: Dimension::Overworld, + layer: 0, + data_version: 9, + }) + .expect("TODO: panic message"); Ok(()) } From 09149bbde7f3aa26a433dd17f6fc0bec44fb9522 Mon Sep 17 00:00:00 2001 From: Akashic Records <76927522+akashic-records-of-the-abyss@users.noreply.github.com> Date: Tue, 7 Jan 2025 23:54:46 +0000 Subject: [PATCH 2/5] Im losing my marbles --- .../src/level/db_interface/bedrock_key.rs | 8 +- crates/level/src/level/db_interface/rusty.rs | 21 ++- crates/level/src/level/error.rs | 41 +++--- crates/level/src/level/file_interface.rs | 2 + crates/level/src/level/level.rs | 124 ++++++++++++------ crates/level/src/level/sub_chunk.rs | 5 +- crates/level/src/utility/miner.rs | 32 ++++- crates/level/test_level/db.7z | Bin 0 -> 2358 bytes crates/level/test_level/db/000003.log | Bin 8242 -> 0 bytes crates/level/test_level/db/000005.ldb | Bin 0 -> 2235 bytes crates/level/test_level/db/000006.log | Bin 0 -> 24438 bytes crates/level/test_level/db/CURRENT | 2 +- crates/level/test_level/db/LOCK | 0 crates/level/test_level/db/LOG | 4 - crates/level/test_level/db/MANIFEST-000002 | Bin 50 -> 0 bytes crates/level/test_level/db/MANIFEST-000004 | Bin 0 -> 95 bytes crates/level/tests/api_test.rs | 94 +++++++++++-- 17 files changed, 245 insertions(+), 88 deletions(-) create mode 100644 crates/level/test_level/db.7z delete mode 100644 crates/level/test_level/db/000003.log create mode 100644 crates/level/test_level/db/000005.ldb create mode 100644 crates/level/test_level/db/000006.log delete mode 100644 crates/level/test_level/db/LOCK delete mode 100644 crates/level/test_level/db/LOG delete mode 100644 crates/level/test_level/db/MANIFEST-000002 create mode 100644 crates/level/test_level/db/MANIFEST-000004 diff --git a/crates/level/src/level/db_interface/bedrock_key.rs b/crates/level/src/level/db_interface/bedrock_key.rs index 0ed50545..5f09b031 100644 --- a/crates/level/src/level/db_interface/bedrock_key.rs +++ b/crates/level/src/level/db_interface/bedrock_key.rs @@ -7,10 +7,10 @@ use vek::{Vec2, Vec3}; #[derive(Debug)] pub struct ChunkKey { - xz: Vec2, - dim: Dimension, - key_type: KeyTypeTag, - y_index: Option, + pub xz: Vec2, + pub dim: Dimension, + pub key_type: KeyTypeTag, + pub y_index: Option, } impl ChunkKey { diff --git a/crates/level/src/level/db_interface/rusty.rs b/crates/level/src/level/db_interface/rusty.rs index f05ab1b8..b5a87bff 100644 --- a/crates/level/src/level/db_interface/rusty.rs +++ b/crates/level/src/level/db_interface/rusty.rs @@ -1,6 +1,7 @@ use crate::level::db_interface::bedrock_key::ChunkKey; use crate::level::db_interface::db::LevelDBKey; use crate::level::db_interface::key_level::KeyTypeTag; +use crate::level::db_interface::key_level::KeyTypeTag::SubChunkPrefix; use crate::level::file_interface::RawWorldTrait; use bedrockrs_shared::world::dimension::Dimension; use byteorder::{LittleEndian, ReadBytesExt}; @@ -71,7 +72,7 @@ pub fn mcpe_options(compression_level: u8) -> Options { const COMPRESSION_LEVEL: u8 = CompressionLevel::DefaultLevel as u8; pub struct RustyDBInterface { - db: DB, + pub db: DB, } #[derive(Debug, Error)] @@ -137,6 +138,10 @@ impl RawWorldTrait for RustyDBInterface { Ok(()) } + fn flush(&mut self) -> Result<(), Self::Err> { + self.db.flush().map_err(|ele| Self::Err::DatabaseError(ele)) + } + fn write_bytes_to_key( &mut self, chunk_info: ChunkKey, @@ -155,6 +160,15 @@ impl RawWorldTrait for RustyDBInterface { Ok(self.db.delete(&Self::build_key(&chunk_info))?) } + fn set_sub_chunk_raw( + &mut self, + chunk_info: ChunkKey, + chunk_bytes: &[u8], + ) -> Result<(), Self::Err> { + println!("{:?}, {chunk_info:?}", Self::build_key(&chunk_info)); + self.write_bytes_to_key(chunk_info, chunk_bytes) + } + fn write_sub_chunk_batch( &mut self, sub_chunk_batch_info: Vec<(ChunkKey, Vec)>, @@ -195,6 +209,11 @@ impl RawWorldTrait for RustyDBInterface { let mut key_bytes: Vec = vec![0; key.estimate_size()]; let mut buff: Cursor<&mut [u8]> = Cursor::new(&mut key_bytes); key.write_key(&mut buff); + + if key.key_type == SubChunkPrefix { + println!("{:?}", buff); + } + key_bytes } diff --git a/crates/level/src/level/error.rs b/crates/level/src/level/error.rs index d73b04a7..a6bab32f 100644 --- a/crates/level/src/level/error.rs +++ b/crates/level/src/level/error.rs @@ -16,7 +16,7 @@ pub enum SubChunkSerDeError { } #[derive(Error, Debug)] -pub enum SubChunkReadWriteError { +pub enum WholeLevelError { #[error(transparent)] TranslationError(TranslationError), #[error(transparent)] @@ -28,7 +28,12 @@ pub enum SubChunkReadWriteError { +pub enum WorldPipelineError< + WorldError: Debug, + Decode: Debug, + Encode: Debug, + TranslationError: Debug, +> { #[error(transparent)] TranslationError(TranslationError), #[error(transparent)] @@ -42,39 +47,35 @@ pub enum LevelFilLError - LevelFilLError + WorldPipelineError { - pub fn from_decode( - other: SubChunkReadWriteError, - ) -> Self { + pub fn from_decode(other: WholeLevelError) -> Self { match other { - SubChunkReadWriteError::TranslationError(r) => Self::TranslationError(r), - SubChunkReadWriteError::WorldError(r) => Self::WorldError(r), - SubChunkReadWriteError::SerDeError(r) => Self::DecodeError(r), - SubChunkReadWriteError::SubChunkError(r) => Self::SubChunkError(r), + WholeLevelError::TranslationError(r) => Self::TranslationError(r), + WholeLevelError::WorldError(r) => Self::WorldError(r), + WholeLevelError::SerDeError(r) => Self::DecodeError(r), + WholeLevelError::SubChunkError(r) => Self::SubChunkError(r), } } - pub fn from_encode( - other: SubChunkReadWriteError, - ) -> Self { + pub fn from_encode(other: WholeLevelError) -> Self { match other { - SubChunkReadWriteError::TranslationError(r) => Self::TranslationError(r), - SubChunkReadWriteError::WorldError(r) => Self::WorldError(r), - SubChunkReadWriteError::SerDeError(r) => Self::EncoderError(r), - SubChunkReadWriteError::SubChunkError(r) => Self::SubChunkError(r), + WholeLevelError::TranslationError(r) => Self::TranslationError(r), + WholeLevelError::WorldError(r) => Self::WorldError(r), + WholeLevelError::SerDeError(r) => Self::EncoderError(r), + WholeLevelError::SubChunkError(r) => Self::SubChunkError(r), } } } impl From> - for SubChunkReadWriteError + for WholeLevelError { fn from(value: SubChunkSerDeError) -> Self { match value { - SubChunkSerDeError::WorldError(e) => SubChunkReadWriteError::WorldError(e), - SubChunkSerDeError::SerDeError(e) => SubChunkReadWriteError::SerDeError(e), + SubChunkSerDeError::WorldError(e) => WholeLevelError::WorldError(e), + SubChunkSerDeError::SerDeError(e) => WholeLevelError::SerDeError(e), } } } diff --git a/crates/level/src/level/file_interface.rs b/crates/level/src/level/file_interface.rs index b18f362c..f1d169ae 100644 --- a/crates/level/src/level/file_interface.rs +++ b/crates/level/src/level/file_interface.rs @@ -42,6 +42,8 @@ pub trait RawWorldTrait: Sized { fn close(&mut self) -> Result<(), Self::Err>; + fn flush(&mut self) -> Result<(), Self::Err>; + fn write_bytes_to_key( &mut self, chunk_info: ChunkKey, diff --git a/crates/level/src/level/level.rs b/crates/level/src/level/level.rs index 65233d64..b915952e 100644 --- a/crates/level/src/level/level.rs +++ b/crates/level/src/level/level.rs @@ -10,6 +10,7 @@ use std::collections::hash_set::Iter; use std::collections::HashSet; use std::fmt::Debug; use std::path::Path; +use std::time::{SystemTime, UNIX_EPOCH}; use vek::{Vec2, Vec3}; /// This is used when filtering chunks. @@ -145,7 +146,7 @@ where config: FillConfig, ) -> Result< (), - LevelFilLError< + WorldPipelineError< UserWorldInterface::Err, ::Err, ::Err, @@ -165,7 +166,7 @@ where encoder: &mut Encoder, ) -> Result< (), - LevelFilLError< + WorldPipelineError< UserWorldInterface::Err, Decoder::Err, Encoder::Err, @@ -184,11 +185,9 @@ where let block = config.block.into_transition(); - println!("{min} {max}"); for x in min.x..=max.x { for z in min.z..=max.z { for y in min.y..=max.y { - println!("{x} {y} {z}"); if in_range(min.x, max.x, x) && in_range(min.y, max.y, y) && in_range(min.z, max.z, z) @@ -200,10 +199,9 @@ where SubChunkTransition::full(pos, config.data_version, block.clone()); self.set_sub_chunk_raw(encoder, data, pos, config.dim) - .map_err(|err| LevelFilLError::from_encode(err.into()))?; + .map_err(|err| WorldPipelineError::from_encode(err.into()))?; } else { // Now we need to find where this sub chunk lies in the boundary - println!("Called"); let invert_x = x == max.x; let invert_y = y == max.y; @@ -236,7 +234,7 @@ where config.dim, decoder, ) - .map_err(|err| LevelFilLError::from_decode(err.into()))? + .map_err(|err| WorldPipelineError::from_decode(err.into()))? .map(|transition| { SubChunk::decode_from_transition(transition, config.dim, &mut ()) .unwrap() // This is a safe unwrap because `SubChunk::decode_from_translation` never fails @@ -251,15 +249,14 @@ where for z in z_range.clone() { for y in y_range.clone() { data.set_block((x, y, z).into(), block.clone()) - .map_err(|err| LevelFilLError::SubChunkError(err))?; + .map_err(|err| WorldPipelineError::SubChunkError(err))?; } } } let translation = data.to_transition(&mut ()).unwrap(); // Safe because this can't fail - dbg!(&translation); self.set_sub_chunk_raw(encoder, translation, (x, y, z).into(), config.dim) - .map_err(|err| LevelFilLError::from_encode(err.into()))?; + .map_err(|err| WorldPipelineError::from_encode(err.into()))?; } } } @@ -280,7 +277,7 @@ where config: GetBlockConfig, ) -> Result< Option, - SubChunkReadWriteError< + WholeLevelError< UserWorldInterface::Err, ::Err, ::Err, @@ -310,11 +307,7 @@ where decoder: &mut Decoder, ) -> Result< Option, - SubChunkReadWriteError< - UserWorldInterface::Err, - Decoder::Err, - ::Err, - >, + WholeLevelError::Err>, > where Decoder::Err: Debug, @@ -359,8 +352,9 @@ where config: SetBlockConfig, ) -> Result< (), - SubChunkReadWriteError< + WorldPipelineError< UserWorldInterface::Err, + ::Err, ::Err, ::Err, >, @@ -397,8 +391,9 @@ where encoder: &mut Encoder, ) -> Result< (), - SubChunkReadWriteError< + WorldPipelineError< UserWorldInterface::Err, + Decoder::Err, Encoder::Err, ::Err, >, @@ -408,12 +403,15 @@ where Encoder::Err: Debug, { let (sub_chunk_pos, local_pos) = LevelBlock::block_pos_to_sub_chunk(pos); - - let mut data = if let Ok(Some(data)) = self.get_sub_chunk::( - sub_chunk_pos, - dim, - SerDeStoreRef::new(decoder, &mut ()), - ) { + println!("{}, {local_pos}", sub_chunk_pos); + let mut data = if let Some(data) = self + .get_sub_chunk::( + sub_chunk_pos, + dim, + SerDeStoreRef::new(decoder, &mut ()), + ) + .map_err(|e| WorldPipelineError::from_decode(e))? + { data } else { let mut out = SubChunk::empty(sub_chunk_pos, dim); @@ -431,6 +429,7 @@ where data.set_block(local_pos, block.into_transition())?; self.set_sub_chunk::(&data, SerDeStoreRef::new(encoder, &mut ())) + .map_err(|e| WorldPipelineError::from_encode(e)) } /// High level function to fetch a sub chunk that contains a specific block. @@ -442,7 +441,7 @@ where config: impl SerDeTrait, ) -> Result< Option, - SubChunkReadWriteError, + WholeLevelError, > where Decoder::Err: Debug, @@ -461,7 +460,7 @@ where mut config: impl SerDeTrait, ) -> Result< Option, - SubChunkReadWriteError, + WholeLevelError, > where Decoder::Err: Debug, @@ -476,7 +475,7 @@ where Ok(Some( SubChunkType::decode_from_transition(ser, dim, config.info()) - .map_err(|ele| SubChunkReadWriteError::TranslationError(ele))?, + .map_err(|ele| WholeLevelError::TranslationError(ele))?, )) } pub fn get_sub_chunk_raw( @@ -488,6 +487,7 @@ where where Decoder::Err: Debug, { + println!("Reading from {pos} and dim {dim:?}"); let bytes = self .db .get_sub_chunk_raw(ChunkKey::new_sub_chunk(pos, dim)) @@ -498,6 +498,17 @@ where Some(e) => e, }; + dump_u8_array_to_file( + format!( + "{}_{:?}.bin", + pos, + SystemTime::from(UNIX_EPOCH).elapsed().unwrap().as_nanos() + ) + .as_str(), + &bytes, + ) + .unwrap(); + Ok(Some( decoder .decode_bytes_as_sub_chunk(&mut std::io::Cursor::new(bytes), (pos.x, pos.z).into()) @@ -520,11 +531,7 @@ where >, ) -> Result< (), - SubChunkReadWriteError< - UserWorldInterface::Err, - SubChunkEncoderType::Err, - SubChunkType::Err, - >, + WholeLevelError, > where SubChunkEncoderType::Err: Debug, @@ -555,11 +562,7 @@ where dim: Dimension, ) -> Result< (), - SubChunkReadWriteError< - UserWorldInterface::Err, - SubChunkEncoderType::Err, - SubChunkType::Err, - >, + WholeLevelError, > where SubChunkType::Err: Debug, @@ -567,7 +570,7 @@ where { let intermediate = sub_chunk .to_transition(encode_data.info()) - .map_err(|ele| SubChunkReadWriteError::TranslationError(ele))?; + .map_err(|ele| WholeLevelError::TranslationError(ele))?; Ok(self.set_sub_chunk_raw(encode_data.serde(), intermediate, pos, dim)?) } @@ -592,6 +595,40 @@ where .set_sub_chunk_raw(ChunkKey::new_sub_chunk(pos, dim), &bytes) .map_err(|ele| SubChunkSerDeError::WorldError(ele))?; + let wrote = self + .db + .get_sub_chunk_raw(ChunkKey::new_sub_chunk(pos, dim)) + .unwrap(); + + if let Some(wrote) = wrote { + assert_eq!(wrote, bytes); + + let sub_chunk_data = SubChunkSerDe::decode_bytes_as_sub_chunk( + &mut SubChunkSerDe, + &mut std::io::Cursor::new(wrote.clone()), + (pos.x, pos.z).into(), + ) + .unwrap(); + + let sub_chunk = SubChunk::decode_from_transition(sub_chunk_data, dim, &mut ()).unwrap(); + println!("{sub_chunk:?}"); + + println!("{:?}", wrote.len()); + + dump_u8_array_to_file( + format!( + "write_{}_{:?}.bin", + pos, + SystemTime::from(UNIX_EPOCH).elapsed().unwrap().as_nanos() + ) + .as_str(), + &wrote, + ) + .unwrap(); + } else { + panic!("No data returned from key which was just wrote"); + } + self.handle_exist((pos.x, pos.z).into(), dim); Ok(()) } @@ -674,9 +711,14 @@ where .collect() } + pub fn flush(&mut self) -> Result<(), UserWorldInterface::Err> { + self.flush_existence_buffer()?; + self.db.flush() + } + // Internal functions which mainly handle DB layer interactions fn close_internal(&mut self) -> Result<(), UserWorldInterface::Err> { - self.flush_existence_buffer()?; + self.flush()?; // Must come after all the other closing steps self.db.close() @@ -716,7 +758,7 @@ pub mod default_impl { pub type BedrockLevel = Level; } -use crate::level::error::{LevelFilLError, SubChunkReadWriteError, SubChunkSerDeError}; -use crate::utility::miner::{bounds_left_optimized, in_range}; +use crate::level::error::{SubChunkSerDeError, WholeLevelError, WorldPipelineError}; +use crate::utility::miner::{bounds_left_optimized, dump_u8_array_to_file, in_range}; #[cfg(feature = "default-impl")] pub use default_impl::*; diff --git a/crates/level/src/level/sub_chunk.rs b/crates/level/src/level/sub_chunk.rs index 0f6855d2..94958525 100644 --- a/crates/level/src/level/sub_chunk.rs +++ b/crates/level/src/level/sub_chunk.rs @@ -473,8 +473,11 @@ impl SubChunkTrait for SubChunk { )); } } + + let layers = unsafe { std::mem::transmute(layers) }; + Ok(Self { - blocks: unsafe { std::mem::transmute(layers) }, + blocks: layers, position: data.position, dimension, active_layer: 0, diff --git a/crates/level/src/utility/miner.rs b/crates/level/src/utility/miner.rs index 6154a389..268f5de2 100644 --- a/crates/level/src/utility/miner.rs +++ b/crates/level/src/utility/miner.rs @@ -1,14 +1,24 @@ use std::cmp::{max, min}; +use std::io; +use std::io::Write; use vek::Vec3; -pub fn idx_3_to_1>(vec: Vec3, height: T, depth: T) -> usize +pub fn idx_3_to_1 + vek::num_traits::AsPrimitive>( + vec: Vec3, + height: T, + depth: T, +) -> usize where usize: From, { - (usize::from(vec.x) * usize::from(depth) * usize::from(height) - + usize::from(vec.z) * usize::from(height) - + usize::from(vec.y)) - .into() + let vec = vec.as_::(); + let x = vec.x; + let y = vec.y; + let z = vec.z; + let height = usize::from(height); + let depth = usize::from(depth); + + x * depth * height + z * height + y } /// Checks EXCLUSIVELY if something is in range pub fn in_range(min: T, max: T, val: T) -> bool { @@ -48,3 +58,15 @@ mod test { ); } } + +pub fn dump_u8_array_to_file(file_name: &str, data: &[u8]) -> io::Result<()> { + return Ok(()); + // Create or open the file + let mut file = std::fs::File::create(file_name)?; + + // Write the u8 array to the file + file.write_all(data)?; + + println!("Data successfully written to {}", file_name); + Ok(()) +} diff --git a/crates/level/test_level/db.7z b/crates/level/test_level/db.7z new file mode 100644 index 0000000000000000000000000000000000000000..5771279f02d70e1154d0e6a6c6f70e7cdc50a7f7 GIT binary patch literal 2358 zcmV-63CZ?1dc3bE8~_A@QpCaY2mk;80000Z000000001uMf&sLC1D66T>w#rQSLSw z=+qQjMPAhSfiKRS6wDqDwSeZS-qQ5c<`#0NZ&QSjP?^(@BQeca0=fh=N@JR0C^{c$ zSS%E%mVQD1r$-_Vekg;hViJ1l>N&k=B^E?GW!`RCW~)YyRC|^Bo@xx2`8{v%!GC;4 ztNt9!${iMLJJ2ZYd_lMZ$!_w>#*pO?dF6ceJ%bnCmSZ=gaRNLirmR(uTD1goM=#;~ zLeURnE`VNcVn*W_!)muWm`OwLU*E4o+;%P8;g~drxax^aVHx(N_>EZASkyt#4#)|! zmioXGkT;99FP4Pj_Bs`Y`w6TX{k+4I%4DsEGGjFaB0-^Pa0f!{)T!sxFcaGw>>!}L zuBi1Ud?oE_fIh&WA4;t#em&lsULJz2n<+HZ7Hj!|r8yMULU)7x^zg`hT20AEI5zuy zn(!T}(++pm$|DkGac1s$$nmkMAD&D-alm<17Pd_4fwyf-kQWv!T<=L^c-uX2>1J>W zzOKy`;&<$0FOUn`Cz{>4o-{FJss2W`WDDWsgw=I) zDR~hAe=w{9r&7jP6j{&@I)vy_5y8P_(Y^{h?70nVW*cm7(8C6UF^aP8w-|{?sN(i6 zO9QCDv|el<{px{FNiCyZjJY#UcBz2#tuXlVWuzpmzL5$FBuNt%{%{!Dqg_md5GnB~!rFo)sFlH~NrMOp|3pClGJ1 zc*OkG+DaE4`M$fzJ-8dPtU@m)=1*O8QcrEflG|UW3@{;tvg6xt`({**p~j{Jzvwd9 z%hPLftKq3t@qtx2>^ug7N2N-NVl5OV6j?-<6O77yXs9R@qN)Xg0o!}b*#nH@u$48r z6Bl&?Sg@(5G;!n{hc}hu9cZPvBR32GkYJK}v!O`Lk^)ZP=!|2HL89-sX#>6D1r_2^ zvw{kdsE;qBTWLR{|_zyRk7d2X4pm?T##0U%8Jm6<~N?V>Wq(q z&n0KeOh4I{%3Z155sP204E*1zNh22FBKJ&~N62M_0e4i5(7C>Mlb`c3K=PgsE=0 zl`W)6QI2=r5QZtB!HYh-hD_Y5nt)Qv!%^&)lA4g__m6L{o>P{h}5huxKhaJGZTGXpXURdjoiXY|ad_!!K! zh{Z&ai2shHavzOviqG?ri}Vm%>?Tv*p*FZ~ybr4r9S{IucUFa^T=gb^r7{el6<08U z#_@?rdf~WOZ8kXnM#&WcZp|W{(3n}>C93%w=@AImrE5kbmr& zA^7UZGUD$<-%!O(Mt^YYB7z6aP}VuP+hc zkBI)Mj{pFZ#6ndqg9j_CD^0RzoWfU`atmtEkIauWLn@>cPD2&%M9YZY;GN)DO|WmuQfX>Y7!I%aCZ}E7Yf#+& zc!nX{it%-ex7%a=@v0jcp6sL|eW*`l*5Q90ymgk;fC zegTmtTnGRG;|!Y9aJu7_LG*rw@SU=36NPRjuF>|PDfkNYGP`v@Ji37{r=jgo%d%=Tv;5Eej5TugG!8H+en$)8AVc)3mU%{A zCJ%`pa3dI@(rAYu+p*3%xt&kzLk&7hQ12voDg`Adk9d6>MF{Dby-%HNS{`V@8L;0O zZJW-aKF(O%bZB6iEJkNguC+P+2W5{Or=$X6 zA~tVVy8IQ($W2StGU|dDSz=mAD8Ly+hvX88iudfd|Or-NrF8Pu;*Y?QXuASyBhn_lvteq*PO;nLH*?nUZU zQf6uvUfkg$;FkWu#qcl1hg!)W93f#m*VX!*g@e0Numf2c?||``lqQ8B9%}*47HnA$ z+(R`RP_}nY%Dt!TLhHa{$wiF^Ta)q?CVZ2YP{GvzC(=Gw?k(7-$si6SRocWgxenyR zC22DBtd5lf54Vv-`sB0T<=nX75)M}miwN0l>|PT-+377_(fWk9-ewmDh$;aIfXM&{ c3jqKDBLe{e1zi9T000buLJ9$4Se=ys01B;ib^rhX literal 0 HcmV?d00001 diff --git a/crates/level/test_level/db/000003.log b/crates/level/test_level/db/000003.log deleted file mode 100644 index d69b2b75cbcd9f5a9b3e29692b3e815eee1a6bbc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8242 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 diff --git a/crates/level/test_level/db/000005.ldb b/crates/level/test_level/db/000005.ldb new file mode 100644 index 0000000000000000000000000000000000000000..10ee7854957600e038229f6ae10c9ba364117501 GIT binary patch literal 2235 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 literal 0 HcmV?d00001 diff --git a/crates/level/test_level/db/CURRENT b/crates/level/test_level/db/CURRENT index 1a848522..cacca757 100644 --- a/crates/level/test_level/db/CURRENT +++ b/crates/level/test_level/db/CURRENT @@ -1 +1 @@ -MANIFEST-000002 +MANIFEST-000004 diff --git a/crates/level/test_level/db/LOCK b/crates/level/test_level/db/LOCK deleted file mode 100644 index e69de29b..00000000 diff --git a/crates/level/test_level/db/LOG b/crates/level/test_level/db/LOG deleted file mode 100644 index 06ad2544..00000000 --- a/crates/level/test_level/db/LOG +++ /dev/null @@ -1,4 +0,0 @@ -Recovered manifest with next_file=5 manifest_num=4 log_num=3 prev_log_num=0 last_seq=0 -reusing manifest "C:\\Users\\Duckos\\AppData\\Roaming\\.minecraft_bedrock\\installations\\Duckos\\Latest Release\\packageData\\minecraftWorlds\\7qAFhawYI3c=\\db\\MANIFEST-000002" -Recovering log file "\\\\?\\C:\\Users\\Duckos\\AppData\\Roaming\\.minecraft_bedrock\\installations\\Duckos\\Latest Release\\packageData\\minecraftWorlds\\7qAFhawYI3c=\\db\\000003.log" -reusing log file "\\\\?\\C:\\Users\\Duckos\\AppData\\Roaming\\.minecraft_bedrock\\installations\\Duckos\\Latest Release\\packageData\\minecraftWorlds\\7qAFhawYI3c=\\db\\000003.log" diff --git a/crates/level/test_level/db/MANIFEST-000002 b/crates/level/test_level/db/MANIFEST-000002 deleted file mode 100644 index bbbc585686bcbcc33686059c69d80b7b4e1291cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50 zcmWIhx#Ncn10$nUPHI_dPD+xVQ)NkNd1i5{bAE0?Vo_pAe$kRS-TOEg7@3$k8JJmE F7y#sj5K{mE diff --git a/crates/level/test_level/db/MANIFEST-000004 b/crates/level/test_level/db/MANIFEST-000004 new file mode 100644 index 0000000000000000000000000000000000000000..cc459559f43180578266e231c40ad9e4d6c52200 GIT binary patch literal 95 zcmWIhx#Ncn10$nUPHI_dPD+xVQ)NkNd1i5{bAE0?Vo_pAevz5<_B2xlMkY2+24;2^ kPId;?-GYJ)P@u!e31NxW<>V(P=EN7|Bvz&tF|t8K0S|c=Bme*a literal 0 HcmV?d00001 diff --git a/crates/level/tests/api_test.rs b/crates/level/tests/api_test.rs index f2ec22aa..569a4b41 100644 --- a/crates/level/tests/api_test.rs +++ b/crates/level/tests/api_test.rs @@ -7,6 +7,7 @@ use bedrockrs_shared::world::dimension::Dimension; // Needed for the test only use copy_dir::copy_dir; +use rusty_leveldb::LdbIterator; use std::path::Path; #[cfg(feature = "default-impl")] @@ -20,6 +21,18 @@ fn get_level_with_copy() -> Result { ) } +fn dump_keys(level: &mut BedrockLevel) { + level.underlying_world_interface().db.flush().unwrap(); + let mut iter = level.underlying_world_interface().db.new_iter().unwrap(); + iter.seek_to_first(); + + println!("dump start"); + while let Some((k, v)) = iter.next() { + println!("{:?}", k); + } + println!("dump end"); +} + #[cfg(feature = "default-impl")] #[test] fn world_test() -> Result<(), anyhow::Error> { @@ -27,6 +40,76 @@ fn world_test() -> Result<(), anyhow::Error> { let mut level = get_level_with_copy()?; + let block = LevelBlock::id("minecraft:tnt".into()); + let range = 48; + // level + // .fill(FillConfig { + // from: (-range, -range, -range).into(), + // to: (30, -30, 30).into(), + // block: block.clone(), + // dim: Dimension::Overworld, + // layer: 0, + // data_version: 9, + // }) + // .expect("TODO: panic message"); + // level.flush()?; + // drop(level); + + level + .set_block(SetBlockConfig { + position: (0, 10, 0).into(), + block: block.clone(), + dim: Dimension::Overworld, + layer: 0, + }) + .expect("Cant Fail"); + // level + // .set_block(SetBlockConfig { + // position: (0, -3, 0).into(), + // block: block.clone(), + // dim: Dimension::Overworld, + // layer: 0, + // }) + // .expect("Cant Fail"); + + // println!( + // "{:?}", + // level + // .get_block::(GetBlockConfig { + // position: (0, 13, 0).into(), + // dim: Dimension::Overworld, + // layer: 0, + // }) + // .unwrap() + // ); + // level.close()?; + + level.flush()?; + dump_keys(&mut level); + // let mut level = get_level_with_copy()?; + // + // println!( + // "{:?}", + // level + // .get_block::(GetBlockConfig { + // position: (0, 13, 0).into(), + // dim: Dimension::Overworld, + // layer: 0, + // }) + // .unwrap() + // ); + // println!( + // "{:?}", + // level + // .get_block::(GetBlockConfig { + // position: (0, -3, 0).into(), + // dim: Dimension::Overworld, + // layer: 0, + // }) + // .unwrap() + // ); + + std::process::exit(0); let mut sub_chunk = level .get_sub_chunk::( (0, 0, 0).into(), @@ -44,17 +127,6 @@ fn world_test() -> Result<(), anyhow::Error> { .set_sub_chunk::(&sub_chunk, SerDeStore::default()) .expect("This should never fail"); - let block = LevelBlock::id("minecraft:tnt".into()); - - level - .set_block(SetBlockConfig { - position: (0, -3, 0).into(), - block: block.clone(), - dim: Dimension::Overworld, - layer: 0, - }) - .expect("Cant Fail"); - assert_eq!( level .get_block::(GetBlockConfig { From 381518df525ddf6e2322d79f6c78b28ee499354e Mon Sep 17 00:00:00 2001 From: Akashic Records <76927522+akashic-records-of-the-abyss@users.noreply.github.com> Date: Wed, 8 Jan 2025 17:49:29 +0000 Subject: [PATCH 3/5] The will to live is slowly leaving me --- crates/level/src/level/world_block.rs | 26 ++++++++----- crates/level/tests/api_test.rs | 54 +++++++++++++++++---------- 2 files changed, 50 insertions(+), 30 deletions(-) diff --git a/crates/level/src/level/world_block.rs b/crates/level/src/level/world_block.rs index 85dbd03d..1338308d 100644 --- a/crates/level/src/level/world_block.rs +++ b/crates/level/src/level/world_block.rs @@ -43,22 +43,24 @@ impl LevelBlock { } pub fn block_pos_to_sub_chunk(pos: Vec3) -> (Vec3, Vec3) { - let sub_chunk_y = (pos.y as f32 / 16f32).floor() as i8; - let sub_chunk_y_inside = Self::interior_position(pos.y); + let sub_chunk_y = pos.y >> 4; + let sub_chunk_y_inside = pos.y & 0xF; let chunk_xz = Vec2::new( - (pos.x as f32 / 16f32).floor() as i32, - (pos.z as f32 / 16f32).floor() as i32, + (pos.x >> 4), + (pos.z >> 4), // Stolen from the game ); - let sub_chunk_xz = Vec2::new( - Self::interior_position(pos.x), - Self::interior_position(pos.z), - ); + let sub_chunk_xz = Vec2::new(pos.x & 0xF, pos.z & 0xF); ( - (chunk_xz.x, sub_chunk_y as i32, chunk_xz.y).into(), - (sub_chunk_xz.x, sub_chunk_y_inside, sub_chunk_xz.y).into(), + (chunk_xz.x, sub_chunk_y, chunk_xz.y).into(), + ( + sub_chunk_xz.x as u8, + sub_chunk_y_inside as u8, + sub_chunk_xz.y as u8, + ) + .into(), ) } @@ -120,5 +122,9 @@ mod test { LevelBlock::block_pos_to_sub_chunk((0, -3, 0).into()), ((0, -1, 0).into(), (0, 13, 0).into()) ); + assert_eq!( + LevelBlock::block_pos_to_sub_chunk((16, -16, 16).into()), + ((1, -1, 1).into(), (0, 0, 0).into()) + ); } } diff --git a/crates/level/tests/api_test.rs b/crates/level/tests/api_test.rs index 569a4b41..24c2eaf5 100644 --- a/crates/level/tests/api_test.rs +++ b/crates/level/tests/api_test.rs @@ -28,7 +28,7 @@ fn dump_keys(level: &mut BedrockLevel) { println!("dump start"); while let Some((k, v)) = iter.next() { - println!("{:?}", k); + println!("{:?} {}", k, v.len()); } println!("dump end"); } @@ -54,7 +54,7 @@ fn world_test() -> Result<(), anyhow::Error> { // .expect("TODO: panic message"); // level.flush()?; // drop(level); - + level.clear(Dimension::Overworld)?; level .set_block(SetBlockConfig { position: (0, 10, 0).into(), @@ -63,14 +63,17 @@ fn world_test() -> Result<(), anyhow::Error> { layer: 0, }) .expect("Cant Fail"); - // level - // .set_block(SetBlockConfig { - // position: (0, -3, 0).into(), - // block: block.clone(), - // dim: Dimension::Overworld, - // layer: 0, - // }) - // .expect("Cant Fail"); + + for y in -16..=16 { + level + .set_block(SetBlockConfig { + position: (0, y, 0).into(), + block: block.clone(), + dim: Dimension::Overworld, + layer: 0, + }) + .expect("Cant Fail"); + } // println!( // "{:?}", @@ -84,8 +87,29 @@ fn world_test() -> Result<(), anyhow::Error> { // ); // level.close()?; + dump_keys(&mut level); + level.close()?; + let mut level = BedrockLevel::open( + Box::from(Path::new("./test_level_temp")), + LevelConfiguration::default(), + &mut (), + )?; + + for y in -16..=16 { + println!( + "{:?}", + level + .get_block::(GetBlockConfig { + position: (0, y, 0).into(), + dim: Dimension::Overworld, + layer: 0, + }) + .expect("Cant Fail") + ); + } level.flush()?; dump_keys(&mut level); + // let mut level = get_level_with_copy()?; // // println!( @@ -98,16 +122,6 @@ fn world_test() -> Result<(), anyhow::Error> { // }) // .unwrap() // ); - // println!( - // "{:?}", - // level - // .get_block::(GetBlockConfig { - // position: (0, -3, 0).into(), - // dim: Dimension::Overworld, - // layer: 0, - // }) - // .unwrap() - // ); std::process::exit(0); let mut sub_chunk = level From 18ee5b5b8685a17481d7acc75b0075845cb17f46 Mon Sep 17 00:00:00 2001 From: Akashic Records <76927522+akashic-records-of-the-abyss@users.noreply.github.com> Date: Sat, 25 Jan 2025 22:25:23 +0000 Subject: [PATCH 4/5] Removed print --- crates/level/src/level/db_interface/rusty.rs | 5 ----- crates/level/src/level/level.rs | 5 ----- 2 files changed, 10 deletions(-) diff --git a/crates/level/src/level/db_interface/rusty.rs b/crates/level/src/level/db_interface/rusty.rs index b5a87bff..b260f82c 100644 --- a/crates/level/src/level/db_interface/rusty.rs +++ b/crates/level/src/level/db_interface/rusty.rs @@ -165,7 +165,6 @@ impl RawWorldTrait for RustyDBInterface { chunk_info: ChunkKey, chunk_bytes: &[u8], ) -> Result<(), Self::Err> { - println!("{:?}, {chunk_info:?}", Self::build_key(&chunk_info)); self.write_bytes_to_key(chunk_info, chunk_bytes) } @@ -210,10 +209,6 @@ impl RawWorldTrait for RustyDBInterface { let mut buff: Cursor<&mut [u8]> = Cursor::new(&mut key_bytes); key.write_key(&mut buff); - if key.key_type == SubChunkPrefix { - println!("{:?}", buff); - } - key_bytes } diff --git a/crates/level/src/level/level.rs b/crates/level/src/level/level.rs index b915952e..5f5f659a 100644 --- a/crates/level/src/level/level.rs +++ b/crates/level/src/level/level.rs @@ -403,7 +403,6 @@ where Encoder::Err: Debug, { let (sub_chunk_pos, local_pos) = LevelBlock::block_pos_to_sub_chunk(pos); - println!("{}, {local_pos}", sub_chunk_pos); let mut data = if let Some(data) = self .get_sub_chunk::( sub_chunk_pos, @@ -487,7 +486,6 @@ where where Decoder::Err: Debug, { - println!("Reading from {pos} and dim {dim:?}"); let bytes = self .db .get_sub_chunk_raw(ChunkKey::new_sub_chunk(pos, dim)) @@ -611,9 +609,6 @@ where .unwrap(); let sub_chunk = SubChunk::decode_from_transition(sub_chunk_data, dim, &mut ()).unwrap(); - println!("{sub_chunk:?}"); - - println!("{:?}", wrote.len()); dump_u8_array_to_file( format!( From f7d0be67469332e4b3a38b71d640c693fa329e98 Mon Sep 17 00:00:00 2001 From: abysal Date: Thu, 13 Feb 2025 21:45:24 +0000 Subject: [PATCH 5/5] Fixed lots of random bugs within the API! --- crates/level/src/level/db_interface/rusty.rs | 3 +- crates/level/src/level/file_interface.rs | 3 +- crates/level/src/level/level.rs | 424 +++++++++--------- crates/level/src/level/sub_chunk.rs | 111 +++-- crates/level/src/level/world_block.rs | 36 +- crates/level/src/utility/miner.rs | 12 - crates/level/test_level/db/000005.ldb | Bin 2235 -> 0 bytes crates/level/test_level/db/000006.log | Bin 24438 -> 0 bytes crates/level/test_level/db/000010.log | Bin 0 -> 32297 bytes crates/level/test_level/db/000011.ldb | Bin 0 -> 3025 bytes crates/level/test_level/db/CURRENT | 2 +- crates/level/test_level/db/MANIFEST-000004 | Bin 95 -> 0 bytes crates/level/test_level/db/MANIFEST-000007 | Bin 0 -> 277 bytes .../test_level/{level.dat => level.dat.bak} | Bin crates/level/test_level/level.dat_old | Bin 0 -> 2899 bytes crates/level/test_level/levelname.txt | 2 +- crates/level/test_level/world_icon.jpeg | Bin 137047 -> 26147 bytes crates/level/tests/api_test.rs | 133 +----- 18 files changed, 289 insertions(+), 437 deletions(-) delete mode 100644 crates/level/test_level/db/000005.ldb delete mode 100644 crates/level/test_level/db/000006.log create mode 100644 crates/level/test_level/db/000010.log create mode 100644 crates/level/test_level/db/000011.ldb delete mode 100644 crates/level/test_level/db/MANIFEST-000004 create mode 100644 crates/level/test_level/db/MANIFEST-000007 rename crates/level/test_level/{level.dat => level.dat.bak} (100%) create mode 100644 crates/level/test_level/level.dat_old diff --git a/crates/level/src/level/db_interface/rusty.rs b/crates/level/src/level/db_interface/rusty.rs index b260f82c..e844e012 100644 --- a/crates/level/src/level/db_interface/rusty.rs +++ b/crates/level/src/level/db_interface/rusty.rs @@ -223,7 +223,7 @@ impl RawWorldTrait for RustyDBInterface { iter.current(&mut key, data); let len = key.len(); - let mut cursor = Cursor::new(&mut key); + let mut cursor = Cursor::new(&key); if len == 9 || len == 13 { // Does a little hack to make sure it isn't reading a key that it doesn't want to @@ -244,6 +244,7 @@ impl RawWorldTrait for RustyDBInterface { } else { Dimension::Overworld }; + out_set.insert((dim, (x, y).into())); } } diff --git a/crates/level/src/level/file_interface.rs b/crates/level/src/level/file_interface.rs index f1d169ae..ad08d7db 100644 --- a/crates/level/src/level/file_interface.rs +++ b/crates/level/src/level/file_interface.rs @@ -81,7 +81,8 @@ pub trait RawWorldTrait: Sized { ) -> Result<(), Self::Err>; fn mark_exist_chunk(&mut self, chunk_info: ChunkKey) -> Result<(), Self::Err> { - self.write_bytes_to_key(chunk_info, &[]) + // If that 41 is removed, the world ends. The sun explodes and the universe ends + self.write_bytes_to_key(chunk_info, &[41]) } fn build_key(key: &ChunkKey) -> Vec; diff --git a/crates/level/src/level/level.rs b/crates/level/src/level/level.rs index 5f5f659a..9326a9d1 100644 --- a/crates/level/src/level/level.rs +++ b/crates/level/src/level/level.rs @@ -1,10 +1,10 @@ use crate::level::db_interface::bedrock_key::ChunkKey; use crate::level::file_interface::RawWorldTrait; use crate::level::sub_chunk::{ - SerDeStoreRef, SerDeTrait, SubChunk, SubChunkDecoder, SubChunkEncoder, SubChunkSerDe, - SubChunkTrait, SubChunkTraitExtended, SubChunkTransition, + SerDeStore, SerDeStoreRef, SerDeTrait, SubChunk, SubChunkDecoder, SubChunkDecoderError, + SubChunkEncoder, SubChunkSerDe, SubChunkTrait, SubChunkTraitExtended, SubChunkTransition, }; -use crate::level::world_block::{BlockTransition, LevelBlock}; +use crate::level::world_block::LevelBlock; use bedrockrs_shared::world::dimension::Dimension; use std::collections::hash_set::Iter; use std::collections::HashSet; @@ -59,9 +59,9 @@ impl Default for LevelConfiguration { } #[derive(Debug)] -pub struct SetBlockConfig { +pub struct SetBlockConfig { pub position: Vec3, - pub block: Block, + pub block: LevelBlock, pub dim: Dimension, pub layer: u8, } @@ -74,10 +74,10 @@ pub struct GetBlockConfig { } #[derive(Debug)] -pub struct FillConfig { +pub struct FillConfig { pub from: Vec3, pub to: Vec3, - pub block: Block, + pub block: LevelBlock, pub dim: Dimension, pub layer: u8, pub data_version: u8, @@ -138,131 +138,135 @@ where &mut self.db } - /// Fills blocks into the world in the boundaries given. - /// This function works on a global scale meaning it works on/over sub chunk boundaries - /// This uses the default Encoders and Decoders. If custom ones are needed use [`Level::fill_ex`]. - pub fn fill( - &mut self, - config: FillConfig, - ) -> Result< - (), - WorldPipelineError< - UserWorldInterface::Err, - ::Err, - ::Err, - ::Err, - >, - > { - self.fill_ex(config, &mut SubChunkSerDe, &mut SubChunkSerDe) - } - - /// Fills blocks into the world in the boundaries given. - /// This function works on a global scale meaning it works on/over sub chunk boundaries - /// This uses custom Encoders and Decoders - pub fn fill_ex( - &mut self, - config: FillConfig, - decoder: &mut Decoder, - encoder: &mut Encoder, - ) -> Result< - (), - WorldPipelineError< - UserWorldInterface::Err, - Decoder::Err, - Encoder::Err, - ::Err, - >, - > - where - Decoder::Err: Debug, - Encoder::Err: Debug, - { - // This gets the `to` and `from` into a state where `from` will always be the bottom left of the selection and `to` will be the top right - let (from, to) = bounds_left_optimized(config.to, config.from); - - let (min, local_min) = LevelBlock::block_pos_to_sub_chunk(from); - let (max, local_max) = LevelBlock::block_pos_to_sub_chunk(to); - - let block = config.block.into_transition(); - - for x in min.x..=max.x { - for z in min.z..=max.z { - for y in min.y..=max.y { - if in_range(min.x, max.x, x) - && in_range(min.y, max.y, y) - && in_range(min.z, max.z, z) - { - // Since this whole sub chunk must be replaced we will just create a new one to cut down on the overhead of reading it - let pos = (x, y, z).into(); - - let data = - SubChunkTransition::full(pos, config.data_version, block.clone()); - - self.set_sub_chunk_raw(encoder, data, pos, config.dim) - .map_err(|err| WorldPipelineError::from_encode(err.into()))?; - } else { - // Now we need to find where this sub chunk lies in the boundary - - let invert_x = x == max.x; - let invert_y = y == max.y; - let invert_z = z == max.z; - - let x_range = if invert_x { - 0..=local_max.x - } else { - local_min.x..=15 - }; - - let y_range = if invert_y { - 0..=local_max.y - } else { - local_min.y..=15 - }; - - let z_range = if invert_z { - 0..=local_max.z - } else { - local_min.z..=15 - }; - - // This is slightly convoluted to allow for fewer allocations, - // by generating a sub chunk directly if it didn't exist in the database - // [`Result::unwrap_or_else`] is used over [`Result::unwrap_or`] to bypass the overhead of creating the default even when it's not needed - let mut data = self - .get_sub_chunk_raw::( - (x, y, z).into(), - config.dim, - decoder, - ) - .map_err(|err| WorldPipelineError::from_decode(err.into()))? - .map(|transition| { - SubChunk::decode_from_transition(transition, config.dim, &mut ()) - .unwrap() // This is a safe unwrap because `SubChunk::decode_from_translation` never fails - }) - .unwrap_or_else(|| { - { - SubChunk::empty((x, y, z).into(), config.dim).to_init() - } - }); - - for x in x_range { - for z in z_range.clone() { - for y in y_range.clone() { - data.set_block((x, y, z).into(), block.clone()) - .map_err(|err| WorldPipelineError::SubChunkError(err))?; - } - } - } - - let translation = data.to_transition(&mut ()).unwrap(); // Safe because this can't fail - self.set_sub_chunk_raw(encoder, translation, (x, y, z).into(), config.dim) - .map_err(|err| WorldPipelineError::from_encode(err.into()))?; - } - } - } - } - Ok(()) - } + // Fills blocks into the world in the boundaries given. + // This function works on a global scale meaning it works on/over sub chunk boundaries + // This uses the default Encoders and Decoders. If custom ones are needed use [`Level::fill_ex`]. + // pub fn fill( + // &mut self, + // config: FillConfig, + // ) -> Result< + // (), + // WorldPipelineError< + // UserWorldInterface::Err, + // ::Err, + // ::Err, + // ::Err, + // >, + // > { + // self.fill_ex(config, &mut SubChunkSerDe, &mut SubChunkSerDe) + // } + + // Fills blocks into the world in the boundaries given. + // This function works on a global scale meaning it works on/over sub chunk boundaries + // This uses custom Encoders and Decoders + // pub fn fill_ex( + // &mut self, + // config: FillConfig, + // decoder: &mut Decoder, + // encoder: &mut Encoder, + // ) -> Result< + // (), + // WorldPipelineError< + // UserWorldInterface::Err, + // Decoder::Err, + // Encoder::Err, + // ::Err, + // >, + // > + // where + // Decoder::Err: Debug, + // Encoder::Err: Debug, + // { + // // This gets the `to` and `from` into a state where `from` will always be the bottom left of the selection and `to` will be the top right + // let (from, to) = bounds_left_optimized(config.to, config.from); + // + // let (min, local_min) = LevelBlock::block_pos_to_sub_chunk(from); + // let (max, local_max) = LevelBlock::block_pos_to_sub_chunk(to); + // + // let block = config.block; + // + // for x in min.x..=max.x { + // for z in min.z..=max.z { + // for y in min.y..=max.y { + // if in_range(min.x, max.x, x) + // && in_range(min.y, max.y, y) + // && in_range(min.z, max.z, z) + // { + // // Since this whole sub chunk must be replaced, + // // we will just create a new one + // // to cut down on the overhead of reading it + // let pos = (x, y, z).into(); + // + // let data = + // SubChunkTransition::full(pos, config.data_version, block.clone()); + // + // self.set_sub_chunk_raw(encoder, data, pos, config.dim) + // .map_err(|err| WorldPipelineError::from_encode(err.into()))?; + // } else { + // // Now we need to find where this sub chunk lies in the boundary + // + // let invert_x = x == max.x; + // let invert_y = y == max.y; + // let invert_z = z == max.z; + // + // let x_range = if invert_x { + // 0..=local_max.x + // } else { + // local_min.x..=15 + // }; + // + // let y_range = if invert_y { + // local_min.y..=15 + // } else { + // 0..=local_max.y + // }; + // + // let z_range = if invert_z { + // local_min.z..=15 + // } else { + // 0..=local_max.z + // }; + // + // // This is slightly convoluted to allow for fewer allocations, + // // by generating a sub chunk directly if it didn't exist in the database + // // [`Result::unwrap_or_else`] is used over [`Result::unwrap_or`] to bypass the overhead of creating the default even when it's not needed + // let mut data = self + // .get_sub_chunk_raw::( + // (x, y, z).into(), + // config.dim, + // decoder, + // ) + // .map_err(|err| WorldPipelineError::from_decode(err.into()))? + // .map(|transition| { + // SubChunk::decode_from_transition(transition, config.dim, &mut ()) + // .unwrap() // This is a safe call to unwrap + // // because `SubChunk::decode_from_translation` + // // never fails + // }) + // .unwrap_or_else(|| { + // { + // SubChunk::empty((x, y, z).into(), config.dim).to_init() + // } + // }); + // + // for x in x_range { + // for z in z_range.clone() { + // for y in y_range.clone() { + // data.set_block((x, y, z).into(), block.clone()) + // .map_err(|err| WorldPipelineError::SubChunkError(err))?; + // } + // } + // } + // + // let translation = data.to_transition().unwrap(); // Safe because this can't fail + // self.set_sub_chunk_raw(encoder, translation, (x, y, z).into(), config.dim) + // .map_err(|err| WorldPipelineError::from_encode(err.into()))?; + // } + // } + // } + // } + // Ok(()) + // } /// # Warning /// This function is incredibly expensive to call due to having to deserialize the sub chunk. Only call this for 1 off cases of getting blocks @@ -272,18 +276,18 @@ where /// # Info /// This function uses the default decoder implementation. /// If a custom Decoder is needed please use [`Level::get_block_ex`]. - pub fn get_block( + pub fn get_block( &mut self, config: GetBlockConfig, ) -> Result< - Option, + Option, WholeLevelError< UserWorldInterface::Err, ::Err, ::Err, >, > { - self.get_block_ex::( + self.get_block_ex::( config.position, config.dim, config.layer, @@ -299,14 +303,14 @@ where /// # Info /// This function uses a custom decoder. /// If a custom layer is not needed and default decoding is fine use [`Level::get_block`]. - pub fn get_block_ex( + pub fn get_block_ex( &mut self, pos: Vec3, dim: Dimension, layer: u8, decoder: &mut Decoder, ) -> Result< - Option, + Option, WholeLevelError::Err>, > where @@ -314,7 +318,7 @@ where { let (sub_chunk_pos, local_pos) = LevelBlock::block_pos_to_sub_chunk(pos); - let data = if let Some(data) = self.get_sub_chunk::( + let data = if let Some(data) = self.get_sub_chunk_ex::( sub_chunk_pos, dim, SerDeStoreRef::new(decoder, &mut ()), @@ -333,10 +337,7 @@ where out }; - Ok(data - .get_block(local_pos) - .cloned() - .map(|ele| BlockType::from_transition(ele))) + Ok(data.get_block(local_pos).cloned()) } /// # Warning @@ -347,9 +348,9 @@ where /// # Info /// This function uses the default decoder and encoder implementations. /// If a custom Decoder or Encoder is needed please use [`Level::set_block_ex`]. - pub fn set_block( + pub fn set_block( &mut self, - config: SetBlockConfig, + config: SetBlockConfig, ) -> Result< (), WorldPipelineError< @@ -359,7 +360,7 @@ where ::Err, >, > { - self.set_block_ex::( + self.set_block_ex::( config.block, config.position, config.dim, @@ -377,13 +378,9 @@ where /// # Info /// This function uses a custom Encoder and Decoder. /// If a custom layer is not needed and default Encoding and Decoding is fine use [`Level::set_block`]. - pub fn set_block_ex< - BlockType: BlockTransition, - Decoder: SubChunkDecoder, - Encoder: SubChunkEncoder, - >( + pub fn set_block_ex( &mut self, - block: BlockType, + block: LevelBlock, pos: Vec3, dim: Dimension, layer: u8, @@ -404,7 +401,7 @@ where { let (sub_chunk_pos, local_pos) = LevelBlock::block_pos_to_sub_chunk(pos); let mut data = if let Some(data) = self - .get_sub_chunk::( + .get_sub_chunk_ex::( sub_chunk_pos, dim, SerDeStoreRef::new(decoder, &mut ()), @@ -425,15 +422,40 @@ where out }; - data.set_block(local_pos, block.into_transition())?; + data.set_block(local_pos, block)?; - self.set_sub_chunk::(&data, SerDeStoreRef::new(encoder, &mut ())) - .map_err(|e| WorldPipelineError::from_encode(e)) + self.set_sub_chunk_ex::( + &data, + encoder, + data.position(), + data.dimension(), + ) + .map_err(|e| WorldPipelineError::from_encode(e)) } /// High level function to fetch a sub chunk that contains a specific block. /// If the sub chunk doesn't exist this will return None - pub fn get_sub_chunk_block_position( + pub fn get_sub_chunk_block_position( + &mut self, + pos: Vec3, + dim: Dimension, + config: impl SerDeTrait, + ) -> Result< + Option, + WholeLevelError, + > + where + SubChunkType::Err: Debug, + { + let (sub_chunk_pos, _) = LevelBlock::block_pos_to_sub_chunk(pos); + self.get_sub_chunk_block_position_ex::( + sub_chunk_pos, + dim, + config, + ) + } + + pub fn get_sub_chunk_block_position_ex( &mut self, pos: Vec3, dim: Dimension, @@ -447,12 +469,21 @@ where SubChunkType::Err: Debug, { let (sub_chunk_pos, _) = LevelBlock::block_pos_to_sub_chunk(pos); - self.get_sub_chunk::(sub_chunk_pos, dim, config) + self.get_sub_chunk_ex::(sub_chunk_pos, dim, config) } /// High level function to fetch a sub chunk directly from the database. - /// If the sub chunk doesn't exist this will return None - pub fn get_sub_chunk( + /// If the sub chunk doesn't exist, this will return None + pub fn get_sub_chunk( + &mut self, + pos: Vec3, + dim: Dimension, + ) -> Result, WholeLevelError> + { + self.get_sub_chunk_ex(pos, dim, SerDeStore::::default()) + } + + pub fn get_sub_chunk_ex( &mut self, pos: Vec3, dim: Dimension, @@ -477,6 +508,7 @@ where .map_err(|ele| WholeLevelError::TranslationError(ele))?, )) } + pub fn get_sub_chunk_raw( &mut self, pos: Vec3, @@ -496,17 +528,6 @@ where Some(e) => e, }; - dump_u8_array_to_file( - format!( - "{}_{:?}.bin", - pos, - SystemTime::from(UNIX_EPOCH).elapsed().unwrap().as_nanos() - ) - .as_str(), - &bytes, - ) - .unwrap(); - Ok(Some( decoder .decode_bytes_as_sub_chunk(&mut std::io::Cursor::new(bytes), (pos.x, pos.z).into()) @@ -516,28 +537,17 @@ where /// High level function to write a sub chunk directly into the database. /// Writes the sub chunk at its current dimension and position. - /// If a dimension or position override is required please call [`Level::set_sub_chunk_ex`] - pub fn set_sub_chunk< - SubChunkType: SubChunkTraitExtended, - SubChunkEncoderType: SubChunkEncoder, - >( + /// If a dimension or position override is required, please call [`Level::set_sub_chunk_ex`] + pub fn set_sub_chunk( &mut self, sub_chunk: &SubChunkType, - encode_data: impl SerDeTrait< - Info = SubChunkType::TransitionInformation, - SerDe = SubChunkEncoderType, - >, - ) -> Result< - (), - WholeLevelError, - > + ) -> Result<(), WholeLevelError> where - SubChunkEncoderType::Err: Debug, SubChunkType::Err: Debug, { self.set_sub_chunk_ex( sub_chunk, - encode_data, + &mut SubChunkSerDe::default(), sub_chunk.position(), sub_chunk.dimension(), ) @@ -545,17 +555,14 @@ where /// Slightly lower level function to write a sub chunk directly into the database. /// Writes the sub chunk at the position and dimension provided. - /// If you do not need to override this please use [`Level::set_sub_chunk`] instead + /// If you do not need to override this, please use [`Level::set_sub_chunk`] instead pub fn set_sub_chunk_ex< SubChunkType: SubChunkTraitExtended, SubChunkEncoderType: SubChunkEncoder, >( &mut self, sub_chunk: &SubChunkType, - mut encode_data: impl SerDeTrait< - Info = SubChunkType::TransitionInformation, - SerDe = SubChunkEncoderType, - >, + encoder: &mut SubChunkEncoderType, pos: Vec3, dim: Dimension, ) -> Result< @@ -567,9 +574,9 @@ where SubChunkEncoderType::Err: Debug, { let intermediate = sub_chunk - .to_transition(encode_data.info()) + .to_transition() .map_err(|ele| WholeLevelError::TranslationError(ele))?; - Ok(self.set_sub_chunk_raw(encode_data.serde(), intermediate, pos, dim)?) + Ok(self.set_sub_chunk_raw(encoder, intermediate, pos, dim)?) } /// Lowest level setting function for a sub chunk. @@ -593,37 +600,6 @@ where .set_sub_chunk_raw(ChunkKey::new_sub_chunk(pos, dim), &bytes) .map_err(|ele| SubChunkSerDeError::WorldError(ele))?; - let wrote = self - .db - .get_sub_chunk_raw(ChunkKey::new_sub_chunk(pos, dim)) - .unwrap(); - - if let Some(wrote) = wrote { - assert_eq!(wrote, bytes); - - let sub_chunk_data = SubChunkSerDe::decode_bytes_as_sub_chunk( - &mut SubChunkSerDe, - &mut std::io::Cursor::new(wrote.clone()), - (pos.x, pos.z).into(), - ) - .unwrap(); - - let sub_chunk = SubChunk::decode_from_transition(sub_chunk_data, dim, &mut ()).unwrap(); - - dump_u8_array_to_file( - format!( - "write_{}_{:?}.bin", - pos, - SystemTime::from(UNIX_EPOCH).elapsed().unwrap().as_nanos() - ) - .as_str(), - &wrote, - ) - .unwrap(); - } else { - panic!("No data returned from key which was just wrote"); - } - self.handle_exist((pos.x, pos.z).into(), dim); Ok(()) } @@ -754,6 +730,6 @@ pub mod default_impl { } use crate::level::error::{SubChunkSerDeError, WholeLevelError, WorldPipelineError}; -use crate::utility::miner::{bounds_left_optimized, dump_u8_array_to_file, in_range}; +use crate::utility::miner::{bounds_left_optimized, in_range}; #[cfg(feature = "default-impl")] pub use default_impl::*; diff --git a/crates/level/src/level/sub_chunk.rs b/crates/level/src/level/sub_chunk.rs index 94958525..bf9e6658 100644 --- a/crates/level/src/level/sub_chunk.rs +++ b/crates/level/src/level/sub_chunk.rs @@ -1,12 +1,12 @@ pub use crate::level::error::SubChunkError; -use crate::level::world_block::{BlockTransition, LevelBlock}; +use crate::level::world_block::LevelBlock; use crate::utility::miner::idx_3_to_1; use bedrockrs_shared::world::dimension::Dimension; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use nbtx::NbtError; use std::fmt::{Debug, Formatter}; -use std::io::Cursor; use std::io::Write; +use std::io::{Cursor, Seek, SeekFrom}; use std::mem::MaybeUninit; use thiserror::Error; use vek::{Vec2, Vec3}; @@ -15,9 +15,9 @@ pub type BlockLayer = (Box<[u16; 4096]>, Vec); #[allow(dead_code)] pub struct SubChunkTransition { - position: Vec3, - data_version: u8, - layers: Vec>, + pub position: Vec3, + pub data_version: u8, + pub layers: Vec>, } impl Debug for SubChunkTransition { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { @@ -154,10 +154,7 @@ pub trait SubChunkTrait: Sized { ) -> Result; /// This must create a transitional state from the current sub chunk information - fn to_transition( - &self, - info: &mut Self::TransitionInformation, - ) -> Result; + fn to_transition(&self) -> Result; /// This returns if the sub chunk is just air if so nothing is written to the database if this isn't desired behavior just always return false fn is_empty(&self) -> bool; @@ -222,6 +219,11 @@ impl SubChunkDecoder for SubChunkSerDe { let palette_type = bytes.read_u8()?; let network = palette_type & 0x1 == 1; let bits_per_block = palette_type >> 1; + + if bits_per_block == 0 { + break; // This signifies an empty sub chunk + } + let blocks_per_word = 32 / bits_per_block; let word_count = (4096 + (blocks_per_word as i32) - 1) / (blocks_per_word as i32); let mask = (1 << bits_per_block) - 1; @@ -245,15 +247,11 @@ impl SubChunkDecoder for SubChunkSerDe { let mut blocks = Vec::with_capacity(palette_count as usize); for _ in 0_usize..palette_count as usize { if network { - blocks.push(LevelBlock::from_transition(nbtx::from_bytes::< - nbtx::NetworkLittleEndian, - LevelBlock, - >(bytes)?)); + blocks.push(nbtx::from_bytes::( + bytes, + )?); } else { - blocks.push(LevelBlock::from_transition(nbtx::from_bytes::< - nbtx::LittleEndian, - LevelBlock, - >(bytes)?)); + blocks.push(nbtx::from_bytes::(bytes)?); } } transitiondata.new_layer((block_indices, blocks)); @@ -299,9 +297,9 @@ impl SubChunkEncoder for SubChunkSerDe { buffer.write_u32::(layer.1.len() as u32)?; for blk in layer.1 { if network { - buffer.write(&nbtx::to_net_bytes(&blk.into_transition())?)? + buffer.write(&nbtx::to_net_bytes(&blk)?)? } else { - buffer.write(&nbtx::to_le_bytes(&blk.into_transition())?)? + buffer.write(&nbtx::to_le_bytes(&blk)?)? }; } } @@ -340,7 +338,7 @@ impl SubChunk { self.is_empty = true; } - pub fn full(position: Vec3, dimension: Dimension, block: impl BlockTransition) -> Self { + pub fn full(position: Vec3, dimension: Dimension, block: LevelBlock) -> Self { let mut val = Self { blocks: Vec::with_capacity(1), position, @@ -348,9 +346,8 @@ impl SubChunk { active_layer: 0, is_empty: false, }; - val.blocks.push(Box::new(std::array::from_fn(|_| { - block.clone().into_transition() - }))); + val.blocks + .push(Box::new(std::array::from_fn(|_| block.clone()))); val } @@ -382,16 +379,12 @@ impl SubChunk { layer.get_mut(idx_3_to_1::(xyz, 16u8, 16u8)) } - pub fn set_block( - &mut self, - xyz: Vec3, - block: Block, - ) -> Result<(), SubChunkError> { + pub fn set_block(&mut self, xyz: Vec3, block: LevelBlock) -> Result<(), SubChunkError> { let layer = self .blocks .get_mut(self.active_layer as usize) .ok_or(SubChunkError::LayerError(self.active_layer))?; - layer[idx_3_to_1::(xyz, 16u8, 16u8)] = block.into_transition(); + layer[idx_3_to_1::(xyz, 16u8, 16u8)] = block; self.is_empty = false; Ok(()) } @@ -468,27 +461,26 @@ impl SubChunkTrait for SubChunk { for (layer_index, (indices, blocks)) in data.layers.into_iter().enumerate() { let layer: &mut Box<[MaybeUninit; 4096]> = &mut layers[layer_index]; for whole_index in 0..4096usize { - layer[whole_index].write(LevelBlock::from_other( - &blocks[indices[whole_index] as usize], - )); + layer[whole_index].write(blocks[indices[whole_index] as usize].clone()); } } - let layers = unsafe { std::mem::transmute(layers) }; - - Ok(Self { - blocks: layers, - position: data.position, - dimension, - active_layer: 0, - is_empty: false, - }) + if layers.is_empty() { + Ok(Self::empty(data.position, dimension)) + } else { + let layers = unsafe { std::mem::transmute(layers) }; + + Ok(Self { + blocks: layers, + position: data.position, + dimension, + active_layer: 0, + is_empty: false, + }) + } } - fn to_transition( - &self, - _: &mut Self::TransitionInformation, - ) -> Result { + fn to_transition(&self) -> Result { let mut layers: Vec> = Vec::with_capacity(self.blocks.len()); for layer in 0..self.blocks.len() { layers.push(self.encode_single_layer(layer)); @@ -506,9 +498,34 @@ impl SubChunkTrait for SubChunk { } fn bits_needed_to_store(val: u32) -> u8 { - if val == 0 { + if val <= 1 { 1 } else { - (32 - val.leading_zeros()) as u8 + if val.count_ones() == 1 { + // In binary, we might have something like + // 001 + // That is 4. We only need 2 bits + // to store it since 0,1,2,3 is 4 combinations. + // So to compute that we just count how may trailing zeros it has + + val.trailing_zeros() as u8 + } else { + 32 - val.leading_zeros() as u8 + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn verify_store_sanity() { + let test_data = [(2, 1), (4, 2), (3, 2), (5, 3), (8, 3)]; + + for (input, expected) in test_data { + let stored = bits_needed_to_store(input); + + assert_eq!(expected, stored); + } } } diff --git a/crates/level/src/level/world_block.rs b/crates/level/src/level/world_block.rs index 1338308d..fcd33c04 100644 --- a/crates/level/src/level/world_block.rs +++ b/crates/level/src/level/world_block.rs @@ -12,19 +12,12 @@ pub enum BlockStateValue { #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] pub struct LevelBlock { - // If the name of anything in this struct changes the world ends + // If the name of anything in this struct changes, the world ends pub name: String, pub states: HashMap, -} - -pub trait BlockTransition: Clone + PartialEq { - fn from_transition(value: LevelBlock) -> Self; - fn into_transition(self) -> LevelBlock; - - fn get_id(&self) -> &str; - fn from_other(other: &Self) -> Self { - other.clone() - } + #[serde(default)] + // Honestly? I haven't the faintest idea what this does + pub version: i32, } impl LevelBlock { @@ -32,6 +25,7 @@ impl LevelBlock { Self { name: String::from("minecraft:air"), states: HashMap::new(), + version: 18163713, } } @@ -39,6 +33,7 @@ impl LevelBlock { Self { name: id, states: HashMap::new(), + version: 18163713, } } @@ -47,8 +42,8 @@ impl LevelBlock { let sub_chunk_y_inside = pos.y & 0xF; let chunk_xz = Vec2::new( - (pos.x >> 4), - (pos.z >> 4), // Stolen from the game + pos.x >> 4, + pos.z >> 4, // Stolen from the game ); let sub_chunk_xz = Vec2::new(pos.x & 0xF, pos.z & 0xF); @@ -80,24 +75,11 @@ impl, StateType: Into>> Self { name: id.into(), states: states.into(), + version: 18163713, } } } -impl BlockTransition for LevelBlock { - fn from_transition(value: LevelBlock) -> Self { - value - } - - fn into_transition(self) -> LevelBlock { - self - } - - fn get_id(&self) -> &str { - &self.name - } -} - #[cfg(test)] mod test { use super::*; diff --git a/crates/level/src/utility/miner.rs b/crates/level/src/utility/miner.rs index 268f5de2..d5688bbf 100644 --- a/crates/level/src/utility/miner.rs +++ b/crates/level/src/utility/miner.rs @@ -58,15 +58,3 @@ mod test { ); } } - -pub fn dump_u8_array_to_file(file_name: &str, data: &[u8]) -> io::Result<()> { - return Ok(()); - // Create or open the file - let mut file = std::fs::File::create(file_name)?; - - // Write the u8 array to the file - file.write_all(data)?; - - println!("Data successfully written to {}", file_name); - Ok(()) -} diff --git a/crates/level/test_level/db/000005.ldb b/crates/level/test_level/db/000005.ldb deleted file mode 100644 index 10ee7854957600e038229f6ae10c9ba364117501..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2235 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 diff --git a/crates/level/test_level/db/000010.log b/crates/level/test_level/db/000010.log new file mode 100644 index 0000000000000000000000000000000000000000..cad5b0458ecfba03457e0781e2db17bbf3f8fb9b GIT binary patch literal 32297 zcmeHQeQX>@6`%E;?Yr|wobR+r(}Wh4lE{fm3sp;-_$!Hp9fzFLKqWZM-Hz?8_ja$d zd;XA6>wk+XD1!12A1YAs7v)br{wbwGsDkv5sz`_mB#@9mNC*%@1tnm9Z+CBZ?rdM3 zO}{-`IrnDXym>os-n`kFdGq?(@oRVdu3(%nSn%AAe~n(#nyanh39^iQ13dO@F3a?P z+gSn`(gpo&UkP-Cc7l<;*hC76!8mT*$qH18h)ysCHWau+finu6Rp3qq?o!}x1?LrvmR%;N3>j-fMr$=Z$OGub}<2-!1mOEENowa zF?>2Xyef9Gs^y4^QyN+9R>)yEITfQZ{Bam)>4{JfJOj;f~f*vD3jm}tdrEMNX*+Q0?+~@f|HfdGM zZljv8EHnBZgLJuFs@Z%+$d~R1MMs7WTk^P(?_|TW>dHbV)!NBM-MZtmjxILFt9(ub zoqQh30%Q-n%)JX%WkHyiYVXa$`2rjEeF?3Gju-mTzbK%R$D^Iy0<+0u<(9XpywkAz{}m3kjQNhE+C5HM42Tpepeq5gVW) zHgTs6D1%{MaND0x7`;(@RL;$eR@XDyqcGxS&y_VFc3|twPASc7VRmX=s}3v7795y` zGOsL@r0}5q+^L8L+n29gc_0yHkWz)Yw)Xhro3IT~t-{=;u&ug_qDqPosYl`0-}!jL zDoEW3t6LOSb;ra&hRv0wx0vgGg{#dM`8s}3>L%?(N^K={aa_r0?nj_n1-eh+H|yHA zyHt|gflZ0W-0G=Bs9>&YNigQr3}S~8(O?LnFfST&YV)}7lg^l-4U73<2q|M6spjT2 zWi&SrsMhAG{6fPzIA2HeWYWGj7=zcx@1P>ih@V)V=XI}nC_Zz#sg0*^sEr%&^tfM_ zyqRbFc7Fe-XOx#{k}IKF<*KSp)J|J03VX|9ZPKGAc98;wd6C*|&_uCZ6Vei7Y7FSh zyzqQshLfZ1N@jFnmIQDY%k;BR`h2pSxk^ShA?ix6;~a2A1bNJErKLzGQJ)R~5;D-BV18lqepqI?>nLK>o;G(^2=i2BkH^`{})k%nj> z4bjdtM7z=u?M_3qCk@fwG(`K-5baMxbRZ4UO=*a3ZYApMWoNi;k2Std$x`>TbKHYT zcSfZ!E#G4uId-lt{ctYG=UHOe>6inHuMt}RMhkOwjq5wfdK#QDHQBByHe8W6<_BZ_ z>aC?lOi_BW2-dWrAoOGrX@s6EB0=cMA`*n2EFwX?Ig>@hMsK{s7Fwf&tuxG4hz_M8 zx+M+K;WR|IrXf0#hUjP-q7S4Yx@{fNu4G<6W7*i!!NQQ%dGTzotP*=Ew{AN^^0FN) zG$*`aXHKrb>45=^_k?M^IpK})GB7zn<5+y7S_2;u-dWo+g9IVY+HRTK%oqSXVcXcf zm7~1ob1Zj_0`jJ14XaIB$@WvK?oZE-x{fbh&=CHD6mt%iqhZw-T^_uCz_VE4_>c(^ zMZfL@wGd5l$*STM0k6}I)C{&hQHl^Vk~SuQ$mZFk;NHBD6Jw22+RRPJoAqO{%NmmHSwX4A8?am(6|^p@8^&rCaK zB{u9$mIb^7T{S@g;jJ{n1~c-Js{^6&7a%}hW4`yy=7p=MZ zjr*C-Q_!&&U7R;jw^RD~ou7a4$uB%T0%a<4)3nj|ncXZ@4Plb9LJgou;r_|q7!xZ} z@KuY7r~+(=2Rd5-=q`=8zVB88gyjc{ndxH11JIVXH&koGZ> zM$tl(FJcSq%URU7gUv{8it%KJjJV{(+!>2x$4D73Io6|fF)>EtgA@{nhsyZC_`l4h zDV&*ki7m1*96|~l!bHMHg*%(@v94y0uFs!vLSj`P3wg%NmK_|W!5W3T!Bw~H8Ae}} z3U?Nf(aFkn%Qg*|P1ALy2P5MxS$+k(enw}G&Dtx)8usR1zB2g$3^9`Ea08WE4o)K? z(q5zzTW-KK+obmn=>~|ZdRu!iAfbmrL- zw-+MTS014LFVje;{*M#X|8au)pGVP-bft*x zP|R$f5Gyef^(DDEdP!;|{xSm$8@W}>qfJ*Y(4t@{?PDRQ3oS@8L4^S(Ma8O#Qr)2? zIl3@Ez(QDxw`p{rGF{t zCocU=AFF%dPwFC;eJ?nTW>0+W>j%Dg#CV9>i>D8N(7ot6_WMJhY{2?=TS`E?NH#u( zw&`zZCPIU#m0_DIL8IAP85)w%ziV0^tLIYYEv<}1)P?aIhjDcjaiR(4k z6N#P^>}oD=uq-W0FX=~1Kucg-N`Q<9jKYn1wQO8LyQ?3HAu`Ai8QGa_DSq8(Edeb7 zlE6D}h}N!V^Trx3UFl~#OJH+FY&W58JOy5dXDEvgg-#~m-O1N`<91`CYwk}_r*cnx z^R-|6`RX5EP4aN1(Kr+wr;Xyq%fgiK`50~=j#+^N2bU{Qi%8r9c1BXgO_lE4pmwf@B64PKE2jH4ex9u}Fj;;kABjc!& zj$H%few?m1?u z8iCxl%R!}Uah(BXO_LA-+tb4uI&ks!zexErlgf&d+I>kYOM^hC^Vpz zLF+&)0j&(NC^VpzLF+&)0aY2|%os=(o2{`Ca1)S$HZG}wZ72a!9yo!&gLmVjE2+`{ YTANlv`q_pO*jy3cT~|_*i>pij2l$^3l>h($ literal 0 HcmV?d00001 diff --git a/crates/level/test_level/db/000011.ldb b/crates/level/test_level/db/000011.ldb new file mode 100644 index 0000000000000000000000000000000000000000..3e5109252a6d45612da62119c762e7044f6f572e GIT binary patch literal 3025 zcmV;?3oi8STTN^vM-_fGKi=-~&)U1*|4p(Xn<(JLi5JKZO4dJP@5aPluXbkRBnL({ z(={`l>FFA(yFIpq5?_(HZ~$>Zij+MdA#sFoB18xYX>$SzA;AGD5)wB~Iq+j%RrSoY z$3Hu1*eu|#mNi}V-m6#j-m93^w z{O5A8!Zw(*;P6Vr$bd_mY!sTd%PN9a{jd72pBnJeKk1z$9qE(` zk6q*%~Qk*;pAryCx80@@~kHRP3Fq7n@BccZG(w67mk$~gB#3cLbaj5nDqz#_doyO@~{4s9_KLlJIy3#1ncO2ip03hYm|d&;x2aoWsWdd{<{Xi~w)?>j7-wKj z*fraw&VhAa?6M40W$Hq&gEOU>7c}qErq5i9Sp-sNq0k5%=Q^4m%WSuHi-{^1O+`Ed zcaTlAaW^7X=muS(4R+n$RJ7BuE-OXLEHH<+-(-S^B>3LfzkB63H%Gqn8M@>D#B#r% zYOXK+^|yaH*mANzGtWK3&0~?sbAn2vr{>-Hs2a@eR!3cEJASqPS?cjJsO`TE4uMggzgB5M> zRWunr@hf{=wA8w1U}duhE1TV;?9qs_hRbmv(MqEvn1_pFileck_M?0EUhb(?xv1Km zop;_jK&>M6t344>+vFRpDf<$t%Kh%WmwKvH?%z@MNJLf8h56D0)_NuylsKjA(WhJ= z@#A)Xbi&esene8gzS9wXRqo(SRT6ThjHsL4rEd0qVWWd(ci3#t?$bS-QDw|n8V?@K z;EU?h@H&kPf1HK}9u}WY--W&U*k+vu-tM9J?%b|E&i$u#dejf}LT%7~{`eQy57H?~ zhlNe3(~;Y%2ZT2Jnzey?-Xwh8c6@a>EH`D{(#V!>M3x4Qyoo3~YFD`I z&AT z+tJBZYuQA zO?5Qks8e`(YQ9#j#%l(eam~FUkl%s~zPHiwH(9%DkukWXP735T(JHrCR<~6bsykqH z=x{Pxa!c@GN<~-X`tq6nRB~UbxMDbxf0$dG^x#AI|Mum_yGOsAW0msPswDBFvJJg+4>C z>ywF7T}oGhT7pVDVg+@k11yKHyuHAEpEni3_Ei+6hoI1~U41u^LN14~mR$M@Wm=V$ zeCBaAG-c%E>vHX^4Ix=vMjNUsY@~Fj`ur3vVap!9j!fJ8Qr3BKy0`K&q9$h1;RGwSUEE29q9d5=0WU#Jb7b7R3dM$? zNw-jrAjZo>JVH{yTLcl4x6p!}pvI;c;A23yaCnid32o{$Le=+Pmi{lxk^Va9U{IMi zqM0|tJza&m5+j0^9<=5#SgdNhD!x2J-M+$9|7oJU+uUr)7+(tSHZkn0yq4$9CR(H* zO{N0H7z26xOa0PD$~AWm^an}>9Oej%ud=N!5$o%f8p;a#FFg(zrcK+E?etW%sG)Qk zw5D5qMD^YII0#m;TdWkga%7?l&2iB0%qnS`r#>HlGSc*QE*cBy&3eeOY?Z2lH%pv0 zE*{E;qb%^SVi^Z))Az!!!Y1w-oonnVGN;QP_7{uE^KhT!i_s&V2K{0*ui3g^jD}xd z?D}GKDXePI!<^^$ z!JHR%Va|)tOV(L{U{3$1)xQ7)e5>tGH4rhdK!O*7>wj2PCXl5~L#?p99U|gZ585Xn zDK*UC7O-YIBSsSBB$Pxsssk63AfMcd=rV9Bu;wGh1^@s606hQz01E(88JA!HA_Hb= zY;V(P=EN7|Bvz&tF|t8K0S|c=Bme*a diff --git a/crates/level/test_level/db/MANIFEST-000007 b/crates/level/test_level/db/MANIFEST-000007 new file mode 100644 index 0000000000000000000000000000000000000000..2b690e6691d137b6465e7f1017e5e2ee68b94e97 GIT binary patch literal 277 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 literal 137047 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�mr| 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

sPdT-$r{&YN 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 Result<(), anyhow::Error> { let mut level = get_level_with_copy()?; - let block = LevelBlock::id("minecraft:tnt".into()); - let range = 48; - // level - // .fill(FillConfig { - // from: (-range, -range, -range).into(), - // to: (30, -30, 30).into(), - // block: block.clone(), - // dim: Dimension::Overworld, - // layer: 0, - // data_version: 9, - // }) - // .expect("TODO: panic message"); - // level.flush()?; - // drop(level); - level.clear(Dimension::Overworld)?; - level - .set_block(SetBlockConfig { - position: (0, 10, 0).into(), - block: block.clone(), - dim: Dimension::Overworld, - layer: 0, - }) - .expect("Cant Fail"); - - for y in -16..=16 { - level - .set_block(SetBlockConfig { - position: (0, y, 0).into(), - block: block.clone(), - dim: Dimension::Overworld, - layer: 0, - }) - .expect("Cant Fail"); - } - - // println!( - // "{:?}", - // level - // .get_block::(GetBlockConfig { - // position: (0, 13, 0).into(), - // dim: Dimension::Overworld, - // layer: 0, - // }) - // .unwrap() - // ); - // level.close()?; - - dump_keys(&mut level); - level.close()?; - let mut level = BedrockLevel::open( - Box::from(Path::new("./test_level_temp")), - LevelConfiguration::default(), - &mut (), - )?; + let exists = level.chunk_exists((0, 0).into(), Overworld); - for y in -16..=16 { - println!( - "{:?}", - level - .get_block::(GetBlockConfig { - position: (0, y, 0).into(), - dim: Dimension::Overworld, - layer: 0, - }) - .expect("Cant Fail") - ); + if !exists { + panic!("Expected chunk doesnt exist!") } - level.flush()?; - dump_keys(&mut level); - - // let mut level = get_level_with_copy()?; - // - // println!( - // "{:?}", - // level - // .get_block::(GetBlockConfig { - // position: (0, 13, 0).into(), - // dim: Dimension::Overworld, - // layer: 0, - // }) - // .unwrap() - // ); - - std::process::exit(0); - let mut sub_chunk = level - .get_sub_chunk::( - (0, 0, 0).into(), - Dimension::Overworld, - SerDeStore::default(), - ) - .expect("Read failed") - .unwrap_or_else(|| SubChunk::empty((0, 0, 0).into(), Dimension::Overworld).to_init()); - - sub_chunk - .set_block((0, 0, 0).into(), LevelBlock::id("minecraft:tnt".into())) - .expect(""); - - level - .set_sub_chunk::(&sub_chunk, SerDeStore::default()) - .expect("This should never fail"); - - assert_eq!( - level - .get_block::(GetBlockConfig { - position: (0, -3, 0).into(), - dim: Dimension::Overworld, - layer: 0, - }) - .expect("Cant Fail") - .unwrap(), - block - ); - - level - .fill(FillConfig { - from: (-11, -11, -11).into(), - to: (10, 10, 10).into(), - block: block.clone(), - dim: Dimension::Overworld, - layer: 0, - data_version: 9, - }) - .expect("TODO: panic message"); Ok(()) }