aboutsummaryrefslogtreecommitdiff
path: root/azalea/src/client_impl/entity_query.rs
diff options
context:
space:
mode:
authormat <27899617+mat-1@users.noreply.github.com>2025-12-28 04:31:29 -0600
committerGitHub <noreply@github.com>2025-12-28 04:31:29 -0600
commit20c7e27250148f62bab9e7b99e4f0cd6deb82325 (patch)
tree163b312ef4dc72c5c23be923f8ca17cdf2a7278c /azalea/src/client_impl/entity_query.rs
parent7ab3b8924f64f7eadb6b8928b6fae73cb06e4c2f (diff)
downloadazalea-drasl-20c7e27250148f62bab9e7b99e4f0cd6deb82325.tar.xz
Change Client::component to return a reference (#298)
* change Client::component to return a reference * write docs * merge main * remove unused parking_lot feature
Diffstat (limited to 'azalea/src/client_impl/entity_query.rs')
-rw-r--r--azalea/src/client_impl/entity_query.rs144
1 files changed, 109 insertions, 35 deletions
diff --git a/azalea/src/client_impl/entity_query.rs b/azalea/src/client_impl/entity_query.rs
index 85e46525..ae623dc7 100644
--- a/azalea/src/client_impl/entity_query.rs
+++ b/azalea/src/client_impl/entity_query.rs
@@ -9,41 +9,100 @@ use bevy_ecs::{
query::{QueryData, QueryEntityError, QueryFilter, QueryItem, ROQueryItem},
world::World,
};
-use parking_lot::Mutex;
+use parking_lot::{MappedRwLockReadGuard, RwLock, RwLockReadGuard};
use crate::Client;
impl Client {
- /// A convenience function for getting components from our client's entity.
+ /// Get a component from the client.
///
- /// To query another entity, you can use [`Self::query_entity`].
+ /// This allows you to access certain data stored about the client entity
+ /// that isn't accessible in a simpler way.
+ ///
+ /// This returns a reference to the component wrapped by a read guard. This
+ /// makes the component cheap to access, but means that the ECS cannot be
+ /// mutated while it's in scope. In some cases, it may be simpler for you to
+ /// immediately clone the component after accessing it.
+ ///
+ /// If the component isn't guaranteed to be present, consider using
+ /// [`Self::get_component`] instead.
+ ///
+ /// To do more complex queries or to mutate data, see [`Self::query_self`].
+ ///
+ /// To access data about other entities, you can use
+ /// [`Self::entity_component`] (and its other related functions).
+ ///
+ /// You may also use [`Self::ecs`] directly if you need more control over
+ /// when the ECS is locked.
+ ///
+ /// # Panics
+ ///
+ /// This will panic if the component doesn't exist on the client. Use
+ /// [`Self::get_component`] to avoid this.
///
/// # Examples
+ ///
/// ```
/// # use azalea_world::InstanceName;
+ /// # fn example(client: &azalea::Client) {
+ /// let world_name = client.component::<InstanceName>();
+ /// # }
+ pub fn component<T: Component>(&self) -> MappedRwLockReadGuard<'_, T> {
+ self.get_component::<T>().unwrap_or_else(|| {
+ panic!(
+ "Our client is missing a required component: {:?}",
+ any::type_name::<&T>()
+ )
+ })
+ }
+
+ /// Get a component on this client, or `None` if it doesn't exist.
+ ///
+ /// If the component is guaranteed to be present, consider using
+ /// [`Self::component`]. Also see that function for more details.
+ pub fn get_component<T: Component>(&self) -> Option<MappedRwLockReadGuard<'_, T>> {
+ self.get_entity_component::<T>(self.entity)
+ }
+
+ /// Query the ECS for data from our client entity.
+ ///
+ /// To query another entity, you can use [`Self::query_entity`].
+ ///
+ /// You can use this to mutate data on the client.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use azalea_entity::Position;
/// # fn example(mut client: azalea::Client) {
- /// let is_logged_in = client.query_self::<Option<&InstanceName>, _>(|ins| ins.is_some());
+ /// // teleport one block up
+ /// client.query_self::<&mut Position, _>(|mut pos| pos.y += 1.0);
/// # }
/// ```
///
/// # Panics
///
- /// This will panic if the component doesn't exist on the client.
+ /// This will panic if the client is missing a component required by the
+ /// query.
pub fn query_self<D: QueryData, R>(&self, f: impl FnOnce(QueryItem<D>) -> R) -> R {
- let mut ecs = self.ecs.lock();
+ let mut ecs = self.ecs.write();
let mut qs = ecs.query::<D>();
let res = qs.get_mut(&mut ecs, self.entity).unwrap_or_else(|_| {
panic!(
- "Our client is missing a required component {:?}",
+ "`Client::query_self` failed when querying for {:?}",
any::type_name::<D>()
)
});
f(res)
}
- /// A convenience function for getting components from any entity.
+ /// Query the ECS for data from an entity.
///
- /// If you're querying the client, you should use [`Self::query_self`].
+ /// Note that it is often simpler to use [`Self::entity_component`].
+ ///
+ /// To query the client, you should use [`Self::query_self`].
+ ///
+ /// You can also use this to mutate data on an entity.
///
/// # Panics
///
@@ -73,7 +132,7 @@ impl Client {
entity: Entity,
f: impl FnOnce(QueryItem<D>) -> R,
) -> Result<R, QueryEntityError> {
- let mut ecs = self.ecs.lock();
+ let mut ecs = self.ecs.write();
let mut qs = ecs.query::<D>();
qs.get_mut(&mut ecs, entity).map(f)
}
@@ -138,7 +197,7 @@ impl Client {
/// if let Some(nearest_player) =
/// bot.nearest_entity_by::<(), (With<Player>, Without<LocalEntity>)>(|_: ()| true)
/// {
- /// let nearest_player_pos = *bot.entity_component::<Position>(nearest_player);
+ /// let nearest_player_pos = **bot.entity_component::<Position>(nearest_player);
/// bot.chat(format!("You are at {nearest_player_pos}"));
/// }
/// # }
@@ -175,46 +234,61 @@ impl Client {
let Some(position) = self.get_component::<Position>() else {
return vec![];
};
+ let (instance_name, position) = (instance_name.clone(), *position);
predicate.find_all_sorted(self.ecs.clone(), &instance_name, (&position).into())
}
/// Get a component from an entity.
///
- /// Note that this will return an owned type (i.e. not a reference) so it
- /// may be expensive for larger types.
+ /// This allows you to access data stored about entities that isn't
+ /// accessible in a simpler way.
+ ///
+ /// This returns a reference to the component wrapped by a read guard. This
+ /// makes the component cheap to access, but means that the ECS cannot be
+ /// mutated while it's in scope. In some cases, it may be simpler for you to
+ /// immediately clone the component after accessing it.
+ ///
+ /// If you're trying to get a component for this client, you should use
+ /// [`Self::component`] instead.
+ ///
+ /// To do more complex queries or to mutate data, see
+ /// [`Self::query_entity`].
///
- /// If you're trying to get a component for this client, use
- /// [`Self::component`].
- pub fn entity_component<Q: Component + Clone>(&self, entity: Entity) -> Q {
- let mut ecs = self.ecs.lock();
- let mut q = ecs.query::<&Q>();
- let components = q.get(&ecs, entity).unwrap_or_else(|_| {
+ /// # Panics
+ ///
+ /// This will panic if the component doesn't exist on the entity. Use
+ /// [`Self::get_entity_component`] to avoid this.
+ pub fn entity_component<T: Component>(&self, entity: Entity) -> MappedRwLockReadGuard<'_, T> {
+ self.get_entity_component::<T>(entity).unwrap_or_else(|| {
panic!(
- "Entity is missing a required component {:?}",
- any::type_name::<Q>()
+ "Entity {entity} is missing a required component: {:?}",
+ any::type_name::<&T>()
)
- });
- components.clone()
+ })
}
/// Get a component from an entity, if it exists.
///
/// This is similar to [`Self::entity_component`] but returns an `Option`
/// instead of panicking if the component isn't present.
- pub fn get_entity_component<Q: Component + Clone>(&self, entity: Entity) -> Option<Q> {
- let mut ecs = self.ecs.lock();
- let mut q = ecs.query::<&Q>();
- let components = q.get(&ecs, entity).ok();
- components.cloned()
+ pub fn get_entity_component<T: Component>(
+ &self,
+ entity: Entity,
+ ) -> Option<MappedRwLockReadGuard<'_, T>> {
+ let ecs = self.ecs.read();
+ RwLockReadGuard::try_map(ecs, |ecs: &World| ecs.get(entity)).ok()
}
}
pub trait EntityPredicate<Q: QueryData, Filter: QueryFilter> {
- fn find_any(&self, ecs_lock: Arc<Mutex<World>>, instance_name: &InstanceName)
- -> Option<Entity>;
+ fn find_any(
+ &self,
+ ecs_lock: Arc<RwLock<World>>,
+ instance_name: &InstanceName,
+ ) -> Option<Entity>;
fn find_all_sorted(
&self,
- ecs_lock: Arc<Mutex<World>>,
+ ecs_lock: Arc<RwLock<World>>,
instance_name: &InstanceName,
nearest_to: Vec3,
) -> Vec<Entity>;
@@ -226,10 +300,10 @@ where
{
fn find_any(
&self,
- ecs_lock: Arc<Mutex<World>>,
+ ecs_lock: Arc<RwLock<World>>,
instance_name: &InstanceName,
) -> Option<Entity> {
- let mut ecs = ecs_lock.lock();
+ let mut ecs = ecs_lock.write();
let mut query = ecs.query_filtered::<(Entity, &InstanceName, Q), Filter>();
query
.iter(&ecs)
@@ -239,11 +313,11 @@ where
fn find_all_sorted(
&self,
- ecs_lock: Arc<Mutex<World>>,
+ ecs_lock: Arc<RwLock<World>>,
instance_name: &InstanceName,
nearest_to: Vec3,
) -> Vec<Entity> {
- let mut ecs = ecs_lock.lock();
+ let mut ecs = ecs_lock.write();
let mut query = ecs.query_filtered::<(Entity, &InstanceName, &Position, Q), Filter>();
let mut entities = query
.iter(&ecs)