diff options
| author | mat <git@matdoes.dev> | 2026-01-15 09:18:28 -0900 |
|---|---|---|
| committer | mat <git@matdoes.dev> | 2026-01-16 04:14:36 +0930 |
| commit | 914f881dd834aef67755f2e0fd4989253fc9f794 (patch) | |
| tree | 0075816dff58f5458e48d56bd1874cc8c67114fa | |
| parent | 9c8cf7adea6da1c76d96f447b71345f63146be10 (diff) | |
| download | azalea-drasl-914f881dd834aef67755f2e0fd4989253fc9f794.tar.xz | |
minor pathfinder optimizations and api+doc improvements
| -rw-r--r-- | azalea/examples/testbot/commands/movement.rs | 2 | ||||
| -rw-r--r-- | azalea/src/pathfinder/goals.rs | 32 | ||||
| -rw-r--r-- | azalea/src/pathfinder/moves/basic.rs | 12 | ||||
| -rw-r--r-- | azalea/src/pathfinder/tests.rs | 4 |
4 files changed, 36 insertions, 14 deletions
diff --git a/azalea/examples/testbot/commands/movement.rs b/azalea/examples/testbot/commands/movement.rs index 209a1c80..06c7ebd1 100644 --- a/azalea/examples/testbot/commands/movement.rs +++ b/azalea/examples/testbot/commands/movement.rs @@ -26,7 +26,7 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) { source.reply("ok"); source .bot - .start_goto(BlockPosGoal(BlockPos::from(position))); + .start_goto(BlockPosGoal(BlockPos::from(position.up(0.5)))); 1 }) .then(literal("xz").then(argument("x", integer()).then( diff --git a/azalea/src/pathfinder/goals.rs b/azalea/src/pathfinder/goals.rs index dc933936..0760b5ac 100644 --- a/azalea/src/pathfinder/goals.rs +++ b/azalea/src/pathfinder/goals.rs @@ -21,6 +21,17 @@ pub trait Goal: Debug + Send + Sync { } /// Move to the given block position. +/// +/// If you're converting to a `BlockPosGoal` from a [`Vec3`], you should move +/// the Y up by 0.5 to avoid the pathfinding destroying the final block if it's +/// non-full like farmland. +/// +/// ``` +/// # use azalea::{prelude::*, pathfinder::goals::BlockPosGoal}; +/// # fn example(bot: &Client, entity_position: azalea::Vec3) { +/// bot.start_goto(BlockPosGoal(entity_position.up(0.5).into())); +/// # } +/// ``` #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] pub struct BlockPosGoal(pub BlockPos); @@ -33,9 +44,7 @@ impl Goal for BlockPosGoal { xz_heuristic(dx, dz) + y_heuristic(dy) } fn success(&self, n: BlockPos) -> bool { - // the second half of this condition is intended to fix issues when pathing to - // non-full blocks - n == self.0 || n.down(1) == self.0 + n == self.0 } } @@ -74,9 +83,19 @@ impl Goal for XZGoal { n.x == self.x && n.z == self.z } } +impl From<BlockPos> for XZGoal { + fn from(block: BlockPos) -> Self { + Self { + x: block.x, + z: block.z, + } + } +} fn y_heuristic(dy: f32) -> f32 { - if dy > 0.0 { + if dy == 0. { + 0. + } else if dy > 0.0 { (*JUMP_ONE_BLOCK_COST + JUMP_PENALTY) * dy } else { // this assumes that we always descend 2 blocks at a time, which is fine because @@ -100,6 +119,11 @@ impl Goal for YGoal { n.y == self.y } } +impl From<BlockPos> for YGoal { + fn from(block: BlockPos) -> Self { + Self { y: block.y } + } +} /// Get within the given radius of the given position. #[derive(Clone, Copy, Debug, Default, PartialEq)] diff --git a/azalea/src/pathfinder/moves/basic.rs b/azalea/src/pathfinder/moves/basic.rs index 2814641c..b0df32e4 100644 --- a/azalea/src/pathfinder/moves/basic.rs +++ b/azalea/src/pathfinder/moves/basic.rs @@ -94,6 +94,9 @@ fn ascend_move(ctx: &mut PathfinderCtx, pos: RelBlockPos) { return; } + let base_cost = + f32::max(WALK_ONE_BLOCK_COST, *JUMP_ONE_BLOCK_COST) + JUMP_PENALTY + break_cost_1; + for dir in CardinalDirection::iter() { if let Some(stair_facing) = stair_facing { let expected_stair_facing = cardinal_direction_to_facing_property(dir); @@ -109,10 +112,7 @@ fn ascend_move(ctx: &mut PathfinderCtx, pos: RelBlockPos) { continue; } - let cost = f32::max(WALK_ONE_BLOCK_COST, *JUMP_ONE_BLOCK_COST) - + JUMP_PENALTY - + break_cost_1 - + break_cost_2; + let cost = base_cost + break_cost_2; ctx.edges.push(Edge { movement: astar::Movement { @@ -271,8 +271,8 @@ fn descend_move(ctx: &mut PathfinderCtx, pos: RelBlockPos) { FALL_N_BLOCKS_COST .get(fall_distance as usize) .copied() - // avoid panicking if we fall more than the size of FALL_N_BLOCKS_COST - // probably not possible but just in case + // this isn't possible because we already checked bounds on the fall distance, + // but it might be faster to default? .unwrap_or(f32::INFINITY), CENTER_AFTER_FALL_COST, ) diff --git a/azalea/src/pathfinder/tests.rs b/azalea/src/pathfinder/tests.rs index a630a5b1..a428b177 100644 --- a/azalea/src/pathfinder/tests.rs +++ b/azalea/src/pathfinder/tests.rs @@ -281,8 +281,6 @@ fn test_mine_through_non_colliding_block() { let mut simulation = setup_simulation_world( &mut partial_chunks, - // the pathfinder can't actually dig straight down, so we start a block to the side so - // it can descend correctly BlockPos::new(0, 72, 1), &[BlockPos::new(0, 71, 1)], &[ @@ -296,7 +294,7 @@ fn test_mine_through_non_colliding_block() { simulation.app.world_mut().write_message(GotoEvent { entity: simulation.entity, - goal: Arc::new(BlockPosGoal(BlockPos::new(0, 69, 0))), + goal: Arc::new(BlockPosGoal(BlockPos::new(0, 70, 0))), opts: PathfinderOpts::new() .min_timeout(PathfinderTimeout::Nodes(1_000_000)) .max_timeout(PathfinderTimeout::Nodes(5_000_000)), |
