1
2
Fork 0
mirror of https://github.com/mat-1/azalea.git synced 2025-08-02 14:26:04 +00:00

add StartUseItemEvent and improve code related to interactions

This commit is contained in:
mat 2025-05-10 06:22:08 +03:30
parent e9b3128103
commit e1d3b902ba
16 changed files with 325 additions and 144 deletions

View file

@ -25,7 +25,8 @@ is breaking anyways, semantic versioning is not followed.
- [BREAKING] The `BlockState::id` field is now private, use `.id()` instead. - [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] Update to [Bevy 0.16](https://bevyengine.org/news/bevy-0-16/).
- [BREAKING] Rename `InstanceContainer::insert` to `get_or_insert`. - [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 ### Fixed

View file

@ -17,7 +17,7 @@ use bevy_ecs::prelude::*;
use tracing::{error, trace}; use tracing::{error, trace};
use crate::{ 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, packet::game::SendPacketEvent, respawn::perform_respawn,
}; };
@ -33,7 +33,7 @@ impl Plugin for ChunksPlugin {
) )
.chain() .chain()
.before(InventorySet) .before(InventorySet)
.before(handle_block_interact_event) .before(handle_start_use_item_queued)
.before(perform_respawn), .before(perform_respawn),
) )
.add_event::<ReceiveChunkEvent>() .add_event::<ReceiveChunkEvent>()

View file

