mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 23:44:38 +00:00
add CustomPathfinderState
This commit is contained in:
parent
1d3a7c969f
commit
99659bd9a3
6 changed files with 168 additions and 59 deletions
|
@ -110,11 +110,7 @@ impl Client {
|
||||||
|
|
||||||
pub trait EntityPredicate<Q: QueryData, Filter: QueryFilter> {
|
pub trait EntityPredicate<Q: QueryData, Filter: QueryFilter> {
|
||||||
fn find(&self, ecs_lock: Arc<Mutex<World>>, instance_name: &InstanceName) -> Option<Entity>;
|
fn find(&self, ecs_lock: Arc<Mutex<World>>, instance_name: &InstanceName) -> Option<Entity>;
|
||||||
fn find_all<'a>(
|
fn find_all(&self, ecs_lock: Arc<Mutex<World>>, instance_name: &InstanceName) -> Vec<Entity>;
|
||||||
&'a self,
|
|
||||||
ecs_lock: Arc<Mutex<World>>,
|
|
||||||
instance_name: &InstanceName,
|
|
||||||
) -> Vec<Entity>;
|
|
||||||
}
|
}
|
||||||
impl<F, Q: QueryData, Filter: QueryFilter> EntityPredicate<Q, Filter> for F
|
impl<F, Q: QueryData, Filter: QueryFilter> EntityPredicate<Q, Filter> for F
|
||||||
where
|
where
|
||||||
|
@ -129,11 +125,7 @@ where
|
||||||
.map(|(e, _, _)| e)
|
.map(|(e, _, _)| e)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_all<'a>(
|
fn find_all(&self, ecs_lock: Arc<Mutex<World>>, instance_name: &InstanceName) -> Vec<Entity> {
|
||||||
&'a self,
|
|
||||||
ecs_lock: Arc<Mutex<World>>,
|
|
||||||
instance_name: &InstanceName,
|
|
||||||
) -> Vec<Entity> {
|
|
||||||
let mut ecs = ecs_lock.lock();
|
let mut ecs = ecs_lock.lock();
|
||||||
let mut query = ecs.query_filtered::<(Entity, &InstanceName, Q), Filter>();
|
let mut query = ecs.query_filtered::<(Entity, &InstanceName, Q), Filter>();
|
||||||
query
|
query
|
||||||
|
|
|
@ -221,7 +221,12 @@ where
|
||||||
best_successor = Some(successor);
|
best_successor = Some(successor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let found_successor = best_successor.expect("No successor found");
|
let Some(found_successor) = best_successor else {
|
||||||
|
warn!(
|
||||||
|
"a successor stopped being possible while reconstructing the path, returning empty path"
|
||||||
|
);
|
||||||
|
return vec![];
|
||||||
|
};
|
||||||
|
|
||||||
path.push(Movement {
|
path.push(Movement {
|
||||||
target: node_position,
|
target: node_position,
|
||||||
|
|
46
azalea/src/pathfinder/custom_state.rs
Normal file
46
azalea/src/pathfinder/custom_state.rs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
use std::{
|
||||||
|
any::{Any, TypeId},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
|
use bevy_ecs::component::Component;
|
||||||
|
use parking_lot::RwLock;
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
|
/// The component that holds the custom pathfinder state for one of our bots.
|
||||||
|
///
|
||||||
|
/// See [`CustomPathfinderStateRef`] for more information about the inner type.
|
||||||
|
///
|
||||||
|
/// Azalea won't automatically insert this component, so if you're trying to use
|
||||||
|
/// it then you should also have logic to insert the component if it's not
|
||||||
|
/// present.
|
||||||
|
///
|
||||||
|
/// Be aware that a read lock is held on the `RwLock` while a path is being
|
||||||
|
/// calculated, which may take up to several seconds. For this reason, it may be
|
||||||
|
/// favorable to use [`RwLock::try_write`] instead of [`RwLock::write`] when
|
||||||
|
/// updating it to avoid blocking the current thread.
|
||||||
|
#[derive(Clone, Component, Default)]
|
||||||
|
pub struct CustomPathfinderState(pub Arc<RwLock<CustomPathfinderStateRef>>);
|
||||||
|
|
||||||
|
/// Arbitrary state that's passed to the pathfinder, intended to be used for
|
||||||
|
/// custom moves that need to access things that are usually inaccessible.
|
||||||
|
///
|
||||||
|
/// This is included in [`PathfinderCtx`].
|
||||||
|
///
|
||||||
|
/// [`PathfinderCtx`]: crate::pathfinder::PathfinderCtx
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct CustomPathfinderStateRef {
|
||||||
|
map: FxHashMap<TypeId, Box<dyn Any + Send + Sync>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CustomPathfinderStateRef {
|
||||||
|
pub fn insert<T: 'static + Send + Sync>(&mut self, t: T) {
|
||||||
|
self.map.insert(TypeId::of::<T>(), Box::new(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get<T: 'static + Send + Sync>(&self) -> Option<&T> {
|
||||||
|
self.map
|
||||||
|
.get(&TypeId::of::<T>())
|
||||||
|
.map(|value| value.downcast_ref().unwrap())
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,8 +5,12 @@ use bevy_ecs::prelude::*;
|
||||||
use super::ExecutingPath;
|
use super::ExecutingPath;
|
||||||
|
|
||||||
/// A component that makes bots run /particle commands while pathfinding to show
|
/// A component that makes bots run /particle commands while pathfinding to show
|
||||||
/// where they're going. This requires the bots to have server operator
|
/// where they're going.
|
||||||
/// permissions, and it'll make them spam *a lot* of commands.
|
///
|
||||||
|
/// This requires the bots to have server operator permissions, and it'll make
|
||||||
|
/// them spam *a lot* of commands. You may want to run `/gamerule
|
||||||
|
/// sendCommandFeedback false` to hide the "Displaying particle minecraft:dust"
|
||||||
|
/// spam.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use azalea::prelude::*;
|
/// # use azalea::prelude::*;
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
pub mod astar;
|
pub mod astar;
|
||||||
pub mod costs;
|
pub mod costs;
|
||||||
|
pub mod custom_state;
|
||||||
pub mod debug;
|
pub mod debug;
|
||||||
pub mod goals;
|
pub mod goals;
|
||||||
pub mod mining;
|
pub mod mining;
|
||||||
|
@ -38,6 +39,7 @@ use azalea_world::{InstanceContainer, InstanceName};
|
||||||
use bevy_app::{PreUpdate, Update};
|
use bevy_app::{PreUpdate, Update};
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
use bevy_tasks::{AsyncComputeTaskPool, Task};
|
use bevy_tasks::{AsyncComputeTaskPool, Task};
|
||||||
|
use custom_state::{CustomPathfinderState, CustomPathfinderStateRef};
|
||||||
use futures_lite::future;
|
use futures_lite::future;
|
||||||
use goals::BlockPosGoal;
|
use goals::BlockPosGoal;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
|
@ -280,6 +282,7 @@ impl PathfinderClientExt for azalea_client::Client {
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct ComputePath(Task<Option<PathFoundEvent>>);
|
pub struct ComputePath(Task<Option<PathFoundEvent>>);
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
pub fn goto_listener(
|
pub fn goto_listener(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut events: EventReader<GotoEvent>,
|
mut events: EventReader<GotoEvent>,
|
||||||
|
@ -289,13 +292,14 @@ pub fn goto_listener(
|
||||||
&Position,
|
&Position,
|
||||||
&InstanceName,
|
&InstanceName,
|
||||||
&Inventory,
|
&Inventory,
|
||||||
|
Option<&CustomPathfinderState>,
|
||||||
)>,
|
)>,
|
||||||
instance_container: Res<InstanceContainer>,
|
instance_container: Res<InstanceContainer>,
|
||||||
) {
|
) {
|
||||||
let thread_pool = AsyncComputeTaskPool::get();
|
let thread_pool = AsyncComputeTaskPool::get();
|
||||||
|
|
||||||
for event in events.read() {
|
for event in events.read() {
|
||||||
let Ok((mut pathfinder, executing_path, position, instance_name, inventory)) =
|
let Ok((mut pathfinder, executing_path, position, instance_name, inventory, custom_state)) =
|
||||||
query.get_mut(event.entity)
|
query.get_mut(event.entity)
|
||||||
else {
|
else {
|
||||||
warn!("got goto event for an entity that can't pathfind");
|
warn!("got goto event for an entity that can't pathfind");
|
||||||
|
@ -361,6 +365,8 @@ pub fn goto_listener(
|
||||||
None
|
None
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let custom_state = custom_state.cloned().unwrap_or_default();
|
||||||
|
|
||||||
let min_timeout = event.min_timeout;
|
let min_timeout = event.min_timeout;
|
||||||
let max_timeout = event.max_timeout;
|
let max_timeout = event.max_timeout;
|
||||||
|
|
||||||
|
@ -374,6 +380,7 @@ pub fn goto_listener(
|
||||||
goto_id_atomic,
|
goto_id_atomic,
|
||||||
allow_mining,
|
allow_mining,
|
||||||
mining_cache,
|
mining_cache,
|
||||||
|
custom_state,
|
||||||
min_timeout,
|
min_timeout,
|
||||||
max_timeout,
|
max_timeout,
|
||||||
})
|
})
|
||||||
|
@ -392,6 +399,7 @@ pub struct CalculatePathOpts {
|
||||||
pub goto_id_atomic: Arc<AtomicUsize>,
|
pub goto_id_atomic: Arc<AtomicUsize>,
|
||||||
pub allow_mining: bool,
|
pub allow_mining: bool,
|
||||||
pub mining_cache: MiningCache,
|
pub mining_cache: MiningCache,
|
||||||
|
pub custom_state: CustomPathfinderState,
|
||||||
/// Also see [`GotoEvent::min_timeout`].
|
/// Also see [`GotoEvent::min_timeout`].
|
||||||
pub min_timeout: PathfinderTimeout,
|
pub min_timeout: PathfinderTimeout,
|
||||||
pub max_timeout: PathfinderTimeout,
|
pub max_timeout: PathfinderTimeout,
|
||||||
|
@ -413,7 +421,13 @@ pub fn calculate_path(opts: CalculatePathOpts) -> Option<PathFoundEvent> {
|
||||||
let origin = opts.start;
|
let origin = opts.start;
|
||||||
let cached_world = CachedWorld::new(opts.world_lock, origin);
|
let cached_world = CachedWorld::new(opts.world_lock, origin);
|
||||||
let successors = |pos: RelBlockPos| {
|
let successors = |pos: RelBlockPos| {
|
||||||
call_successors_fn(&cached_world, &opts.mining_cache, opts.successors_fn, pos)
|
call_successors_fn(
|
||||||
|
&cached_world,
|
||||||
|
&opts.mining_cache,
|
||||||
|
&opts.custom_state.0.read(),
|
||||||
|
opts.successors_fn,
|
||||||
|
pos,
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
|
@ -518,6 +532,7 @@ pub fn handle_tasks(
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the path for the target entity when we get the PathFoundEvent
|
// set the path for the target entity when we get the PathFoundEvent
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
pub fn path_found_listener(
|
pub fn path_found_listener(
|
||||||
mut events: EventReader<PathFoundEvent>,
|
mut events: EventReader<PathFoundEvent>,
|
||||||
mut query: Query<(
|
mut query: Query<(
|
||||||
|
@ -525,12 +540,13 @@ pub fn path_found_listener(
|
||||||
Option<&mut ExecutingPath>,
|
Option<&mut ExecutingPath>,
|
||||||
&InstanceName,
|
&InstanceName,
|
||||||
&Inventory,
|
&Inventory,
|
||||||
|
Option<&CustomPathfinderState>,
|
||||||
)>,
|
)>,
|
||||||
instance_container: Res<InstanceContainer>,
|
instance_container: Res<InstanceContainer>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
for event in events.read() {
|
for event in events.read() {
|
||||||
let (mut pathfinder, executing_path, instance_name, inventory) = query
|
let (mut pathfinder, executing_path, instance_name, inventory, custom_state) = query
|
||||||
.get_mut(event.entity)
|
.get_mut(event.entity)
|
||||||
.expect("Path found for an entity that doesn't have a pathfinder");
|
.expect("Path found for an entity that doesn't have a pathfinder");
|
||||||
if let Some(path) = &event.path {
|
if let Some(path) = &event.path {
|
||||||
|
@ -551,8 +567,16 @@ pub fn path_found_listener(
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
});
|
});
|
||||||
|
let custom_state = custom_state.cloned().unwrap_or_default();
|
||||||
|
let custom_state_ref = custom_state.0.read();
|
||||||
let successors = |pos: RelBlockPos| {
|
let successors = |pos: RelBlockPos| {
|
||||||
call_successors_fn(&cached_world, &mining_cache, successors_fn, pos)
|
call_successors_fn(
|
||||||
|
&cached_world,
|
||||||
|
&mining_cache,
|
||||||
|
&custom_state_ref,
|
||||||
|
successors_fn,
|
||||||
|
pos,
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(first_node_of_new_path) = path.front() {
|
if let Some(first_node_of_new_path) = path.front() {
|
||||||
|
@ -626,11 +650,20 @@ pub fn timeout_movement(
|
||||||
Option<&Mining>,
|
Option<&Mining>,
|
||||||
&InstanceName,
|
&InstanceName,
|
||||||
&Inventory,
|
&Inventory,
|
||||||
|
Option<&CustomPathfinderState>,
|
||||||
)>,
|
)>,
|
||||||
instance_container: Res<InstanceContainer>,
|
instance_container: Res<InstanceContainer>,
|
||||||
) {
|
) {
|
||||||
for (entity, mut pathfinder, mut executing_path, position, mining, instance_name, inventory) in
|
for (
|
||||||
&mut query
|
entity,
|
||||||
|
mut pathfinder,
|
||||||
|
mut executing_path,
|
||||||
|
position,
|
||||||
|
mining,
|
||||||
|
instance_name,
|
||||||
|
inventory,
|
||||||
|
custom_state,
|
||||||
|
) in &mut query
|
||||||
{
|
{
|
||||||
// don't timeout if we're mining
|
// don't timeout if we're mining
|
||||||
if let Some(mining) = mining {
|
if let Some(mining) = mining {
|
||||||
|
@ -656,6 +689,8 @@ pub fn timeout_movement(
|
||||||
.expect("Entity tried to pathfind but the entity isn't in a valid world");
|
.expect("Entity tried to pathfind but the entity isn't in a valid world");
|
||||||
let successors_fn: moves::SuccessorsFn = pathfinder.successors_fn.unwrap();
|
let successors_fn: moves::SuccessorsFn = pathfinder.successors_fn.unwrap();
|
||||||
|
|
||||||
|
let custom_state = custom_state.cloned().unwrap_or_default();
|
||||||
|
|
||||||
// try to fix the path without recalculating everything.
|
// try to fix the path without recalculating everything.
|
||||||
// (though, it'll still get fully recalculated by `recalculate_near_end_of_path`
|
// (though, it'll still get fully recalculated by `recalculate_near_end_of_path`
|
||||||
// if the new path is too short)
|
// if the new path is too short)
|
||||||
|
@ -667,6 +702,7 @@ pub fn timeout_movement(
|
||||||
entity,
|
entity,
|
||||||
successors_fn,
|
successors_fn,
|
||||||
world_lock,
|
world_lock,
|
||||||
|
custom_state,
|
||||||
);
|
);
|
||||||
// reset last_node_reached_at so we don't immediately try to patch again
|
// reset last_node_reached_at so we don't immediately try to patch again
|
||||||
executing_path.last_node_reached_at = Instant::now();
|
executing_path.last_node_reached_at = Instant::now();
|
||||||
|
@ -770,6 +806,7 @@ pub fn check_node_reached(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
pub fn check_for_path_obstruction(
|
pub fn check_for_path_obstruction(
|
||||||
mut query: Query<(
|
mut query: Query<(
|
||||||
Entity,
|
Entity,
|
||||||
|
@ -777,10 +814,13 @@ pub fn check_for_path_obstruction(
|
||||||
&mut ExecutingPath,
|
&mut ExecutingPath,
|
||||||
&InstanceName,
|
&InstanceName,
|
||||||
&Inventory,
|
&Inventory,
|
||||||
|
Option<&CustomPathfinderState>,
|
||||||
)>,
|
)>,
|
||||||
instance_container: Res<InstanceContainer>,
|
instance_container: Res<InstanceContainer>,
|
||||||
) {
|
) {
|
||||||
for (entity, mut pathfinder, mut executing_path, instance_name, inventory) in &mut query {
|
for (entity, mut pathfinder, mut executing_path, instance_name, inventory, custom_state) in
|
||||||
|
&mut query
|
||||||
|
{
|
||||||
let Some(successors_fn) = pathfinder.successors_fn else {
|
let Some(successors_fn) = pathfinder.successors_fn else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
@ -797,52 +837,66 @@ pub fn check_for_path_obstruction(
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
});
|
});
|
||||||
let successors =
|
let custom_state = custom_state.cloned().unwrap_or_default();
|
||||||
|pos: RelBlockPos| call_successors_fn(&cached_world, &mining_cache, successors_fn, pos);
|
let custom_state_ref = custom_state.0.read();
|
||||||
|
let successors = |pos: RelBlockPos| {
|
||||||
|
call_successors_fn(
|
||||||
|
&cached_world,
|
||||||
|
&mining_cache,
|
||||||
|
&custom_state_ref,
|
||||||
|
successors_fn,
|
||||||
|
pos,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(obstructed_index) = check_path_obstructed(
|
let Some(obstructed_index) = check_path_obstructed(
|
||||||
origin,
|
origin,
|
||||||
RelBlockPos::from_origin(origin, executing_path.last_reached_node),
|
RelBlockPos::from_origin(origin, executing_path.last_reached_node),
|
||||||
&executing_path.path,
|
&executing_path.path,
|
||||||
successors,
|
successors,
|
||||||
) {
|
) else {
|
||||||
warn!(
|
continue;
|
||||||
"path obstructed at index {obstructed_index} (starting at {:?}, path: {:?})",
|
};
|
||||||
executing_path.last_reached_node, executing_path.path
|
|
||||||
);
|
drop(custom_state_ref);
|
||||||
// if it's near the end, don't bother recalculating a patch, just truncate and
|
|
||||||
// mark it as partial
|
warn!(
|
||||||
if obstructed_index + 5 > executing_path.path.len() {
|
"path obstructed at index {obstructed_index} (starting at {:?}, path: {:?})",
|
||||||
debug!(
|
executing_path.last_reached_node, executing_path.path
|
||||||
"obstruction is near the end of the path, truncating and marking path as partial"
|
);
|
||||||
);
|
// if it's near the end, don't bother recalculating a patch, just truncate and
|
||||||
executing_path.path.truncate(obstructed_index);
|
// mark it as partial
|
||||||
executing_path.is_path_partial = true;
|
if obstructed_index + 5 > executing_path.path.len() {
|
||||||
continue;
|
debug!(
|
||||||
}
|
"obstruction is near the end of the path, truncating and marking path as partial"
|
||||||
|
|
||||||
let Some(successors_fn) = pathfinder.successors_fn else {
|
|
||||||
error!("got PatchExecutingPathEvent but the bot has no successors_fn");
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
let world_lock = instance_container
|
|
||||||
.get(instance_name)
|
|
||||||
.expect("Entity tried to pathfind but the entity isn't in a valid world");
|
|
||||||
|
|
||||||
// patch up to 20 nodes
|
|
||||||
let patch_end_index = cmp::min(obstructed_index + 20, executing_path.path.len() - 1);
|
|
||||||
|
|
||||||
patch_path(
|
|
||||||
obstructed_index..=patch_end_index,
|
|
||||||
&mut executing_path,
|
|
||||||
&mut pathfinder,
|
|
||||||
inventory,
|
|
||||||
entity,
|
|
||||||
successors_fn,
|
|
||||||
world_lock,
|
|
||||||
);
|
);
|
||||||
|
executing_path.path.truncate(obstructed_index);
|
||||||
|
executing_path.is_path_partial = true;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let Some(successors_fn) = pathfinder.successors_fn else {
|
||||||
|
error!("got PatchExecutingPathEvent but the bot has no successors_fn");
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let world_lock = instance_container
|
||||||
|
.get(instance_name)
|
||||||
|
.expect("Entity tried to pathfind but the entity isn't in a valid world");
|
||||||
|
|
||||||
|
// patch up to 20 nodes
|
||||||
|
let patch_end_index = cmp::min(obstructed_index + 20, executing_path.path.len() - 1);
|
||||||
|
|
||||||
|
patch_path(
|
||||||
|
obstructed_index..=patch_end_index,
|
||||||
|
&mut executing_path,
|
||||||
|
&mut pathfinder,
|
||||||
|
inventory,
|
||||||
|
entity,
|
||||||
|
successors_fn,
|
||||||
|
world_lock,
|
||||||
|
custom_state.clone(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -851,6 +905,7 @@ pub fn check_for_path_obstruction(
|
||||||
///
|
///
|
||||||
/// You should avoid making the range too large, since the timeout for the A*
|
/// You should avoid making the range too large, since the timeout for the A*
|
||||||
/// calculation is very low. About 20 nodes is a good amount.
|
/// calculation is very low. About 20 nodes is a good amount.
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn patch_path(
|
fn patch_path(
|
||||||
patch_nodes: RangeInclusive<usize>,
|
patch_nodes: RangeInclusive<usize>,
|
||||||
executing_path: &mut ExecutingPath,
|
executing_path: &mut ExecutingPath,
|
||||||
|
@ -859,6 +914,7 @@ fn patch_path(
|
||||||
entity: Entity,
|
entity: Entity,
|
||||||
successors_fn: SuccessorsFn,
|
successors_fn: SuccessorsFn,
|
||||||
world_lock: Arc<RwLock<azalea_world::Instance>>,
|
world_lock: Arc<RwLock<azalea_world::Instance>>,
|
||||||
|
custom_state: CustomPathfinderState,
|
||||||
) {
|
) {
|
||||||
let patch_start = if *patch_nodes.start() == 0 {
|
let patch_start = if *patch_nodes.start() == 0 {
|
||||||
executing_path.last_reached_node
|
executing_path.last_reached_node
|
||||||
|
@ -893,6 +949,7 @@ fn patch_path(
|
||||||
goto_id_atomic,
|
goto_id_atomic,
|
||||||
allow_mining,
|
allow_mining,
|
||||||
mining_cache,
|
mining_cache,
|
||||||
|
custom_state,
|
||||||
min_timeout: PathfinderTimeout::Nodes(10_000),
|
min_timeout: PathfinderTimeout::Nodes(10_000),
|
||||||
max_timeout: PathfinderTimeout::Nodes(10_000),
|
max_timeout: PathfinderTimeout::Nodes(10_000),
|
||||||
});
|
});
|
||||||
|
@ -1181,6 +1238,7 @@ where
|
||||||
pub fn call_successors_fn(
|
pub fn call_successors_fn(
|
||||||
cached_world: &CachedWorld,
|
cached_world: &CachedWorld,
|
||||||
mining_cache: &MiningCache,
|
mining_cache: &MiningCache,
|
||||||
|
custom_state: &CustomPathfinderStateRef,
|
||||||
successors_fn: SuccessorsFn,
|
successors_fn: SuccessorsFn,
|
||||||
pos: RelBlockPos,
|
pos: RelBlockPos,
|
||||||
) -> Vec<astar::Edge<RelBlockPos, moves::MoveData>> {
|
) -> Vec<astar::Edge<RelBlockPos, moves::MoveData>> {
|
||||||
|
@ -1189,6 +1247,7 @@ pub fn call_successors_fn(
|
||||||
edges: &mut edges,
|
edges: &mut edges,
|
||||||
world: cached_world,
|
world: cached_world,
|
||||||
mining_cache,
|
mining_cache,
|
||||||
|
custom_state,
|
||||||
};
|
};
|
||||||
successors_fn(&mut ctx, pos);
|
successors_fn(&mut ctx, pos);
|
||||||
edges
|
edges
|
||||||
|
|
|
@ -19,6 +19,7 @@ use parking_lot::RwLock;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
astar,
|
astar,
|
||||||
|
custom_state::CustomPathfinderStateRef,
|
||||||
mining::MiningCache,
|
mining::MiningCache,
|
||||||
rel_block_pos::RelBlockPos,
|
rel_block_pos::RelBlockPos,
|
||||||
world::{CachedWorld, is_block_state_passable},
|
world::{CachedWorld, is_block_state_passable},
|
||||||
|
@ -222,4 +223,6 @@ pub struct PathfinderCtx<'a> {
|
||||||
pub edges: &'a mut Vec<Edge>,
|
pub edges: &'a mut Vec<Edge>,
|
||||||
pub world: &'a CachedWorld,
|
pub world: &'a CachedWorld,
|
||||||
pub mining_cache: &'a MiningCache,
|
pub mining_cache: &'a MiningCache,
|
||||||
|
|
||||||
|
pub custom_state: &'a CustomPathfinderStateRef,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue