aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock3
-rw-r--r--[-rwxr-xr-x]Cargo.toml0
-rwxr-xr-xazalea-chat/src/component.rs4
-rw-r--r--azalea-physics/Cargo.toml1
-rw-r--r--azalea-physics/src/collision/world_collisions.rs60
-rw-r--r--azalea-world/Cargo.toml2
-rw-r--r--azalea-world/src/container.rs3
-rw-r--r--azalea/Cargo.toml4
-rw-r--r--azalea/benches/physics.rs144
-rw-r--r--azalea/src/pathfinder/simulation.rs3
10 files changed, 201 insertions, 23 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 6f61f0c1..168ac60b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -457,6 +457,7 @@ dependencies = [
"bevy_app",
"bevy_ecs",
"bevy_time",
+ "nohash-hasher",
"once_cell",
"parking_lot",
"tracing",
@@ -543,10 +544,10 @@ dependencies = [
"bevy_ecs",
"criterion",
"derive_more",
- "enum-as-inner",
"nohash-hasher",
"once_cell",
"parking_lot",
+ "rustc-hash",
"serde",
"serde_json",
"simdnbt",
diff --git a/Cargo.toml b/Cargo.toml
index 9ce1fc84..9ce1fc84 100755..100644
--- a/Cargo.toml
+++ b/Cargo.toml
diff --git a/azalea-chat/src/component.rs b/azalea-chat/src/component.rs
index 3855a715..d301aba1 100755
--- a/azalea-chat/src/component.rs
+++ b/azalea-chat/src/component.rs
@@ -397,9 +397,7 @@ impl simdnbt::FromNbtTag for FormattedText {
trace!("keybind text components aren't yet supported");
return None;
} else {
- let Some(_nbt) = compound.get("nbt") else {
- return None;
- };
+ let _nbt = compound.get("nbt")?;
let _separator = FormattedText::parse_separator_nbt(compound)?;
let _interpret = match compound.get("interpret") {
diff --git a/azalea-physics/Cargo.toml b/azalea-physics/Cargo.toml
index 340dc096..043c0548 100644
--- a/azalea-physics/Cargo.toml
+++ b/azalea-physics/Cargo.toml
@@ -20,6 +20,7 @@ bevy_ecs = "0.13.0"
tracing = "0.1.40"
once_cell = "1.19.0"
parking_lot = "^0.12.1"
+nohash-hasher = "0.2.0"
[dev-dependencies]
bevy_time = "0.13.0"
diff --git a/azalea-physics/src/collision/world_collisions.rs b/azalea-physics/src/collision/world_collisions.rs
index c4c6a846..cb721f00 100644
--- a/azalea-physics/src/collision/world_collisions.rs
+++ b/azalea-physics/src/collision/world_collisions.rs
@@ -4,11 +4,11 @@ use azalea_block::BlockState;
use azalea_core::{
cursor3d::{Cursor3d, CursorIterationType},
math::EPSILON,
- position::{ChunkPos, ChunkSectionPos},
+ position::{BlockPos, ChunkPos, ChunkSectionBlockPos, ChunkSectionPos},
};
use azalea_world::{Chunk, Instance};
use parking_lot::RwLock;
-use std::{ops::Deref, sync::Arc};
+use std::sync::Arc;
pub fn get_block_collisions(world: &Instance, aabb: AABB) -> BlockCollisions<'_> {
BlockCollisions::new(world, aabb)
@@ -20,6 +20,8 @@ pub struct BlockCollisions<'a> {
pub entity_shape: VoxelShape,
pub cursor: Cursor3d,
pub only_suffocating_blocks: bool,
+
+ cached_sections: Vec<(ChunkSectionPos, azalea_world::Section)>,
}
impl<'a> BlockCollisions<'a> {
@@ -40,6 +42,8 @@ impl<'a> BlockCollisions<'a> {
entity_shape: VoxelShape::from(aabb),
cursor,
only_suffocating_blocks: false,
+
+ cached_sections: Vec::new(),
}
}
@@ -63,6 +67,36 @@ impl<'a> BlockCollisions<'a> {
self.world.chunks.get(&chunk_pos)
}
+
+ fn get_block_state(&mut self, block_pos: BlockPos) -> BlockState {
+ let section_pos = ChunkSectionPos::from(block_pos);
+ let section_block_pos = ChunkSectionBlockPos::from(block_pos);
+
+ for (cached_section_pos, cached_section) in &self.cached_sections {
+ if section_pos == *cached_section_pos {
+ return cached_section.get(section_block_pos);
+ }
+ }
+
+ let chunk = self.get_chunk(block_pos.x, block_pos.z);
+ let Some(chunk) = chunk else {
+ return BlockState::AIR;
+ };
+ let chunk = chunk.read();
+
+ let sections = &chunk.sections;
+ let section_index =
+ azalea_world::chunk_storage::section_index(block_pos.y, self.world.chunks.min_y)
+ as usize;
+
+ let Some(section) = sections.get(section_index) else {
+ return BlockState::AIR;
+ };
+
+ self.cached_sections.push((section_pos, section.clone()));
+
+ section.get(section_block_pos)
+ }
}
impl<'a> Iterator for BlockCollisions<'a> {
@@ -74,24 +108,18 @@ impl<'a> Iterator for BlockCollisions<'a> {
continue;
}
- let chunk = self.get_chunk(item.pos.x, item.pos.z);
- let Some(chunk) = chunk else {
- continue;
- };
+ let block_state = self.get_block_state(item.pos);
- let pos = item.pos;
- let block_state: BlockState = chunk
- .read()
- .get(&(&pos).into(), self.world.chunks.min_y)
- .unwrap_or(BlockState::AIR);
+ if block_state.is_air() {
+ // fast path since we can't collide with air
+ continue;
+ }
// TODO: continue if self.only_suffocating_blocks and the block is not
// suffocating
- let block_shape = block_state.shape();
-
// if it's a full block do a faster collision check
- if block_shape == BLOCK_SHAPE.deref() {
+ if block_state.is_shape_full() {
if !self.aabb.intersects_aabb(&AABB {
min_x: item.pos.x as f64,
min_y: item.pos.y as f64,
@@ -103,13 +131,15 @@ impl<'a> Iterator for BlockCollisions<'a> {
continue;
}
- return Some(block_shape.move_relative(
+ return Some(BLOCK_SHAPE.move_relative(
item.pos.x as f64,
item.pos.y as f64,
item.pos.z as f64,
));
}
+ let block_shape = block_state.shape();
+
let block_shape =
block_shape.move_relative(item.pos.x as f64, item.pos.y as f64, item.pos.z as f64);
// if the entity shape and block shape don't collide, continue
diff --git a/azalea-world/Cargo.toml b/azalea-world/Cargo.toml
index b2d9b9df..20e3a261 100644
--- a/azalea-world/Cargo.toml
+++ b/azalea-world/Cargo.toml
@@ -19,7 +19,6 @@ azalea-inventory = { version = "0.9.0", path = "../azalea-inventory" }
azalea-registry = { path = "../azalea-registry", version = "0.9.0" }
bevy_ecs = "0.13.0"
derive_more = { version = "0.99.17", features = ["deref", "deref_mut"] }
-enum-as-inner = "0.6.0"
tracing = "0.1.40"
nohash-hasher = "0.2.0"
once_cell = "1.19.0"
@@ -28,6 +27,7 @@ thiserror = "1.0.57"
uuid = "1.7.0"
serde_json = "1.0.113"
serde = "1.0.196"
+rustc-hash = "1.1.0"
[dev-dependencies]
azalea-client = { path = "../azalea-client" }
diff --git a/azalea-world/src/container.rs b/azalea-world/src/container.rs
index 0b68ead6..69b8f908 100644
--- a/azalea-world/src/container.rs
+++ b/azalea-world/src/container.rs
@@ -3,6 +3,7 @@ use bevy_ecs::{component::Component, system::Resource};
use derive_more::{Deref, DerefMut};
use nohash_hasher::IntMap;
use parking_lot::RwLock;
+use rustc_hash::FxHashMap;
use std::{
collections::HashMap,
sync::{Arc, Weak},
@@ -27,7 +28,7 @@ pub struct InstanceContainer {
// telling them apart. We hope most servers are nice and don't do that though. It's only an
// issue when there's multiple clients with the same WorldContainer in different worlds
// anyways.
- pub instances: HashMap<ResourceLocation, Weak<RwLock<Instance>>>,
+ pub instances: FxHashMap<ResourceLocation, Weak<RwLock<Instance>>>,
}
impl InstanceContainer {
diff --git a/azalea/Cargo.toml b/azalea/Cargo.toml
index 8a11fc46..d1d9a4f8 100644
--- a/azalea/Cargo.toml
+++ b/azalea/Cargo.toml
@@ -57,3 +57,7 @@ log = ["azalea-client/log"]
[[bench]]
name = "pathfinder"
harness = false
+
+[[bench]]
+name = "physics"
+harness = false
diff --git a/azalea/benches/physics.rs b/azalea/benches/physics.rs
new file mode 100644
index 00000000..6f8dc7bc
--- /dev/null
+++ b/azalea/benches/physics.rs
@@ -0,0 +1,144 @@
+use std::{hint::black_box, sync::Arc, time::Duration};
+
+use azalea::{
+ pathfinder::{
+ astar::{self, a_star},
+ goals::{BlockPosGoal, Goal},
+ mining::MiningCache,
+ simulation::{SimulatedPlayerBundle, Simulation, SimulationSet},
+ world::CachedWorld,
+ },
+ BlockPos, Vec3,
+};
+use azalea_core::position::{ChunkBlockPos, ChunkPos};
+use azalea_inventory::Menu;
+use azalea_world::{Chunk, ChunkStorage, PartialChunkStorage};
+use criterion::{criterion_group, criterion_main, Bencher, Criterion};
+use parking_lot::RwLock;
+
+#[allow(dead_code)]
+fn generate_world(partial_chunks: &mut PartialChunkStorage, size: u32) -> ChunkStorage {
+ let size = size as i32;
+
+ let mut chunks = ChunkStorage::default();
+ for chunk_x in -size..size {
+ for chunk_z in -size..size {
+ let chunk_pos = ChunkPos::new(chunk_x, chunk_z);
+ partial_chunks.set(&chunk_pos, Some(Chunk::default()), &mut chunks);
+ }
+ }
+
+ // for chunk_x in -size..size {
+ // for chunk_z in -size..size {
+ // let chunk_pos = ChunkPos::new(chunk_x, chunk_z);
+ // let chunk = chunks.get(&chunk_pos).unwrap();
+ // let mut chunk = chunk.write();
+ // for x in 0..16_u8 {
+ // for z in 0..16_u8 {
+ // chunk.set(
+ // &ChunkBlockPos::new(x, 1, z),
+ // azalea_registry::Block::Bedrock.into(),
+ // chunks.min_y,
+ // );
+ // if rng.gen_bool(0.5) {
+ // chunk.set(
+ // &ChunkBlockPos::new(x, 2, z),
+ // azalea_registry::Block::Bedrock.into(),
+ // chunks.min_y,
+ // );
+ // }
+ // }
+ // }
+ // }
+ // }
+
+ // let mut start = BlockPos::new(-64, 4, -64);
+ // // move start down until it's on a solid block
+ // while chunks.get_block_state(&start).unwrap().is_air() {
+ // start = start.down(1);
+ // }
+ // start = start.up(1);
+
+ // let mut end = BlockPos::new(63, 4, 63);
+ // // move end down until it's on a solid block
+ // while chunks.get_block_state(&end).unwrap().is_air() {
+ // end = end.down(1);
+ // }
+ // end = end.up(1);
+
+ chunks
+}
+
+fn generate_mining_world(
+ partial_chunks: &mut PartialChunkStorage,
+ size: u32,
+) -> (ChunkStorage, BlockPos, BlockPos) {
+ let size = size as i32;
+
+ let mut chunks = ChunkStorage::default();
+ for chunk_x in -size..size {
+ for chunk_z in -size..size {
+ let chunk_pos = ChunkPos::new(chunk_x, chunk_z);
+ partial_chunks.set(&chunk_pos, Some(Chunk::default()), &mut chunks);
+ }
+ }
+
+ // let mut rng = StdRng::seed_from_u64(0);
+
+ for chunk_x in -size..size {
+ for chunk_z in -size..size {
+ let chunk_pos = ChunkPos::new(chunk_x, chunk_z);
+ let chunk = chunks.get(&chunk_pos).unwrap();
+ let mut chunk = chunk.write();
+ for y in chunks.min_y..(chunks.min_y + chunks.height as i32) {
+ for x in 0..16_u8 {
+ for z in 0..16_u8 {
+ chunk.set(
+ &ChunkBlockPos::new(x, y, z),
+ azalea_registry::Block::Stone.into(),
+ chunks.min_y,
+ );
+ }
+ }
+ }
+ }
+ }
+
+ let start = BlockPos::new(-64, 4, -64);
+ let end = BlockPos::new(0, 4, 0);
+
+ (chunks, start, end)
+}
+
+fn run_physics_benchmark(b: &mut Bencher<'_>) {
+ let mut partial_chunks = PartialChunkStorage::new(32);
+
+ let world = generate_world(&mut partial_chunks, 4);
+
+ let mut simulation_set = SimulationSet::new(world);
+
+ // let entity = simulation_set.spawn(SimulatedPlayerBundle::new(Vec3::new(0.0,
+ // 4.0, 0.0))); for _ in 0..20 {
+ // simulation_set.tick();
+ // println!("tick over");
+ // }
+ // simulation_set.despawn(entity);
+ // std::process::exit(0);
+
+ b.iter(|| {
+ let entity = simulation_set.spawn(SimulatedPlayerBundle::new(Vec3::new(0.0, 4.0, 0.0)));
+ for _ in 0..20 {
+ simulation_set.tick();
+ }
+ simulation_set.despawn(entity);
+ })
+}
+
+fn bench_pathfinder(c: &mut Criterion) {
+ c.bench_function("physics", |b| {
+ run_physics_benchmark(b);
+ });
+}
+
+criterion_group!(benches, bench_pathfinder);
+criterion_main!(benches);
diff --git a/azalea/src/pathfinder/simulation.rs b/azalea/src/pathfinder/simulation.rs
index bea99e93..a5e8113f 100644
--- a/azalea/src/pathfinder/simulation.rs
+++ b/azalea/src/pathfinder/simulation.rs
@@ -113,8 +113,7 @@ fn create_simulation_player(
));
entity.insert(player);
- let entity_id = entity.id();
- entity_id
+ entity.id()
}
/// Simulate the Minecraft world to see if certain movements would be possible.