aboutsummaryrefslogtreecommitdiff
path: root/azalea-world/src
diff options
context:
space:
mode:
Diffstat (limited to 'azalea-world/src')
-rw-r--r--azalea-world/src/chunk.rs33
-rw-r--r--azalea-world/src/entity.rs47
-rw-r--r--azalea-world/src/lib.rs80
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.
-
-// }
-// }