aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--azalea-client/src/local_player.rs2
-rw-r--r--azalea-client/src/packet_handling/game.rs4
-rw-r--r--azalea-core/Cargo.toml1
-rwxr-xr-xazalea-core/src/position.rs18
-rwxr-xr-xazalea-world/src/chunk_storage.rs5
-rw-r--r--azalea-world/src/lib.rs6
-rw-r--r--azalea/benches/pathfinder.rs6
-rw-r--r--azalea/src/pathfinder/astar.rs4
-rw-r--r--azalea/src/pathfinder/mod.rs31
-rw-r--r--azalea/src/pathfinder/moves/basic.rs52
-rw-r--r--azalea/src/pathfinder/moves/mod.rs175
-rw-r--r--azalea/src/pathfinder/moves/parkour.rs54
13 files changed, 209 insertions, 150 deletions
diff --git a/Cargo.lock b/Cargo.lock
index c4c36605..8db4e6eb 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -327,6 +327,7 @@ dependencies = [
"azalea-nbt",
"azalea-registry",
"bevy_ecs",
+ "nohash-hasher",
"num-traits",
"serde",
"uuid",
diff --git a/azalea-client/src/local_player.rs b/azalea-client/src/local_player.rs
index 22ea2232..c2b16434 100644
--- a/azalea-client/src/local_player.rs
+++ b/azalea-client/src/local_player.rs
@@ -127,7 +127,7 @@ impl InstanceHolder {
InstanceHolder {
instance: world,
partial_instance: Arc::new(RwLock::new(PartialInstance::new(
- azalea_world::calculate_chunk_storage_range(
+ azalea_world::chunk_storage::calculate_chunk_storage_range(
client_information.view_distance.into(),
),
Some(entity),
diff --git a/azalea-client/src/packet_handling/game.rs b/azalea-client/src/packet_handling/game.rs
index 049eab03..46eb21fe 100644
--- a/azalea-client/src/packet_handling/game.rs
+++ b/azalea-client/src/packet_handling/game.rs
@@ -267,7 +267,7 @@ pub fn process_packet_events(ecs: &mut World) {
// instance_container)
*instance_holder.partial_instance.write() = PartialInstance::new(
- azalea_world::calculate_chunk_storage_range(
+ azalea_world::chunk_storage::calculate_chunk_storage_range(
client_information.view_distance.into(),
),
// this argument makes it so other clients don't update this player entity
@@ -1287,7 +1287,7 @@ pub fn process_packet_events(ecs: &mut World) {
// (when we add chunks or entities those will be in the
// instance_container)
*instance_holder.partial_instance.write() = PartialInstance::new(
- azalea_world::calculate_chunk_storage_range(
+ azalea_world::chunk_storage::calculate_chunk_storage_range(
client_information.view_distance.into(),
),
Some(player_entity),
diff --git a/azalea-core/Cargo.toml b/azalea-core/Cargo.toml
index a660c1e2..1859c6a8 100644
--- a/azalea-core/Cargo.toml
+++ b/azalea-core/Cargo.toml
@@ -14,6 +14,7 @@ azalea-inventory = { version = "0.8.0", path = "../azalea-inventory" }
azalea-nbt = { path = "../azalea-nbt", version = "0.8.0" }
azalea-registry = { path = "../azalea-registry", version = "0.8.0" }
bevy_ecs = { version = "0.11.2", default-features = false, optional = true }
+nohash-hasher = "0.2.0"
num-traits = "0.2.16"
serde = { version = "^1.0", optional = true }
uuid = "^1.4.1"
diff --git a/azalea-core/src/position.rs b/azalea-core/src/position.rs
index 09b10563..09ed44fd 100755
--- a/azalea-core/src/position.rs
+++ b/azalea-core/src/position.rs
@@ -5,6 +5,7 @@
use azalea_buf::{BufReadError, McBuf, McBufReadable, McBufWritable};
use std::{
+ hash::Hash,
io::{Cursor, Write},
ops::{Add, AddAssign, Mul, Rem, Sub},
};
@@ -193,7 +194,7 @@ impl BlockPos {
/// Chunk coordinates are used to represent where a chunk is in the world. You
/// can convert the x and z to block coordinates by multiplying them by 16.
-#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, McBuf)]
+#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, McBuf)]
pub struct ChunkPos {
pub x: i32,
pub z: i32,
@@ -213,6 +214,16 @@ impl Add<ChunkPos> for ChunkPos {
}
}
}
+impl Hash for ChunkPos {
+ fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+ // optimized hash that only calls hash once
+ let combined = (self.x as u64) << 32 | (self.z as u64);
+ combined.hash(state);
+ }
+}
+/// nohash_hasher lets us have IntMap<ChunkPos, _> which is significantly faster
+/// than a normal HashMap
+impl nohash_hasher::IsEnabled for ChunkPos {}
/// The coordinates of a chunk section in the world.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
@@ -273,6 +284,7 @@ pub struct GlobalPos {
}
impl From<&BlockPos> for ChunkPos {
+ #[inline]
fn from(pos: &BlockPos) -> Self {
ChunkPos {
x: pos.x.div_floor(16),
@@ -298,6 +310,7 @@ impl From<ChunkSectionPos> for ChunkPos {
}
impl From<&BlockPos> for ChunkBlockPos {
+ #[inline]
fn from(pos: &BlockPos) -> Self {
ChunkBlockPos {
x: pos.x.rem_euclid(16) as u8,
@@ -318,6 +331,7 @@ impl From<&BlockPos> for ChunkSectionBlockPos {
}
impl From<&ChunkBlockPos> for ChunkSectionBlockPos {
+ #[inline]
fn from(pos: &ChunkBlockPos) -> Self {
ChunkSectionBlockPos {
x: pos.x,
@@ -327,6 +341,7 @@ impl From<&ChunkBlockPos> for ChunkSectionBlockPos {
}
}
impl From<&Vec3> for BlockPos {
+ #[inline]
fn from(pos: &Vec3) -> Self {
BlockPos {
x: pos.x.floor() as i32,
@@ -336,6 +351,7 @@ impl From<&Vec3> for BlockPos {
}
}
impl From<Vec3> for BlockPos {
+ #[inline]
fn from(pos: Vec3) -> Self {
BlockPos::from(&pos)
}
diff --git a/azalea-world/src/chunk_storage.rs b/azalea-world/src/chunk_storage.rs
index 2e93f2fb..ce611a61 100755
--- a/azalea-world/src/chunk_storage.rs
+++ b/azalea-world/src/chunk_storage.rs
@@ -7,6 +7,7 @@ use azalea_buf::{BufReadError, McBufReadable, McBufWritable};
use azalea_core::position::{BlockPos, ChunkBlockPos, ChunkPos, ChunkSectionBlockPos};
use azalea_nbt::NbtCompound;
use log::{debug, trace, warn};
+use nohash_hasher::IntMap;
use parking_lot::RwLock;
use std::str::FromStr;
use std::{
@@ -37,7 +38,7 @@ pub struct PartialChunkStorage {
pub struct ChunkStorage {
pub height: u32,
pub min_y: i32,
- pub map: HashMap<ChunkPos, Weak<RwLock<Chunk>>>,
+ pub map: IntMap<ChunkPos, Weak<RwLock<Chunk>>>,
}
/// A single chunk in a world (16*?*16 blocks). This only contains the blocks
@@ -214,7 +215,7 @@ impl ChunkStorage {
ChunkStorage {
height,
min_y,
- map: HashMap::new(),
+ map: IntMap::default(),
}
}
diff --git a/azalea-world/src/lib.rs b/azalea-world/src/lib.rs
index 0d80f75d..55e47676 100644
--- a/azalea-world/src/lib.rs
+++ b/azalea-world/src/lib.rs
@@ -3,7 +3,7 @@
#![feature(error_generic_member_access)]
mod bit_storage;
-mod chunk_storage;
+pub mod chunk_storage;
mod container;
pub mod heightmap;
pub mod iterators;
@@ -13,9 +13,7 @@ mod world;
use std::backtrace::Backtrace;
pub use bit_storage::BitStorage;
-pub use chunk_storage::{
- calculate_chunk_storage_range, Chunk, ChunkStorage, PartialChunkStorage, Section,
-};
+pub use chunk_storage::{Chunk, ChunkStorage, PartialChunkStorage, Section};
pub use container::*;
use thiserror::Error;
pub use world::*;
diff --git a/azalea/benches/pathfinder.rs b/azalea/benches/pathfinder.rs
index 34b6f91c..e7c52d57 100644
--- a/azalea/benches/pathfinder.rs
+++ b/azalea/benches/pathfinder.rs
@@ -4,6 +4,7 @@ use azalea::{
pathfinder::{
astar::{self, a_star},
goals::BlockPosGoal,
+ moves::PathfinderCtx,
Goal,
},
BlockPos,
@@ -73,13 +74,14 @@ fn generate_bedrock_world(
fn bench_pathfinder(c: &mut Criterion) {
c.bench_function("bedrock", |b| {
let mut partial_chunks = PartialChunkStorage::new(32);
- let successors_fn = azalea::pathfinder::moves::parkour::parkour_move;
+ let successors_fn = azalea::pathfinder::moves::default_move;
b.iter(|| {
let (world, start, end) = generate_bedrock_world(&mut partial_chunks, 4);
+ let ctx = PathfinderCtx::new(&world);
let goal = BlockPosGoal(end);
- let successors = |pos: BlockPos| successors_fn(&world, pos);
+ let successors = |pos: BlockPos| successors_fn(&ctx, pos);
let astar::Path { movements, partial } = a_star(
start,
diff --git a/azalea/src/pathfinder/astar.rs b/azalea/src/pathfinder/astar.rs
index 505b09c0..bc7b2309 100644
--- a/azalea/src/pathfinder/astar.rs
+++ b/azalea/src/pathfinder/astar.rs
@@ -26,14 +26,14 @@ const MIN_IMPROVEMENT: f32 = 0.01;
pub fn a_star<P, M, HeuristicFn, SuccessorsFn, SuccessFn>(
start: P,
heuristic: HeuristicFn,
- successors: SuccessorsFn,
+ mut successors: SuccessorsFn,
success: SuccessFn,
timeout: Duration,
) -> Path<P, M>
where
P: Eq + Hash + Copy + Debug,
HeuristicFn: Fn(P) -> f32,
- SuccessorsFn: Fn(P) -> Vec<Edge<P, M>>,
+ SuccessorsFn: FnMut(P) -> Vec<Edge<P, M>>,
SuccessFn: Fn(P) -> bool,
{
let start_time = Instant::now();
diff --git a/azalea/src/pathfinder/mod.rs b/azalea/src/pathfinder/mod.rs
index 02b7d935..831a5524 100644
--- a/azalea/src/pathfinder/mod.rs
+++ b/azalea/src/pathfinder/mod.rs
@@ -19,6 +19,7 @@ use crate::ecs::{
query::{With, Without},
system::{Commands, Query, Res},
};
+use crate::pathfinder::moves::PathfinderCtx;
use azalea_client::movement::walk_listener;
use azalea_client::{StartSprintEvent, StartWalkEvent};
use azalea_core::position::BlockPos;
@@ -178,7 +179,8 @@ fn goto_listener(
debug!("start: {start:?}");
let world = &world_lock.read().chunks;
- let successors = |pos: BlockPos| successors_fn(world, pos);
+ let ctx = PathfinderCtx::new(world);
+ let successors = |pos: BlockPos| successors_fn(&ctx, pos);
let mut attempt_number = 0;
@@ -192,15 +194,20 @@ fn goto_listener(
|n| goal.heuristic(n),
successors,
|n| goal.success(n),
- Duration::from_secs(if attempt_number == 0 { 1 } else { 5 }),
+ Duration::from_secs(if attempt_number == 0 { 10 } else { 10 }),
);
let end_time = std::time::Instant::now();
debug!("partial: {partial:?}");
- debug!("time: {:?}", end_time - start_time);
+ let duration = end_time - start_time;
+ if partial {
+ info!("Pathfinder took {duration:?} (timed out)");
+ } else {
+ info!("Pathfinder took {duration:?}");
+ }
- info!("Path:");
+ debug!("Path:");
for movement in &movements {
- info!(" {:?}", movement.target);
+ debug!(" {:?}", movement.target);
}
path = movements.into_iter().collect::<VecDeque<_>>();
@@ -275,11 +282,10 @@ fn path_found_listener(
let world_lock = instance_container.get(instance_name).expect(
"Entity tried to pathfind but the entity isn't in a valid world",
);
+ let world = &world_lock.read().chunks;
+ let ctx = PathfinderCtx::new(&world);
let successors_fn: moves::SuccessorsFn = event.successors_fn;
- let successors = |pos: BlockPos| {
- let world = &world_lock.read().chunks;
- successors_fn(world, pos)
- };
+ let successors = |pos: BlockPos| successors_fn(&ctx, pos);
if successors(last_node.target)
.iter()
@@ -439,10 +445,9 @@ fn tick_execute_path(
{
// obstruction check (the path we're executing isn't possible anymore)
- let successors = |pos: BlockPos| {
- let world = &world_lock.read().chunks;
- successors_fn(world, pos)
- };
+ let world = &world_lock.read().chunks;
+ let ctx = PathfinderCtx::new(&world);
+ let successors = |pos: BlockPos| successors_fn(&ctx, pos);
if let Some(last_reached_node) = pathfinder.last_reached_node {
if let Some(obstructed_index) =
diff --git a/azalea/src/pathfinder/moves/basic.rs b/azalea/src/pathfinder/moves/basic.rs
index 1785ec2e..b7bb1116 100644
--- a/azalea/src/pathfinder/moves/basic.rs
+++ b/azalea/src/pathfinder/moves/basic.rs
@@ -5,33 +5,29 @@ use azalea_core::{
direction::CardinalDirection,
position::{BlockPos, Vec3},
};
-use azalea_world::ChunkStorage;
use crate::{
pathfinder::{astar, costs::*},
JumpEvent, LookAtEvent,
};
-use super::{
- default_is_reached, fall_distance, is_block_passable, is_passable, is_standable, Edge,
- ExecuteCtx, IsReachedCtx, MoveData,
-};
+use super::{default_is_reached, Edge, ExecuteCtx, IsReachedCtx, MoveData, PathfinderCtx};
-pub fn basic_move(world: &ChunkStorage, node: BlockPos) -> Vec<Edge> {
+pub fn basic_move(ctx: &PathfinderCtx, node: BlockPos) -> Vec<Edge> {
let mut edges = Vec::new();
- edges.extend(forward_move(world, node));
- edges.extend(ascend_move(world, node));
- edges.extend(descend_move(world, node));
- edges.extend(diagonal_move(world, node));
+ edges.extend(forward_move(ctx, node));
+ edges.extend(ascend_move(ctx, node));
+ edges.extend(descend_move(ctx, node));
+ edges.extend(diagonal_move(ctx, node));
edges
}
-fn forward_move(world: &ChunkStorage, pos: BlockPos) -> Vec<Edge> {
+fn forward_move(ctx: &PathfinderCtx, pos: BlockPos) -> Vec<Edge> {
let mut edges = Vec::new();
for dir in CardinalDirection::iter() {
let offset = BlockPos::new(dir.x(), 0, dir.z());
- if !is_standable(&(pos + offset), world) {
+ if !ctx.is_standable(&(pos + offset)) {
continue;
}
@@ -72,15 +68,15 @@ fn execute_forward_move(
});
}
-fn ascend_move(world: &ChunkStorage, pos: BlockPos) -> Vec<Edge> {
+fn ascend_move(ctx: &PathfinderCtx, pos: BlockPos) -> Vec<Edge> {
let mut edges = Vec::new();
for dir in CardinalDirection::iter() {
let offset = BlockPos::new(dir.x(), 1, dir.z());
- if !is_block_passable(&pos.up(2), world) {
+ if !ctx.is_block_passable(&pos.up(2)) {
continue;
}
- if !is_standable(&(pos + offset), world) {
+ if !ctx.is_standable(&(pos + offset)) {
continue;
}
@@ -156,23 +152,23 @@ pub fn ascend_is_reached(
BlockPos::from(position) == target || BlockPos::from(position) == target.down(1)
}
-fn descend_move(world: &ChunkStorage, pos: BlockPos) -> Vec<Edge> {
+fn descend_move(ctx: &PathfinderCtx, pos: BlockPos) -> Vec<Edge> {
let mut edges = Vec::new();
for dir in CardinalDirection::iter() {
let dir_delta = BlockPos::new(dir.x(), 0, dir.z());
let new_horizontal_position = pos + dir_delta;
- let fall_distance = fall_distance(&new_horizontal_position, world);
+ let fall_distance = ctx.fall_distance(&new_horizontal_position);
if fall_distance == 0 || fall_distance > 3 {
continue;
}
let new_position = new_horizontal_position.down(fall_distance as i32);
// check whether 3 blocks vertically forward are passable
- if !is_passable(&new_horizontal_position, world) {
+ if !ctx.is_passable(&new_horizontal_position) {
continue;
}
// check whether we can stand on the target position
- if !is_standable(&new_position, world) {
+ if !ctx.is_standable(&new_position) {
continue;
}
@@ -258,22 +254,22 @@ pub fn descend_is_reached(
&& (position.y - target.y as f64) < 0.5
}
-fn diagonal_move(world: &ChunkStorage, pos: BlockPos) -> Vec<Edge> {
+fn diagonal_move(ctx: &PathfinderCtx, pos: BlockPos) -> Vec<Edge> {
let mut edges = Vec::new();
for dir in CardinalDirection::iter() {
let right = dir.right();
let offset = BlockPos::new(dir.x() + right.x(), 0, dir.z() + right.z());
- if !is_passable(
- &BlockPos::new(pos.x + dir.x(), pos.y, pos.z + dir.z()),
- world,
- ) && !is_passable(
- &BlockPos::new(pos.x + dir.right().x(), pos.y, pos.z + dir.right().z()),
- world,
- ) {
+ if !ctx.is_passable(&BlockPos::new(pos.x + dir.x(), pos.y, pos.z + dir.z()))
+ && !ctx.is_passable(&BlockPos::new(
+ pos.x + dir.right().x(),
+ pos.y,
+ pos.z + dir.right().z(),
+ ))
+ {
continue;
}
- if !is_standable(&(pos + offset), world) {
+ if !ctx.is_standable(&(pos + offset)) {
continue;
}
// +0.001 so it doesn't unnecessarily go diagonal sometimes
diff --git a/azalea/src/pathfinder/moves/mod.rs b/azalea/src/pathfinder/moves/mod.rs
index 197229fd..caf4bbb4 100644
--- a/azalea/src/pathfinder/moves/mod.rs
+++ b/azalea/src/pathfinder/moves/mod.rs
@@ -1,21 +1,22 @@
pub mod basic;
pub mod parkour;
-use std::fmt::Debug;
+use std::{cell::RefCell, fmt::Debug};
use crate::{JumpEvent, LookAtEvent};
use super::astar;
+use azalea_block::BlockState;
use azalea_client::{StartSprintEvent, StartWalkEvent};
-use azalea_core::position::{BlockPos, Vec3};
+use azalea_core::position::{BlockPos, ChunkBlockPos, ChunkPos, Vec3};
use azalea_physics::collision::{self, BlockWithShape};
use azalea_world::ChunkStorage;
use bevy_ecs::{entity::Entity, event::EventWriter};
+use nohash_hasher::IntMap;
type Edge = astar::Edge<BlockPos, MoveData>;
-pub type SuccessorsFn =
- fn(&azalea_world::ChunkStorage, BlockPos) -> Vec<astar::Edge<BlockPos, MoveData>>;
+pub type SuccessorsFn = fn(&PathfinderCtx, BlockPos) -> Vec<astar::Edge<BlockPos, MoveData>>;
#[derive(Clone)]
pub struct MoveData {
@@ -33,71 +34,110 @@ impl Debug for MoveData {
}
}
-/// whether this block is passable
-fn is_block_passable(pos: &BlockPos, world: &ChunkStorage) -> bool {
- let Some(block) = world.get_block_state(pos) else {
- return false;
- };
- if block.is_air() {
- // fast path
- return true;
- }
- if block.shape() != &*collision::EMPTY_SHAPE {
- return false;
- }
- if block == azalea_registry::Block::Water.into() {
- return false;
- }
- if block.waterlogged() {
- return false;
+pub struct PathfinderCtx<'a> {
+ world: &'a ChunkStorage,
+ cached_chunks: RefCell<IntMap<ChunkPos, Vec<azalea_world::Section>>>,
+}
+
+impl<'a> PathfinderCtx<'a> {
+ pub fn new(world: &'a ChunkStorage) -> Self {
+ Self {
+ world,
+ cached_chunks: Default::default(),
+ }
}
- // block.waterlogged currently doesn't account for seagrass and some other water
- // blocks
- if block == azalea_registry::Block::Seagrass.into() {
- return false;
+
+ fn get_block_state(&self, pos: &BlockPos) -> Option<BlockState> {
+ let chunk_pos = ChunkPos::from(pos);
+
+ let mut cached_chunks = self.cached_chunks.borrow_mut();
+ if let Some(sections) = cached_chunks.get(&chunk_pos) {
+ return azalea_world::chunk_storage::get_block_state_from_sections(
+ sections,
+ &ChunkBlockPos::from(pos),
+ self.world.min_y,
+ );
+ }
+
+ let chunk = self.world.get(&chunk_pos)?;
+ let chunk = chunk.read();
+
+ cached_chunks.insert(chunk_pos, chunk.sections.clone());
+
+ azalea_world::chunk_storage::get_block_state_from_sections(
+ &chunk.sections,
+ &ChunkBlockPos::from(pos),
+ self.world.min_y,
+ )
}
- true
-}
+ /// whether this block is passable
+ pub fn is_block_passable(&self, pos: &BlockPos) -> bool {
+ let Some(block) = self.get_block_state(pos) else {
+ return false;
+ };
+ if block.is_air() {
+ // fast path
+ return true;
+ }
+ if block.shape() != &*collision::EMPTY_SHAPE {
+ return false;
+ }
+ if block == azalea_registry::Block::Water.into() {
+ return false;
+ }
+ if block.waterlogged() {
+ return false;
+ }
+ // block.waterlogged currently doesn't account for seagrass and some other water
+ // blocks
+ if block == azalea_registry::Block::Seagrass.into() {
+ return false;
+ }
-/// whether this block has a solid hitbox (i.e. we can stand on it)
-fn is_block_solid(pos: &BlockPos, world: &ChunkStorage) -> bool {
- let Some(block) = world.get_block_state(pos) else {
- return false;
- };
- if block.is_air() {
- // fast path
- return false;
+ true
}
- block.shape() == &*collision::BLOCK_SHAPE
-}
-/// Whether this block and the block above are passable
-fn is_passable(pos: &BlockPos, world: &ChunkStorage) -> bool {
- is_block_passable(pos, world) && is_block_passable(&pos.up(1), world)
-}
+ /// whether this block has a solid hitbox (i.e. we can stand on it)
+ pub fn is_block_solid(&self, pos: &BlockPos) -> bool {
+ let Some(block) = self.get_block_state(pos) else {
+ return false;
+ };
+ if block.is_air() {
+ // fast path
+ return false;
+ }
+ block.shape() == &*collision::BLOCK_SHAPE
+ }
-/// Whether we can stand in this position. Checks if the block below is solid,
-/// and that the two blocks above that are passable.
+ /// Whether this block and the block above are passable
+ pub fn is_passable(&self, pos: &BlockPos) -> bool {
+ self.is_block_passable(pos) && self.is_block_passable(&pos.up(1))
+ }
-fn is_standable(pos: &BlockPos, world: &ChunkStorage) -> bool {
- is_block_solid(&pos.down(1), world) && is_passable(pos, world)
-}
+ /// Whether we can stand in this position. Checks if the block below is
+ /// solid, and that the two blocks above that are passable.
-/// Get the amount of air blocks until the next solid block below this one.
-fn fall_distance(pos: &BlockPos, world: &ChunkStorage) -> u32 {
- let mut distance = 0;
- let mut current_pos = pos.down(1);
- while is_block_passable(&current_pos, world) {
- distance += 1;
- current_pos = current_pos.down(1);
+ pub fn is_standable(&self, pos: &BlockPos) -> bool {
+ self.is_block_solid(&pos.down(1)) && self.is_passable(pos)
+ }
+
+ /// Get the amount of air blocks until the next solid block below this one.
+ pub fn fall_distance(&self, pos: &BlockPos) -> u32 {
+ let mut distance = 0;
+ let mut current_pos = pos.down(1);
+ while self.is_block_passable(&current_pos) {
+ distance += 1;
+ current_pos = current_pos.down(1);
- if current_pos.y < world.min_y {
- return u32::MAX;
+ if current_pos.y < self.world.min_y {
+ return u32::MAX;
+ }
}
+ distance
}
- distance
}
+
pub struct ExecuteCtx<'w1, 'w2, 'w3, 'w4, 'a> {
pub entity: Entity,
/// The node that we're trying to reach.
@@ -121,10 +161,10 @@ pub struct IsReachedCtx<'a> {
pub physics: &'a azalea_entity::Physics,
}
-pub fn default_move(world: &ChunkStorage, node: BlockPos) -> Vec<Edge> {
+pub fn default_move(ctx: &PathfinderCtx, node: BlockPos) -> Vec<Edge> {
let mut edges = Vec::new();
- edges.extend(basic::basic_move(world, node));
- edges.extend(parkour::parkour_move(world, node));
+ edges.extend(basic::basic_move(ctx, node));
+ edges.extend(parkour::parkour_move(ctx, node));
edges
}
@@ -163,8 +203,9 @@ mod tests {
.chunks
.set_block_state(&BlockPos::new(0, 1, 0), BlockState::AIR, &world);
- assert!(!is_block_passable(&BlockPos::new(0, 0, 0), &world));
- assert!(is_block_passable(&BlockPos::new(0, 1, 0), &world));
+ let ctx = PathfinderCtx::new(&world);
+ assert!(!ctx.is_block_passable(&BlockPos::new(0, 0, 0)));
+ assert!(ctx.is_block_passable(&BlockPos::new(0, 1, 0),));
}
#[test]
@@ -183,8 +224,9 @@ mod tests {
.chunks
.set_block_state(&BlockPos::new(0, 1, 0), BlockState::AIR, &world);
- assert!(is_block_solid(&BlockPos::new(0, 0, 0), &world));
- assert!(!is_block_solid(&BlockPos::new(0, 1, 0), &world));
+ let ctx = PathfinderCtx::new(&world);
+ assert!(ctx.is_block_solid(&BlockPos::new(0, 0, 0)));
+ assert!(!ctx.is_block_solid(&BlockPos::new(0, 1, 0)));
}
#[test]
@@ -209,8 +251,9 @@ mod tests {
.chunks
.set_block_state(&BlockPos::new(0, 3, 0), BlockState::AIR, &world);
- assert!(is_standable(&BlockPos::new(0, 1, 0), &world));
- assert!(!is_standable(&BlockPos::new(0, 0, 0), &world));
- assert!(!is_standable(&BlockPos::new(0, 2, 0), &world));
+ let ctx = PathfinderCtx::new(&world);
+ assert!(ctx.is_standable(&BlockPos::new(0, 1, 0)));
+ assert!(!ctx.is_standable(&BlockPos::new(0, 0, 0)));
+ assert!(!ctx.is_standable(&BlockPos::new(0, 2, 0)));
}
}
diff --git a/azalea/src/pathfinder/moves/parkour.rs b/azalea/src/pathfinder/moves/parkour.rs
index 03635faa..53f5a348 100644
--- a/azalea/src/pathfinder/moves/parkour.rs
+++ b/azalea/src/pathfinder/moves/parkour.rs
@@ -1,46 +1,42 @@
use azalea_client::{SprintDirection, StartSprintEvent, StartWalkEvent, WalkDirection};
use azalea_core::{direction::CardinalDirection, position::BlockPos};
-use azalea_world::ChunkStorage;
use crate::{
pathfinder::{astar, costs::*},
JumpEvent, LookAtEvent,
};
-use super::{
- default_is_reached, is_block_passable, is_block_solid, is_passable, is_standable, Edge,
- ExecuteCtx, IsReachedCtx, MoveData,
-};
+use super::{default_is_reached, Edge, ExecuteCtx, IsReachedCtx, MoveData, PathfinderCtx};
-pub fn parkour_move(world: &ChunkStorage, node: BlockPos) -> Vec<Edge> {
+pub fn parkour_move(ctx: &PathfinderCtx, node: BlockPos) -> Vec<Edge> {
let mut edges = Vec::new();
- edges.extend(parkour_forward_1_move(world, node));
- edges.extend(parkour_headhitter_forward_1_move(world, node));
- edges.extend(parkour_forward_2_move(world, node));
+ edges.extend(parkour_forward_1_move(ctx, node));
+ edges.extend(parkour_headhitter_forward_1_move(ctx, node));
+ edges.extend(parkour_forward_2_move(ctx, node));
edges
}
-fn parkour_forward_1_move(world: &ChunkStorage, pos: BlockPos) -> Vec<Edge> {
+fn parkour_forward_1_move(ctx: &PathfinderCtx, pos: BlockPos) -> Vec<Edge> {
let mut edges = Vec::new();
for dir in CardinalDirection::iter() {
let gap_offset = BlockPos::new(dir.x(), 0, dir.z());
let offset = BlockPos::new(dir.x() * 2, 0, dir.z() * 2);
- if !is_standable(&(pos + offset), world) {
+ if !ctx.is_standable(&(pos + offset)) {
continue;
}
- if !is_passable(&(pos + gap_offset), world) {
+ if !ctx.is_passable(&(pos + gap_offset)) {
continue;
}
- if !is_block_passable(&(pos + gap_offset).up(2), world) {
+ if !ctx.is_block_passable(&(pos + gap_offset).up(2)) {
continue;
}
// make sure we actually have to jump
- if is_block_solid(&(pos + gap_offset).down(1), world) {
+ if ctx.is_block_solid(&(pos + gap_offset).down(1)) {
continue;
}
// make sure it's not a headhitter
- if !is_block_passable(&pos.up(2), world) {
+ if !ctx.is_block_passable(&pos.up(2)) {
continue;
}
@@ -61,34 +57,34 @@ fn parkour_forward_1_move(world: &ChunkStorage, pos: BlockPos) -> Vec<Edge> {
edges
}
-fn parkour_forward_2_move(world: &ChunkStorage, pos: BlockPos) -> Vec<Edge> {
+fn parkour_forward_2_move(ctx: &PathfinderCtx, pos: BlockPos) -> Vec<Edge> {
let mut edges = Vec::new();
for dir in CardinalDirection::iter() {
let gap_1_offset = BlockPos::new(dir.x(), 0, dir.z());
let gap_2_offset = BlockPos::new(dir.x() * 2, 0, dir.z() * 2);
let offset = BlockPos::new(dir.x() * 3, 0, dir.z() * 3);
- if !is_standable(&(pos + offset), world) {
+ if !ctx.is_standable(&(pos + offset)) {
continue;
}
- if !is_passable(&(pos + gap_1_offset), world) {
+ if !ctx.is_passable(&(pos + gap_1_offset)) {
continue;
}
- if !is_block_passable(&(pos + gap_1_offset).up(2), world) {
+ if !ctx.is_block_passable(&(pos + gap_1_offset).up(2)) {
continue;
}
- if !is_passable(&(pos + gap_2_offset), world) {
+ if !ctx.is_passable(&(pos + gap_2_offset)) {
continue;
}
- if !is_block_passable(&(pos + gap_2_offset).up(2), world) {
+ if !ctx.is_block_passable(&(pos + gap_2_offset).up(2)) {
continue;
}
// make sure we actually have to jump
- if is_block_solid(&(pos + gap_1_offset).down(1), world) {
+ if ctx.is_block_solid(&(pos + gap_1_offset).down(1)) {
continue;
}
// make sure it's not a headhitter
- if !is_block_passable(&pos.up(2), world) {
+ if !ctx.is_block_passable(&pos.up(2)) {
continue;
}
@@ -112,27 +108,27 @@ fn parkour_forward_2_move(world: &ChunkStorage, pos: BlockPos) -> Vec<Edge> {
edges
}
-fn parkour_headhitter_forward_1_move(world: &ChunkStorage, pos: BlockPos) -> Vec<Edge> {
+fn parkour_headhitter_forward_1_move(ctx: &PathfinderCtx, pos: BlockPos) -> Vec<Edge> {
let mut edges = Vec::new();
for dir in CardinalDirection::iter() {
let gap_offset = BlockPos::new(dir.x(), 0, dir.z());
let offset = BlockPos::new(dir.x() * 2, 0, dir.z() * 2);
- if !is_standable(&(pos + offset), world) {
+ if !ctx.is_standable(&(pos + offset)) {
continue;
}
- if !is_passable(&(pos + gap_offset), world) {
+ if !ctx.is_passable(&(pos + gap_offset)) {
continue;
}
- if !is_block_passable(&(pos + gap_offset).up(2), world) {
+ if !ctx.is_block_passable(&(pos + gap_offset).up(2)) {
continue;
}
// make sure we actually have to jump
- if is_block_solid(&(pos + gap_offset).down(1), world) {
+ if ctx.is_block_solid(&(pos + gap_offset).down(1)) {
continue;
}
// make sure it is a headhitter
- if !is_block_solid(&pos.up(2), world) {
+ if !ctx.is_block_solid(&pos.up(2)) {
continue;
}