azalea_client/entity_query.rs
1use std::{any, sync::Arc};
2
3use azalea_core::position::Vec3;
4use azalea_entity::Position;
5use azalea_world::InstanceName;
6use bevy_ecs::{
7 component::Component,
8 entity::Entity,
9 query::{QueryData, QueryEntityError, QueryFilter, QueryItem, ROQueryItem},
10 world::World,
11};
12use parking_lot::Mutex;
13
14use crate::Client;
15
16impl Client {
17 /// A convenience function for getting components from our client's entity.
18 ///
19 /// To query another entity, you can use [`Self::query_entity`].
20 ///
21 /// # Examples
22 /// ```
23 /// # use azalea_world::InstanceName;
24 /// # fn example(mut client: azalea_client::Client) {
25 /// let is_logged_in = client.query_self::<Option<&InstanceName>, _>(|ins| ins.is_some());
26 /// # }
27 /// ```
28 ///
29 /// # Panics
30 ///
31 /// This will panic if the component doesn't exist on the client.
32 pub fn query_self<D: QueryData, R>(&self, f: impl FnOnce(QueryItem<D>) -> R) -> R {
33 let mut ecs = self.ecs.lock();
34 let mut qs = ecs.query::<D>();
35 let res = qs.get_mut(&mut ecs, self.entity).unwrap_or_else(|_| {
36 panic!(
37 "Our client is missing a required component {:?}",
38 any::type_name::<D>()
39 )
40 });
41 f(res)
42 }
43
44 /// A convenience function for getting components from any entity.
45 ///
46 /// If you're querying the client, you should use [`Self::query_self`].
47 ///
48 /// # Panics
49 ///
50 /// This will panic if the entity doesn't exist or if the query isn't valid
51 /// for the entity. For a non-panicking version, you may use
52 /// [`Self::try_query_entity`].
53 pub fn query_entity<D: QueryData, R>(
54 &self,
55 entity: Entity,
56 f: impl FnOnce(QueryItem<D>) -> R,
57 ) -> R {
58 self.try_query_entity(entity, f).unwrap_or_else(|_| {
59 panic!(
60 "Entity is missing a required component {:?}",
61 any::type_name::<D>()
62 )
63 })
64 }
65
66 /// A convenience function for getting components from any entity, or None
67 /// if the query fails.
68 ///
69 /// If you're sure that the entity exists and that the query will succeed,
70 /// you can use [`Self::query_entity`].
71 pub fn try_query_entity<D: QueryData, R>(
72 &self,
73 entity: Entity,
74 f: impl FnOnce(QueryItem<D>) -> R,
75 ) -> Result<R, QueryEntityError> {
76 let mut ecs = self.ecs.lock();
77 let mut qs = ecs.query::<D>();
78 qs.get_mut(&mut ecs, entity).map(f)
79 }
80
81 /// Quickly returns a lightweight [`Entity`] for an arbitrary entity that
82 /// matches the given predicate function that is in the same
83 /// [`Instance`] as the client.
84 ///
85 /// You can then use [`Self::entity_component`] to get components from this
86 /// entity.
87 ///
88 /// If you want to find the nearest entity, consider using
89 /// [`Self::nearest_entity_by`] instead. If you want to find all entities
90 /// that match the predicate, use [`Self::nearest_entities_by`].
91 ///
92 /// # Example
93 /// ```
94 /// use azalea_client::{Client, player::GameProfileComponent};
95 /// use azalea_entity::{Position, metadata::Player};
96 /// use bevy_ecs::query::With;
97 ///
98 /// # fn example(mut bot: Client, sender_name: String) {
99 /// let entity = bot.any_entity_by::<&GameProfileComponent, With<Player>>(
100 /// |profile: &GameProfileComponent| profile.name == sender_name,
101 /// );
102 /// if let Some(entity) = entity {
103 /// let position = bot.entity_component::<Position>(entity);
104 /// // ...
105 /// }
106 /// # }
107 /// ```
108 ///
109 /// [`Entity`]: bevy_ecs::entity::Entity
110 /// [`Instance`]: azalea_world::Instance
111 pub fn any_entity_by<Q: QueryData, F: QueryFilter>(
112 &self,
113 predicate: impl EntityPredicate<Q, F>,
114 ) -> Option<Entity> {
115 let instance_name = self.get_component::<InstanceName>()?;
116 predicate.find_any(self.ecs.clone(), &instance_name)
117 }
118
119 /// Return a lightweight [`Entity`] for the nearest entity that matches the
120 /// given predicate function.
121 ///
122 /// You can then use [`Self::entity_component`] to get components from this
123 /// entity.
124 ///
125 /// If you don't need the entity to be the nearest one, it may be more
126 /// efficient to use [`Self::any_entity_by`] instead. You can also use
127 /// [`Self::nearest_entities_by`] to get all nearby entities.
128 ///
129 /// ```
130 /// use azalea_entity::{LocalEntity, Position, metadata::Player};
131 /// use bevy_ecs::query::{With, Without};
132 ///
133 /// # fn example(mut bot: azalea_client::Client, sender_name: String) {
134 /// // get the position of the nearest player
135 /// if let Some(nearest_player) =
136 /// bot.nearest_entity_by::<(), (With<Player>, Without<LocalEntity>)>(|_: ()| true)
137 /// {
138 /// let nearest_player_pos = *bot.entity_component::<Position>(nearest_player);
139 /// bot.chat(format!("You are at {nearest_player_pos}"));
140 /// }
141 /// # }
142 /// ```
143 ///
144 /// [`Entity`]: bevy_ecs::entity::Entity
145 pub fn nearest_entity_by<Q: QueryData, F: QueryFilter>(
146 &self,
147 predicate: impl EntityPredicate<Q, F>,
148 ) -> Option<Entity> {
149 self.nearest_entities_by(predicate).first().copied()
150 }
151
152 /// Similar to [`Self::nearest_entity_by`] but returns a `Vec<Entity>` of
153 /// all entities in our instance that match the predicate.
154 ///
155 /// The first entity is the nearest one.
156 ///
157 /// ```
158 /// # use azalea_entity::{LocalEntity, Position, metadata::Player};
159 /// # use bevy_ecs::query::{With, Without};
160 /// # fn example(mut bot: azalea_client::Client, sender_name: String) {
161 /// let nearby_players =
162 /// bot.nearest_entities_by::<(), (With<Player>, Without<LocalEntity>)>(|_: ()| true);
163 /// # }
164 /// ```
165 pub fn nearest_entities_by<Q: QueryData, F: QueryFilter>(
166 &self,
167 predicate: impl EntityPredicate<Q, F>,
168 ) -> Vec<Entity> {
169 let Some(instance_name) = self.get_component::<InstanceName>() else {
170 return vec![];
171 };
172 let Some(position) = self.get_component::<Position>() else {
173 return vec![];
174 };
175 predicate.find_all_sorted(self.ecs.clone(), &instance_name, (&position).into())
176 }
177
178 /// Get a component from an entity.
179 ///
180 /// Note that this will return an owned type (i.e. not a reference) so it
181 /// may be expensive for larger types.
182 ///
183 /// If you're trying to get a component for this client, use
184 /// [`Self::component`].
185 pub fn entity_component<Q: Component + Clone>(&self, entity: Entity) -> Q {
186 let mut ecs = self.ecs.lock();
187 let mut q = ecs.query::<&Q>();
188 let components = q.get(&ecs, entity).unwrap_or_else(|_| {
189 panic!(
190 "Entity is missing a required component {:?}",
191 any::type_name::<Q>()
192 )
193 });
194 components.clone()
195 }
196
197 /// Get a component from an entity, if it exists.
198 ///
199 /// This is similar to [`Self::entity_component`] but returns an `Option`
200 /// instead of panicking if the component isn't present.
201 pub fn get_entity_component<Q: Component + Clone>(&self, entity: Entity) -> Option<Q> {
202 let mut ecs = self.ecs.lock();
203 let mut q = ecs.query::<&Q>();
204 let components = q.get(&ecs, entity).ok();
205 components.cloned()
206 }
207}
208
209pub trait EntityPredicate<Q: QueryData, Filter: QueryFilter> {
210 fn find_any(&self, ecs_lock: Arc<Mutex<World>>, instance_name: &InstanceName)
211 -> Option<Entity>;
212 fn find_all_sorted(
213 &self,
214 ecs_lock: Arc<Mutex<World>>,
215 instance_name: &InstanceName,
216 nearest_to: Vec3,
217 ) -> Vec<Entity>;
218}
219impl<F, Q: QueryData, Filter: QueryFilter> EntityPredicate<Q, Filter> for F
220where
221 F: Fn(ROQueryItem<Q>) -> bool,
222 for<'w, 's> <<Q as QueryData>::ReadOnly as QueryData>::Item<'w, 's>: Copy,
223{
224 fn find_any(
225 &self,
226 ecs_lock: Arc<Mutex<World>>,
227 instance_name: &InstanceName,
228 ) -> Option<Entity> {
229 let mut ecs = ecs_lock.lock();
230 let mut query = ecs.query_filtered::<(Entity, &InstanceName, Q), Filter>();
231 query
232 .iter(&ecs)
233 .find(|(_, e_instance_name, q)| *e_instance_name == instance_name && (self)(*q))
234 .map(|(e, _, _)| e)
235 }
236
237 fn find_all_sorted(
238 &self,
239 ecs_lock: Arc<Mutex<World>>,
240 instance_name: &InstanceName,
241 nearest_to: Vec3,
242 ) -> Vec<Entity> {
243 let mut ecs = ecs_lock.lock();
244 let mut query = ecs.query_filtered::<(Entity, &InstanceName, &Position, Q), Filter>();
245 let mut entities = query
246 .iter(&ecs)
247 .filter(|(_, e_instance_name, _, q)| *e_instance_name == instance_name && (self)(*q))
248 .map(|(e, _, position, _)| (e, Vec3::from(position)))
249 .collect::<Vec<(Entity, Vec3)>>();
250
251 entities.sort_by_cached_key(|(_, position)| {
252 // to_bits is fine here as long as the number is positive
253 position.distance_squared_to(nearest_to).to_bits()
254 });
255
256 entities
257 .into_iter()
258 .map(|(e, _)| e)
259 .collect::<Vec<Entity>>()
260 }
261}