diff --git a/CHANGELOG.md b/CHANGELOG.md index af76fc77..e54daf8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,7 +25,8 @@ is breaking anyways, semantic versioning is not followed. - [BREAKING] The `BlockState::id` field is now private, use `.id()` instead. - [BREAKING] Update to [Bevy 0.16](https://bevyengine.org/news/bevy-0-16/). - [BREAKING] Rename `InstanceContainer::insert` to `get_or_insert`. -- ClientBuilder and SwarmBuilder are now Send. +- [BREAKING] Replace `BlockInteractEvent` with the more general-purpose `StartUseItemEvent`, and add `client.start_use_item()`. +- `ClientBuilder` and `SwarmBuilder` are now Send. ### Fixed diff --git a/azalea-client/src/plugins/chunks.rs b/azalea-client/src/plugins/chunks.rs index 2d31aabb..5e062887 100644 --- a/azalea-client/src/plugins/chunks.rs +++ b/azalea-client/src/plugins/chunks.rs @@ -17,7 +17,7 @@ use bevy_ecs::prelude::*; use tracing::{error, trace}; use crate::{ - InstanceHolder, interact::handle_block_interact_event, inventory::InventorySet, + InstanceHolder, interact::handle_start_use_item_queued, inventory::InventorySet, packet::game::SendPacketEvent, respawn::perform_respawn, }; @@ -33,7 +33,7 @@ impl Plugin for ChunksPlugin { ) .chain() .before(InventorySet) - .before(handle_block_interact_event) + .before(handle_start_use_item_queued) .before(perform_respawn), ) .add_event::() diff --git a/azalea-client/src/plugins/interact.rs b/azalea-client/src/plugins/interact.rs index 81c69121..5ec95b46 100644 --- a/azalea-client/src/plugins/interact.rs +++ b/azalea-client/src/plugins/interact.rs @@ -1,21 +1,22 @@ -use std::ops::AddAssign; - use azalea_block::BlockState; use azalea_core::{ - block_hit_result::BlockHitResult, direction::Direction, game_type::GameMode, + hit_result::{BlockHitResult, HitResult}, position::{BlockPos, Vec3}, + tick::GameTick, }; use azalea_entity::{ Attributes, EyeHeight, LocalEntity, LookDirection, Position, clamp_look_direction, view_vector, }; use azalea_inventory::{ItemStack, ItemStackData, components}; -use azalea_physics::clip::{BlockShapeType, ClipContext, FluidPickType}; +use azalea_physics::{ + PhysicsSet, + clip::{BlockShapeType, ClipContext, FluidPickType}, +}; use azalea_protocol::packets::game::{ - s_interact::InteractionHand, - s_swing::ServerboundSwing, - s_use_item_on::{BlockHit, ServerboundUseItemOn}, + ServerboundUseItem, s_interact::InteractionHand, s_swing::ServerboundSwing, + s_use_item_on::ServerboundUseItemOn, }; use azalea_world::{Instance, InstanceContainer, InstanceName}; use bevy_app::{App, Plugin, Update}; @@ -23,6 +24,7 @@ use bevy_ecs::prelude::*; use derive_more::{Deref, DerefMut}; use tracing::warn; +use super::mining::{Mining, MiningSet}; use crate::{ Client, attack::handle_attack_event, @@ -37,14 +39,14 @@ use crate::{ pub struct InteractPlugin; impl Plugin for InteractPlugin { fn build(&self, app: &mut App) { - app.add_event::() + app.add_event::() .add_event::() .add_systems( Update, ( ( + handle_start_use_item_event, update_hit_result_component.after(clamp_look_direction), - handle_block_interact_event, handle_swing_arm_event, ) .after(InventorySet) @@ -56,34 +58,47 @@ impl Plugin for InteractPlugin { .after(MoveEventsSet), ), ) + .add_systems( + GameTick, + handle_start_use_item_queued + .after(MiningSet) + .before(PhysicsSet), + ) .add_observer(handle_swing_arm_trigger); } } impl Client { - /// Right click a block. The behavior of this depends on the target block, + /// Right-click a block. + /// + /// The behavior of this depends on the target block, /// and it'll either place the block you're holding in your hand or use the /// block you clicked (like toggling a lever). /// /// Note that this may trigger anticheats as it doesn't take into account /// whether you're actually looking at the block. pub fn block_interact(&self, position: BlockPos) { - self.ecs.lock().send_event(BlockInteractEvent { + self.ecs.lock().send_event(StartUseItemEvent { entity: self.entity, - position, + hand: InteractionHand::MainHand, + force_block: Some(position), }); } -} -/// Right click a block. The behavior of this depends on the target block, -/// and it'll either place the block you're holding in your hand or use the -/// block you clicked (like toggling a lever). -#[derive(Event)] -pub struct BlockInteractEvent { - /// The local player entity that's opening the container. - pub entity: Entity, - /// The coordinates of the container. - pub position: BlockPos, + /// Use the current item. + /// + /// If the item is consumable, then it'll act as if right-click was held + /// until the item finished being consumed. You can use this to eat food. + /// + /// If we're looking at a block or entity, then it will be clicked. Also see + /// [`Client::block_interact`]. + pub fn start_use_item(&self) { + self.ecs.lock().send_event(StartUseItemEvent { + entity: self.entity, + hand: InteractionHand::MainHand, + force_block: None, + }); + } } /// A component that contains the number of changes this client has made to @@ -91,66 +106,149 @@ pub struct BlockInteractEvent { #[derive(Component, Copy, Clone, Debug, Default, Deref)] pub struct CurrentSequenceNumber(u32); -impl AddAssign for CurrentSequenceNumber { - fn add_assign(&mut self, rhs: u32) { - self.0 += rhs; +impl CurrentSequenceNumber { + /// Get the next sequence number that we're going to use and increment the + /// value. + pub fn get_and_increment(&mut self) -> u32 { + let cur = self.0; + self.0 += 1; + cur } } -/// A component that contains the block that the player is currently looking at. +/// A component that contains the block or entity that the player is currently +/// looking at. #[doc(alias("looking at", "looking at block", "crosshair"))] #[derive(Component, Clone, Debug, Deref, DerefMut)] -pub struct HitResultComponent(BlockHitResult); +pub struct HitResultComponent(HitResult); -pub fn handle_block_interact_event( - mut events: EventReader, - mut query: Query<(Entity, &mut CurrentSequenceNumber, &HitResultComponent)>, +/// An event that makes one of our clients simulate a right-click. +/// +/// This event just inserts the [`StartUseItemQueued`] component on the given +/// entity. +#[doc(alias("right click"))] +#[derive(Event)] +pub struct StartUseItemEvent { + pub entity: Entity, + pub hand: InteractionHand, + /// See [`QueuedStartUseItem::force_block`]. + pub force_block: Option, +} +pub fn handle_start_use_item_event( mut commands: Commands, + mut events: EventReader, ) { for event in events.read() { - let Ok((entity, mut sequence_number, hit_result)) = query.get_mut(event.entity) else { - warn!("Sent BlockInteractEvent for entity that doesn't have the required components"); - continue; - }; + commands.entity(event.entity).insert(StartUseItemQueued { + hand: event.hand, + force_block: event.force_block, + }); + } +} - // TODO: check to make sure we're within the world border +/// A component that makes our client simulate a right-click on the next +/// [`GameTick`]. It's removed after that tick. +/// +/// You may find it more convenient to use [`StartUseItemEvent`] instead, which +/// just inserts this component for you. +/// +/// [`GameTick`]: azalea_core::tick::GameTick +#[derive(Component)] +pub struct StartUseItemQueued { + pub hand: InteractionHand, + /// Optionally force us to send a [`ServerboundUseItemOn`] on the given + /// block. + /// + /// This is useful if you want to interact with a block without looking at + /// it, but should be avoided to stay compatible with anticheats. + pub force_block: Option, +} +pub fn handle_start_use_item_queued( + mut commands: Commands, + query: Query<( + Entity, + &StartUseItemQueued, + &mut CurrentSequenceNumber, + &HitResultComponent, + &LookDirection, + Option<&Mining>, + )>, +) { + for (entity, start_use_item, mut sequence_number, hit_result, look_direction, mining) in query { + commands.entity(entity).remove::(); - *sequence_number += 1; + if mining.is_some() { + warn!("Got a StartUseItemEvent for a client that was mining"); + } - // minecraft also does the interaction client-side (so it looks like clicking a - // button is instant) but we don't really need that + // TODO: this also skips if LocalPlayer.handsBusy is true, which is used when + // rowing a boat - // the block_hit data will depend on whether we're looking at the block and - // whether we can reach it + let mut hit_result = hit_result.0.clone(); - let block_hit = if hit_result.block_pos == event.position { - // we're looking at the block :) - BlockHit { - block_pos: hit_result.block_pos, - direction: hit_result.direction, - location: hit_result.location, - inside: hit_result.inside, - world_border: hit_result.world_border, + if let Some(force_block) = start_use_item.force_block { + let hit_result_matches = if let HitResult::Block(block_hit_result) = hit_result { + block_hit_result.block_pos == force_block + } else { + false + }; + + if !hit_result_matches { + // we're not looking at the block, so make up some numbers + hit_result = HitResult::Block(BlockHitResult { + location: force_block.center(), + direction: Direction::Up, + block_pos: force_block, + inside: false, + world_border: false, + miss: false, + }); } - } else { - // we're not looking at the block, so make up some numbers - BlockHit { - block_pos: event.position, - direction: Direction::Up, - location: event.position.center(), - inside: false, - world_border: false, - } - }; + } - commands.trigger(SendPacketEvent::new( - entity, - ServerboundUseItemOn { - hand: InteractionHand::MainHand, - block_hit, - sequence: sequence_number.0, - }, - )); + match hit_result { + HitResult::Block(block_hit_result) => { + if block_hit_result.miss { + commands.trigger(SendPacketEvent::new( + entity, + ServerboundUseItem { + hand: start_use_item.hand, + sequence: sequence_number.get_and_increment(), + x_rot: look_direction.x_rot, + y_rot: look_direction.y_rot, + }, + )); + } else { + commands.trigger(SendPacketEvent::new( + entity, + ServerboundUseItemOn { + hand: start_use_item.hand, + block_hit: block_hit_result.into(), + sequence: sequence_number.get_and_increment(), + }, + )); + // TODO: depending on the result of useItemOn, this might + // also need to send a SwingArmEvent. + // basically, this TODO is for + // simulating block interactions/placements on the + // client-side. + } + } + HitResult::Entity => { + // TODO: implement HitResult::Entity + + // TODO: worldborder check + + // commands.trigger(SendPacketEvent::new( + // entity, + // ServerboundInteract { + // entity_id: todo!(), + // action: todo!(), + // using_secondary_action: todo!(), + // }, + // )); + } + } } } @@ -198,12 +296,32 @@ pub fn update_hit_result_component( } } +/// Get the block or entity that a player would be looking at if their eyes were +/// at the given direction and position. +/// +/// If you need to get the block/entity the player is looking at right now, use +/// [`HitResultComponent`]. +/// +/// Also see [`pick_block`]. +/// +/// TODO: does not currently check for entities +pub fn pick( + look_direction: &LookDirection, + eye_position: &Vec3, + chunks: &azalea_world::ChunkStorage, + pick_range: f64, +) -> HitResult { + // TODO + // let entity_hit_result = ; + + HitResult::Block(pick_block(look_direction, eye_position, chunks, pick_range)) +} + /// Get the block that a player would be looking at if their eyes were at the /// given direction and position. /// -/// If you need to get the block the player is looking at right now, use -/// [`HitResultComponent`]. -pub fn pick( +/// Also see [`pick`]. +pub fn pick_block( look_direction: &LookDirection, eye_position: &Vec3, chunks: &azalea_world::ChunkStorage, @@ -211,6 +329,7 @@ pub fn pick( ) -> BlockHitResult { let view_vector = view_vector(look_direction); let end_position = eye_position + &(view_vector * pick_range); + azalea_physics::clip::clip( chunks, ClipContext { diff --git a/azalea-client/src/plugins/mining.rs b/azalea-client/src/plugins/mining.rs index 7882ddc6..797c4361 100644 --- a/azalea-client/src/plugins/mining.rs +++ b/azalea-client/src/plugins/mining.rs @@ -39,7 +39,8 @@ impl Plugin for MiningPlugin { handle_mining_queued, ) .chain() - .before(PhysicsSet), + .before(PhysicsSet) + .in_set(MiningSet), ) .add_systems( Update, @@ -56,7 +57,7 @@ impl Plugin for MiningPlugin { .after(azalea_entity::update_fluid_on_eyes) .after(crate::interact::update_hit_result_component) .after(crate::attack::handle_attack_event) - .after(crate::interact::handle_block_interact_event) + .after(crate::interact::handle_start_use_item_queued) .before(crate::interact::handle_swing_arm_event), ); } @@ -121,22 +122,25 @@ fn handle_auto_mine( current_mining_item, ) in &mut query.iter_mut() { - let block_pos = hit_result_component.block_pos; + let block_pos = hit_result_component + .as_block_hit_result_if_not_miss() + .map(|b| b.block_pos); - if (mining.is_none() - || !is_same_mining_target( - block_pos, - inventory, - current_mining_pos, - current_mining_item, - )) - && !hit_result_component.miss + // start mining if we're looking at a block and we're not already mining it + if let Some(block_pos) = block_pos + && (mining.is_none() + || !is_same_mining_target( + block_pos, + inventory, + current_mining_pos, + current_mining_item, + )) { start_mining_block_event.write(StartMiningBlockEvent { entity, position: block_pos, }); - } else if mining.is_some() && hit_result_component.miss { + } else if mining.is_some() && hit_result_component.is_miss() { stop_mining_block_event.write(StopMiningBlockEvent { entity }); } } @@ -166,9 +170,11 @@ fn handle_start_mining_block_event( ) { for event in events.read() { let hit_result = query.get_mut(event.entity).unwrap(); - let direction = if hit_result.block_pos == event.position { + let direction = if let Some(block_hit_result) = hit_result.as_block_hit_result_if_not_miss() + && block_hit_result.block_pos == event.position + { // we're looking at the block - hit_result.direction + block_hit_result.direction } else { // we're not looking at the block, arbitrary direction Direction::Down @@ -241,7 +247,6 @@ fn handle_mining_queued( // is outside of the worldborder if game_mode.current == GameMode::Creative { - *sequence_number += 1; finish_mining_events.write(FinishMiningBlockEvent { entity, position: mining_queued.position, @@ -318,14 +323,13 @@ fn handle_mining_queued( }); } - *sequence_number += 1; commands.trigger(SendPacketEvent::new( entity, ServerboundPlayerAction { action: s_player_action::Action::StartDestroyBlock, pos: mining_queued.position, direction: mining_queued.direction, - sequence: **sequence_number, + sequence: sequence_number.get_and_increment(), }, )); commands.trigger(SwingArmEvent { entity }); @@ -558,14 +562,13 @@ pub fn continue_mining_block( entity, position: mining.pos, }); - *sequence_number += 1; commands.trigger(SendPacketEvent::new( entity, ServerboundPlayerAction { action: s_player_action::Action::StartDestroyBlock, pos: mining.pos, direction: mining.dir, - sequence: **sequence_number, + sequence: sequence_number.get_and_increment(), }, )); commands.trigger(SwingArmEvent { entity }); @@ -602,7 +605,6 @@ pub fn continue_mining_block( if **mine_progress >= 1. { commands.entity(entity).remove::(); - *sequence_number += 1; println!("finished mining block at {:?}", mining.pos); finish_mining_events.write(FinishMiningBlockEvent { entity, @@ -614,7 +616,7 @@ pub fn continue_mining_block( action: s_player_action::Action::StopDestroyBlock, pos: mining.pos, direction: mining.dir, - sequence: **sequence_number, + sequence: sequence_number.get_and_increment(), }, )); **mine_progress = 0.; @@ -638,16 +640,16 @@ pub fn continue_mining_block( } } -fn update_mining_component( +pub fn update_mining_component( mut commands: Commands, mut query: Query<(Entity, &mut Mining, &HitResultComponent)>, ) { for (entity, mut mining, hit_result_component) in &mut query.iter_mut() { - if hit_result_component.miss { - commands.entity(entity).remove::(); + if let Some(block_hit_result) = hit_result_component.as_block_hit_result_if_not_miss() { + mining.pos = block_hit_result.block_pos; + mining.dir = block_hit_result.direction; } else { - mining.pos = hit_result_component.block_pos; - mining.dir = hit_result_component.direction; + commands.entity(entity).remove::(); } } } diff --git a/azalea-core/src/aabb.rs b/azalea-core/src/aabb.rs index 6796e79c..42ae797d 100644 --- a/azalea-core/src/aabb.rs +++ b/azalea-core/src/aabb.rs @@ -1,6 +1,6 @@ use crate::{ - block_hit_result::BlockHitResult, direction::{Axis, Direction}, + hit_result::BlockHitResult, math::EPSILON, position::{BlockPos, Vec3}, }; diff --git a/azalea-core/src/block_hit_result.rs b/azalea-core/src/block_hit_result.rs deleted file mode 100644 index 4d930453..00000000 --- a/azalea-core/src/block_hit_result.rs +++ /dev/null @@ -1,34 +0,0 @@ -use crate::{ - direction::Direction, - position::{BlockPos, Vec3}, -}; - -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct BlockHitResult { - pub location: Vec3, - pub direction: Direction, - pub block_pos: BlockPos, - pub miss: bool, - pub inside: bool, - pub world_border: bool, -} - -impl BlockHitResult { - pub fn miss(location: Vec3, direction: Direction, block_pos: BlockPos) -> Self { - Self { - location, - direction, - block_pos, - miss: true, - inside: false, - world_border: false, - } - } - - pub fn with_direction(&self, direction: Direction) -> Self { - Self { direction, ..*self } - } - pub fn with_position(&self, block_pos: BlockPos) -> Self { - Self { block_pos, ..*self } - } -} diff --git a/azalea-core/src/hit_result.rs b/azalea-core/src/hit_result.rs new file mode 100644 index 00000000..2fc78115 --- /dev/null +++ b/azalea-core/src/hit_result.rs @@ -0,0 +1,68 @@ +use crate::{ + direction::Direction, + position::{BlockPos, Vec3}, +}; + +/// The block or entity that our player is looking at and can interact with. +/// +/// If there's nothing, it'll be a [`BlockHitResult`] with `miss` set to true. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum HitResult { + Block(BlockHitResult), + /// TODO + Entity, +} +impl HitResult { + pub fn is_miss(&self) -> bool { + match self { + HitResult::Block(block_hit_result) => block_hit_result.miss, + HitResult::Entity => false, + } + } + + pub fn is_block_hit_and_not_miss(&self) -> bool { + match self { + HitResult::Block(block_hit_result) => !block_hit_result.miss, + HitResult::Entity => false, + } + } + + /// Returns the [`BlockHitResult`], if we were looking at a block and it + /// wasn't a miss. + pub fn as_block_hit_result_if_not_miss(&self) -> Option<&BlockHitResult> { + match self { + HitResult::Block(block_hit_result) if !block_hit_result.miss => Some(block_hit_result), + _ => None, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct BlockHitResult { + pub location: Vec3, + pub direction: Direction, + pub block_pos: BlockPos, + pub inside: bool, + pub world_border: bool, + pub miss: bool, +} + +impl BlockHitResult { + pub fn miss(location: Vec3, direction: Direction, block_pos: BlockPos) -> Self { + Self { + location, + direction, + block_pos, + miss: true, + inside: false, + world_border: false, + } + } + + pub fn with_direction(&self, direction: Direction) -> Self { + Self { direction, ..*self } + } + pub fn with_position(&self, block_pos: BlockPos) -> Self { + Self { block_pos, ..*self } + } +} diff --git a/azalea-core/src/lib.rs b/azalea-core/src/lib.rs index a539dbd4..6c5e57c1 100644 --- a/azalea-core/src/lib.rs +++ b/azalea-core/src/lib.rs @@ -2,7 +2,6 @@ pub mod aabb; pub mod bitset; -pub mod block_hit_result; pub mod color; pub mod cursor3d; pub mod data_registry; @@ -11,6 +10,7 @@ pub mod difficulty; pub mod direction; pub mod filterable; pub mod game_type; +pub mod hit_result; pub mod math; pub mod objectives; pub mod position; diff --git a/azalea-entity/src/lib.rs b/azalea-entity/src/lib.rs index 88a1cfff..0f49039e 100644 --- a/azalea-entity/src/lib.rs +++ b/azalea-entity/src/lib.rs @@ -218,9 +218,9 @@ pub struct Jumping(pub bool); /// A component that contains the direction an entity is looking. #[derive(Debug, Component, Copy, Clone, Default, PartialEq, AzBuf)] pub struct LookDirection { - /// Left and right. Aka yaw. + /// Left and right. AKA yaw. pub y_rot: f32, - /// Up and down. Aka pitch. + /// Up and down. AKA pitch. pub x_rot: f32, } diff --git a/azalea-physics/src/clip.rs b/azalea-physics/src/clip.rs index 7c16f5d4..96c11d1a 100644 --- a/azalea-physics/src/clip.rs +++ b/azalea-physics/src/clip.rs @@ -6,8 +6,8 @@ use azalea_block::{ }; use azalea_core::{ aabb::AABB, - block_hit_result::BlockHitResult, direction::{Axis, Direction}, + hit_result::BlockHitResult, math::{self, EPSILON, lerp}, position::{BlockPos, Vec3}, }; diff --git a/azalea-physics/src/collision/shape.rs b/azalea-physics/src/collision/shape.rs index fc5615c3..e27e4c2a 100644 --- a/azalea-physics/src/collision/shape.rs +++ b/azalea-physics/src/collision/shape.rs @@ -1,8 +1,8 @@ use std::{cmp, num::NonZeroU32, sync::LazyLock}; use azalea_core::{ - block_hit_result::BlockHitResult, direction::{Axis, AxisCycle, Direction}, + hit_result::BlockHitResult, math::{EPSILON, binary_search}, position::{BlockPos, Vec3}, }; diff --git a/azalea-protocol/src/packets/game/s_interact.rs b/azalea-protocol/src/packets/game/s_interact.rs index 89fc896f..a3007749 100644 --- a/azalea-protocol/src/packets/game/s_interact.rs +++ b/azalea-protocol/src/packets/game/s_interact.rs @@ -80,8 +80,9 @@ impl AzaleaRead for ActionType { } } -#[derive(AzBuf, Clone, Copy, Debug)] +#[derive(AzBuf, Clone, Copy, Debug, Default)] pub enum InteractionHand { + #[default] MainHand = 0, OffHand = 1, } diff --git a/azalea-protocol/src/packets/game/s_use_item.rs b/azalea-protocol/src/packets/game/s_use_item.rs index ac8ae217..d6ccca87 100644 --- a/azalea-protocol/src/packets/game/s_use_item.rs +++ b/azalea-protocol/src/packets/game/s_use_item.rs @@ -8,6 +8,6 @@ pub struct ServerboundUseItem { pub hand: InteractionHand, #[var] pub sequence: u32, - pub yaw: f32, - pub pitch: f32, + pub y_rot: f32, + pub x_rot: f32, } diff --git a/azalea-protocol/src/packets/game/s_use_item_on.rs b/azalea-protocol/src/packets/game/s_use_item_on.rs index 4c87cb72..967fb5e9 100644 --- a/azalea-protocol/src/packets/game/s_use_item_on.rs +++ b/azalea-protocol/src/packets/game/s_use_item_on.rs @@ -3,6 +3,7 @@ use std::io::{Cursor, Write}; use azalea_buf::{AzBuf, AzaleaRead, AzaleaWrite, BufReadError}; use azalea_core::{ direction::Direction, + hit_result::BlockHitResult, position::{BlockPos, Vec3}, }; use azalea_protocol_macros::ServerboundGamePacket; @@ -77,3 +78,19 @@ impl AzaleaRead for BlockHit { }) } } + +impl From for BlockHit { + /// Converts a [`BlockHitResult`] to a [`BlockHit`]. + /// + /// The only difference is that the `miss` field is not present in + /// [`BlockHit`]. + fn from(hit_result: BlockHitResult) -> Self { + Self { + block_pos: hit_result.block_pos, + direction: hit_result.direction, + location: hit_result.location, + inside: hit_result.inside, + world_border: hit_result.world_border, + } + } +} diff --git a/azalea/examples/testbot/commands/debug.rs b/azalea/examples/testbot/commands/debug.rs index 31b0ff91..0bac22d4 100644 --- a/azalea/examples/testbot/commands/debug.rs +++ b/azalea/examples/testbot/commands/debug.rs @@ -104,10 +104,10 @@ pub fn register(commands: &mut CommandDispatcher>) { let hit_result = *source.bot.component::(); - if hit_result.miss { + let Some(hit_result) = hit_result.as_block_hit_result_if_not_miss() else { source.reply("I'm not looking at anything"); return 1; - } + }; let block_pos = hit_result.block_pos; let block = source.bot.world().read().get_block_state(&block_pos); @@ -174,6 +174,13 @@ pub fn register(commands: &mut CommandDispatcher>) { 1 })); + commands.register(literal("startuseitem").executes(|ctx: &Ctx| { + let source = ctx.source.lock(); + source.bot.start_use_item(); + source.reply("Ok!"); + 1 + })); + commands.register(literal("debugecsleak").executes(|ctx: &Ctx| { let source = ctx.source.lock(); diff --git a/azalea/src/pathfinder/goals.rs b/azalea/src/pathfinder/goals.rs index 9052c8fd..3ef45993 100644 --- a/azalea/src/pathfinder/goals.rs +++ b/azalea/src/pathfinder/goals.rs @@ -212,7 +212,7 @@ impl Goal for ReachBlockPosGoal { let eye_position = n.to_vec3_floored() + Vec3::new(0.5, 1.62, 0.5); let look_direction = crate::direction_looking_at(&eye_position, &self.pos.center()); - let block_hit_result = azalea_client::interact::pick( + let block_hit_result = azalea_client::interact::pick_block( &look_direction, &eye_position, &self.chunk_storage,