aboutsummaryrefslogtreecommitdiff
path: root/azalea/src/client_impl
diff options
context:
space:
mode:
Diffstat (limited to 'azalea/src/client_impl')
-rw-r--r--azalea/src/client_impl/attack.rs4
-rw-r--r--azalea/src/client_impl/client_information.rs11
-rw-r--r--azalea/src/client_impl/entity_query.rs159
-rw-r--r--azalea/src/client_impl/error.rs29
-rw-r--r--azalea/src/client_impl/interact.rs6
-rw-r--r--azalea/src/client_impl/inventory.rs10
-rw-r--r--azalea/src/client_impl/mining.rs2
-rw-r--r--azalea/src/client_impl/mod.rs106
-rw-r--r--azalea/src/client_impl/movement.rs26
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.