From bacb5c0199511272b1884fa59ccde88d09a73e10 Mon Sep 17 00:00:00 2001 From: mat Date: Sat, 24 May 2025 01:20:14 +0300 Subject: [PATCH] patch pathfinder path on cost increase --- azalea/src/pathfinder/astar.rs | 1 + azalea/src/pathfinder/debug.rs | 3 +- azalea/src/pathfinder/mod.rs | 106 +++++++++++++++++++++++---------- 3 files changed, 76 insertions(+), 34 deletions(-) diff --git a/azalea/src/pathfinder/astar.rs b/azalea/src/pathfinder/astar.rs index cd05d2ba..feddb57b 100644 --- a/azalea/src/pathfinder/astar.rs +++ b/azalea/src/pathfinder/astar.rs @@ -239,6 +239,7 @@ pub struct Node { pub g_score: f32, } +#[derive(Clone, Debug)] pub struct Edge { pub movement: Movement, pub cost: f32, diff --git a/azalea/src/pathfinder/debug.rs b/azalea/src/pathfinder/debug.rs index da27708d..b1cc966e 100644 --- a/azalea/src/pathfinder/debug.rs +++ b/azalea/src/pathfinder/debug.rs @@ -54,7 +54,8 @@ pub fn debug_render_path_with_particles( let chunks = &instance_holder.instance.read().chunks; let mut start = executing_path.last_reached_node; - for (i, movement) in executing_path.path.iter().enumerate() { + for (i, edge) in executing_path.path.iter().enumerate() { + let movement = &edge.movement; let end = movement.target; let start_vec3 = start.center(); diff --git a/azalea/src/pathfinder/mod.rs b/azalea/src/pathfinder/mod.rs index 820d6f8b..733a3603 100644 --- a/azalea/src/pathfinder/mod.rs +++ b/azalea/src/pathfinder/mod.rs @@ -19,7 +19,7 @@ use std::sync::atomic::{self, AtomicUsize}; use std::time::{Duration, Instant}; use std::{cmp, thread}; -use astar::PathfinderTimeout; +use astar::{Edge, PathfinderTimeout}; use azalea_client::inventory::{Inventory, InventorySet, SetSelectedHotbarSlotEvent}; use azalea_client::mining::{Mining, StartMiningBlockEvent}; use azalea_client::movement::MoveEventsSet; @@ -116,8 +116,8 @@ pub struct Pathfinder { /// pathfinder path. #[derive(Component, Clone)] pub struct ExecutingPath { - pub path: VecDeque>, - pub queued_path: Option>>, + pub path: VecDeque>, + pub queued_path: Option>>, pub last_reached_node: BlockPos, pub last_node_reached_at: Instant, pub is_path_partial: bool, @@ -161,7 +161,7 @@ pub struct GotoEvent { pub struct PathFoundEvent { pub entity: Entity, pub start: BlockPos, - pub path: Option>>, + pub path: Option>>, pub is_partial: bool, pub successors_fn: SuccessorsFn, pub allow_mining: bool, @@ -313,7 +313,12 @@ pub fn goto_listener( && let Some(final_node) = executing_path.path.back() { // if we're currently pathfinding and got a goto event, start a little ahead - executing_path.path.get(50).unwrap_or(final_node).target + executing_path + .path + .get(50) + .unwrap_or(final_node) + .movement + .target } else { BlockPos::from(position) }; @@ -448,13 +453,39 @@ pub fn calculate_path(opts: CalculatePathOpts) -> Option { } // replace the RelBlockPos types with BlockPos - let mapped_path = path - .into_iter() - .map(|movement| astar::Movement { - target: movement.target.apply(origin), - data: movement.data, - }) - .collect(); + // let mapped_path = path + // .into_iter() + // .map(|movement| astar::Movement { + // target: movement.target.apply(origin), + // data: movement.data, + // }) + // .collect(); + let mut mapped_path = VecDeque::with_capacity(path.len()); + let mut current_position = RelBlockPos::get_origin(origin); + for movement in path { + let mut found_edge = None; + for edge in successors(current_position) { + if edge.movement.target == movement.target { + found_edge = Some(edge); + break; + } + } + + let found_edge = found_edge.expect( + "path should always still be possible because we're using the same world cache", + ); + current_position = found_edge.movement.target; + + // we don't just copy the found_edge because we're using BlockPos instead of + // RelBlockPos as the target type + mapped_path.push_back(Edge { + movement: astar::Movement { + target: movement.target.apply(origin), + data: movement.data, + }, + cost: found_edge.cost, + }); + } Some(PathFoundEvent { entity: opts.entity, @@ -523,10 +554,14 @@ pub fn path_found_listener( }; if let Some(first_node_of_new_path) = path.front() { - let last_target_of_current_path = - RelBlockPos::from_origin(origin, last_node_of_current_path.target); - let first_target_of_new_path = - RelBlockPos::from_origin(origin, first_node_of_new_path.target); + let last_target_of_current_path = RelBlockPos::from_origin( + origin, + last_node_of_current_path.movement.target, + ); + let first_target_of_new_path = RelBlockPos::from_origin( + origin, + first_node_of_new_path.movement.target, + ); if successors(last_target_of_current_path) .iter() @@ -654,7 +689,7 @@ pub fn check_node_reached( // we don't unnecessarily execute a movement when it wasn't necessary // see if we already reached any future nodes and can skip ahead - for (i, movement) in executing_path + for (i, edge) in executing_path .path .clone() .into_iter() @@ -662,6 +697,7 @@ pub fn check_node_reached( .take(20) .rev() { + let movement = edge.movement; let is_reached_ctx = IsReachedCtx { target: movement.target, start: executing_path.last_reached_node, @@ -825,10 +861,12 @@ fn patch_path( let patch_start = if *patch_nodes.start() == 0 { executing_path.last_reached_node } else { - executing_path.path[*patch_nodes.start() - 1].target + executing_path.path[*patch_nodes.start() - 1] + .movement + .target }; - let patch_end = executing_path.path[*patch_nodes.end()].target; + let patch_end = executing_path.path[*patch_nodes.end()].movement.target; // this doesn't override the main goal, it's just the goal for this A* // calculation @@ -996,10 +1034,10 @@ pub fn tick_execute_path( for (entity, executing_path, position, physics, mining, instance_holder, inventory_component) in &mut query { - if let Some(movement) = executing_path.path.front() { + if let Some(edge) = executing_path.path.front() { let ctx = ExecuteCtx { entity, - target: movement.target, + target: edge.movement.target, position: **position, start: executing_path.last_reached_node, physics, @@ -1018,7 +1056,7 @@ pub fn tick_execute_path( "executing move, position: {}, last_reached_node: {}", **position, executing_path.last_reached_node ); - (movement.data.execute)(ctx); + (edge.movement.data.execute)(ctx); } } } @@ -1110,26 +1148,28 @@ pub fn stop_pathfinding_on_instance_change( pub fn check_path_obstructed( origin: BlockPos, mut current_position: RelBlockPos, - path: &VecDeque>, + path: &VecDeque>, successors_fn: SuccessorsFn, ) -> Option where SuccessorsFn: Fn(RelBlockPos) -> Vec>, { - for (i, movement) in path.iter().enumerate() { - let movement_target = RelBlockPos::from_origin(origin, movement.target); + for (i, edge) in path.iter().enumerate() { + let movement_target = RelBlockPos::from_origin(origin, edge.movement.target); - let mut found_obstruction = false; - for edge in successors_fn(current_position) { - if edge.movement.target == movement_target { - current_position = movement_target; - found_obstruction = false; + let mut found_edge = None; + for candidate_edge in successors_fn(current_position) { + if candidate_edge.movement.target == movement_target { + found_edge = Some(candidate_edge); break; - } else { - found_obstruction = true; } } - if found_obstruction { + + if let Some(found_edge) = found_edge + && found_edge.cost <= edge.cost + { + current_position = found_edge.movement.target; + } else { return Some(i); } }