diff options
Diffstat (limited to 'azalea/src/client_impl')
| -rw-r--r-- | azalea/src/client_impl/attack.rs | 4 | ||||
| -rw-r--r-- | azalea/src/client_impl/client_information.rs | 11 | ||||
| -rw-r--r-- | azalea/src/client_impl/entity_query.rs | 159 | ||||
| -rw-r--r-- | azalea/src/client_impl/error.rs | 29 | ||||
| -rw-r--r-- | azalea/src/client_impl/interact.rs | 6 | ||||
| -rw-r--r-- | azalea/src/client_impl/inventory.rs | 10 | ||||
| -rw-r--r-- | azalea/src/client_impl/mining.rs | 2 | ||||
| -rw-r--r-- | azalea/src/client_impl/mod.rs | 106 | ||||
| -rw-r--r-- | azalea/src/client_impl/movement.rs | 26 |
9 files changed, 203 insertions, 150 deletions
diff --git a/azalea/src/client_impl/attack.rs b/azalea/src/client_impl/attack.rs index 16721c1c..cdb4c0b1 100644 --- a/azalea/src/client_impl/attack.rs +++ b/azalea/src/client_impl/attack.rs @@ -22,9 +22,9 @@ impl Client { /// /// Also see [`Client::attack_cooldown_remaining_ticks`]. pub fn has_attack_cooldown(&self) -> bool { - let Some(attack_strength_scale) = self.get_component::<AttackStrengthScale>() else { + let Ok(attack_strength_scale) = self.component::<AttackStrengthScale>() else { // they don't even have an AttackStrengthScale so they probably can't even - // attack? whatever, just return false + // attack. whatever, just return false return false; }; **attack_strength_scale < 1.0 diff --git a/azalea/src/client_impl/client_information.rs b/azalea/src/client_impl/client_information.rs index b3cf7927..3b408d58 100644 --- a/azalea/src/client_impl/client_information.rs +++ b/azalea/src/client_impl/client_information.rs @@ -2,7 +2,7 @@ use azalea_client::ClientInformation; use azalea_protocol::packets::game; use tracing::debug; -use crate::Client; +use crate::{Client, client_impl::error::AzaleaResult}; impl Client { /// Tell the server we changed our game options (i.e. render distance, main @@ -20,10 +20,13 @@ impl Client { /// # Ok(()) /// # } /// ``` - pub fn set_client_information(&self, client_information: ClientInformation) { + pub fn set_client_information( + &self, + client_information: ClientInformation, + ) -> AzaleaResult<()> { self.query_self::<&mut ClientInformation, _>(|mut ci| { *ci = client_information.clone(); - }); + })?; if self.logged_in() { debug!( @@ -34,5 +37,7 @@ impl Client { client_information, }); } + + Ok(()) } } 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::<WorldName>(); /// # } - 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>() - ) + pub fn component<T: Component>( + &self, + ) -> Result<MappedRwLockReadGuard<'_, T>, MissingComponentError> { + self.entity_component::<T>(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<T: Component>(&self) -> Option<MappedRwLockReadGuard<'_, T>> { - self.get_entity_component::<T>(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<D: QueryData, R>(&self, f: impl FnOnce(QueryItem<D>) -> R) -> R { - self.try_query_self::<D, R>(f).unwrap_or_else(|_| { - panic!( - "`Client::query_self` failed when querying for {:?}", - any::type_name::<D>() - ) + pub fn query_self<D: QueryData, R>( + &self, + f: impl FnOnce(QueryItem<D>) -> R, + ) -> AzaleaResult<R> { + 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<D: QueryData, R>( &self, f: impl FnOnce(QueryItem<D>) -> 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<D: QueryData, R>( &self, entity: Entity, f: impl FnOnce(QueryItem<D>) -> R, - ) -> R { - self.try_query_entity(entity, f).unwrap_or_else(|_| { - panic!( - "Querying entity {entity} failed when getting {:?}", - any::type_name::<D>() - ) - }) + ) -> AzaleaResult<R> { + let mut ecs = self.ecs.write(); + let mut qs = ecs.query::<D>(); + qs.get_mut(&mut ecs, entity) + .map(f) + .map_err(|_| MissingComponentError { + entity_description: "Entity", + entity, + component: any::type_name::<D>(), + }) } - /// 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<D: QueryData, R>( &self, entity: Entity, @@ -158,9 +147,10 @@ impl Client { pub fn any_entity_by<Q: QueryData, F: QueryFilter>( &self, predicate: impl EntityPredicate<Q, F>, - ) -> Option<EntityRef> { - self.any_entity_id_by(predicate) - .map(|e| self.entity_ref_for(e)) + ) -> AzaleaResult<Option<EntityRef>> { + 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<Q: QueryData, F: QueryFilter>( &self, predicate: impl EntityPredicate<Q, F>, - ) -> Option<Entity> { - let world_name = self.get_component::<WorldName>()?.clone(); - predicate.find_any(self.ecs.clone(), &world_name) + ) -> AzaleaResult<Option<Entity>> { + let world_name = self.component::<WorldName>()?.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<Q: QueryData, F: QueryFilter>( &self, predicate: impl EntityPredicate<Q, F>, - ) -> Option<EntityRef> { - self.nearest_entity_id_by(predicate) - .map(|e| self.entity_ref_for(e)) + ) -> AzaleaResult<Option<EntityRef>> { + 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<Q: QueryData, F: QueryFilter>( &self, predicate: impl EntityPredicate<Q, F>, - ) -> Option<Entity> { - self.nearest_entity_ids_by(predicate).first().copied() + ) -> AzaleaResult<Option<Entity>> { + 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<Q: QueryData, F: QueryFilter>( &self, predicate: impl EntityPredicate<Q, F>, - ) -> Box<[EntityRef]> { - self.nearest_entity_ids_by(predicate) + ) -> AzaleaResult<Box<[EntityRef]>> { + 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<F: QueryFilter>(&self) -> Box<[EntityRef]> { + pub fn nearest_entities<F: QueryFilter>(&self) -> AzaleaResult<Box<[EntityRef]>> { 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<Box<[EntityRef]>> { self.nearest_entities::<(With<metadata::Player>, Without<LocalEntity>)>() } @@ -280,19 +272,15 @@ impl Client { pub fn nearest_entity_ids_by<Q: QueryData, F: QueryFilter>( &self, predicate: impl EntityPredicate<Q, F>, - ) -> Box<[Entity]> { + ) -> AzaleaResult<Box<[Entity]>> { let (world_name, position) = { - let Some(world_name) = self.get_component::<WorldName>() else { - return Box::new([]); - }; - let Some(position) = self.get_component::<Position>() else { - return Box::new([]); - }; + let world_name = self.component::<WorldName>()?; + let position = self.component::<Position>()?; (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<T: Component>(&self, entity: Entity) -> MappedRwLockReadGuard<'_, T> { - self.get_entity_component::<T>(entity).unwrap_or_else(|| { - panic!( - "Entity {entity} is missing a required component: {:?}", - any::type_name::<&T>() - ) - }) + pub fn entity_component<T: Component>( + &self, + entity: Entity, + ) -> Result<MappedRwLockReadGuard<'_, T>, MissingComponentError> { + self.get_entity_component::<T>(entity) + .ok_or_else(|| MissingComponentError { + entity_description: "Entity", + entity, + component: any::type_name::<T>(), + }) } /// Get a component from an entity, if it exists. diff --git a/azalea/src/client_impl/error.rs b/azalea/src/client_impl/error.rs new file mode 100644 index 00000000..ce3ecab4 --- /dev/null +++ b/azalea/src/client_impl/error.rs @@ -0,0 +1,29 @@ +use bevy_ecs::entity::Entity; +use thiserror::Error; + +/// An error that occurs when we tried to access data from an entity that it +/// doesn't have. +/// +/// This could happen because the data does not occur for this type of entity, +/// or because the entity is not currently loaded. +/// +/// If this error happened when trying to access data on a client, then it may +/// be because the client isn't currently in a world. +/// +/// As an alias for `Result<T, MissingComponentError>`, you may use +/// [`AzaleaResult`]. Using the `eyre` or `anyhow` crates may also simplify +/// error handling in your bots. +#[derive(Error, Debug)] +#[error("{entity_description} {entity} is missing a required component: '{component}'")] +pub struct MissingComponentError { + /// Should be "Entity" or "Client" + pub entity_description: &'static str, + pub entity: Entity, + pub component: &'static str, +} + +/// An error that occurs when we tried to access data from an entity that it +/// doesn't have. +/// +/// See [`MissingComponentError`] for more details. +pub type AzaleaResult<T> = Result<T, MissingComponentError>; diff --git a/azalea/src/client_impl/interact.rs b/azalea/src/client_impl/interact.rs index b3cda1e2..bec62c20 100644 --- a/azalea/src/client_impl/interact.rs +++ b/azalea/src/client_impl/interact.rs @@ -3,13 +3,13 @@ use azalea_core::{hit_result::HitResult, position::BlockPos}; use azalea_protocol::packets::game::s_interact::InteractionHand; use bevy_ecs::entity::Entity; -use crate::Client; +use crate::{Client, client_impl::error::AzaleaResult}; impl Client { /// Returns the current [`HitResult`], which is the block or entity in the /// client's crosshair. - pub fn hit_result(&self) -> HitResult { - (**self.component::<HitResultComponent>()).clone() + pub fn hit_result(&self) -> AzaleaResult<HitResult> { + Ok((**self.component::<HitResultComponent>()?).clone()) } /// Right-click a block. diff --git a/azalea/src/client_impl/inventory.rs b/azalea/src/client_impl/inventory.rs index 72a05136..93e599aa 100644 --- a/azalea/src/client_impl/inventory.rs +++ b/azalea/src/client_impl/inventory.rs @@ -2,7 +2,7 @@ use azalea_client::inventory::SetSelectedHotbarSlotEvent; use azalea_entity::inventory::Inventory; use azalea_inventory::Menu; -use crate::Client; +use crate::{Client, client_impl::error::AzaleaResult}; impl Client { /// Return the menu that is currently open, or the player's inventory if no @@ -10,8 +10,8 @@ impl Client { /// /// If you need to interact with the menu, consider using /// [`Self::open_inventory`] instead. - pub fn menu(&self) -> Menu { - self.component::<Inventory>().menu().clone() + pub fn menu(&self) -> AzaleaResult<Menu> { + Ok(self.component::<Inventory>()?.menu().clone()) } /// Returns the index of the hotbar slot that's currently selected. @@ -21,8 +21,8 @@ impl Client { /// the start of [`azalea_inventory::Menu::hotbar_slots_range`]. /// /// You can use [`Self::set_selected_hotbar_slot`] to change it. - pub fn selected_hotbar_slot(&self) -> u8 { - self.component::<Inventory>().selected_hotbar_slot + pub fn selected_hotbar_slot(&self) -> AzaleaResult<u8> { + Ok(self.component::<Inventory>()?.selected_hotbar_slot) } /// Update the selected hotbar slot index. diff --git a/azalea/src/client_impl/mining.rs b/azalea/src/client_impl/mining.rs index 11c74480..daaf2b3f 100644 --- a/azalea/src/client_impl/mining.rs +++ b/azalea/src/client_impl/mining.rs @@ -16,7 +16,7 @@ impl Client { /// Returns true if the client is currently trying to mine a block. pub fn is_mining(&self) -> bool { - self.get_component::<Mining>().is_some() + self.component::<Mining>().is_ok() } /// When enabled, the bot will mine any block that it is looking at if it is diff --git a/azalea/src/client_impl/mod.rs b/azalea/src/client_impl/mod.rs index be711ef1..ba78fee4 100644 --- a/azalea/src/client_impl/mod.rs +++ b/azalea/src/client_impl/mod.rs @@ -34,6 +34,7 @@ use uuid::Uuid; use crate::{ bot::DefaultBotPlugins, + client_impl::error::AzaleaResult, entity_ref::EntityRef, events::{Event, LocalPlayerEvents}, swarm::DefaultSwarmPlugins, @@ -43,6 +44,7 @@ pub mod attack; pub mod chat; pub mod client_information; pub mod entity_query; +pub mod error; pub mod interact; pub mod inventory; pub mod mining; @@ -307,10 +309,13 @@ impl Client { self.ecs.write().write_message(AppExit::Success); } - pub fn with_raw_connection<R>(&self, f: impl FnOnce(&RawConnection) -> R) -> R { + pub fn with_raw_connection<R>(&self, f: impl FnOnce(&RawConnection) -> R) -> AzaleaResult<R> { self.query_self::<&RawConnection, _>(f) } - pub fn with_raw_connection_mut<R>(&self, f: impl FnOnce(Mut<'_, RawConnection>) -> R) -> R { + pub fn with_raw_connection_mut<R>( + &self, + f: impl FnOnce(Mut<'_, RawConnection>) -> R, + ) -> AzaleaResult<R> { self.query_self::<&mut RawConnection, _>(f) } @@ -340,9 +345,9 @@ impl Client { /// component. If it's a normal client, then it'll be the same as the /// world the client has loaded. If the client is using a shared world, /// then the shared world will be a superset of the client's world. - pub fn world(&self) -> Arc<RwLock<World>> { - let world_holder = self.component::<WorldHolder>(); - world_holder.shared.clone() + pub fn world(&self) -> AzaleaResult<Arc<RwLock<World>>> { + let world_holder = self.component::<WorldHolder>()?; + Ok(world_holder.shared.clone()) } /// Get an `RwLock` with a reference to the world that this client has @@ -354,15 +359,15 @@ impl Client { /// let world = client.partial_world(); /// let is_0_0_loaded = world.read().chunks.limited_get(&ChunkPos::new(0, 0)).is_some(); /// # } - pub fn partial_world(&self) -> Arc<RwLock<PartialWorld>> { - let world_holder = self.component::<WorldHolder>(); - world_holder.partial.clone() + pub fn partial_world(&self) -> AzaleaResult<Arc<RwLock<PartialWorld>>> { + let world_holder = self.component::<WorldHolder>()?; + Ok(world_holder.partial.clone()) } /// Returns whether we have a received the login packet yet. pub fn logged_in(&self) -> bool { // the login packet tells us the world name - self.query_self::<Option<&WorldName>, _>(|ins| ins.is_some()) + self.query_self::<&WorldName, _>(|_| {}).is_ok() } /// Returns the client as an [`EntityRef`], allowing you to treat it as any @@ -381,31 +386,47 @@ impl Client { /// Get the hunger level of this client, which includes both food and /// saturation. /// - /// This is a shortcut for `self.component::<Hunger>().to_owned()`. - pub fn hunger(&self) -> Hunger { - self.component::<Hunger>().to_owned() + /// This is a shortcut for `self.component::<Hunger>()?.to_owned()`. + pub fn hunger(&self) -> AzaleaResult<Hunger> { + Ok(self.component::<Hunger>()?.to_owned()) } /// Get the experience of this client. /// /// This is a shortcut for `self.component::<Experience>().to_owned()`. - pub fn experience(&self) -> Experience { - self.component::<Experience>().to_owned() + pub fn experience(&self) -> AzaleaResult<Experience> { + Ok(self.component::<Experience>()?.to_owned()) } - /// Get the username of this client. + /// Get the username of this client's account. /// - /// This is a shortcut for - /// `bot.component::<GameProfileComponent>().name.to_owned()`. + /// This is a shortcut for `bot.account().username().to_owned()`. pub fn username(&self) -> String { - self.profile().name.to_owned() + self.account().username().to_owned() + } + /// Get the username of this client, as sent to us by the server. + /// + /// This is a shortcut for `bot.profile()?.name.to_owned()`. + /// + /// In some cases, this may be different from [`Self::username`] if the + /// server sends us a [`GameProfile`] with a mismatching username. Using + /// [`Self::username`] is recommended if you're not sure which one to use. + pub fn server_username(&self) -> AzaleaResult<String> { + Ok(self.profile()?.name.to_owned()) + } + + /// Get the Minecraft UUID of this client's account. + /// + /// This is a shortcut for `**self.component::<EntityUuid>()`. + pub fn uuid(&self) -> Uuid { + self.account().uuid() } /// Get a map of player UUIDs to their information in the tab list. /// /// This is a shortcut for `*bot.component::<TabList>()`. - pub fn tab_list(&self) -> HashMap<Uuid, PlayerInfo> { - (**self.component::<TabList>()).clone() + pub fn tab_list(&self) -> AzaleaResult<HashMap<Uuid, PlayerInfo>> { + Ok((**self.component::<TabList>()?).clone()) } /// Returns the [`GameProfile`] for our client. This contains your username, @@ -417,13 +438,17 @@ impl Client { /// the tab list, which you can get from [`Self::tab_list`]. /// /// This as also available from the ECS as [`GameProfileComponent`]. - pub fn profile(&self) -> GameProfile { - (**self.component::<GameProfileComponent>()).clone() + pub fn profile(&self) -> AzaleaResult<GameProfile> { + Ok((**self.component::<GameProfileComponent>()?).clone()) } /// Returns the [`Account`] for our client. pub fn account(&self) -> Account { - self.component::<Account>().clone() + self.component::<Account>() + .expect( + "clients cannot exist without an Account, and Account isn't removed from clients", + ) + .clone() } /// A convenience function to get the Minecraft Uuid of a player by their @@ -431,11 +456,12 @@ impl Client { /// /// You can chain this with [`Client::entity_by_uuid`] to get the ECS /// `Entity` for the player. - pub fn player_uuid_by_username(&self, username: &str) -> Option<Uuid> { - self.tab_list() + pub fn player_uuid_by_username(&self, username: &str) -> AzaleaResult<Option<Uuid>> { + Ok(self + .tab_list()? .values() .find(|player| player.profile.name == username) - .map(|player| player.profile.uuid) + .map(|player| player.profile.uuid)) } /// Get an [`Entity`] in the world by its Minecraft UUID, if it's within @@ -457,7 +483,7 @@ impl Client { /// Get an [`Entity`] in the world by its [`MinecraftEntityId`]. /// /// Also see [`Self::entity_by_uuid`] and [`Self::entity_id_by_uuid`]. - pub fn entity_id_by_minecraft_id(&self, id: MinecraftEntityId) -> Option<Entity> { + pub fn entity_id_by_minecraft_id(&self, id: MinecraftEntityId) -> AzaleaResult<Option<Entity>> { self.query_self::<&EntityIdIndex, _>(|entity_id_index| { entity_id_index.get_by_minecraft_entity(id) }) @@ -465,9 +491,10 @@ impl Client { /// Get an [`EntityRef`] in the world by its [`MinecraftEntityId`]. /// /// Also see [`Self::entity_id_by_uuid`]. - pub fn entity_by_minecraft_id(&self, id: MinecraftEntityId) -> Option<EntityRef> { - self.entity_id_by_minecraft_id(id) - .map(|e| EntityRef::new(self.clone(), e)) + pub fn entity_by_minecraft_id(&self, id: MinecraftEntityId) -> AzaleaResult<Option<EntityRef>> { + Ok(self + .entity_id_by_minecraft_id(id)? + .map(|e| EntityRef::new(self.clone(), e))) } /// Call the given function with the client's [`RegistryHolder`]. @@ -480,10 +507,10 @@ impl Client { pub fn with_registry_holder<R>( &self, f: impl FnOnce(&azalea_core::registry_holder::RegistryHolder) -> R, - ) -> R { - let world = self.world(); + ) -> AzaleaResult<R> { + let world = self.world()?; let registries = &world.read().registries; - f(registries) + Ok(f(registries)) } /// Resolve the given registry to its name. @@ -495,7 +522,7 @@ impl Client { pub fn resolve_registry_name( &self, registry: &impl ResolvableDataRegistry, - ) -> Option<Identifier> { + ) -> AzaleaResult<Option<Identifier>> { self.with_registry_holder(|registries| registry.key(registries).map(|r| r.into_ident())) } @@ -508,7 +535,10 @@ impl Client { /// `.map(|r| r.into_ident())`. /// /// [`Enchantment`]: azalea_registry::data::Enchantment - pub fn resolve_registry_key<R: ResolvableDataRegistry>(&self, registry: &R) -> Option<R::Key> { + pub fn resolve_registry_key<R: ResolvableDataRegistry>( + &self, + registry: &R, + ) -> AzaleaResult<Option<R::Key>> { self.with_registry_holder(|registries| registry.key_owned(registries)) } @@ -525,7 +555,7 @@ impl Client { &self, registry: R, f: impl FnOnce(&Identifier, &R::DeserializesTo) -> Ret, - ) -> Option<Ret> { + ) -> AzaleaResult<Option<Ret>> { self.with_registry_holder(|registries| { registry .resolve(registries) @@ -538,8 +568,6 @@ impl Client { /// /// This is a shortcut for getting the [`TicksConnected`] component. pub fn ticks_connected(&self) -> u64 { - self.get_component::<TicksConnected>() - .map(|c| c.0) - .unwrap_or(0) + self.component::<TicksConnected>().map(|c| c.0).unwrap_or(0) } } diff --git a/azalea/src/client_impl/movement.rs b/azalea/src/client_impl/movement.rs index 9645f09e..4eec15d2 100644 --- a/azalea/src/client_impl/movement.rs +++ b/azalea/src/client_impl/movement.rs @@ -2,9 +2,8 @@ use azalea_client::{ ClientMovementState, SprintDirection, StartSprintEvent, StartWalkEvent, WalkDirection, }; use azalea_entity::{Jumping, LookDirection}; -use parking_lot::MappedRwLockReadGuard; -use crate::Client; +use crate::{Client, client_impl::error::AzaleaResult}; impl Client { /// Set whether we're jumping. This acts as if you held space in @@ -14,17 +13,17 @@ impl Client { /// /// If you're making a realistic client, calling this function every tick is /// recommended. - pub fn set_jumping(&self, jumping: bool) { - self.query_self::<&mut Jumping, _>(|mut j| **j = jumping); + pub fn set_jumping(&self, jumping: bool) -> AzaleaResult<()> { + self.query_self::<&mut Jumping, _>(|mut j| **j = jumping) } /// Returns whether the player will try to jump next tick. pub fn jumping(&self) -> bool { - **self.component::<Jumping>() + self.component::<Jumping>().map(|j| **j).unwrap_or_default() } - pub fn set_crouching(&self, crouching: bool) { - self.query_self::<&mut ClientMovementState, _>(|mut p| p.trying_to_crouch = crouching); + pub fn set_crouching(&self, crouching: bool) -> AzaleaResult<()> { + self.query_self::<&mut ClientMovementState, _>(|mut p| p.trying_to_crouch = crouching) } /// Whether the client is currently trying to sneak. @@ -32,6 +31,7 @@ impl Client { /// You may want to check the [`Pose`](azalea_entity::Pose) instead. pub fn crouching(&self) -> bool { self.query_self::<&ClientMovementState, _>(|p| p.trying_to_crouch) + .unwrap_or(false) } /// Sets the direction the client is looking. @@ -40,17 +40,17 @@ impl Client { /// is pitch (looking up and down, between -90 to 90). /// /// You can get these numbers from the vanilla f3 screen. - pub fn set_direction(&self, y_rot: f32, x_rot: f32) { + pub fn set_direction(&self, y_rot: f32, x_rot: f32) -> AzaleaResult<()> { self.query_self::<&mut LookDirection, _>(|mut ld| { ld.update(LookDirection::new(y_rot, x_rot)); - }); + }) } /// Returns the direction the client is looking. /// /// See [`Self::set_direction`] for more details. - pub fn direction(&self) -> LookDirection { - *self.component::<LookDirection>() + pub fn direction(&self) -> AzaleaResult<LookDirection> { + Ok(*self.component::<LookDirection>()?) } /// Start walking in the given direction. @@ -82,8 +82,8 @@ impl Client { /// /// This includes the direction that we're walking/sprinting in, and whether /// we're trying to sprint or crouch. - pub fn movement_state(&self) -> ClientMovementState { - self.component::<ClientMovementState>().clone() + pub fn movement_state(&self) -> AzaleaResult<ClientMovementState> { + Ok(self.component::<ClientMovementState>()?.clone()) } /// Start sprinting in the given direction. |
