diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs index 7b63ff12..02625326 100644 --- a/azalea-client/src/client.rs +++ b/azalea-client/src/client.rs @@ -342,13 +342,13 @@ impl Client { /// ``` /// # use azalea_client::{Client, mining::Mining}; /// # fn example(bot: &Client) { - /// let is_mining = bot.map_get_component::(|m| m.is_some()); + /// let is_mining = bot.map_get_component::(|m| m).is_some(); /// # } /// ``` - pub fn map_get_component(&self, f: impl FnOnce(Option<&T>) -> R) -> R { + pub fn map_get_component(&self, f: impl FnOnce(&T) -> R) -> Option { let mut ecs = self.ecs.lock(); let value = self.query::>(&mut ecs); - f(value) + value.map(f) } /// Get an `RwLock` with a reference to our (potentially shared) world. diff --git a/azalea-client/src/plugins/inventory.rs b/azalea-client/src/plugins/inventory.rs index 23651db3..a7e45ffb 100644 --- a/azalea-client/src/plugins/inventory.rs +++ b/azalea-client/src/plugins/inventory.rs @@ -95,8 +95,7 @@ impl Client { /// A component present on all local players that have an inventory. #[derive(Component, Debug, Clone)] pub struct Inventory { - /// A component that contains the player's inventory menu. This is - /// guaranteed to be a `Menu::Player`. + /// The player's inventory menu. This is guaranteed to be a `Menu::Player`. /// /// We keep it as a [`Menu`] since `Menu` has some useful functions that /// bare [`azalea_inventory::Player`] doesn't have. diff --git a/azalea-core/src/position.rs b/azalea-core/src/position.rs index 357e9b39..beb8eedb 100644 --- a/azalea-core/src/position.rs +++ b/azalea-core/src/position.rs @@ -335,8 +335,9 @@ impl Vec3 { } } -/// The coordinates of a block in the world. For entities (if the coordinate -/// have decimals), use [`Vec3`] instead. +/// The coordinates of a block in the world. +/// +/// For entities (if the coordinates are floating-point), use [`Vec3`] instead. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct BlockPos { diff --git a/azalea-world/src/chunk_storage.rs b/azalea-world/src/chunk_storage.rs index 23d1bb89..a6e33739 100644 --- a/azalea-world/src/chunk_storage.rs +++ b/azalea-world/src/chunk_storage.rs @@ -41,6 +41,9 @@ pub struct PartialChunkStorage { /// A storage for chunks where they're only stored weakly, so if they're not /// actively being used somewhere else they'll be forgotten. This is used for /// shared worlds. +/// +/// This is relatively cheap to clone since it's just an `IntMap` with `Weak` +/// pointers. #[derive(Debug, Clone)] pub struct ChunkStorage { pub height: u32, diff --git a/azalea/src/container.rs b/azalea/src/container.rs index e5896d8a..da3ddb8a 100644 --- a/azalea/src/container.rs +++ b/azalea/src/container.rs @@ -32,6 +32,7 @@ pub trait ContainerClientExt { fn open_inventory(&self) -> Option; fn get_held_item(&self) -> ItemStack; fn get_open_container(&self) -> Option; + fn view_inventory(&self) -> Menu; } impl ContainerClientExt for Client { @@ -100,9 +101,8 @@ impl ContainerClientExt for Client { /// Get the item in the bot's hotbar that is currently being held in its /// main hand. fn get_held_item(&self) -> ItemStack { - let ecs = self.ecs.lock(); - let inventory = ecs.get::(self.entity).expect("no inventory"); - inventory.held_item() + self.map_get_component::(|inventory| inventory.held_item()) + .expect("no inventory") } /// Get a handle to the open container. This will return None if no @@ -123,6 +123,15 @@ impl ContainerClientExt for Client { }) } } + + /// Returns the player's inventory menu. + /// + /// This is a shortcut for accessing the client's + /// [`Inventory::inventory_menu`]. + fn view_inventory(&self) -> Menu { + self.map_get_component::(|inventory| inventory.inventory_menu.clone()) + .expect("no inventory") + } } /// A handle to a container that may be open. This does not close the container diff --git a/azalea/src/pathfinder/goals.rs b/azalea/src/pathfinder/goals.rs index 5ab969c9..36fca762 100644 --- a/azalea/src/pathfinder/goals.rs +++ b/azalea/src/pathfinder/goals.rs @@ -29,7 +29,9 @@ impl Goal for BlockPosGoal { xz_heuristic(dx, dz) + y_heuristic(dy) } fn success(&self, n: BlockPos) -> bool { - n == self.0 + // the second half of this condition is intended to fix issues when pathing to + // non-full blocks + n == self.0 || n.down(1) == self.0 } } @@ -219,8 +221,8 @@ impl Goal for ReachBlockPosGoal { } fn success(&self, n: BlockPos) -> bool { // only do the expensive check if we're close enough - let distance = (self.pos - n).length_squared(); - if distance > self.max_check_distance * self.max_check_distance { + let distance = self.pos.distance_squared_to(&n); + if distance > self.max_check_distance.pow(2) { return false; } diff --git a/azalea/src/pathfinder/mod.rs b/azalea/src/pathfinder/mod.rs index 5ee56643..b05d2aab 100644 --- a/azalea/src/pathfinder/mod.rs +++ b/azalea/src/pathfinder/mod.rs @@ -274,10 +274,8 @@ impl PathfinderClientExt for azalea_client::Client { } fn is_goto_target_reached(&self) -> bool { - self.map_get_component::(|p| { - p.map(|p| p.goal.is_none() && !p.is_calculating) - .unwrap_or(true) - }) + self.map_get_component::(|p| p.goal.is_none() && !p.is_calculating) + .unwrap_or(true) } } @@ -689,7 +687,12 @@ pub fn timeout_movement( let world_lock = instance_container .get(instance_name) .expect("Entity tried to pathfind but the entity isn't in a valid world"); - let successors_fn: moves::SuccessorsFn = pathfinder.successors_fn.unwrap(); + let Some(successors_fn) = pathfinder.successors_fn else { + warn!( + "pathfinder was going to patch path because of timeout, but there was no successors_fn" + ); + return; + }; let custom_state = custom_state.cloned().unwrap_or_default(); @@ -749,7 +752,8 @@ pub fn check_node_reached( let z_difference_from_center = position.z - (movement.target.z as f64 + 0.5); // this is to make sure we don't fall off immediately after finishing the path physics.on_ground() - && BlockPos::from(position) == movement.target + // 0.5 to handle non-full blocks + && BlockPos::from(position.up(0.5)) == movement.target // adding the delta like this isn't a perfect solution but it helps to make // sure we don't keep going if our delta is high && (x_difference_from_center + physics.velocity.x).abs() < 0.2