Skip to content

Commit 958848e

Browse files
committed
improve some docs and apis related to pathfinder
1 parent 30cbeec commit 958848e

File tree

7 files changed

+232
-77
lines changed

7 files changed

+232
-77
lines changed

azalea-client/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ license = { workspace = true }
77
repository = { workspace = true }
88

99
[dependencies]
10-
anyhow = { workspace = true }
1110
azalea-auth = { path = "../azalea-auth", version = "0.11.0" }
1211
azalea-block = { path = "../azalea-block", version = "0.11.0" }
1312
azalea-buf = { path = "../azalea-buf", version = "0.11.0" }
@@ -36,6 +35,9 @@ tokio = { workspace = true, features = ["sync"] }
3635
tracing = { workspace = true }
3736
uuid = { workspace = true }
3837

38+
[dev-dependencies]
39+
anyhow = { workspace = true }
40+
3941
[features]
4042
default = ["log"]
4143
# enables bevy_log::LogPlugin by default

azalea-client/src/client.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,14 @@ impl Client {
518518
/// Get a component from this client. This will clone the component and
519519
/// return it.
520520
///
521+
///
522+
/// If the component can't be cloned, try [`Self::map_component`] instead.
523+
/// If it isn't guaranteed to be present, use [`Self::get_component`] or
524+
/// [`Self::map_get_component`].
525+
///
526+
/// You may also use [`Self::ecs`] and [`Self::query`] directly if you need
527+
/// more control over when the ECS is locked.
528+
///
521529
/// # Panics
522530
///
523531
/// This will panic if the component doesn't exist on the client.
@@ -534,10 +542,56 @@ impl Client {
534542
}
535543

536544
/// Get a component from this client, or `None` if it doesn't exist.
545+
///
546+
/// If the component can't be cloned, try [`Self::map_component`] instead.
547+
/// You may also have to use [`Self::ecs`] and [`Self::query`] directly.
537548
pub fn get_component<T: Component + Clone>(&self) -> Option<T> {
538549
self.query::<Option<&T>>(&mut self.ecs.lock()).cloned()
539550
}
540551

552+
/// Get a required component for this client and call the given function.
553+
///
554+
/// Similar to [`Self::component`], but doesn't clone the component since
555+
/// it's passed as a reference. [`Self::ecs`] will remain locked while the
556+
/// callback is being run.
557+
///
558+
/// If the component is not guaranteed to be present, use
559+
/// [`Self::get_component`] instead.
560+
///
561+
/// # Panics
562+
///
563+
/// This will panic if the component doesn't exist on the client.
564+
///
565+
/// ```
566+
/// # use azalea_client::{Client, Hunger};
567+
/// # fn example(bot: &Client) {
568+
/// let hunger = bot.map_component::<Hunger, _>(|h| h.food);
569+
/// # }
570+
/// ```
571+
pub fn map_component<T: Component, R>(&self, f: impl FnOnce(&T) -> R) -> R {
572+
let mut ecs = self.ecs.lock();
573+
let value = self.query::<&T>(&mut ecs);
574+
f(value)
575+
}
576+
577+
/// Optionally get a component for this client and call the given function.
578+
///
579+
/// Similar to [`Self::get_component`], but doesn't clone the component
580+
/// since it's passed as a reference. [`Self::ecs`] will remain locked
581+
/// while the callback is being run.
582+
///
583+
/// ```
584+
/// # use azalea_client::{Client, mining::Mining};
585+
/// # fn example(bot: &Client) {
586+
/// let is_mining = bot.map_get_component::<Mining, _>(|m| m.is_some());
587+
/// # }
588+
/// ```
589+
pub fn map_get_component<T: Component, R>(&self, f: impl FnOnce(Option<&T>) -> R) -> R {
590+
let mut ecs = self.ecs.lock();
591+
let value = self.query::<Option<&T>>(&mut ecs);
592+
f(value)
593+
}
594+
541595
/// Get an `RwLock` with a reference to our (potentially shared) world.
542596
///
543597
/// This gets the [`Instance`] from the client's [`InstanceHolder`]

azalea-client/src/events.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ pub enum Event {
5858
/// it's actually spawned. This can be useful for setting the client
5959
/// information with `Client::set_client_information`, so the packet
6060
/// doesn't have to be sent twice.
61+
///
62+
/// You may want to use [`Event::Login`] instead to wait for the bot to be
63+
/// in the world.
6164
Init,
6265
/// The client is now in the world. Fired when we receive a login packet.
6366
Login,

azalea-client/src/local_player.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ pub enum HandlePacketError {
144144
#[error(transparent)]
145145
Io(#[from] io::Error),
146146
#[error(transparent)]
147-
Other(#[from] anyhow::Error),
147+
Other(#[from] Box<dyn std::error::Error + Send + Sync>),
148148
#[error("{0}")]
149149
Send(#[from] mpsc::error::SendError<AzaleaEvent>),
150150
}

azalea/src/bot.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,11 @@ impl BotClientExt for azalea_client::Client {
104104
});
105105
}
106106

107+
/// Returns a Receiver that receives a message every game tick.
108+
///
109+
/// This is useful if you want to efficiently loop until a certain condition
110+
/// is met.
111+
///
107112
/// ```
108113
/// # use azalea::prelude::*;
109114
/// # use azalea::container::WaitingForInventoryOpen;

azalea/src/pathfinder/astar.rs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,24 @@ const COEFFICIENTS: [f32; 7] = [1.5, 2., 2.5, 3., 4., 5., 10.];
2323

2424
const MIN_IMPROVEMENT: f32 = 0.01;
2525

26+
pub enum PathfinderTimeout {
27+
/// Time out after a certain duration has passed. This is a good default so
28+
/// you don't waste too much time calculating a path if you're on a slow
29+
/// computer.
30+
Time(Duration),
31+
/// Time out after this many nodes have been considered.
32+
///
33+
/// This is useful as an alternative to a time limit if you're doing
34+
/// something like running tests where you want consistent results.
35+
Nodes(usize),
36+
}
37+
2638
pub fn a_star<P, M, HeuristicFn, SuccessorsFn, SuccessFn>(
2739
start: P,
2840
heuristic: HeuristicFn,
2941
mut successors: SuccessorsFn,
3042
success: SuccessFn,
31-
timeout: Duration,
43+
timeout: PathfinderTimeout,
3244
) -> Path<P, M>
3345
where
3446
P: Eq + Hash + Copy + Debug,
@@ -104,10 +116,16 @@ where
104116
}
105117

106118
// check for timeout every ~1ms
107-
if num_nodes % 1000 == 0 && start_time.elapsed() > timeout {
108-
// timeout, just return the best path we have so far
109-
trace!("A* couldn't find a path in time, returning best path");
110-
break;
119+
if num_nodes % 1000 == 0 {
120+
let timed_out = match timeout {
121+
PathfinderTimeout::Time(max_duration) => start_time.elapsed() > max_duration,
122+
PathfinderTimeout::Nodes(max_nodes) => num_nodes > max_nodes,
123+
};
124+
if timed_out {
125+
// timeout, just return the best path we have so far
126+
trace!("A* couldn't find a path in time, returning best path");
127+
break;
128+
}
111129
}
112130
}
113131

0 commit comments

Comments
 (0)