From bd63fa577353849912e8721c481b86c32092632c Mon Sep 17 00:00:00 2001 From: Liyze09 Date: Wed, 2 Oct 2024 14:37:20 +0800 Subject: [PATCH] refactor(world): implement Chunk and Dimension improvements - Rewrite Chunk to use Section data structure for better organization - Optimize Dimension to use DashMap for chunk storage - Implement Item and BlockItem traits for future item system - Update world and entity modules to accommodate new changes --- crates/kernel/config.toml | 5 -- crates/kernel/logs/latest.log | 8 -- crates/kernel/src/block.rs | 1 + crates/kernel/src/entity.rs | 5 +- crates/kernel/src/item.rs | 59 ++++++++++++++ crates/kernel/src/main.rs | 1 + crates/kernel/src/test/world_test.rs | 23 ++++-- crates/kernel/src/world.rs | 19 +++-- crates/kernel/src/world/chunk.rs | 112 +++++++++++++++++++++++---- crates/kernel/src/world/dimension.rs | 40 ++++++---- 10 files changed, 215 insertions(+), 58 deletions(-) delete mode 100644 crates/kernel/config.toml delete mode 100644 crates/kernel/logs/latest.log create mode 100644 crates/kernel/src/item.rs diff --git a/crates/kernel/config.toml b/crates/kernel/config.toml deleted file mode 100644 index 753ecab..0000000 --- a/crates/kernel/config.toml +++ /dev/null @@ -1,5 +0,0 @@ -max-players = 20 -port = 25565 -seed = 0 -simulation-distance = 16 -view-distance = 12 diff --git a/crates/kernel/logs/latest.log b/crates/kernel/logs/latest.log deleted file mode 100644 index 5a7e0b5..0000000 --- a/crates/kernel/logs/latest.log +++ /dev/null @@ -1,8 +0,0 @@ -[INFO] 2024-09-27 23:57:36 main.rs 47:Binding PORT: 25565 -[INFO] 2024-09-27 23:57:36 main.rs 48:Loaded 64 biomes. -[INFO] 2024-09-27 23:57:36 main.rs 49:Loaded 3 dimension types. -[INFO] 2024-09-27 23:57:36 main.rs 50:Loaded 46 damage types. -[INFO] 2024-09-27 23:57:36 main.rs 51:Loaded 9 wolf variants. -[INFO] 2024-09-27 23:57:36 main.rs 52:Loaded 46 painting variants. -[INFO] 2024-09-27 23:57:36 main.rs 74:Network thread started. -[INFO] 2024-09-27 23:57:36 main.rs 75:Time elapsed 428054541 ns diff --git a/crates/kernel/src/block.rs b/crates/kernel/src/block.rs index d41bfd7..19ab8f7 100644 --- a/crates/kernel/src/block.rs +++ b/crates/kernel/src/block.rs @@ -54,6 +54,7 @@ pub(crate) static BLOCKS_BY_ID: Lazy>> = Lazy::new(D pub(crate) static BLOCKS_BY_NAME: Lazy> = Lazy::new(DashMap::new); pub(crate) static BLOCK_STATES_BY_ID: Lazy>> = Lazy::new(DashMap::new); +pub(crate) static BLOCK_ITEM_BY_ID: Lazy> = Lazy::new(DashMap::new); pub fn register_block(id: &str, block: Box) { block.get_block_states().into_iter().for_each(|(k, v)| { diff --git a/crates/kernel/src/entity.rs b/crates/kernel/src/entity.rs index 245ff02..5d0c06e 100644 --- a/crates/kernel/src/entity.rs +++ b/crates/kernel/src/entity.rs @@ -1,7 +1,6 @@ use crate::world::dimension::Dimension; use crate::WORLD; use downcast_rs::{impl_downcast, DowncastSync}; -use parking_lot::RwLock; use std::sync::Arc; pub mod entity_manager; @@ -19,7 +18,7 @@ pub trait Entity: Send + Sync + DowncastSync { self.get_data_mut().pos = (x, y, z); } - fn get_dimension(&mut self) -> Arc> { + fn get_dimension(&mut self) -> Arc { unsafe { WORLD .read() @@ -36,7 +35,7 @@ pub trait Entity: Send + Sync + DowncastSync { .read() .dimensions .iter() - .position(|d| d.read().dimension_name == dimension) + .position(|d| d.dimension_name == dimension) .unwrap(); } } diff --git a/crates/kernel/src/item.rs b/crates/kernel/src/item.rs new file mode 100644 index 0000000..a118019 --- /dev/null +++ b/crates/kernel/src/item.rs @@ -0,0 +1,59 @@ +use crate::registry::protocol_id::get_protocol_id; +use downcast_rs::{impl_downcast, DowncastSync}; + +pub trait Item: Send + Sync + DowncastSync { + fn get_builder(&self) -> &ItemBuilder; +} +impl_downcast!(sync Item); + +pub trait BlockItem: Item { + fn get_block() -> u32; +} + +pub struct ItemBuilder { + pub protocol_id: u32, + pub item_settings: ItemSettings, +} + +impl ItemBuilder { + fn new(id: &str, item_settings: ItemSettings) -> ItemBuilder { + ItemBuilder { + protocol_id: get_protocol_id("minecraft:item", &id).unwrap(), + item_settings, + } + } +} + +pub struct ItemSettings { + pub max_count: u8, + pub max_damage: u16, + pub fireproof: bool, +} + +impl ItemSettings { + pub fn new() -> ItemSettings { + ItemSettings { + max_count: 64, + max_damage: 0, + fireproof: false, + } + } + pub fn max_count(mut self, max_count: u8) -> ItemSettings { + self.max_count = max_count; + self + } + pub fn max_damage(mut self, max_damage: u16) -> ItemSettings { + self.max_damage = max_damage; + self + } + pub fn fireproof(mut self) -> ItemSettings { + self.fireproof = true; + self + } +} + +impl Default for ItemSettings { + fn default() -> Self { + Self::new() + } +} diff --git a/crates/kernel/src/main.rs b/crates/kernel/src/main.rs index 57bdcf6..66d45a2 100644 --- a/crates/kernel/src/main.rs +++ b/crates/kernel/src/main.rs @@ -1,6 +1,7 @@ pub mod block; pub mod config; pub mod entity; +pub mod item; pub mod nbt; pub(crate) mod network; pub mod registry; diff --git a/crates/kernel/src/test/world_test.rs b/crates/kernel/src/test/world_test.rs index fb679e6..de0d184 100644 --- a/crates/kernel/src/test/world_test.rs +++ b/crates/kernel/src/test/world_test.rs @@ -2,7 +2,18 @@ mod chunk { #[test] fn chunk() { use crate::world::chunk::Chunk; - let mut chunk = Chunk::new(384); + use crate::world::dimension::Dimension; + let chunk = Chunk::new( + &Dimension::new( + crate::registry::dimension_type::DIMENSION_TYPES + .get("minecraft:overworld") + .unwrap() + .clone(), + "overworld".to_string(), + 0, + ), + 0, + ); chunk.set_block(0, 0, 0, 9); chunk.set_block(11, 45, 14, 9); chunk.set_block(15, 383, 15, 9); @@ -16,16 +27,18 @@ mod dimension { #[test] fn dimension() { use crate::world::dimension::Dimension; - let mut dimension = Dimension::new( + let mut chunks = Vec::with_capacity(3); + let dimension = Dimension::new( crate::registry::dimension_type::DIMENSION_TYPES .get("minecraft:overworld") .unwrap() .clone(), "overworld".to_string(), + 0, ); - dimension.set_block(1144657482, 319, -138848321, 9); - dimension.set_block(1145, 14, 1919, 9); - dimension.set_block(0, -64, 0, 9); + chunks.push(dimension.set_block(1144657482, 319, -138848321, 9)); + chunks.push(dimension.set_block(1145, 14, 1919, 9)); + chunks.push(dimension.set_block(0, -64, 0, 9)); assert_eq!(dimension.get_block(1144657482, 319, -138848321), Some(9)); assert_eq!(dimension.get_block(1145, 14, 1919), Some(9)); assert_eq!(dimension.get_block(0, -64, 0), Some(9)); diff --git a/crates/kernel/src/world.rs b/crates/kernel/src/world.rs index 200597f..30676e8 100644 --- a/crates/kernel/src/world.rs +++ b/crates/kernel/src/world.rs @@ -4,7 +4,6 @@ use crate::registry::DIMENSION_TYPES_INDEX; use crate::world::block_update::{BlockUpdate, BlockUpdateType}; use crate::world::dimension::Dimension; use dashmap::DashSet; -use parking_lot::RwLock; use rayon::prelude::*; use std::sync::Arc; use std::thread; @@ -15,7 +14,7 @@ pub mod dimension; pub struct World { default_dimension: usize, - pub dimensions: Vec>>, + pub dimensions: Vec>, pub(crate) entities: EntityManager, block_updates_queue_1: Vec, block_updates_queue_2: Vec, @@ -24,16 +23,20 @@ pub struct World { impl World { pub fn new() -> World { let mut dimensions = Vec::with_capacity(DIMENSION_TYPES_INDEX.len()); - for dim in DIMENSION_TYPES_INDEX.iter() { - dimensions.push(Arc::new(RwLock::new(Dimension::new( + let mut idx = 0; + while idx < DIMENSION_TYPES_INDEX.len() { + let dim = DIMENSION_TYPES_INDEX.get(idx).unwrap(); + dimensions.push(Arc::new(Dimension::new( DIMENSION_TYPES.get(dim).unwrap().value().clone(), dim.to_string(), - )))); + idx as u32, + ))); + idx += 1; } World { default_dimension: dimensions .iter() - .position(|it| it.read().dimension_name == "minecraft:overworld") + .position(|it| it.dimension_name == "minecraft:overworld") .unwrap(), dimensions, entities: EntityManager::default(), @@ -104,11 +107,11 @@ impl World { } self.swap_queues(); } - pub fn find_dimension(&self, name: &str) -> Option>> { + pub fn find_dimension(&self, name: &str) -> Option> { Some( self.dimensions .iter() - .find(|&dim| dim.read().dimension_name == name)? + .find(|&dim| dim.dimension_name == name)? .clone(), ) } diff --git a/crates/kernel/src/world/chunk.rs b/crates/kernel/src/world/chunk.rs index 3d058e2..6fd15e8 100644 --- a/crates/kernel/src/world/chunk.rs +++ b/crates/kernel/src/world/chunk.rs @@ -1,15 +1,28 @@ -use crate::util::to_chunk_yzx; +use crate::world::dimension::Dimension; +use crate::WORLD; +use parking_lot::{Mutex, MutexGuard}; pub struct Chunk { - pub(crate) data: Vec, + pub(crate) data: Vec
, + pub(crate) pos: u64, pub(crate) height: i32, + pub(crate) idx: u32, } impl Chunk { - pub fn new(height: i32) -> Chunk { - let size = 16 * 16 * height as usize; - let data: Vec = vec![0; size]; - Chunk { data, height } + pub fn new(dimension: &Dimension, pos: u64) -> Chunk { + let height = dimension.dimension_type.height; + let size = (height / 16) as usize; + let mut data = Vec::with_capacity(size); + while data.len() < size { + data.push(Section::new()); + } + Chunk { + data, + height, + idx: dimension.dim_idx, + pos, + } } pub fn get_block(&self, x: i32, y: i32, z: i32) -> Option { if y < 0 || y >= self.height { @@ -18,21 +31,94 @@ impl Chunk { if !(0..16).contains(&x) || !(0..16).contains(&z) { return None; } - let yzx = to_chunk_yzx(x, y, z); - Some(*self.data.get(yzx)?) + let idx = y as usize / 16; + let section = self.data.get(idx); + let sy = ((y as usize) - (16 * idx)) as u32; + Some(section?.get_state(x as u32, sy, z as u32)) } - pub fn set_block(&mut self, x: i32, y: i32, z: i32, block: u32) { + pub fn set_block(&self, x: i32, y: i32, z: i32, block: u32) { if y < 0 || y >= self.height { return; } if !(0..16).contains(&x) || !(0..16).contains(&z) { return; } - let yzx = to_chunk_yzx(x, y, z); - if yzx >= self.data.capacity() { - return; + let idx = y as usize / 16; + let section = match self.data.get(idx) { + Some(s) => s, + None => return, + }; + let sy = ((y as usize) - (16 * idx)) as u32; + section.set_state(x as u32, sy, z as u32, block) + } + + pub fn get_section(&self, y: usize) -> Option { + Some(self.data.get(y / 16)?.get_data_guard()) + } +} + +impl Drop for Chunk { + fn drop(&mut self) { + unsafe { + WORLD + .read() + .dimensions + .get(self.idx as usize) + .unwrap() + .chunks + .remove(&self.pos); } - self.data.insert(yzx, block); + } +} + +pub struct Section { + pub data: Mutex<[u32; 4096]>, +} + +impl Section { + pub fn new() -> Section { + Section { + data: Mutex::new([0; 4096]), + } + } + #[inline] + pub fn get_state(&self, x: u32, y: u32, z: u32) -> u32 { + let index = (x + 16 * (y + 16 * z)) as usize; + self.data.lock()[index] + } + #[inline] + pub fn set_state(&self, x: u32, y: u32, z: u32, state: u32) { + let index = (x + 16 * (y + 16 * z)) as usize; + self.data.lock()[index] = state; + } + + #[inline] + pub fn get_data_guard(&self) -> SectionDataGuard { + SectionDataGuard { + data: self.data.lock(), + } + } +} + +impl Default for Section { + fn default() -> Self { + Self::new() + } +} + +pub struct SectionDataGuard<'a> { + pub data: MutexGuard<'a, [u32; 4096]>, +} + +impl SectionDataGuard<'_> { + pub fn get_state(&self, x: i32, y: i32, z: i32) -> u32 { + let index = (x + 16 * (y + 16 * z)) as usize; + self.data[index] + } + + pub fn set_state(&mut self, x: i32, y: i32, z: i32, state: u32) { + let index = (x + 16 * (y + 16 * z)) as usize; + self.data[index] = state; } } diff --git a/crates/kernel/src/world/dimension.rs b/crates/kernel/src/world/dimension.rs index 04b8cb0..8c08246 100644 --- a/crates/kernel/src/world/dimension.rs +++ b/crates/kernel/src/world/dimension.rs @@ -1,49 +1,57 @@ use crate::registry::dimension_type::DimensionType; use crate::util::to_dim_xz; use crate::world::chunk::Chunk; -use nohash_hasher::{BuildNoHashHasher, IntMap}; +use dashmap::DashMap; +use std::sync::{Arc, Weak}; pub struct Dimension { + pub(crate) dim_idx: u32, pub dimension_type: DimensionType, pub dimension_name: String, - pub chunks: IntMap, + pub chunks: DashMap>, } impl Dimension { - pub fn new(dimension_type: DimensionType, dimension_name: String) -> Dimension { + pub fn new(dimension_type: DimensionType, dimension_name: String, dim_idx: u32) -> Dimension { Dimension { dimension_type, dimension_name, - chunks: IntMap::with_capacity_and_hasher(256, BuildNoHashHasher::default()), + chunks: DashMap::with_capacity(512), + dim_idx, } } - pub fn get_chunk(&mut self, chunk_x: i32, chunk_z: i32) -> &mut Chunk { + pub fn get_chunk(&self, chunk_x: i32, chunk_z: i32) -> Arc { let key = to_dim_xz(chunk_x, chunk_z); - if self.chunks.contains_key(&to_dim_xz(chunk_x, chunk_z)) { - return self.chunks.get_mut(&key).unwrap(); + let tv = self.chunks.get_mut(&key); + if tv.is_some() { + let tvv = tv.unwrap().value_mut().upgrade(); + if let Some(ret) = tvv { + return ret; + } } - let chunk = self.create_new_chunk(chunk_x, chunk_z); - self.insert_new_chunk(chunk_x, chunk_z, chunk); - self.chunks.get_mut(&to_dim_xz(chunk_x, chunk_z)).unwrap() + let chunk = Arc::new(self.create_new_chunk(chunk_x, chunk_z)); + self.insert_new_chunk(chunk_x, chunk_z, &chunk); + chunk } - fn create_new_chunk(&self, _x: i32, _z: i32) -> Chunk { + fn create_new_chunk(&self, x: i32, z: i32) -> Chunk { // TODO - Chunk::new(self.dimension_type.height) + Chunk::new(self, to_dim_xz(x, z)) } - pub fn get_block(&mut self, x: i32, y: i32, z: i32) -> Option { + pub fn get_block(&self, x: i32, y: i32, z: i32) -> Option { let chunk_x = x >> 4; let chunk_z = z >> 4; let min_y = self.dimension_type.min_y; let chunk = self.get_chunk(chunk_x, chunk_z); chunk.get_block(x - chunk_x * 16, y - min_y, z - chunk_z * 16) } - pub fn set_block(&mut self, x: i32, y: i32, z: i32, block: u32) { + pub fn set_block(&self, x: i32, y: i32, z: i32, block: u32) -> Arc { let chunk_x = x >> 4; let chunk_z = z >> 4; let min_y = self.dimension_type.min_y; let chunk = self.get_chunk(chunk_x, chunk_z); chunk.set_block(x - chunk_x * 16, y - min_y, z - chunk_z * 16, block); + chunk } - fn insert_new_chunk(&mut self, x: i32, z: i32, chunk: Chunk) { - self.chunks.insert(to_dim_xz(x, z), chunk); + fn insert_new_chunk(&self, x: i32, z: i32, chunk: &Arc) { + self.chunks.insert(to_dim_xz(x, z), Arc::downgrade(chunk)); } }