diff options
| -rw-r--r-- | CHANGELOG.md | 2 | ||||
| -rw-r--r-- | azalea-client/src/plugins/inventory.rs | 50 | ||||
| -rw-r--r-- | azalea-client/src/plugins/mining.rs | 87 | ||||
| -rw-r--r-- | azalea/src/pathfinder/mod.rs | 6 | ||||
| -rw-r--r-- | azalea/src/pathfinder/moves/mod.rs | 30 |
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 |
