diff options
Diffstat (limited to 'azalea-world/src')
| -rw-r--r-- | azalea-world/src/chunk.rs | 33 | ||||
| -rw-r--r-- | azalea-world/src/entity.rs | 47 | ||||
| -rw-r--r-- | azalea-world/src/lib.rs | 80 |
3 files changed, 121 insertions, 39 deletions
diff --git a/azalea-world/src/chunk.rs b/azalea-world/src/chunk.rs index 96bbd922..cbf77b20 100644 --- a/azalea-world/src/chunk.rs +++ b/azalea-world/src/chunk.rs @@ -1,4 +1,3 @@ -use crate::bit_storage::BitStorage; use crate::palette::PalettedContainer; use crate::palette::PalettedContainerType; use crate::World; @@ -13,10 +12,13 @@ use std::{ const SECTION_HEIGHT: u32 = 16; +#[derive(Debug)] pub struct ChunkStorage { pub view_center: ChunkPos, chunk_radius: u32, view_range: u32, + pub height: u32, + pub min_y: i32, // chunks is a list of size chunk_radius * chunk_radius chunks: Vec<Option<Arc<Mutex<Chunk>>>>, } @@ -32,12 +34,14 @@ fn floor_mod(x: i32, y: u32) -> u32 { } impl ChunkStorage { - pub fn new(chunk_radius: u32) -> Self { + pub fn new(chunk_radius: u32, height: u32, min_y: i32) -> Self { let view_range = chunk_radius * 2 + 1; ChunkStorage { view_center: ChunkPos::new(0, 0), chunk_radius, view_range, + height, + min_y, chunks: vec![None; (view_range * view_range) as usize], } } @@ -61,6 +65,29 @@ impl ChunkStorage { None => None, } } + + pub fn replace_with_packet_data( + &mut self, + pos: &ChunkPos, + data: &mut impl Read, + ) -> Result<(), String> { + if !self.in_range(pos) { + println!( + "Ignoring chunk since it's not in the view range: {}, {}", + pos.x, pos.z + ); + return Ok(()); + } + + let chunk = Arc::new(Mutex::new(Chunk::read_with_world_height( + data, + self.height, + )?)); + println!("Loaded chunk {:?}", pos); + self[pos] = Some(chunk); + + Ok(()) + } } impl Index<&ChunkPos> for ChunkStorage { @@ -84,7 +111,7 @@ pub struct Chunk { impl Chunk { pub fn read_with_world(buf: &mut impl Read, data: &World) -> Result<Self, String> { - Self::read_with_world_height(buf, data.height) + Self::read_with_world_height(buf, data.height()) } pub fn read_with_world_height(buf: &mut impl Read, world_height: u32) -> Result<Self, String> { diff --git a/azalea-world/src/entity.rs b/azalea-world/src/entity.rs index 49e1ae73..7077d0c4 100644 --- a/azalea-world/src/entity.rs +++ b/azalea-world/src/entity.rs @@ -1,14 +1,14 @@ -use std::collections::HashMap; - use azalea_core::ChunkPos; use azalea_entity::Entity; -use nohash_hasher::IntMap; +use log::warn; +use nohash_hasher::{IntMap, IntSet}; +use std::collections::HashMap; #[derive(Debug)] pub struct EntityStorage { by_id: IntMap<u32, Entity>, // TODO: this doesn't work yet (should be updated in the set_pos method in azalea-entity) - by_chunk: HashMap<ChunkPos, u32>, + by_chunk: HashMap<ChunkPos, IntSet<u32>>, } impl EntityStorage { @@ -22,13 +22,24 @@ impl EntityStorage { /// Add an entity to the storage. #[inline] pub fn insert(&mut self, entity: Entity) { + self.by_chunk + .entry(ChunkPos::from(entity.pos())) + .or_default() + .insert(entity.id); self.by_id.insert(entity.id, entity); } /// Remove an entity from the storage by its id. #[inline] pub fn remove_by_id(&mut self, id: u32) { - self.by_id.remove(&id); + if let Some(entity) = self.by_id.remove(&id) { + let entity_chunk = ChunkPos::from(entity.pos()); + if let None = self.by_chunk.remove(&entity_chunk) { + warn!("Tried to remove entity with id {id} from chunk {entity_chunk:?} but it was not found."); + } + } else { + warn!("Tried to remove entity with id {id} but it was not found.") + } } /// Get a reference to an entity by its id. @@ -42,4 +53,30 @@ impl EntityStorage { pub fn get_mut_by_id(&mut self, id: u32) -> Option<&mut Entity> { self.by_id.get_mut(&id) } + + /// Clear all entities in a chunk. + pub fn clear_chunk(&mut self, chunk: &ChunkPos) { + if let Some(entities) = self.by_chunk.remove(chunk) { + for entity_id in entities { + self.by_id.remove(&entity_id); + } + } + } + + /// Updates an entity from its old chunk. + #[inline] + pub fn update_entity_chunk( + &mut self, + entity_id: u32, + old_chunk: &ChunkPos, + new_chunk: &ChunkPos, + ) { + if let Some(entities) = self.by_chunk.get_mut(old_chunk) { + entities.remove(&entity_id); + } + self.by_chunk + .entry(*new_chunk) + .or_default() + .insert(entity_id); + } } diff --git a/azalea-world/src/lib.rs b/azalea-world/src/lib.rs index b47126d4..746143c7 100644 --- a/azalea-world/src/lib.rs +++ b/azalea-world/src/lib.rs @@ -6,7 +6,8 @@ mod entity; mod palette; use azalea_block::BlockState; -use azalea_core::{BlockPos, ChunkBlockPos, ChunkPos, ChunkSectionBlockPos}; +use azalea_core::{BlockPos, ChunkBlockPos, ChunkPos, ChunkSectionBlockPos, EntityPos}; +use azalea_entity::Entity; use azalea_protocol::mc_buf::{McBufReadable, McBufWritable}; pub use bit_storage::BitStorage; pub use chunk::{Chunk, ChunkStorage}; @@ -26,61 +27,78 @@ mod tests { } } +#[derive(Debug)] pub struct World { - pub storage: ChunkStorage, - pub entities: EntityStorage, - pub height: u32, - pub min_y: i32, + chunk_storage: ChunkStorage, + entity_storage: EntityStorage, } impl World { + pub fn new(chunk_radius: u32, height: u32, min_y: i32) -> Self { + World { + chunk_storage: ChunkStorage::new(chunk_radius, height, min_y), + entity_storage: EntityStorage::new(), + } + } + pub fn replace_with_packet_data( &mut self, pos: &ChunkPos, data: &mut impl Read, ) -> Result<(), String> { - if !self.storage.in_range(pos) { - println!( - "Ignoring chunk since it's not in the view range: {}, {}", - pos.x, pos.z - ); - return Ok(()); - } - // let existing_chunk = &self.storage[pos]; + self.chunk_storage.replace_with_packet_data(pos, data) + } - let chunk = Arc::new(Mutex::new(Chunk::read_with_world(data, self)?)); - println!("Loaded chunk {:?}", pos); - self.storage[pos] = Some(chunk); + pub fn update_view_center(&mut self, pos: &ChunkPos) { + self.chunk_storage.view_center = *pos; + } + + pub fn get_block_state(&self, pos: &BlockPos) -> Option<BlockState> { + self.chunk_storage.get_block_state(pos, self.min_y()) + } + pub fn move_entity(&mut self, entity_id: u32, new_pos: EntityPos) -> Result<(), String> { + let entity = self + .entity_storage + .get_mut_by_id(entity_id) + .ok_or("Moving entity that doesn't exist".to_string())?; + let old_chunk = ChunkPos::from(entity.pos()); + let new_chunk = ChunkPos::from(&new_pos); + // this is fine because we update the chunk below + entity.unsafe_move(new_pos); + if old_chunk != new_chunk { + self.entity_storage + .update_entity_chunk(entity_id, &old_chunk, &new_chunk); + } Ok(()) } - pub fn update_view_center(&mut self, pos: &ChunkPos) { - self.storage.view_center = *pos; + pub fn add_entity(&mut self, entity: Entity) { + self.entity_storage.insert(entity); } - pub fn get_block_state(&self, pos: &BlockPos) -> Option<BlockState> { - self.storage.get_block_state(pos, self.min_y) + pub fn height(&self) -> u32 { + self.chunk_storage.height + } + + pub fn min_y(&self) -> i32 { + self.chunk_storage.min_y + } + + pub fn entity_by_id(&self, id: u32) -> Option<&Entity> { + self.entity_storage.get_by_id(id) } } + impl Index<&ChunkPos> for World { type Output = Option<Arc<Mutex<Chunk>>>; fn index(&self, pos: &ChunkPos) -> &Self::Output { - &self.storage[pos] + &self.chunk_storage[pos] } } impl IndexMut<&ChunkPos> for World { fn index_mut<'a>(&'a mut self, pos: &ChunkPos) -> &'a mut Self::Output { - &mut self.storage[pos] + &mut self.chunk_storage[pos] } } -// impl Index<&BlockPos> for World { -// type Output = Option<Arc<Mutex<Chunk>>>; - -// fn index(&self, pos: &BlockPos) -> &Self::Output { -// let chunk = &self[ChunkPos::from(pos)]; -// // chunk. - -// } -// } |
