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, QueryFilter, ROQueryItem},
10    world::World,
11};
12use parking_lot::Mutex;
13
14use crate::Client;
15
16impl Client {
17    /// A convenience function for getting components of our player's entity.
18    ///
19    /// # Examples
20    /// ```
21    /// # use azalea_world::InstanceName;
22    /// # fn example(mut client: azalea_client::Client) {
23    /// let is_logged_in = client
24    ///     .query::<Option<&InstanceName>>(&mut client.ecs.lock())
25    ///     .is_some();
26    /// # }
27    /// ```
28    pub fn query<'w, D: QueryData>(&self, ecs: &'w mut World) -> D::Item<'w> {
29        ecs.query::<D>()
30            .get_mut(ecs, self.entity)
31            .unwrap_or_else(|_| {
32                panic!(
33                    "Our client is missing a required component {:?}",
34                    any::type_name::<D>()
35                )
36            })
37    }
38
39    /// Return a lightweight [`Entity`] for an arbitrary entity that matches the
40    /// given predicate function that is in the same [`Instance`] as the
41    /// client.
42    ///
43    /// You can then use [`Self::entity_component`] to get components from this
44    /// entity.
45    ///
46    /// Also see [`Self::entities_by`] which will return all entities that match
47    /// the predicate and sorts them by distance (unlike `entity_by`).
48    ///
49    /// # Example
50    /// ```
51    /// use azalea_client::{Client, player::GameProfileComponent};
52    /// use azalea_entity::{Position, metadata::Player};
53    /// use bevy_ecs::query::With;
54    ///
55    /// # fn example(mut bot: Client, sender_name: String) {
56    /// let entity = bot.entity_by::<With<Player>, (&GameProfileComponent,)>(
57    ///     |(profile,): &(&GameProfileComponent,)| profile.name == sender_name,
58    /// );
59    /// if let Some(entity) = entity {
60    ///     let position = bot.entity_component::<Position>(entity);
61    ///     // ...
62    /// }
63    /// # }
64    /// ```
65    ///
66    /// [`Entity`]: bevy_ecs::entity::Entity
67    /// [`Instance`]: azalea_world::Instance
68    pub fn entity_by<F: QueryFilter, Q: QueryData>(
69        &self,
70        predicate: impl EntityPredicate<Q, F>,
71    ) -> Option<Entity> {
72        let instance_name = self.get_component::<InstanceName>()?;
73        predicate.find_any(self.ecs.clone(), &instance_name)
74    }
75
76    /// Similar to [`Self::entity_by`] but returns a `Vec<Entity>` of all
77    /// entities in our instance that match the predicate.
78    ///
79    /// Unlike `entity_by`, the result is sorted by distance to our client's
80    /// position, so the closest entity is first.
81    pub fn entities_by<F: QueryFilter, Q: QueryData>(
82        &self,
83        predicate: impl EntityPredicate<Q, F>,
84    ) -> Vec<Entity> {
85        let Some(instance_name) = self.get_component::<InstanceName>() else {
86            return vec![];
87        };
88        let Some(position) = self.get_component::<Position>() else {
89            return vec![];
90        };
91        predicate.find_all_sorted(self.ecs.clone(), &instance_name, (&position).into())
92    }
93
94    /// Get a component from an entity. Note that this will return an owned type
95    /// (i.e. not a reference) so it may be expensive for larger types.
96    ///
97    /// If you're trying to get a component for this client, use
98    /// [`Self::component`].
99    pub fn entity_component<Q: Component + Clone>(&self, entity: Entity) -> Q {
100        let mut ecs = self.ecs.lock();
101        let mut q = ecs.query::<&Q>();
102        let components = q.get(&ecs, entity).unwrap_or_else(|_| {
103            panic!(
104                "Entity is missing a required component {:?}",
105                any::type_name::<Q>()
106            )
107        });
108        components.clone()
109    }
110
111    /// Get a component from an entity, if it exists. This is similar to
112    /// [`Self::entity_component`] but returns an `Option` instead of panicking
113    /// if the component isn't present.
114    pub fn get_entity_component<Q: Component + Clone>(&self, entity: Entity) -> Option<Q> {
115        let mut ecs = self.ecs.lock();
116        let mut q = ecs.query::<&Q>();
117        let components = q.get(&ecs, entity).ok();
118        components.cloned()
119    }
120}
121
122pub trait EntityPredicate<Q: QueryData, Filter: QueryFilter> {
123    fn find_any(&self, ecs_lock: Arc<Mutex<World>>, instance_name: &InstanceName)
124    -> Option<Entity>;
125    fn find_all_sorted(
126        &self,
127        ecs_lock: Arc<Mutex<World>>,
128        instance_name: &InstanceName,
129        nearest_to: Vec3,
130    ) -> Vec<Entity>;
131}
132impl<F, Q: QueryData, Filter: QueryFilter> EntityPredicate<Q, Filter> for F
133where
134    F: Fn(&ROQueryItem<Q>) -> bool,
135{
136    fn find_any(
137        &self,
138        ecs_lock: Arc<Mutex<World>>,
139        instance_name: &InstanceName,
140    ) -> Option<Entity> {
141        let mut ecs = ecs_lock.lock();
142        let mut query = ecs.query_filtered::<(Entity, &InstanceName, Q), Filter>();
143        query
144            .iter(&ecs)
145            .find(|(_, e_instance_name, q)| *e_instance_name == instance_name && (self)(q))
146            .map(|(e, _, _)| e)
147    }
148
149    fn find_all_sorted(
150        &self,
151        ecs_lock: Arc<Mutex<World>>,
152        instance_name: &InstanceName,
153        nearest_to: Vec3,
154    ) -> Vec<Entity> {
155        let mut ecs = ecs_lock.lock();
156        let mut query = ecs.query_filtered::<(Entity, &InstanceName, &Position, Q), Filter>();
157        let mut entities = query
158            .iter(&ecs)
159            .filter(|(_, e_instance_name, _, q)| *e_instance_name == instance_name && (self)(q))
160            .map(|(e, _, position, _)| (e, Vec3::from(position)))
161            .collect::<Vec<(Entity, Vec3)>>();
162
163        entities.sort_by_cached_key(|(_, position)| {
164            // to_bits is fine here as long as the number is positive
165            position.distance_squared_to(nearest_to).to_bits()
166        });
167
168        entities
169            .into_iter()
170            .map(|(e, _)| e)
171            .collect::<Vec<Entity>>()
172    }
173}