@ -1,21 +1,22 @@
use std::ops::AddAssign;
use azalea_block::BlockState; use azalea_block::BlockState;
use azalea_core::{ use azalea_core::{
block_hit_result::BlockHitResult,
direction::Direction, direction::Direction,
game_type::GameMode, game_type::GameMode,
hit_result::{BlockHitResult, HitResult},
position::{BlockPos, Vec3}, position::{BlockPos, Vec3},
tick::GameTick,
}; };
use azalea_entity::{ use azalea_entity::{
Attributes, EyeHeight, LocalEntity, LookDirection, Position, clamp_look_direction, view_vector, Attributes, EyeHeight, LocalEntity, LookDirection, Position, clamp_look_direction, view_vector,
}; };
use azalea_inventory::{ItemStack, ItemStackData, components}; 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::{ use azalea_protocol::packets::game::{
s_interact::InteractionHand, ServerboundUseItem, s_interact::InteractionHand, s_swing::ServerboundSwing,
s_swing::ServerboundSwing, s_use_item_on::ServerboundUseItemOn,
s_use_item_on::{BlockHit, ServerboundUseItemOn},
}; };
use azalea_world::{Instance, InstanceContainer, InstanceName}; use azalea_world::{Instance, InstanceContainer, InstanceName};
use bevy_app::{App, Plugin, Update}; use bevy_app::{App, Plugin, Update};
@ -23,6 +24,7 @@ use bevy_ecs::prelude::*;
use derive_more::{Deref, DerefMut}; use derive_more::{Deref, DerefMut};
use tracing::warn; use tracing::warn;
use super::mining::{Mining, MiningSet};
use crate::{ use crate::{
Client, Client,
attack::handle_attack_event, attack::handle_attack_event,
@ -37,14 +39,14 @@ use crate::{
pub struct InteractPlugin; pub struct InteractPlugin;
impl Plugin for InteractPlugin { impl Plugin for InteractPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_event::<BlockInteractEvent>() app.add_event::<StartUseItemEvent>()
.add_event::<SwingArmEvent>() .add_event::<SwingArmEvent>()
.add_systems( .add_systems(
Update, Update,
( (
( (
handle_start_use_item_event,
update_hit_result_component.after(clamp_look_direction), update_hit_result_component.after(clamp_look_direction),
handle_block_interact_event,
handle_swing_arm_event, handle_swing_arm_event,
) )
.after(InventorySet) .after(InventorySet)
@ -56,34 +58,47 @@ impl Plugin for InteractPlugin {
.after(MoveEventsSet), .after(MoveEventsSet),
), ),
) )
.add_systems(
GameTick,
handle_start_use_item_queued
.after(MiningSet)
.before(PhysicsSet),
)
.add_observer(handle_swing_arm_trigger); .add_observer(handle_swing_arm_trigger);
} }
} }
impl Client { 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 /// and it'll either place the block you're holding in your hand or use the
/// block you clicked (like toggling a lever). /// block you clicked (like toggling a lever).
/// ///
/// Note that this may trigger anticheats as it doesn't take into account /// Note that this may trigger anticheats as it doesn't take into account
/// whether you're actually looking at the block. /// whether you're actually looking at the block.
pub fn block_interact(&self, position: BlockPos) { pub fn block_interact(&self, position: BlockPos) {
self.ecs.lock().send_event(BlockInteractEvent { self.ecs.lock().send_event(StartUseItemEvent {
entity: self.entity, entity: self.entity,
position, hand: InteractionHand::MainHand,
force_block: Some(position),
}); });
} }
}
/// Right click a block. The behavior of this depends on the target block, /// Use the current item.
/// and it'll either place the block you're holding in your hand or use the ///
/// block you clicked (like toggling a lever). /// If the item is consumable, then it'll act as if right-click was held
#[derive(Event)] /// until the item finished being consumed. You can use this to eat food.
pub struct BlockInteractEvent { ///
/// The local player entity that's opening the container. /// If we're looking at a block or entity, then it will be clicked. Also see
pub entity: Entity, /// [`Client::block_interact`].
/// The coordinates of the container. pub fn start_use_item(&self) {
pub position: BlockPos, 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 /// 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)] #[derive(Component, Copy, Clone, Debug, Default, Deref)]
pub struct CurrentSequenceNumber(u32); pub struct CurrentSequenceNumber(u32);
impl AddAssign<u32> for CurrentSequenceNumber { impl CurrentSequenceNumber {
fn add_assign(&mut self, rhs: u32) { /// Get the next sequence number that we're going to use and increment the
self.0 += rhs; /// 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"))] #[doc(alias("looking at", "looking at block", "crosshair"))]
#[derive(Component, Clone, Debug, Deref, DerefMut)] #[derive(Component, Clone, Debug, Deref, DerefMut)]
pub struct HitResultComponent(BlockHitResult); pub struct HitResultComponent(HitResult);
pub fn handle_block_interact_event( /// An event that makes one of our clients simulate a right-click.
mut events: EventReader<BlockInteractEvent>, ///
mut query: Query<(Entity, &mut CurrentSequenceNumber, &HitResultComponent)>, /// 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<BlockPos>,
}
pub fn handle_start_use_item_event(
mut commands: Commands, mut commands: Commands,
mut events: EventReader<StartUseItemEvent>,
) { ) {
for event in events.read() { for event in events.read() {
let Ok((entity, mut sequence_number, hit_result)) = query.get_mut(event.entity) else { commands.entity(event.entity).insert(StartUseItemQueued {
warn!("Sent BlockInteractEvent for entity that doesn't have the required components"); hand: event.hand,
continue; 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<BlockPos>,
}
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::<StartUseItemQueued>();
*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 // TODO: this also skips if LocalPlayer.handsBusy is true, which is used when
// button is instant) but we don't really need that // rowing a boat
// the block_hit data will depend on whether we're looking at the block and let mut hit_result = hit_result.0.clone();
// whether we can reach it
let block_hit = if hit_result.block_pos == event.position { if let Some(force_block) = start_use_item.force_block {
// we're looking at the block :) let hit_result_matches = if let HitResult::Block(block_hit_result) = hit_result {
BlockHit { block_hit_result.block_pos == force_block
block_pos: hit_result.block_pos, } else {
direction: hit_result.direction, false
location: hit_result.location, };
inside: hit_result.inside,
world_border: hit_result.world_border, 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( match hit_result {
entity, HitResult::Block(block_hit_result) => {
ServerboundUseItemOn { if block_hit_result.miss {
hand: InteractionHand::MainHand, commands.trigger(SendPacketEvent::new(
block_hit, entity,
sequence: sequence_number.0, 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 /// Get the block that a player would be looking at if their eyes were at the
/// given direction and position. /// given direction and position.
/// ///
/// If you need to get the block the player is looking at right now, use /// Also see [`pick`].
/// [`HitResultComponent`]. pub fn pick_block(
pub fn pick(
look_direction: &LookDirection, look_direction: &LookDirection,
eye_position: &Vec3, eye_position: &Vec3,
chunks: &azalea_world::ChunkStorage, chunks: &azalea_world::ChunkStorage,
@ -211,6 +329,7 @@ pub fn pick(
) -> BlockHitResult { ) -> BlockHitResult {
let view_vector = view_vector(look_direction); let view_vector = view_vector(look_direction);
let end_position = eye_position + &(view_vector * pick_range); let end_position = eye_position + &(view_vector * pick_range);
azalea_physics::clip::clip( azalea_physics::clip::clip(
chunks, chunks,
ClipContext { ClipContext {

View file

@ -39,7 +39,8 @@ impl Plugin for MiningPlugin {
handle_mining_queued, handle_mining_queued,
) )
.chain() .chain()
.before(PhysicsSet), .before(PhysicsSet)
.in_set(MiningSet),
) )
.add_systems( .add_systems(
Update, Update,
@ -56,7 +57,7 @@ impl Plugin for MiningPlugin {
.after(azalea_entity::update_fluid_on_eyes) .after(azalea_entity::update_fluid_on_eyes)
.after(crate::interact::update_hit_result_component) .after(crate::interact::update_hit_result_component)
.after(crate::attack::handle_attack_event) .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), .before(crate::interact::handle_swing_arm_event),
); );
} }
@ -121,22 +122,25 @@ fn handle_auto_mine(
current_mining_item, current_mining_item,
) in &mut query.iter_mut() ) 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() // start mining if we're looking at a block and we're not already mining it
|| !is_same_mining_target( if let Some(block_pos) = block_pos
block_pos, && (mining.is_none()
inventory, || !is_same_mining_target(
current_mining_pos, block_pos,
current_mining_item, inventory,
)) current_mining_pos,
&& !hit_result_component.miss current_mining_item,
))
{ {
start_mining_block_event.write(StartMiningBlockEvent { start_mining_block_event.write(StartMiningBlockEvent {
entity, entity,
position: block_pos, 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 }); stop_mining_block_event.write(StopMiningBlockEvent { entity });
} }
} }
@ -166,9 +170,11 @@ fn handle_start_mining_block_event(
) { ) {
for event in events.read() { for event in events.read() {
let hit_result = query.get_mut(event.entity).unwrap(); 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 // we're looking at the block
hit_result.direction block_hit_result.direction
} else { } else {
// we're not looking at the block, arbitrary direction // we're not looking at the block, arbitrary direction
Direction::Down Direction::Down
@ -241,7 +247,6 @@ fn handle_mining_queued(
// is outside of the worldborder // is outside of the worldborder
if game_mode.current == GameMode::Creative { if game_mode.current == GameMode::Creative {
*sequence_number += 1;
finish_mining_events.write(FinishMiningBlockEvent { finish_mining_events.write(FinishMiningBlockEvent {
entity, entity,
position: mining_queued.position, position: mining_queued.position,
@ -318,14 +323,13 @@ fn handle_mining_queued(
}); });
} }
*sequence_number += 1;
commands.trigger(SendPacketEvent::new( commands.trigger(SendPacketEvent::new(
entity, entity,
ServerboundPlayerAction { ServerboundPlayerAction {
action: s_player_action::Action::StartDestroyBlock, action: s_player_action::Action::StartDestroyBlock,
pos: mining_queued.position, pos: mining_queued.position,
direction: mining_queued.direction, direction: mining_queued.direction,
sequence: **sequence_number, sequence: sequence_number.get_and_increment(),
}, },
)); ));
commands.trigger(SwingArmEvent { entity }); commands.trigger(SwingArmEvent { entity });
@ -558,14 +562,13 @@ pub fn continue_mining_block(
entity, entity,
position: mining.pos, position: mining.pos,
}); });
*sequence_number += 1;
commands.trigger(SendPacketEvent::new( commands.trigger(SendPacketEvent::new(
entity, entity,
ServerboundPlayerAction { ServerboundPlayerAction {
action: s_player_action::Action::StartDestroyBlock, action: s_player_action::Action::StartDestroyBlock,
pos: mining.pos, pos: mining.pos,
direction: mining.dir, direction: mining.dir,
sequence: **sequence_number, sequence: sequence_number.get_and_increment(),
}, },
)); ));
commands.trigger(SwingArmEvent { entity }); commands.trigger(SwingArmEvent { entity });
@ -602,7 +605,6 @@ pub fn continue_mining_block(
if **mine_progress >= 1. { if **mine_progress >= 1. {
commands.entity(entity).remove::<Mining>(); commands.entity(entity).remove::<Mining>();
*sequence_number += 1;
println!("finished mining block at {:?}", mining.pos); println!("finished mining block at {:?}", mining.pos);
finish_mining_events.write(FinishMiningBlockEvent { finish_mining_events.write(FinishMiningBlockEvent {
entity, entity,
@ -614,7 +616,7 @@ pub fn continue_mining_block(
action: s_player_action::Action::StopDestroyBlock, action: s_player_action::Action::StopDestroyBlock,
pos: mining.pos, pos: mining.pos,
direction: mining.dir, direction: mining.dir,
sequence: **sequence_number, sequence: sequence_number.get_and_increment(),
}, },
)); ));
**mine_progress = 0.; **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 commands: Commands,
mut query: Query<(Entity, &mut Mining, &HitResultComponent)>, mut query: Query<(Entity, &mut Mining, &HitResultComponent)>,
) { ) {
for (entity, mut mining, hit_result_component) in &mut query.iter_mut() { for (entity, mut mining, hit_result_component) in &mut query.iter_mut() {
if hit_result_component.miss { if let Some(block_hit_result) = hit_result_component.as_block_hit_result_if_not_miss() {
commands.entity(entity).remove::<Mining>(); mining.pos = block_hit_result.block_pos;
mining.dir = block_hit_result.direction;
} else { } else {
mining.pos = hit_result_component.block_pos; commands.entity(entity).remove::<Mining>();
mining.dir = hit_result_component.direction;
} }
} }
} }

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
block_hit_result::BlockHitResult,
direction::{Axis, Direction}, direction::{Axis, Direction},
hit_result::BlockHitResult,
math::EPSILON, math::EPSILON,
position::{BlockPos, Vec3}, position::{BlockPos, Vec3},
}; };

View file

@ -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 }
}
}

View file

@ -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 }
}
}

View file

@ -2,7 +2,6 @@
pub mod aabb; pub mod aabb;
pub mod bitset; pub mod bitset;
pub mod block_hit_result;
pub mod color; pub mod color;
pub mod cursor3d; pub mod cursor3d;
pub mod data_registry; pub mod data_registry;
@ -11,6 +10,7 @@ pub mod difficulty;
pub mod direction; pub mod direction;
pub mod filterable; pub mod filterable;
pub mod game_type; pub mod game_type;
pub mod hit_result;
pub mod math; pub mod math;
pub mod objectives; pub mod objectives;
pub mod position; pub mod position;

View file

@ -218,9 +218,9 @@ pub struct Jumping(pub bool);
/// A component that contains the direction an entity is looking. /// A component that contains the direction an entity is looking.
#[derive(Debug, Component, Copy, Clone, Default, PartialEq, AzBuf)] #[derive(Debug, Component, Copy, Clone, Default, PartialEq, AzBuf)]
pub struct LookDirection { pub struct LookDirection {
/// Left and right. Aka yaw. /// Left and right. AKA yaw.
pub y_rot: f32, pub y_rot: f32,
/// Up and down. Aka pitch. /// Up and down. AKA pitch.
pub x_rot: f32, pub x_rot: f32,
} }

View file

@ -6,8 +6,8 @@ use azalea_block::{
}; };
use azalea_core::{ use azalea_core::{
aabb::AABB, aabb::AABB,
block_hit_result::BlockHitResult,
direction::{Axis, Direction}, direction::{Axis, Direction},
hit_result::BlockHitResult,
math::{self, EPSILON, lerp}, math::{self, EPSILON, lerp},
position::{BlockPos, Vec3}, position::{BlockPos, Vec3},
}; };

View file

@ -1,8 +1,8 @@
use std::{cmp, num::NonZeroU32, sync::LazyLock}; use std::{cmp, num::NonZeroU32, sync::LazyLock};
use azalea_core::{ use azalea_core::{
block_hit_result::BlockHitResult,
direction::{Axis, AxisCycle, Direction}, direction::{Axis, AxisCycle, Direction},
hit_result::BlockHitResult,
math::{EPSILON, binary_search}, math::{EPSILON, binary_search},
position::{BlockPos, Vec3}, position::{BlockPos, Vec3},
}; };

View file

@ -80,8 +80,9 @@ impl AzaleaRead for ActionType {
} }
} }
#[derive(AzBuf, Clone, Copy, Debug)] #[derive(AzBuf, Clone, Copy, Debug, Default)]
pub enum InteractionHand { pub enum InteractionHand {
#[default]
MainHand = 0, MainHand = 0,
OffHand = 1, OffHand = 1,
} }

View file

@ -8,6 +8,6 @@ pub struct ServerboundUseItem {
pub hand: InteractionHand, pub hand: InteractionHand,
#[var] #[var]
pub sequence: u32, pub sequence: u32,
pub yaw: f32, pub y_rot: f32,
pub pitch: f32, pub x_rot: f32,
} }

View file

@ -3,6 +3,7 @@ use std::io::{Cursor, Write};
use azalea_buf::{AzBuf, AzaleaRead, AzaleaWrite, BufReadError}; use azalea_buf::{AzBuf, AzaleaRead, AzaleaWrite, BufReadError};
use azalea_core::{ use azalea_core::{
direction::Direction, direction::Direction,
hit_result::BlockHitResult,
position::{BlockPos, Vec3}, position::{BlockPos, Vec3},
}; };
use azalea_protocol_macros::ServerboundGamePacket; use azalea_protocol_macros::ServerboundGamePacket;
@ -77,3 +78,19 @@ impl AzaleaRead for BlockHit {
}) })
} }
} }
impl From<BlockHitResult> 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,
}
}
}

View file

@ -104,10 +104,10 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
let hit_result = *source.bot.component::<HitResultComponent>(); let hit_result = *source.bot.component::<HitResultComponent>();
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"); source.reply("I'm not looking at anything");
return 1; return 1;
} };
let block_pos = hit_result.block_pos; let block_pos = hit_result.block_pos;
let block = source.bot.world().read().get_block_state(&block_pos); let block = source.bot.world().read().get_block_state(&block_pos);
@ -174,6 +174,13 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
1 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| { commands.register(literal("debugecsleak").executes(|ctx: &Ctx| {
let source = ctx.source.lock(); let source = ctx.source.lock();

View file

@ -212,7 +212,7 @@ impl Goal for ReachBlockPosGoal {
let eye_position = n.to_vec3_floored() + Vec3::new(0.5, 1.62, 0.5); 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 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, &look_direction,
&eye_position, &eye_position,
&self.chunk_storage, &self.chunk_storage,