mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 06:16:04 +00:00
fix various issues with mining
This commit is contained in:
parent
d58a1c4fa0
commit
e9452032bf
6 changed files with 92 additions and 81 deletions
|
@ -36,3 +36,4 @@ write down most non-trivial breaking changes.
|
|||
- Update the `InstanceName` component correctly when we receive a respawn or second login packet.
|
||||
- Block shapes and some properties were using data from `1.20.3-pre4` due to using an old data generator (Pixlyzer), which has now been replaced with the data generator from [Pumpkin](https://github.com/Pumpkin-MC/Extractor).
|
||||
- No more chunk errors when the client joins another world with the same name but different height.
|
||||
- Mining now cancels correctly and doesn't flag Grim.
|
||||
|
|
|
@ -98,6 +98,7 @@ impl AddAssign<u32> for CurrentSequenceNumber {
|
|||
}
|
||||
|
||||
/// A component that contains the block 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);
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ use azalea_block::{Block, BlockState, fluid_state::FluidState};
|
|||
use azalea_core::{direction::Direction, game_type::GameMode, position::BlockPos, tick::GameTick};
|
||||
use azalea_entity::{FluidOnEyes, Physics, mining::get_mine_progress};
|
||||
use azalea_inventory::ItemStack;
|
||||
use azalea_physics::PhysicsSet;
|
||||
use azalea_physics::{PhysicsSet, collision::BlockWithShape};
|
||||
use azalea_protocol::packets::game::s_player_action::{self, ServerboundPlayerAction};
|
||||
use azalea_world::{InstanceContainer, InstanceName};
|
||||
use bevy_app::{App, Plugin, Update};
|
||||
|
@ -10,7 +10,7 @@ use bevy_ecs::prelude::*;
|
|||
use derive_more::{Deref, DerefMut};
|
||||
|
||||
use crate::{
|
||||
Client,
|
||||
Client, InstanceHolder,
|
||||
interact::{
|
||||
CurrentSequenceNumber, HitResultComponent, SwingArmEvent, can_use_game_master_blocks,
|
||||
check_is_interaction_restricted,
|
||||
|
@ -26,14 +26,18 @@ pub struct MiningPlugin;
|
|||
impl Plugin for MiningPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_event::<StartMiningBlockEvent>()
|
||||
.add_event::<StartMiningBlockWithDirectionEvent>()
|
||||
.add_event::<FinishMiningBlockEvent>()
|
||||
.add_event::<StopMiningBlockEvent>()
|
||||
.add_event::<MineBlockProgressEvent>()
|
||||
.add_event::<AttackBlockEvent>()
|
||||
.add_systems(
|
||||
GameTick,
|
||||
(continue_mining_block, handle_auto_mine)
|
||||
(
|
||||
update_mining_component,
|
||||
continue_mining_block,
|
||||
handle_auto_mine,
|
||||
handle_mining_queued,
|
||||
)
|
||||
.chain()
|
||||
.before(PhysicsSet),
|
||||
)
|
||||
|
@ -41,7 +45,6 @@ impl Plugin for MiningPlugin {
|
|||
Update,
|
||||
(
|
||||
handle_start_mining_block_event,
|
||||
handle_start_mining_block_with_direction_event,
|
||||
handle_finish_mining_block_event,
|
||||
handle_stop_mining_block_event,
|
||||
)
|
||||
|
@ -65,7 +68,9 @@ pub struct MiningSet;
|
|||
|
||||
impl Client {
|
||||
pub fn start_mining(&self, position: BlockPos) {
|
||||
self.ecs.lock().send_event(StartMiningBlockEvent {
|
||||
let mut ecs = self.ecs.lock();
|
||||
|
||||
ecs.send_event(StartMiningBlockEvent {
|
||||
entity: self.entity,
|
||||
position,
|
||||
});
|
||||
|
@ -155,8 +160,8 @@ pub struct StartMiningBlockEvent {
|
|||
pub position: BlockPos,
|
||||
}
|
||||
fn handle_start_mining_block_event(
|
||||
mut commands: Commands,
|
||||
mut events: EventReader<StartMiningBlockEvent>,
|
||||
mut start_mining_events: EventWriter<StartMiningBlockWithDirectionEvent>,
|
||||
mut query: Query<&HitResultComponent>,
|
||||
) {
|
||||
for event in events.read() {
|
||||
|
@ -168,29 +173,29 @@ fn handle_start_mining_block_event(
|
|||
// we're not looking at the block, arbitrary direction
|
||||
Direction::Down
|
||||
};
|
||||
start_mining_events.write(StartMiningBlockWithDirectionEvent {
|
||||
entity: event.entity,
|
||||
commands.entity(event.entity).insert(MiningQueued {
|
||||
position: event.position,
|
||||
direction,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Event)]
|
||||
pub struct StartMiningBlockWithDirectionEvent {
|
||||
pub entity: Entity,
|
||||
/// Present on entities when they're going to start mining a block next tick.
|
||||
#[derive(Component)]
|
||||
pub struct MiningQueued {
|
||||
pub position: BlockPos,
|
||||
pub direction: Direction,
|
||||
}
|
||||
#[allow(clippy::too_many_arguments, clippy::type_complexity)]
|
||||
fn handle_start_mining_block_with_direction_event(
|
||||
mut events: EventReader<StartMiningBlockWithDirectionEvent>,
|
||||
mut finish_mining_events: EventWriter<FinishMiningBlockEvent>,
|
||||
fn handle_mining_queued(
|
||||
mut commands: Commands,
|
||||
mut finish_mining_events: EventWriter<FinishMiningBlockEvent>,
|
||||
mut attack_block_events: EventWriter<AttackBlockEvent>,
|
||||
mut mine_block_progress_events: EventWriter<MineBlockProgressEvent>,
|
||||
mut query: Query<(
|
||||
&InstanceName,
|
||||
query: Query<(
|
||||
Entity,
|
||||
&MiningQueued,
|
||||
&InstanceHolder,
|
||||
&LocalGameMode,
|
||||
&Inventory,
|
||||
&FluidOnEyes,
|
||||
|
@ -203,29 +208,30 @@ fn handle_start_mining_block_with_direction_event(
|
|||
&mut MineItem,
|
||||
&mut MineBlockPos,
|
||||
)>,
|
||||
instances: Res<InstanceContainer>,
|
||||
) {
|
||||
for event in events.read() {
|
||||
let (
|
||||
instance_name,
|
||||
game_mode,
|
||||
inventory,
|
||||
fluid_on_eyes,
|
||||
physics,
|
||||
mining,
|
||||
mut sequence_number,
|
||||
mut mine_delay,
|
||||
mut mine_progress,
|
||||
mut mine_ticks,
|
||||
mut current_mining_item,
|
||||
mut current_mining_pos,
|
||||
) = query.get_mut(event.entity).unwrap();
|
||||
for (
|
||||
entity,
|
||||
mining_queued,
|
||||
instance_holder,
|
||||
game_mode,
|
||||
inventory,
|
||||
fluid_on_eyes,
|
||||
physics,
|
||||
mining,
|
||||
mut sequence_number,
|
||||
mut mine_delay,
|
||||
mut mine_progress,
|
||||
mut mine_ticks,
|
||||
mut current_mining_item,
|
||||
mut current_mining_pos,
|
||||
) in query
|
||||
{
|
||||
commands.entity(entity).remove::<MiningQueued>();
|
||||
|
||||
let instance_lock = instances.get(instance_name).unwrap();
|
||||
let instance = instance_lock.read();
|
||||
let instance = instance_holder.instance.read();
|
||||
if check_is_interaction_restricted(
|
||||
&instance,
|
||||
&event.position,
|
||||
&mining_queued.position,
|
||||
&game_mode.current,
|
||||
inventory,
|
||||
) {
|
||||
|
@ -237,13 +243,13 @@ fn handle_start_mining_block_with_direction_event(
|
|||
if game_mode.current == GameMode::Creative {
|
||||
*sequence_number += 1;
|
||||
finish_mining_events.write(FinishMiningBlockEvent {
|
||||
entity: event.entity,
|
||||
position: event.position,
|
||||
entity,
|
||||
position: mining_queued.position,
|
||||
});
|
||||
**mine_delay = 5;
|
||||
} else if mining.is_none()
|
||||
|| !is_same_mining_target(
|
||||
event.position,
|
||||
mining_queued.position,
|
||||
inventory,
|
||||
¤t_mining_pos,
|
||||
¤t_mining_item,
|
||||
|
@ -252,40 +258,29 @@ fn handle_start_mining_block_with_direction_event(
|
|||
if mining.is_some() {
|
||||
// send a packet to stop mining since we just changed target
|
||||
commands.trigger(SendPacketEvent::new(
|
||||
event.entity,
|
||||
entity,
|
||||
ServerboundPlayerAction {
|
||||
action: s_player_action::Action::AbortDestroyBlock,
|
||||
pos: current_mining_pos
|
||||
.expect("IsMining is true so MineBlockPos must be present"),
|
||||
direction: event.direction,
|
||||
direction: mining_queued.direction,
|
||||
sequence: 0,
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
let target_block_state = instance
|
||||
.get_block_state(&event.position)
|
||||
.get_block_state(&mining_queued.position)
|
||||
.unwrap_or_default();
|
||||
*sequence_number += 1;
|
||||
let target_registry_block = azalea_registry::Block::from(target_block_state);
|
||||
|
||||
// we can't break blocks if they don't have a bounding box
|
||||
|
||||
// TODO: So right now azalea doesn't differenciate between different types of
|
||||
// bounding boxes. See ClipContext::block_shape for more info. Ideally this
|
||||
// should just call ClipContext::block_shape and check if it's empty.
|
||||
let block_is_solid = !target_block_state.is_air()
|
||||
// this is a hack to make sure we can't break water or lava
|
||||
&& !matches!(
|
||||
target_registry_block,
|
||||
azalea_registry::Block::Water | azalea_registry::Block::Lava
|
||||
);
|
||||
let block_is_solid = !target_block_state.outline_shape().is_empty();
|
||||
|
||||
if block_is_solid && **mine_progress == 0. {
|
||||
// interact with the block (like note block left click) here
|
||||
attack_block_events.write(AttackBlockEvent {
|
||||
entity: event.entity,
|
||||
position: event.position,
|
||||
entity,
|
||||
position: mining_queued.position,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -304,34 +299,37 @@ fn handle_start_mining_block_with_direction_event(
|
|||
{
|
||||
// block was broken instantly
|
||||
finish_mining_events.write(FinishMiningBlockEvent {
|
||||
entity: event.entity,
|
||||
position: event.position,
|
||||
entity,
|
||||
position: mining_queued.position,
|
||||
});
|
||||
} else {
|
||||
commands.entity(event.entity).insert(Mining {
|
||||
pos: event.position,
|
||||
dir: event.direction,
|
||||
commands.entity(entity).insert(Mining {
|
||||
pos: mining_queued.position,
|
||||
dir: mining_queued.direction,
|
||||
});
|
||||
**current_mining_pos = Some(event.position);
|
||||
**current_mining_pos = Some(mining_queued.position);
|
||||
**current_mining_item = held_item;
|
||||
**mine_progress = 0.;
|
||||
**mine_ticks = 0.;
|
||||
mine_block_progress_events.write(MineBlockProgressEvent {
|
||||
entity: event.entity,
|
||||
position: event.position,
|
||||
entity,
|
||||
position: mining_queued.position,
|
||||
destroy_stage: mine_progress.destroy_stage(),
|
||||
});
|
||||
}
|
||||
|
||||
*sequence_number += 1;
|
||||
commands.trigger(SendPacketEvent::new(
|
||||
event.entity,
|
||||
entity,
|
||||
ServerboundPlayerAction {
|
||||
action: s_player_action::Action::StartDestroyBlock,
|
||||
pos: event.position,
|
||||
direction: event.direction,
|
||||
pos: mining_queued.position,
|
||||
direction: mining_queued.direction,
|
||||
sequence: **sequence_number,
|
||||
},
|
||||
));
|
||||
commands.trigger(SwingArmEvent { entity });
|
||||
commands.trigger(SwingArmEvent { entity });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -530,8 +528,6 @@ pub fn continue_mining_block(
|
|||
mut commands: Commands,
|
||||
mut mine_block_progress_events: EventWriter<MineBlockProgressEvent>,
|
||||
mut finish_mining_events: EventWriter<FinishMiningBlockEvent>,
|
||||
mut start_mining_events: EventWriter<StartMiningBlockWithDirectionEvent>,
|
||||
mut swing_arm_events: EventWriter<SwingArmEvent>,
|
||||
instances: Res<InstanceContainer>,
|
||||
) {
|
||||
for (
|
||||
|
@ -572,17 +568,20 @@ pub fn continue_mining_block(
|
|||
sequence: **sequence_number,
|
||||
},
|
||||
));
|
||||
swing_arm_events.write(SwingArmEvent { entity });
|
||||
commands.trigger(SwingArmEvent { entity });
|
||||
} else if is_same_mining_target(
|
||||
mining.pos,
|
||||
inventory,
|
||||
current_mining_pos,
|
||||
current_mining_item,
|
||||
) {
|
||||
println!("continue mining block at {:?}", mining.pos);
|
||||
let instance_lock = instances.get(instance_name).unwrap();
|
||||
let instance = instance_lock.read();
|
||||
let target_block_state = instance.get_block_state(&mining.pos).unwrap_or_default();
|
||||
|
||||
println!("target_block_state: {:?}", target_block_state);
|
||||
|
||||
if target_block_state.is_air() {
|
||||
commands.entity(entity).remove::<Mining>();
|
||||
continue;
|
||||
|
@ -604,6 +603,7 @@ pub fn continue_mining_block(
|
|||
if **mine_progress >= 1. {
|
||||
commands.entity(entity).remove::<Mining>();
|
||||
*sequence_number += 1;
|
||||
println!("finished mining block at {:?}", mining.pos);
|
||||
finish_mining_events.write(FinishMiningBlockEvent {
|
||||
entity,
|
||||
position: mining.pos,
|
||||
|
@ -627,15 +627,27 @@ pub fn continue_mining_block(
|
|||
position: mining.pos,
|
||||
destroy_stage: mine_progress.destroy_stage(),
|
||||
});
|
||||
swing_arm_events.write(SwingArmEvent { entity });
|
||||
commands.trigger(SwingArmEvent { entity });
|
||||
} else {
|
||||
start_mining_events.write(StartMiningBlockWithDirectionEvent {
|
||||
entity,
|
||||
println!("switching mining target to {:?}", mining.pos);
|
||||
commands.entity(entity).insert(MiningQueued {
|
||||
position: mining.pos,
|
||||
direction: mining.dir,
|
||||
});
|
||||
}
|
||||
|
||||
swing_arm_events.write(SwingArmEvent { entity });
|
||||
}
|
||||
}
|
||||
|
||||
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::<Mining>();
|
||||
} else {
|
||||
mining.pos = hit_result_component.block_pos;
|
||||
mining.dir = hit_result_component.direction;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ use azalea_protocol::packets::{
|
|||
use azalea_world::Instance;
|
||||
use bevy_ecs::prelude::*;
|
||||
use parking_lot::RwLock;
|
||||
use tracing::error;
|
||||
use tracing::{error, trace};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{PlayerInfo, client::InGameState, connection::RawConnection};
|
||||
|
@ -60,6 +60,7 @@ pub fn handle_outgoing_packets_observer(
|
|||
mut query: Query<(&mut RawConnection, Option<&InGameState>)>,
|
||||
) {
|
||||
let event = trigger.event();
|
||||
trace!("Sending game packet: {:?}", event.packet);
|
||||
|
||||
if let Ok((mut raw_connection, in_game_state)) = query.get_mut(event.sent_by) {
|
||||
if in_game_state.is_none() {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use std::f64::consts::PI;
|
||||
|
||||
use azalea_client::interact::SwingArmEvent;
|
||||
use azalea_client::mining::Mining;
|
||||
use azalea_client::tick_broadcast::{TickBroadcast, UpdateBroadcast};
|
||||
use azalea_core::position::{BlockPos, Vec3};
|
||||
|
@ -172,10 +171,6 @@ impl BotClientExt for azalea_client::Client {
|
|||
|
||||
async fn mine(&self, position: BlockPos) {
|
||||
self.start_mining(position);
|
||||
// vanilla sends an extra swing arm packet when we start mining
|
||||
self.ecs.lock().send_event(SwingArmEvent {
|
||||
entity: self.entity,
|
||||
});
|
||||
|
||||
let mut receiver = self.get_tick_broadcaster();
|
||||
while receiver.recv().await.is_ok() {
|
||||
|
|
|
@ -166,6 +166,7 @@ impl ExecuteCtx<'_, '_, '_, '_, '_, '_, '_> {
|
|||
|
||||
if self.should_mine(block) {
|
||||
if at_start_position {
|
||||
self.look_at(block.center());
|
||||
self.mine(block);
|
||||
} else {
|
||||
self.look_at(self.start.center());
|
||||
|
|
Loading…
Add table
Reference in a new issue