Skip to content

Commit 0ee9ed5

Browse files
committed
optimize pathfinder
1 parent d67aa07 commit 0ee9ed5

File tree

11 files changed

+275
-93
lines changed

11 files changed

+275
-93
lines changed

azalea-client/src/chat.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ pub enum ChatPacket {
4040

4141
macro_rules! regex {
4242
($re:literal $(,)?) => {{
43-
std::sync::LazyLock::new(|| regex::Regex::new($re).unwrap())
43+
static RE: std::sync::LazyLock<regex::Regex> =
44+
std::sync::LazyLock::new(|| regex::Regex::new($re).unwrap());
45+
&RE
4446
}};
4547
}
4648

azalea-core/src/direction.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,15 +88,15 @@ pub enum AxisCycle {
8888

8989
impl CardinalDirection {
9090
#[inline]
91-
pub fn x(self) -> i32 {
91+
pub fn x(self) -> i16 {
9292
match self {
9393
CardinalDirection::East => 1,
9494
CardinalDirection::West => -1,
9595
_ => 0,
9696
}
9797
}
9898
#[inline]
99-
pub fn z(self) -> i32 {
99+
pub fn z(self) -> i16 {
100100
match self {
101101
CardinalDirection::South => 1,
102102
CardinalDirection::North => -1,

azalea/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ bevy_app = { workspace = true }
3030
bevy_ecs = { workspace = true }
3131
bevy_log = { workspace = true }
3232
bevy_tasks = { workspace = true, features = ["multi_threaded"] }
33-
#bevy_time = { workspace = true }
33+
# bevy_time = { workspace = true }
3434
derive_more = { workspace = true, features = ["deref", "deref_mut"] }
3535
futures = { workspace = true }
3636
futures-lite = { workspace = true }

azalea/benches/pathfinder.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use azalea::{
55
astar::{self, a_star, PathfinderTimeout},
66
goals::{BlockPosGoal, Goal},
77
mining::MiningCache,
8+
rel_block_pos::RelBlockPos,
89
world::CachedWorld,
910
},
1011
BlockPos,
@@ -124,21 +125,23 @@ fn run_pathfinder_benchmark(
124125

125126
let (world, start, end) = generate_world(&mut partial_chunks, 4);
126127

128+
let origin = start;
129+
127130
b.iter(|| {
128-
let cached_world = CachedWorld::new(Arc::new(RwLock::new(world.clone().into())));
131+
let cached_world = CachedWorld::new(Arc::new(RwLock::new(world.clone().into())), origin);
129132
let mining_cache =
130133
MiningCache::new(Some(Menu::Player(azalea_inventory::Player::default())));
131134
let goal = BlockPosGoal(end);
132135

133-
let successors = |pos: BlockPos| {
136+
let successors = |pos: RelBlockPos| {
134137
azalea::pathfinder::call_successors_fn(&cached_world, &mining_cache, successors_fn, pos)
135138
};
136139

137140
let astar::Path { movements, partial } = a_star(
138-
start,
139-
|n| goal.heuristic(n),
141+
RelBlockPos::get_origin(origin),
142+
|n| goal.heuristic(n.apply(origin)),
140143
successors,
141-
|n| goal.success(n),
144+
|n| goal.success(n.apply(origin)),
142145
PathfinderTimeout::Time(Duration::MAX),
143146
);
144147

azalea/src/pathfinder/astar.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ where
7474
num_nodes += 1;
7575
if success(current_node) {
7676
debug!("Nodes considered: {num_nodes}");
77+
7778
return Path {
7879
movements: reconstruct_path(nodes, current_node),
7980
partial: false,

azalea/src/pathfinder/mod.rs

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod debug;
88
pub mod goals;
99
pub mod mining;
1010
pub mod moves;
11+
pub mod rel_block_pos;
1112
pub mod simulation;
1213
pub mod world;
1314

@@ -35,6 +36,7 @@ use bevy_ecs::schedule::IntoSystemConfigs;
3536
use bevy_tasks::{AsyncComputeTaskPool, Task};
3637
use futures_lite::future;
3738
use parking_lot::RwLock;
39+
use rel_block_pos::RelBlockPos;
3840
use tracing::{debug, error, info, trace, warn};
3941

4042
use self::debug::debug_render_path_with_particles;
@@ -321,8 +323,9 @@ pub async fn calculate_path(opts: CalculatePathOpts) -> Option<PathFoundEvent> {
321323

322324
let goto_id = opts.goto_id_atomic.fetch_add(1, atomic::Ordering::SeqCst) + 1;
323325

324-
let cached_world = CachedWorld::new(opts.world_lock);
325-
let successors = |pos: BlockPos| {
326+
let origin = opts.start;
327+
let cached_world = CachedWorld::new(opts.world_lock, origin);
328+
let successors = |pos: RelBlockPos| {
326329
call_successors_fn(&cached_world, &opts.mining_cache, opts.successors_fn, pos)
327330
};
328331

@@ -345,10 +348,10 @@ pub async fn calculate_path(opts: CalculatePathOpts) -> Option<PathFoundEvent> {
345348
};
346349

347350
let astar::Path { movements, partial } = a_star(
348-
opts.start,
349-
|n| opts.goal.heuristic(n),
351+
RelBlockPos::get_origin(origin),
352+
|n| opts.goal.heuristic(n.apply(origin)),
350353
successors,
351-
|n| opts.goal.success(n),
354+
|n| opts.goal.success(n.apply(origin)),
352355
timeout,
353356
);
354357
let end_time = Instant::now();
@@ -368,7 +371,7 @@ pub async fn calculate_path(opts: CalculatePathOpts) -> Option<PathFoundEvent> {
368371

369372
debug!("Path:");
370373
for movement in &movements {
371-
debug!(" {}", movement.target);
374+
debug!(" {}", movement.target.apply(origin));
372375
}
373376

374377
path = movements.into_iter().collect::<VecDeque<_>>();
@@ -394,10 +397,19 @@ pub async fn calculate_path(opts: CalculatePathOpts) -> Option<PathFoundEvent> {
394397
break;
395398
}
396399

400+
// replace the RelBlockPos types with BlockPos
401+
let mapped_path = path
402+
.into_iter()
403+
.map(|movement| astar::Movement {
404+
target: movement.target.apply(origin),
405+
data: movement.data,
406+
})
407+
.collect();
408+
397409
Some(PathFoundEvent {
398410
entity: opts.entity,
399411
start: opts.start,
400-
path: Some(path),
412+
path: Some(mapped_path),
401413
is_partial,
402414
successors_fn: opts.successors_fn,
403415
allow_mining: opts.allow_mining,
@@ -448,21 +460,27 @@ pub fn path_found_listener(
448460
let world_lock = instance_container
449461
.get(instance_name)
450462
.expect("Entity tried to pathfind but the entity isn't in a valid world");
463+
let origin = event.start;
451464
let successors_fn: moves::SuccessorsFn = event.successors_fn;
452-
let cached_world = CachedWorld::new(world_lock);
465+
let cached_world = CachedWorld::new(world_lock, origin);
453466
let mining_cache = MiningCache::new(if event.allow_mining {
454467
Some(inventory.inventory_menu.clone())
455468
} else {
456469
None
457470
});
458-
let successors = |pos: BlockPos| {
471+
let successors = |pos: RelBlockPos| {
459472
call_successors_fn(&cached_world, &mining_cache, successors_fn, pos)
460473
};
461474

462475
if let Some(first_node_of_new_path) = path.front() {
463-
if successors(last_node_of_current_path.target)
476+
let last_target_of_current_path =
477+
RelBlockPos::from_origin(origin, last_node_of_current_path.target);
478+
let first_target_of_new_path =
479+
RelBlockPos::from_origin(origin, first_node_of_new_path.target);
480+
481+
if successors(last_target_of_current_path)
464482
.iter()
465-
.any(|edge| edge.movement.target == first_node_of_new_path.target)
483+
.any(|edge| edge.movement.target == first_target_of_new_path)
466484
{
467485
debug!("combining old and new paths");
468486
debug!(
@@ -655,17 +673,19 @@ pub fn check_for_path_obstruction(
655673
.expect("Entity tried to pathfind but the entity isn't in a valid world");
656674

657675
// obstruction check (the path we're executing isn't possible anymore)
658-
let cached_world = CachedWorld::new(world_lock);
676+
let origin = executing_path.last_reached_node;
677+
let cached_world = CachedWorld::new(world_lock, origin);
659678
let mining_cache = MiningCache::new(if pathfinder.allow_mining {
660679
Some(inventory.inventory_menu.clone())
661680
} else {
662681
None
663682
});
664683
let successors =
665-
|pos: BlockPos| call_successors_fn(&cached_world, &mining_cache, successors_fn, pos);
684+
|pos: RelBlockPos| call_successors_fn(&cached_world, &mining_cache, successors_fn, pos);
666685

667686
if let Some(obstructed_index) = check_path_obstructed(
668-
executing_path.last_reached_node,
687+
origin,
688+
RelBlockPos::from_origin(origin, executing_path.last_reached_node),
669689
&executing_path.path,
670690
successors,
671691
) {
@@ -873,18 +893,21 @@ pub fn stop_pathfinding_on_instance_change(
873893
/// Checks whether the path has been obstructed, and returns Some(index) if it
874894
/// has been. The index is of the first obstructed node.
875895
pub fn check_path_obstructed<SuccessorsFn>(
876-
mut current_position: BlockPos,
896+
origin: BlockPos,
897+
mut current_position: RelBlockPos,
877898
path: &VecDeque<astar::Movement<BlockPos, moves::MoveData>>,
878899
successors_fn: SuccessorsFn,
879900
) -> Option<usize>
880901
where
881-
SuccessorsFn: Fn(BlockPos) -> Vec<astar::Edge<BlockPos, moves::MoveData>>,
902+
SuccessorsFn: Fn(RelBlockPos) -> Vec<astar::Edge<RelBlockPos, moves::MoveData>>,
882903
{
883904
for (i, movement) in path.iter().enumerate() {
905+
let movement_target = RelBlockPos::from_origin(origin, movement.target);
906+
884907
let mut found_obstruction = false;
885908
for edge in successors_fn(current_position) {
886-
if edge.movement.target == movement.target {
887-
current_position = movement.target;
909+
if edge.movement.target == movement_target {
910+
current_position = movement_target;
888911
found_obstruction = false;
889912
break;
890913
} else {
@@ -903,8 +926,8 @@ pub fn call_successors_fn(
903926
cached_world: &CachedWorld,
904927
mining_cache: &MiningCache,
905928
successors_fn: SuccessorsFn,
906-
pos: BlockPos,
907-
) -> Vec<astar::Edge<BlockPos, moves::MoveData>> {
929+
pos: RelBlockPos,
930+
) -> Vec<astar::Edge<RelBlockPos, moves::MoveData>> {
908931
let mut edges = Vec::with_capacity(16);
909932
let mut ctx = PathfinderCtx {
910933
edges: &mut edges,

azalea/src/pathfinder/moves/basic.rs

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ use azalea_core::{
77
};
88

99
use super::{default_is_reached, Edge, ExecuteCtx, IsReachedCtx, MoveData, PathfinderCtx};
10-
use crate::pathfinder::{astar, costs::*};
10+
use crate::pathfinder::{astar, costs::*, rel_block_pos::RelBlockPos};
1111

12-
pub fn basic_move(ctx: &mut PathfinderCtx, node: BlockPos) {
12+
pub fn basic_move(ctx: &mut PathfinderCtx, node: RelBlockPos) {
1313
forward_move(ctx, node);
1414
ascend_move(ctx, node);
1515
descend_move(ctx, node);
@@ -18,9 +18,9 @@ pub fn basic_move(ctx: &mut PathfinderCtx, node: BlockPos) {
1818
downward_move(ctx, node);
1919
}
2020

21-
fn forward_move(ctx: &mut PathfinderCtx, pos: BlockPos) {
21+
fn forward_move(ctx: &mut PathfinderCtx, pos: RelBlockPos) {
2222
for dir in CardinalDirection::iter() {
23-
let offset = BlockPos::new(dir.x(), 0, dir.z());
23+
let offset = RelBlockPos::new(dir.x(), 0, dir.z());
2424

2525
let mut cost = SPRINT_ONE_BLOCK_COST;
2626

@@ -57,9 +57,9 @@ fn execute_forward_move(mut ctx: ExecuteCtx) {
5757
ctx.sprint(SprintDirection::Forward);
5858
}
5959

60-
fn ascend_move(ctx: &mut PathfinderCtx, pos: BlockPos) {
60+
fn ascend_move(ctx: &mut PathfinderCtx, pos: RelBlockPos) {
6161
for dir in CardinalDirection::iter() {
62-
let offset = BlockPos::new(dir.x(), 1, dir.z());
62+
let offset = RelBlockPos::new(dir.x(), 1, dir.z());
6363

6464
let break_cost_1 = ctx
6565
.world
@@ -126,7 +126,7 @@ fn execute_ascend_move(mut ctx: ExecuteCtx) {
126126
+ x_axis as f64 * (target_center.z - position.z).abs();
127127

128128
let lateral_motion = x_axis as f64 * physics.velocity.z + z_axis as f64 * physics.velocity.x;
129-
if lateral_motion > 0.1 {
129+
if lateral_motion.abs() > 0.1 {
130130
return;
131131
}
132132

@@ -147,9 +147,9 @@ pub fn ascend_is_reached(
147147
BlockPos::from(position) == target || BlockPos::from(position) == target.down(1)
148148
}
149149

150-
fn descend_move(ctx: &mut PathfinderCtx, pos: BlockPos) {
150+
fn descend_move(ctx: &mut PathfinderCtx, pos: RelBlockPos) {
151151
for dir in CardinalDirection::iter() {
152-
let dir_delta = BlockPos::new(dir.x(), 0, dir.z());
152+
let dir_delta = RelBlockPos::new(dir.x(), 0, dir.z());
153153
let new_horizontal_position = pos + dir_delta;
154154

155155
let break_cost_1 = ctx
@@ -271,9 +271,9 @@ pub fn descend_is_reached(
271271
&& (position.y - target.y as f64) < 0.5
272272
}
273273

274-
fn descend_forward_1_move(ctx: &mut PathfinderCtx, pos: BlockPos) {
274+
fn descend_forward_1_move(ctx: &mut PathfinderCtx, pos: RelBlockPos) {
275275
for dir in CardinalDirection::iter() {
276-
let dir_delta = BlockPos::new(dir.x(), 0, dir.z());
276+
let dir_delta = RelBlockPos::new(dir.x(), 0, dir.z());
277277
let gap_horizontal_position = pos + dir_delta;
278278
let new_horizontal_position = pos + dir_delta * 2;
279279

@@ -323,12 +323,12 @@ fn descend_forward_1_move(ctx: &mut PathfinderCtx, pos: BlockPos) {
323323
}
324324
}
325325

326-
fn diagonal_move(ctx: &mut PathfinderCtx, pos: BlockPos) {
326+
fn diagonal_move(ctx: &mut PathfinderCtx, pos: RelBlockPos) {
327327
for dir in CardinalDirection::iter() {
328328
let right = dir.right();
329-
let offset = BlockPos::new(dir.x() + right.x(), 0, dir.z() + right.z());
330-
let left_pos = BlockPos::new(pos.x + dir.x(), pos.y, pos.z + dir.z());
331-
let right_pos = BlockPos::new(pos.x + right.x(), pos.y, pos.z + right.z());
329+
let offset = RelBlockPos::new(dir.x() + right.x(), 0, dir.z() + right.z());
330+
let left_pos = RelBlockPos::new(pos.x + dir.x(), pos.y, pos.z + dir.z());
331+
let right_pos = RelBlockPos::new(pos.x + right.x(), pos.y, pos.z + right.z());
332332

333333
// +0.001 so it doesn't unnecessarily go diagonal sometimes
334334
let mut cost = SPRINT_ONE_BLOCK_COST * SQRT_2 + 0.001;
@@ -369,7 +369,7 @@ fn execute_diagonal_move(mut ctx: ExecuteCtx) {
369369
}
370370

371371
/// Go directly down, usually by mining.
372-
fn downward_move(ctx: &mut PathfinderCtx, pos: BlockPos) {
372+
fn downward_move(ctx: &mut PathfinderCtx, pos: RelBlockPos) {
373373
// make sure we land on a solid block after breaking the one below us
374374
if !ctx.world.is_block_solid(pos.down(2)) {
375375
return;

azalea/src/pathfinder/moves/mod.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,16 @@ use parking_lot::RwLock;
1616
use super::{
1717
astar,
1818
mining::MiningCache,
19+
rel_block_pos::RelBlockPos,
1920
world::{is_block_state_passable, CachedWorld},
2021
};
2122
use crate::{auto_tool::best_tool_in_hotbar_for_block, JumpEvent, LookAtEvent};
2223

23-
type Edge = astar::Edge<BlockPos, MoveData>;
24+
type Edge = astar::Edge<RelBlockPos, MoveData>;
2425

25-
pub type SuccessorsFn = fn(&mut PathfinderCtx, BlockPos);
26+
pub type SuccessorsFn = fn(&mut PathfinderCtx, RelBlockPos);
2627

27-
pub fn default_move(ctx: &mut PathfinderCtx, node: BlockPos) {
28+
pub fn default_move(ctx: &mut PathfinderCtx, node: RelBlockPos) {
2829
basic::basic_move(ctx, node);
2930
parkour::parkour_move(ctx, node);
3031
}

0 commit comments

Comments
 (0)