aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormat <github@matdoes.dev>2022-06-17 23:55:11 -0500
committermat <github@matdoes.dev>2022-06-17 23:55:11 -0500
commit614b21129804930159f041ce3f51202bc3e1c0b6 (patch)
tree9c78d1f1bb13de5ae91d2ca2092da28c1b8ae6ef
parentf993e79a7e6acc7aadd1e6cf9462d7a3e2c3ac3e (diff)
downloadazalea-drasl-614b21129804930159f041ce3f51202bc3e1c0b6.tar.xz
EntityStorage
-rwxr-xr-xCargo.lock8
-rwxr-xr-xazalea-client/src/connect.rs4
-rw-r--r--azalea-entity/src/lib.rs13
-rw-r--r--azalea-world/Cargo.toml7
-rw-r--r--azalea-world/src/chunk.rs182
-rw-r--r--azalea-world/src/entity.rs44
-rw-r--r--azalea-world/src/lib.rs177
7 files changed, 260 insertions, 175 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 85dc7a98..c63ef7a8 100755
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -193,8 +193,10 @@ version = "0.1.0"
dependencies = [
"azalea-block",
"azalea-core",
+ "azalea-entity",
"azalea-nbt",
"azalea-protocol",
+ "nohash-hasher",
]
[[package]]
@@ -772,6 +774,12 @@ dependencies = [
]
[[package]]
+name = "nohash-hasher"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
+
+[[package]]
name = "num"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/azalea-client/src/connect.rs b/azalea-client/src/connect.rs
index 1551ca69..2deaab68 100755
--- a/azalea-client/src/connect.rs
+++ b/azalea-client/src/connect.rs
@@ -19,7 +19,7 @@ use azalea_protocol::{
},
resolver, ServerAddress,
};
-use azalea_world::{ChunkStorage, World};
+use azalea_world::{ChunkStorage, EntityStorage, World};
use std::{fmt::Debug, sync::Arc};
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
use tokio::sync::Mutex;
@@ -266,6 +266,7 @@ impl Client {
height,
min_y,
storage: ChunkStorage::new(16),
+ entities: EntityStorage::new(),
});
conn.lock()
@@ -356,6 +357,7 @@ impl Client {
y: p.y,
z: p.z,
};
+ p.id;
}
GamePacket::ClientboundSetEntityDataPacket(p) => {
// println!("Got set entity data packet {:?}", p);
diff --git a/azalea-entity/src/lib.rs b/azalea-entity/src/lib.rs
index 506f5780..0e0178b5 100644
--- a/azalea-entity/src/lib.rs
+++ b/azalea-entity/src/lib.rs
@@ -4,7 +4,18 @@ use azalea_core::EntityPos;
pub struct Entity {
/// The incrementing numerical id of the entity.
pub id: u32,
- pub pos: EntityPos,
+ pos: EntityPos,
+}
+
+impl Entity {
+ pub fn pos(&self) -> &EntityPos {
+ &self.pos
+ }
+
+ pub fn set_pos(&mut self, pos: EntityPos) {
+ // TODO: check if it moved to another chunk
+ self.pos = pos;
+ }
}
// #[cfg(test)]
diff --git a/azalea-world/Cargo.toml b/azalea-world/Cargo.toml
index 79e6155d..e5e9da1d 100644
--- a/azalea-world/Cargo.toml
+++ b/azalea-world/Cargo.toml
@@ -6,7 +6,12 @@ version = "0.1.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
+azalea-block = {path = "../azalea-block"}
azalea-core = {path = "../azalea-core"}
+azalea-entity = {path = "../azalea-entity"}
azalea-nbt = {path = "../azalea-nbt"}
azalea-protocol = {path = "../azalea-protocol"}
-azalea-block = {path = "../azalea-block"}
+nohash-hasher = "0.2.0"
+
+[profile.release]
+lto = true
diff --git a/azalea-world/src/chunk.rs b/azalea-world/src/chunk.rs
new file mode 100644
index 00000000..96bbd922
--- /dev/null
+++ b/azalea-world/src/chunk.rs
@@ -0,0 +1,182 @@
+use crate::bit_storage::BitStorage;
+use crate::palette::PalettedContainer;
+use crate::palette::PalettedContainerType;
+use crate::World;
+use azalea_block::BlockState;
+use azalea_core::{BlockPos, ChunkBlockPos, ChunkPos, ChunkSectionBlockPos};
+use azalea_protocol::mc_buf::{McBufReadable, McBufWritable};
+use std::{
+ io::{Read, Write},
+ ops::{Index, IndexMut},
+ sync::{Arc, Mutex},
+};
+
+const SECTION_HEIGHT: u32 = 16;
+
+pub struct ChunkStorage {
+ pub view_center: ChunkPos,
+ chunk_radius: u32,
+ view_range: u32,
+ // chunks is a list of size chunk_radius * chunk_radius
+ chunks: Vec<Option<Arc<Mutex<Chunk>>>>,
+}
+
+// java moment
+// it might be possible to replace this with just a modulo, but i copied java's floorMod just in case
+fn floor_mod(x: i32, y: u32) -> u32 {
+ if x < 0 {
+ y - ((-x) as u32 % y)
+ } else {
+ x as u32 % y
+ }
+}
+
+impl ChunkStorage {
+ pub fn new(chunk_radius: u32) -> Self {
+ let view_range = chunk_radius * 2 + 1;
+ ChunkStorage {
+ view_center: ChunkPos::new(0, 0),
+ chunk_radius,
+ view_range,
+ chunks: vec![None; (view_range * view_range) as usize],
+ }
+ }
+
+ fn get_index(&self, chunk_pos: &ChunkPos) -> usize {
+ (floor_mod(chunk_pos.x, self.view_range) * self.view_range
+ + floor_mod(chunk_pos.z, self.view_range)) as usize
+ }
+
+ pub fn in_range(&self, chunk_pos: &ChunkPos) -> bool {
+ (chunk_pos.x - self.view_center.x).unsigned_abs() <= self.chunk_radius
+ && (chunk_pos.z - self.view_center.z).unsigned_abs() <= self.chunk_radius
+ }
+
+ pub fn get_block_state(&self, pos: &BlockPos, min_y: i32) -> Option<BlockState> {
+ let chunk_pos = ChunkPos::from(pos);
+ println!("chunk_pos {:?} block_pos {:?}", chunk_pos, pos);
+ let chunk = &self[&chunk_pos];
+ match chunk {
+ Some(chunk) => Some(chunk.lock().unwrap().get(&ChunkBlockPos::from(pos), min_y)),
+ None => None,
+ }
+ }
+}
+
+impl Index<&ChunkPos> for ChunkStorage {
+ type Output = Option<Arc<Mutex<Chunk>>>;
+
+ fn index(&self, pos: &ChunkPos) -> &Self::Output {
+ &self.chunks[self.get_index(pos)]
+ }
+}
+impl IndexMut<&ChunkPos> for ChunkStorage {
+ fn index_mut<'a>(&'a mut self, pos: &ChunkPos) -> &'a mut Self::Output {
+ let index = self.get_index(pos);
+ &mut self.chunks[index]
+ }
+}
+
+#[derive(Debug)]
+pub struct Chunk {
+ pub sections: Vec<Section>,
+}
+
+impl Chunk {
+ pub fn read_with_world(buf: &mut impl Read, data: &World) -> Result<Self, String> {
+ Self::read_with_world_height(buf, data.height)
+ }
+
+ pub fn read_with_world_height(buf: &mut impl Read, world_height: u32) -> Result<Self, String> {
+ let section_count = world_height / SECTION_HEIGHT;
+ let mut sections = Vec::with_capacity(section_count as usize);
+ for _ in 0..section_count {
+ let section = Section::read_into(buf)?;
+ sections.push(section);
+ }
+ Ok(Chunk { sections })
+ }
+
+ pub fn section_index(&self, y: i32, min_y: i32) -> u32 {
+ // TODO: check the build height and stuff, this code will be broken if the min build height is 0
+ // (LevelHeightAccessor.getMinSection in vanilla code)
+ assert!(y >= 0);
+ let min_section_index = min_y.div_floor(16);
+ (y.div_floor(16) - min_section_index) as u32
+ }
+
+ pub fn get(&self, pos: &ChunkBlockPos, min_y: i32) -> BlockState {
+ let section_index = self.section_index(pos.y, min_y);
+ // TODO: make sure the section exists
+ let section = &self.sections[section_index as usize];
+ let chunk_section_pos = ChunkSectionBlockPos::from(pos);
+ let block_state = section.get(chunk_section_pos);
+ block_state
+ }
+}
+
+impl McBufWritable for Chunk {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ for section in &self.sections {
+ section.write_into(buf)?;
+ }
+ Ok(())
+ }
+}
+
+#[derive(Clone, Debug)]
+pub struct Section {
+ pub block_count: u16,
+ pub states: PalettedContainer,
+ pub biomes: PalettedContainer,
+}
+
+impl McBufReadable for Section {
+ fn read_into(buf: &mut impl Read) -> Result<Self, String> {
+ let block_count = u16::read_into(buf)?;
+
+ // this is commented out because the vanilla server is wrong
+ // assert!(
+ // block_count <= 16 * 16 * 16,
+ // "A section has more blocks than what should be possible. This is a bug!"
+ // );
+
+ let states = PalettedContainer::read_with_type(buf, &PalettedContainerType::BlockStates)?;
+
+ for i in 0..states.storage.size() {
+ if !BlockState::is_valid_state(states.storage.get(i) as u32) {
+ return Err(format!(
+ "Invalid block state {} (index {}) found in section.",
+ states.storage.get(i),
+ i
+ ));
+ }
+ }
+
+ let biomes = PalettedContainer::read_with_type(buf, &PalettedContainerType::Biomes)?;
+ Ok(Section {
+ block_count,
+ states,
+ biomes,
+ })
+ }
+}
+
+impl McBufWritable for Section {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ self.block_count.write_into(buf)?;
+ self.states.write_into(buf)?;
+ self.biomes.write_into(buf)?;
+ Ok(())
+ }
+}
+
+impl Section {
+ fn get(&self, pos: ChunkSectionBlockPos) -> BlockState {
+ // TODO: use the unsafe method and do the check earlier
+ self.states
+ .get(pos.x as usize, pos.y as usize, pos.z as usize)
+ .try_into()
+ .expect("Invalid block state.")
+ }
+}
diff --git a/azalea-world/src/entity.rs b/azalea-world/src/entity.rs
new file mode 100644
index 00000000..a25b1f40
--- /dev/null
+++ b/azalea-world/src/entity.rs
@@ -0,0 +1,44 @@
+use std::collections::HashMap;
+
+use azalea_core::ChunkPos;
+use azalea_entity::Entity;
+use nohash_hasher::IntMap;
+
+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>,
+}
+
+impl EntityStorage {
+ pub fn new() -> Self {
+ Self {
+ by_id: IntMap::default(),
+ by_chunk: HashMap::default(),
+ }
+ }
+
+ /// Add an entity to the storage.
+ #[inline]
+ pub fn insert(&mut self, entity: Entity) {
+ 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);
+ }
+
+ /// Get a reference to an entity by its id.
+ #[inline]
+ pub fn get_by_id(&self, id: u32) -> Option<&Entity> {
+ self.by_id.get(&id)
+ }
+
+ /// Get a mutable reference to an entity by its id.
+ #[inline]
+ pub fn get_mut_by_id(&mut self, id: u32) -> Option<&mut Entity> {
+ self.by_id.get_mut(&id)
+ }
+}
diff --git a/azalea-world/src/lib.rs b/azalea-world/src/lib.rs
index e651e455..b47126d4 100644
--- a/azalea-world/src/lib.rs
+++ b/azalea-world/src/lib.rs
@@ -1,14 +1,16 @@
#![feature(int_roundings)]
mod bit_storage;
+mod chunk;
+mod entity;
mod palette;
-use crate::palette::PalettedContainerType;
use azalea_block::BlockState;
use azalea_core::{BlockPos, ChunkBlockPos, ChunkPos, ChunkSectionBlockPos};
use azalea_protocol::mc_buf::{McBufReadable, McBufWritable};
pub use bit_storage::BitStorage;
-use palette::PalettedContainer;
+pub use chunk::{Chunk, ChunkStorage};
+pub use entity::EntityStorage;
use std::{
io::{Read, Write},
ops::{Index, IndexMut},
@@ -24,10 +26,9 @@ mod tests {
}
}
-const SECTION_HEIGHT: u32 = 16;
-
pub struct World {
pub storage: ChunkStorage,
+ pub entities: EntityStorage,
pub height: u32,
pub min_y: i32,
}
@@ -83,171 +84,3 @@ impl IndexMut<&ChunkPos> for World {
// }
// }
-
-pub struct ChunkStorage {
- view_center: ChunkPos,
- chunk_radius: u32,
- view_range: u32,
- // chunks is a list of size chunk_radius * chunk_radius
- chunks: Vec<Option<Arc<Mutex<Chunk>>>>,
-}
-
-// java moment
-// it might be possible to replace this with just a modulo, but i copied java's floorMod just in case
-fn floor_mod(x: i32, y: u32) -> u32 {
- if x < 0 {
- y - ((-x) as u32 % y)
- } else {
- x as u32 % y
- }
-}
-
-impl ChunkStorage {
- pub fn new(chunk_radius: u32) -> Self {
- let view_range = chunk_radius * 2 + 1;
- ChunkStorage {
- view_center: ChunkPos::new(0, 0),
- chunk_radius,
- view_range,
- chunks: vec![None; (view_range * view_range) as usize],
- }
- }
-
- fn get_index(&self, chunk_pos: &ChunkPos) -> usize {
- (floor_mod(chunk_pos.x, self.view_range) * self.view_range
- + floor_mod(chunk_pos.z, self.view_range)) as usize
- }
-
- pub fn in_range(&self, chunk_pos: &ChunkPos) -> bool {
- (chunk_pos.x - self.view_center.x).unsigned_abs() <= self.chunk_radius
- && (chunk_pos.z - self.view_center.z).unsigned_abs() <= self.chunk_radius
- }
-
- pub fn get_block_state(&self, pos: &BlockPos, min_y: i32) -> Option<BlockState> {
- let chunk_pos = ChunkPos::from(pos);
- println!("chunk_pos {:?} block_pos {:?}", chunk_pos, pos);
- let chunk = &self[&chunk_pos];
- match chunk {
- Some(chunk) => Some(chunk.lock().unwrap().get(&ChunkBlockPos::from(pos), min_y)),
- None => None,
- }
- }
-}
-
-impl Index<&ChunkPos> for ChunkStorage {
- type Output = Option<Arc<Mutex<Chunk>>>;
-
- fn index(&self, pos: &ChunkPos) -> &Self::Output {
- &self.chunks[self.get_index(pos)]
- }
-}
-impl IndexMut<&ChunkPos> for ChunkStorage {
- fn index_mut<'a>(&'a mut self, pos: &ChunkPos) -> &'a mut Self::Output {
- let index = self.get_index(pos);
- &mut self.chunks[index]
- }
-}
-
-#[derive(Debug)]
-pub struct Chunk {
- pub sections: Vec<Section>,
-}
-
-impl Chunk {
- pub fn read_with_world(buf: &mut impl Read, data: &World) -> Result<Self, String> {
- Self::read_with_world_height(buf, data.height)
- }
-
- pub fn read_with_world_height(buf: &mut impl Read, world_height: u32) -> Result<Self, String> {
- let section_count = world_height / SECTION_HEIGHT;
- let mut sections = Vec::with_capacity(section_count as usize);
- for _ in 0..section_count {
- let section = Section::read_into(buf)?;
- sections.push(section);
- }
- Ok(Chunk { sections })
- }
-
- pub fn section_index(&self, y: i32, min_y: i32) -> u32 {
- // TODO: check the build height and stuff, this code will be broken if the min build height is 0
- // (LevelHeightAccessor.getMinSection in vanilla code)
- assert!(y >= 0);
- let min_section_index = min_y.div_floor(16);
- (y.div_floor(16) - min_section_index) as u32
- }
-
- pub fn get(&self, pos: &ChunkBlockPos, min_y: i32) -> BlockState {
- let section_index = self.section_index(pos.y, min_y);
- // TODO: make sure the section exists
- let section = &self.sections[section_index as usize];
- let chunk_section_pos = ChunkSectionBlockPos::from(pos);
- let block_state = section.get(chunk_section_pos);
- block_state
- }
-}
-
-impl McBufWritable for Chunk {
- fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
- for section in &self.sections {
- section.write_into(buf)?;
- }
- Ok(())
- }
-}
-
-#[derive(Clone, Debug)]
-pub struct Section {
- pub block_count: u16,
- pub states: PalettedContainer,
- pub biomes: PalettedContainer,
-}
-
-impl McBufReadable for Section {
- fn read_into(buf: &mut impl Read) -> Result<Self, String> {
- let block_count = u16::read_into(buf)?;
-
- // this is commented out because the vanilla server is wrong
- // assert!(
- // block_count <= 16 * 16 * 16,
- // "A section has more blocks than what should be possible. This is a bug!"
- // );
-
- let states = PalettedContainer::read_with_type(buf, &PalettedContainerType::BlockStates)?;
-
- for i in 0..states.storage.size() {
- if !BlockState::is_valid_state(states.storage.get(i) as u32) {
- return Err(format!(
- "Invalid block state {} (index {}) found in section.",
- states.storage.get(i),
- i
- ));
- }
- }
-
- let biomes = PalettedContainer::read_with_type(buf, &PalettedContainerType::Biomes)?;
- Ok(Section {
- block_count,
- states,
- biomes,
- })
- }
-}
-
-impl McBufWritable for Section {
- fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
- self.block_count.write_into(buf)?;
- self.states.write_into(buf)?;
- self.biomes.write_into(buf)?;
- Ok(())
- }
-}
-
-impl Section {
- fn get(&self, pos: ChunkSectionBlockPos) -> BlockState {
- // TODO: use the unsafe method and do the check earlier
- self.states
- .get(pos.x as usize, pos.y as usize, pos.z as usize)
- .try_into()
- .expect("Invalid block state.")
- }
-}