From 9ffd0e80bbb3feace231553d6539124585b03e3c Mon Sep 17 00:00:00 2001 From: mat Date: Wed, 6 May 2026 03:36:16 -0100 Subject: change panicking functions in Client and EntityRef to return an AzaleaResult instead --- azalea/src/client_impl/entity_query.rs | 159 ++++++++++++++++----------------- 1 file changed, 75 insertions(+), 84 deletions(-) (limited to 'azalea/src/client_impl/entity_query.rs') diff --git a/azalea/src/client_impl/entity_query.rs b/azalea/src/client_impl/entity_query.rs index 0c123b78..c71cd180 100644 --- a/azalea/src/client_impl/entity_query.rs +++ b/azalea/src/client_impl/entity_query.rs @@ -9,11 +9,13 @@ use bevy_ecs::{ query::{QueryData, QueryEntityError, QueryFilter, QueryItem, ROQueryItem, With, Without}, world::World, }; -use parking_lot::{ - MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLock, RwLockReadGuard, RwLockWriteGuard, -}; +use parking_lot::{MappedRwLockReadGuard, RwLock, RwLockReadGuard}; -use crate::{Client, entity_ref::EntityRef}; +use crate::{ + Client, + client_impl::error::{AzaleaResult, MissingComponentError}, + entity_ref::EntityRef, +}; impl Client { /// Get a component from the client. @@ -27,9 +29,6 @@ impl Client { /// 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 @@ -38,11 +37,6 @@ impl Client { /// 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 /// /// ``` @@ -50,21 +44,19 @@ impl Client { /// # fn example(client: &azalea::Client) { /// let world_name = client.component::(); /// # } - pub fn component(&self) -> MappedRwLockReadGuard<'_, T> { - self.get_component::().unwrap_or_else(|| { - panic!( - "Our client is missing a required component: {:?}", - any::type_name::<&T>() - ) + pub fn component( + &self, + ) -> Result, MissingComponentError> { + self.entity_component::(self.entity).map_err(|mut err| { + err.entity_description = "Player"; + err }) } - /// 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. + #[doc(hidden)] + #[deprecated = "replaced with `Self::component`."] pub fn get_component(&self) -> Option> { - self.get_entity_component::(self.entity) + self.component().ok() } /// Query the ECS for data from our client entity. @@ -87,19 +79,18 @@ impl Client { /// /// This will panic if the client is missing a component required by the /// query. Consider using [`Self::try_query_self`] to avoid this. - pub fn query_self(&self, f: impl FnOnce(QueryItem) -> R) -> R { - self.try_query_self::(f).unwrap_or_else(|_| { - panic!( - "`Client::query_self` failed when querying for {:?}", - any::type_name::() - ) + pub fn query_self( + &self, + f: impl FnOnce(QueryItem) -> R, + ) -> AzaleaResult { + self.query_entity(self.entity, f).map_err(|mut err| { + err.entity_description = "Player"; + err }) } - /// Query the ECS for data from our client entity, or return `None` if the - /// query failed. - /// - /// Also see [`Self::query_self`]. + #[doc(hidden)] + #[deprecated = "replaced with `Self::query_self`."] pub fn try_query_self( &self, f: impl FnOnce(QueryItem) -> R, @@ -111,35 +102,33 @@ impl Client { /// Query the ECS for data from an entity. /// - /// 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 + /// Note that it is often simpler to use [`Self::entity_component`]. To + /// query the client, you should use [`Self::query_self`]. + /// + /// # Errors /// - /// This will panic if the entity doesn't exist or if the query isn't valid - /// for the entity. For a non-panicking version, you may use - /// [`Self::try_query_entity`]. + /// This will return an error if the entity doesn't exist or if the query + /// isn't valid for the entity. pub fn query_entity( &self, entity: Entity, f: impl FnOnce(QueryItem) -> R, - ) -> R { - self.try_query_entity(entity, f).unwrap_or_else(|_| { - panic!( - "Querying entity {entity} failed when getting {:?}", - any::type_name::() - ) - }) + ) -> AzaleaResult { + let mut ecs = self.ecs.write(); + let mut qs = ecs.query::(); + qs.get_mut(&mut ecs, entity) + .map(f) + .map_err(|_| MissingComponentError { + entity_description: "Entity", + entity, + component: any::type_name::(), + }) } - /// A convenience function for getting components from any entity, or None - /// if the query fails. - /// - /// If you're sure that the entity exists and that the query will succeed, - /// you can use [`Self::query_entity`]. + #[doc(hidden)] + #[deprecated = "replaced with `Self::query_entity`."] pub fn try_query_entity( &self, entity: Entity, @@ -158,9 +147,10 @@ impl Client { pub fn any_entity_by( &self, predicate: impl EntityPredicate, - ) -> Option { - self.any_entity_id_by(predicate) - .map(|e| self.entity_ref_for(e)) + ) -> AzaleaResult> { + Ok(self + .any_entity_id_by(predicate)? + .map(|e| self.entity_ref_for(e))) } /// Quickly returns a lightweight [`Entity`] for an arbitrary entity that /// matches the given predicate function that is in the same @@ -190,9 +180,9 @@ impl Client { pub fn any_entity_id_by( &self, predicate: impl EntityPredicate, - ) -> Option { - let world_name = self.get_component::()?.clone(); - predicate.find_any(self.ecs.clone(), &world_name) + ) -> AzaleaResult> { + let world_name = self.component::()?.clone(); + Ok(predicate.find_any(self.ecs.clone(), &world_name)) } /// Return an [`EntityRef`] for the nearest entity that matches the @@ -207,9 +197,10 @@ impl Client { pub fn nearest_entity_by( &self, predicate: impl EntityPredicate, - ) -> Option { - self.nearest_entity_id_by(predicate) - .map(|e| self.entity_ref_for(e)) + ) -> AzaleaResult> { + Ok(self + .nearest_entity_id_by(predicate)? + .map(|e| self.entity_ref_for(e))) } /// Return a lightweight [`Entity`] for the nearest entity that matches the /// given predicate function. @@ -223,8 +214,8 @@ impl Client { pub fn nearest_entity_id_by( &self, predicate: impl EntityPredicate, - ) -> Option { - self.nearest_entity_ids_by(predicate).first().copied() + ) -> AzaleaResult> { + Ok(self.nearest_entity_ids_by(predicate)?.first().copied()) } /// Returns an array of all [`EntityRef`]s in the world that match the @@ -236,17 +227,18 @@ impl Client { pub fn nearest_entities_by( &self, predicate: impl EntityPredicate, - ) -> Box<[EntityRef]> { - self.nearest_entity_ids_by(predicate) + ) -> AzaleaResult> { + Ok(self + .nearest_entity_ids_by(predicate)? .into_iter() .map(|e| self.entity_ref_for(e)) - .collect() + .collect()) } /// Returns an array of [`EntityRef`] for all known entities in the world /// that match the given filter, sorted by nearest first. /// /// Also see [`Self::nearest_entities_by`]. - pub fn nearest_entities(&self) -> Box<[EntityRef]> { + pub fn nearest_entities(&self) -> AzaleaResult> { self.nearest_entities_by::<(), F>(|_| true) } @@ -258,7 +250,7 @@ impl Client { /// /// If you're in a swarm, this includes all players that are visible by at /// least one client. - pub fn nearby_players(&self) -> Box<[EntityRef]> { + pub fn nearby_players(&self) -> AzaleaResult> { self.nearest_entities::<(With, Without)>() } @@ -280,19 +272,15 @@ impl Client { pub fn nearest_entity_ids_by( &self, predicate: impl EntityPredicate, - ) -> Box<[Entity]> { + ) -> AzaleaResult> { let (world_name, position) = { - let Some(world_name) = self.get_component::() else { - return Box::new([]); - }; - let Some(position) = self.get_component::() else { - return Box::new([]); - }; + let world_name = self.component::()?; + let position = self.component::()?; (world_name.clone(), **position) }; - predicate.find_all_sorted(self.ecs.clone(), &world_name, position) + Ok(predicate.find_all_sorted(self.ecs.clone(), &world_name, position)) } /// Get a component from an entity. @@ -315,13 +303,16 @@ impl Client { /// /// This will panic if the component doesn't exist on the entity. Use /// [`Self::get_entity_component`] to avoid this. - pub fn entity_component(&self, entity: Entity) -> MappedRwLockReadGuard<'_, T> { - self.get_entity_component::(entity).unwrap_or_else(|| { - panic!( - "Entity {entity} is missing a required component: {:?}", - any::type_name::<&T>() - ) - }) + pub fn entity_component( + &self, + entity: Entity, + ) -> Result, MissingComponentError> { + self.get_entity_component::(entity) + .ok_or_else(|| MissingComponentError { + entity_description: "Entity", + entity, + component: any::type_name::(), + }) } /// Get a component from an entity, if it exists. -- cgit v1.2.3