aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormat <git@matdoes.dev>2025-10-04 18:15:44 -1345
committermat <git@matdoes.dev>2025-10-04 18:15:44 -1345
commitc512bf824f1ede6606c5254dbf8b2f4eafed4ecf (patch)
tree6e8d39cae08017b2109ca43a7e0d4d0c9af63caf
parent4a9e6d2c4b5cedfbe1105f4f5d68933a0cbf8214 (diff)
downloadazalea-drasl-c512bf824f1ede6606c5254dbf8b2f4eafed4ecf.tar.xz
make 'force' always explicit when mining
-rw-r--r--CHANGELOG.md2
-rw-r--r--azalea-client/src/plugins/inventory.rs50
-rw-r--r--azalea-client/src/plugins/mining.rs87
-rw-r--r--azalea/src/pathfinder/mod.rs6
-rw-r--r--azalea/src/pathfinder/moves/mod.rs30
5 files changed, 104 insertions, 71 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d2d68bbe..3712fa61 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,7 +22,7 @@ is breaking anyways, semantic versioning is not followed.
### Fixed
- The wrong path was temporarily executed if we received a `GotoEvent` while the path that's being executed was more than 50 nodes long.
-- Don't panic when receiving an unexpected `PathFoundEvent` (@Hiradpi)
+- Don't panic when receiving an unexpected `PathFoundEvent`. (@Hiradpi)
## [0.14.0+mc1.21.8] - 2025-09-28
diff --git a/azalea-client/src/plugins/inventory.rs b/azalea-client/src/plugins/inventory.rs
index 3c57b3cc..a805ae9b 100644
--- a/azalea-client/src/plugins/inventory.rs
+++ b/azalea-client/src/plugins/inventory.rs
@@ -18,32 +18,28 @@ use azalea_protocol::packets::game::{
};
use azalea_registry::MenuKind;
use azalea_world::{InstanceContainer, InstanceName};
-use bevy_app::{App, Plugin, Update};
+use bevy_app::{App, Plugin};
use bevy_ecs::prelude::*;
use indexmap::IndexMap;
use tracing::{error, warn};
-use crate::{Client, packet::game::SendGamePacketEvent, respawn::perform_respawn};
+use crate::{Client, packet::game::SendGamePacketEvent};
pub struct InventoryPlugin;
impl Plugin for InventoryPlugin {
fn build(&self, app: &mut App) {
- app.add_message::<SetSelectedHotbarSlotEvent>()
- .add_systems(
- Update,
- handle_set_selected_hotbar_slot_event
- .in_set(InventorySystems)
- .before(perform_respawn),
- )
- .add_systems(
- GameTick,
- ensure_has_sent_carried_item.after(super::mining::handle_mining_queued),
- )
- .add_observer(handle_client_side_close_container_trigger)
- .add_observer(handle_menu_opened_trigger)
- .add_observer(handle_container_close_event)
- .add_observer(handle_set_container_content_trigger)
- .add_observer(handle_container_click_event);
+ app.add_systems(
+ GameTick,
+ ensure_has_sent_carried_item.after(super::mining::handle_mining_queued),
+ )
+ .add_observer(handle_client_side_close_container_trigger)
+ .add_observer(handle_menu_opened_trigger)
+ .add_observer(handle_container_close_event)
+ .add_observer(handle_set_container_content_trigger)
+ .add_observer(handle_container_click_event)
+ // number keys are checked on tick but scrolling can happen outside of ticks, therefore
+ // this is fine
+ .add_observer(handle_set_selected_hotbar_slot_event);
}
}
@@ -83,7 +79,7 @@ impl Client {
);
let mut ecs = self.ecs.lock();
- ecs.write_message(SetSelectedHotbarSlotEvent {
+ ecs.trigger(SetSelectedHotbarSlotEvent {
entity: self.entity,
slot: new_hotbar_slot_index,
});
@@ -926,26 +922,18 @@ pub fn handle_set_container_content_trigger(
/// An ECS message to switch our hand to a different hotbar slot.
///
/// This is equivalent to using the scroll wheel or number keys in Minecraft.
-#[derive(Message)]
+#[derive(EntityEvent)]
pub struct SetSelectedHotbarSlotEvent {
pub entity: Entity,
/// The hotbar slot to select. This should be in the range 0..=8.
pub slot: u8,
}
pub fn handle_set_selected_hotbar_slot_event(
- mut events: MessageReader<SetSelectedHotbarSlotEvent>,
+ set_selected_hotbar_slot: On<SetSelectedHotbarSlotEvent>,
mut query: Query<&mut Inventory>,
) {
- for event in events.read() {
- let mut inventory = query.get_mut(event.entity).unwrap();
-
- // if the slot is already selected, don't send a packet
- if inventory.selected_hotbar_slot == event.slot {
- continue;
- }
-
- inventory.selected_hotbar_slot = event.slot;
- }
+ let mut inventory = query.get_mut(set_selected_hotbar_slot.entity).unwrap();
+ inventory.selected_hotbar_slot = set_selected_hotbar_slot.slot;
}
/// The item slot that the server thinks we have selected.
diff --git a/azalea-client/src/plugins/mining.rs b/azalea-client/src/plugins/mining.rs
index 5fd0aa4a..fda54521 100644
--- a/azalea-client/src/plugins/mining.rs
+++ b/azalea-client/src/plugins/mining.rs
@@ -8,7 +8,7 @@ use azalea_world::{InstanceContainer, InstanceName};
use bevy_app::{App, Plugin, Update};
use bevy_ecs::prelude::*;
use derive_more::{Deref, DerefMut};
-use tracing::{debug, trace};
+use tracing::{debug, trace, warn};
use crate::{
Client,
@@ -73,6 +73,7 @@ impl Client {
ecs.write_message(StartMiningBlockEvent {
entity: self.entity,
position,
+ force: true,
});
}
@@ -138,6 +139,7 @@ fn handle_auto_mine(
start_mining_block_event.write(StartMiningBlockEvent {
entity,
position: block_pos,
+ force: true,
});
} else if mining.is_some() && hit_result_component.miss() {
stop_mining_block_event.write(StopMiningBlockEvent { entity });
@@ -163,6 +165,14 @@ pub struct Mining {
pub struct StartMiningBlockEvent {
pub entity: Entity,
pub position: BlockPos,
+ /// Whether we should ignore blocks that are blocking the view of this
+ /// block.
+ ///
+ /// Most of the time, you'll want to set this to true as it'll make the
+ /// behavior more predictable. If it's set to false, then it might fail or
+ /// it might mine blocks other than the one at `position` (which may be
+ /// preferable if you're trying to act like vanilla).
+ pub force: bool,
}
fn handle_start_mining_block_event(
mut commands: Commands,
@@ -172,25 +182,44 @@ fn handle_start_mining_block_event(
for event in events.read() {
trace!("{event:?}");
let hit_result = query.get_mut(event.entity).unwrap();
- let (direction, force) = 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
- (block_hit_result.direction, false)
+ if event.force {
+ 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
+ block_hit_result.direction
+ } else {
+ debug!(
+ "Got StartMiningBlockEvent but we're not looking at the block ({hit_result:?}.block_pos != {:?}). Picking an arbitrary direction instead.",
+ event.position
+ );
+ // we're not looking at the block, arbitrary direction
+ Direction::Down
+ };
+ commands.entity(event.entity).insert(MiningQueued {
+ position: event.position,
+ direction,
+ force: true,
+ });
} else {
- debug!(
- "Got StartMiningBlockEvent but we're not looking at the block ({:?}.block_pos != {:?}). Picking an arbitrary direction instead.",
- hit_result, event.position
- );
- // we're not looking at the block, arbitrary direction
- (Direction::Down, true)
- };
- commands.entity(event.entity).insert(MiningQueued {
- position: event.position,
- direction,
- force,
- });
+ // let block_hit_result = hit_result.as_block_hit_result_if_not_miss();
+ // let direction = block_hit_result.map_or(Direction::Down, |b| b.direction);
+ if let Some(block_hit_result) = hit_result.as_block_hit_result_if_not_miss()
+ && block_hit_result.block_pos == event.position
+ {
+ commands.entity(event.entity).insert(MiningQueued {
+ position: event.position,
+ direction: block_hit_result.direction,
+ force: false,
+ });
+ } else {
+ warn!(
+ "Got StartMiningBlockEvent with force=false but we're not looking at the block ({hit_result:?}.block_pos != {:?}). You should've looked at the block before trying to mine with force=false.",
+ event.position
+ );
+ };
+ }
}
}
@@ -215,7 +244,7 @@ pub fn handle_mining_queued(
&Inventory,
&FluidOnEyes,
&Physics,
- Option<&Mining>,
+ Option<&mut Mining>,
&mut BlockStatePredictionHandler,
&mut MineDelay,
&mut MineProgress,
@@ -232,7 +261,7 @@ pub fn handle_mining_queued(
inventory,
fluid_on_eyes,
physics,
- mining,
+ mut mining,
mut sequence_number,
mut mine_delay,
mut mine_progress,
@@ -241,6 +270,7 @@ pub fn handle_mining_queued(
mut current_mining_pos,
) in query
{
+ trace!("handle_mining_queued {mining_queued:?}");
commands.entity(entity).remove::<MiningQueued>();
let instance = instance_holder.instance.read();
@@ -255,6 +285,13 @@ pub fn handle_mining_queued(
// TODO (when world border is implemented): vanilla ignores if the block
// is outside of the worldborder
+ if let Some(mining) = &mut mining {
+ // this matters if we were previously mining a block without force
+ if mining_queued.force {
+ mining.force = true;
+ }
+ }
+
if game_mode.current == GameMode::Creative {
// In creative mode, first send START_DESTROY_BLOCK packet then immediately
// finish mining
@@ -686,7 +723,13 @@ pub fn update_mining_component(
continue;
}
- mining.pos = block_hit_result.block_pos;
+ if mining.pos != block_hit_result.block_pos {
+ debug!(
+ "Updating Mining::pos from {:?} to {:?}",
+ mining.pos, block_hit_result.block_pos
+ );
+ mining.pos = block_hit_result.block_pos;
+ }
mining.dir = block_hit_result.direction;
} else {
if mining.force {
diff --git a/azalea/src/pathfinder/mod.rs b/azalea/src/pathfinder/mod.rs
index d6a16f69..6902e224 100644
--- a/azalea/src/pathfinder/mod.rs
+++ b/azalea/src/pathfinder/mod.rs
@@ -34,7 +34,7 @@ use std::{
use astar::{Edge, PathfinderTimeout};
use azalea_client::{
StartSprintEvent, StartWalkEvent,
- inventory::{Inventory, InventorySystems, SetSelectedHotbarSlotEvent},
+ inventory::{Inventory, InventorySystems},
local_player::InstanceHolder,
mining::{Mining, MiningSystems, StartMiningBlockEvent},
movement::MoveEventsSystems,
@@ -1072,6 +1072,7 @@ pub fn recalculate_near_end_of_path(
#[allow(clippy::type_complexity)]
pub fn tick_execute_path(
+ mut commands: Commands,
mut query: Query<(
Entity,
&mut ExecutingPath,
@@ -1086,7 +1087,6 @@ pub fn tick_execute_path(
mut walk_events: MessageWriter<StartWalkEvent>,
mut jump_events: MessageWriter<JumpEvent>,
mut start_mining_events: MessageWriter<StartMiningBlockEvent>,
- mut set_selected_hotbar_slot_events: MessageWriter<SetSelectedHotbarSlotEvent>,
) {
for (entity, executing_path, position, physics, mining, instance_holder, inventory_component) in
&mut query
@@ -1102,12 +1102,12 @@ pub fn tick_execute_path(
instance: instance_holder.instance.clone(),
menu: inventory_component.inventory_menu.clone(),
+ commands: &mut commands,
look_at_events: &mut look_at_events,
sprint_events: &mut sprint_events,
walk_events: &mut walk_events,
jump_events: &mut jump_events,
start_mining_events: &mut start_mining_events,
- set_selected_hotbar_slot_events: &mut set_selected_hotbar_slot_events,
};
trace!(
"executing move, position: {}, last_reached_node: {}",
diff --git a/azalea/src/pathfinder/moves/mod.rs b/azalea/src/pathfinder/moves/mod.rs
index 1e22f683..e74a79b4 100644
--- a/azalea/src/pathfinder/moves/mod.rs
+++ b/azalea/src/pathfinder/moves/mod.rs
@@ -14,8 +14,9 @@ use azalea_client::{
use azalea_core::position::{BlockPos, Vec3};
use azalea_inventory::Menu;
use azalea_world::Instance;
-use bevy_ecs::{entity::Entity, message::MessageWriter};
+use bevy_ecs::{entity::Entity, message::MessageWriter, system::Commands};
use parking_lot::RwLock;
+use tracing::debug;
use super::{
astar,
@@ -54,7 +55,7 @@ impl Debug for MoveData {
}
}
-pub struct ExecuteCtx<'w1, 'w2, 'w3, 'w4, 'w5, 'w6, 'a> {
+pub struct ExecuteCtx<'s, 'w1, 'w2, 'w3, 'w4, 'w5, 'w6, 'a> {
pub entity: Entity,
/// The node that we're trying to reach.
pub target: BlockPos,
@@ -66,15 +67,15 @@ pub struct ExecuteCtx<'w1, 'w2, 'w3, 'w4, 'w5, 'w6, 'a> {
pub instance: Arc<RwLock<Instance>>,
pub menu: Menu,
- pub look_at_events: &'a mut MessageWriter<'w1, LookAtEvent>,
- pub sprint_events: &'a mut MessageWriter<'w2, StartSprintEvent>,
- pub walk_events: &'a mut MessageWriter<'w3, StartWalkEvent>,
- pub jump_events: &'a mut MessageWriter<'w4, JumpEvent>,
- pub start_mining_events: &'a mut MessageWriter<'w5, StartMiningBlockEvent>,
- pub set_selected_hotbar_slot_events: &'a mut MessageWriter<'w6, SetSelectedHotbarSlotEvent>,
+ pub commands: &'a mut Commands<'w1, 's>,
+ pub look_at_events: &'a mut MessageWriter<'w2, LookAtEvent>,
+ pub sprint_events: &'a mut MessageWriter<'w3, StartSprintEvent>,
+ pub walk_events: &'a mut MessageWriter<'w4, StartWalkEvent>,
+ pub jump_events: &'a mut MessageWriter<'w5, JumpEvent>,
+ pub start_mining_events: &'a mut MessageWriter<'w6, StartMiningBlockEvent>,
}
-impl ExecuteCtx<'_, '_, '_, '_, '_, '_, '_> {
+impl ExecuteCtx<'_, '_, '_, '_, '_, '_, '_, '_> {
pub fn look_at(&mut self, position: Vec3) {
self.look_at_events.write(LookAtEvent {
entity: self.entity,
@@ -149,12 +150,12 @@ impl ExecuteCtx<'_, '_, '_, '_, '_, '_, '_> {
}
let best_tool_result = best_tool_in_hotbar_for_block(block_state, &self.menu);
+ debug!("best tool for {block_state:?}: {best_tool_result:?}");
- self.set_selected_hotbar_slot_events
- .write(SetSelectedHotbarSlotEvent {
- entity: self.entity,
- slot: best_tool_result.index as u8,
- });
+ self.commands.trigger(SetSelectedHotbarSlotEvent {
+ entity: self.entity,
+ slot: best_tool_result.index as u8,
+ });
self.is_currently_mining = true;
@@ -163,6 +164,7 @@ impl ExecuteCtx<'_, '_, '_, '_, '_, '_, '_> {
self.start_mining_events.write(StartMiningBlockEvent {
entity: self.entity,
position: block,
+ force: true,
});
true