diff options
| author | mat <git@matdoes.dev> | 2025-09-20 20:35:16 -1200 |
|---|---|---|
| committer | mat <git@matdoes.dev> | 2025-09-20 20:35:16 -1200 |
| commit | 585b51e91a5335eae37bc5af7c0111bb2092b156 (patch) | |
| tree | c1559014df9db20dd625d9fe972d4e9f88317008 /azalea-client | |
| parent | db793448ff8e656ad80859835edc3b89cb547dd2 (diff) | |
| download | azalea-drasl-585b51e91a5335eae37bc5af7c0111bb2092b156.tar.xz | |
more accurate mining and impl PartialEq for packets
Diffstat (limited to 'azalea-client')
| -rw-r--r-- | azalea-client/Cargo.toml | 1 | ||||
| -rw-r--r-- | azalea-client/src/plugins/connection.rs | 19 | ||||
| -rw-r--r-- | azalea-client/src/plugins/inventory.rs | 12 | ||||
| -rw-r--r-- | azalea-client/src/plugins/mining.rs | 23 | ||||
| -rw-r--r-- | azalea-client/src/test_utils/simulation.rs | 13 | ||||
| -rw-r--r-- | azalea-client/tests/correct_sneak_movement.rs | 20 | ||||
| -rw-r--r-- | azalea-client/tests/mine_block_timing.rs | 127 |
7 files changed, 178 insertions, 37 deletions
diff --git a/azalea-client/Cargo.toml b/azalea-client/Cargo.toml index 1f91a669..5512fb60 100644 --- a/azalea-client/Cargo.toml +++ b/azalea-client/Cargo.toml @@ -27,6 +27,7 @@ bevy_tasks.workspace = true bevy_time.workspace = true chrono = { workspace = true, features = ["now"] } derive_more = { workspace = true, features = ["deref", "deref_mut"] } +indexmap.workspace = true minecraft_folder_path.workspace = true parking_lot.workspace = true pastey.workspace = true diff --git a/azalea-client/src/plugins/connection.rs b/azalea-client/src/plugins/connection.rs index a929a4c7..f439ac33 100644 --- a/azalea-client/src/plugins/connection.rs +++ b/azalea-client/src/plugins/connection.rs @@ -1,4 +1,12 @@ -use std::{fmt::Debug, io::Cursor, mem, sync::Arc}; +use std::{ + fmt::Debug, + io::Cursor, + mem, + sync::{ + Arc, + atomic::{self, AtomicBool}, + }, +}; use azalea_crypto::Aes128CfbEnc; use azalea_protocol::{ @@ -232,9 +240,12 @@ impl RawConnection { if let Some(network) = &mut self.network { network.write(packet)?; } else { - debug!( - "tried to write packet to the network but there is no NetworkConnection. if you're trying to send a packet from the handler function, use self.write instead" - ); + static WARNED: AtomicBool = AtomicBool::new(false); + if !WARNED.swap(true, atomic::Ordering::Relaxed) { + debug!( + "tried to write packet to the network but there is no NetworkConnection. if you're trying to send a packet from the handler function, use self.write instead" + ); + } } Ok(()) } diff --git a/azalea-client/src/plugins/inventory.rs b/azalea-client/src/plugins/inventory.rs index ecc8e826..8037f8fc 100644 --- a/azalea-client/src/plugins/inventory.rs +++ b/azalea-client/src/plugins/inventory.rs @@ -1,7 +1,4 @@ -use std::{ - cmp, - collections::{HashMap, HashSet}, -}; +use std::{cmp, collections::HashSet}; use azalea_chat::FormattedText; use azalea_entity::PlayerAbilities; @@ -22,6 +19,7 @@ use azalea_registry::MenuKind; use azalea_world::{InstanceContainer, InstanceName}; use bevy_app::{App, Plugin, Update}; use bevy_ecs::prelude::*; +use indexmap::IndexMap; use tracing::{error, warn}; use crate::{Client, packet::game::SendPacketEvent, respawn::perform_respawn}; @@ -869,9 +867,9 @@ pub fn handle_container_click_event( let registry_holder = &instance.read().registries; - // see which slots changed after clicking and put them in the hashmap - // the server uses this to check if we desynced - let mut changed_slots: HashMap<u16, HashedStack> = HashMap::new(); + // see which slots changed after clicking and put them in the map the server + // uses this to check if we desynced + let mut changed_slots: IndexMap<u16, HashedStack> = IndexMap::new(); for (slot_index, old_slot) in old_slots.iter().enumerate() { let new_slot = &new_slots[slot_index]; if old_slot != new_slot { diff --git a/azalea-client/src/plugins/mining.rs b/azalea-client/src/plugins/mining.rs index 1b7adadc..b6ac113a 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::trace; +use tracing::{debug, trace}; use crate::{ Client, @@ -35,14 +35,14 @@ impl Plugin for MiningPlugin { GameTick, ( update_mining_component, - continue_mining_block, handle_auto_mine, handle_mining_queued, + continue_mining_block, ) .chain() - .after(PhysicsSet) - .after(super::movement::send_position) - .after(super::attack::handle_attack_queued) + .before(PhysicsSet) + .before(super::movement::send_position) + .before(super::interact::handle_start_use_item_queued) .in_set(MiningSet), ) .add_systems( @@ -358,9 +358,9 @@ fn handle_mining_queued( seq: sequence_number.start_predicting(), }, )); - // vanilla really does send two swing arm packets - commands.trigger(SwingArmEvent { entity }); commands.trigger(SwingArmEvent { entity }); + // another swing packet gets sent in the same tick in + // continue_mining_block, vanilla does this too } } } @@ -687,9 +687,18 @@ pub fn update_mining_component( ) { for (entity, mut mining, hit_result_component) in &mut query.iter_mut() { if let Some(block_hit_result) = hit_result_component.as_block_hit_result_if_not_miss() { + if mining.force && block_hit_result.block_pos != mining.pos { + continue; + } + mining.pos = block_hit_result.block_pos; mining.dir = block_hit_result.direction; } else { + if mining.force { + continue; + } + + debug!("Removing mining component because we're no longer looking at the block"); commands.entity(entity).remove::<Mining>(); } } diff --git a/azalea-client/src/test_utils/simulation.rs b/azalea-client/src/test_utils/simulation.rs index a763962b..aa3cef1b 100644 --- a/azalea-client/src/test_utils/simulation.rs +++ b/azalea-client/src/test_utils/simulation.rs @@ -232,9 +232,22 @@ impl SentPackets { panic!("Expected {expected_formatted}, got nothing"); } } + + pub fn maybe_expect(&self, check: impl FnOnce(&ServerboundGamePacket) -> bool) { + let sent_packet = self.peek(); + if let Some(sent_packet) = sent_packet + && check(&sent_packet) + { + self.next(); + } + } + pub fn next(&self) -> Option<ServerboundGamePacket> { self.list.lock().pop_front() } + pub fn peek(&self) -> Option<ServerboundGamePacket> { + self.list.lock().front().cloned() + } } #[allow(clippy::type_complexity)] diff --git a/azalea-client/tests/correct_sneak_movement.rs b/azalea-client/tests/correct_sneak_movement.rs index cf1d17b5..9a9e8f4f 100644 --- a/azalea-client/tests/correct_sneak_movement.rs +++ b/azalea-client/tests/correct_sneak_movement.rs @@ -8,7 +8,6 @@ use azalea_protocol::{ common::movements::{PositionMoveRotation, RelativeMovements}, packets::{ ConnectionProtocol, - config::{ClientboundFinishConfiguration, ClientboundRegistryData}, game::{ ClientboundBlockUpdate, ClientboundPlayerPosition, ServerboundGamePacket, ServerboundPlayerInput, @@ -16,31 +15,14 @@ use azalea_protocol::{ }, }; use azalea_registry::{Block, DataRegistry, DimensionType}; -use simdnbt::owned::{NbtCompound, NbtTag}; #[test] fn test_correct_sneak_movement() { init_tracing(); - let mut simulation = Simulation::new(ConnectionProtocol::Configuration); + let mut simulation = Simulation::new(ConnectionProtocol::Game); let sent_packets = SentPackets::new(&mut simulation); - simulation.receive_packet(ClientboundRegistryData { - registry_id: ResourceLocation::new("minecraft:dimension_type"), - entries: vec![( - ResourceLocation::new("minecraft:overworld"), - Some(NbtCompound::from_values(vec![ - ("height".into(), NbtTag::Int(384)), - ("min_y".into(), NbtTag::Int(-64)), - ])), - )] - .into_iter() - .collect(), - }); - simulation.tick(); - simulation.receive_packet(ClientboundFinishConfiguration); - simulation.tick(); - simulation.receive_packet(make_basic_login_packet( DimensionType::new_raw(0), // overworld ResourceLocation::new("minecraft:overworld"), diff --git a/azalea-client/tests/mine_block_timing.rs b/azalea-client/tests/mine_block_timing.rs new file mode 100644 index 00000000..1216f178 --- /dev/null +++ b/azalea-client/tests/mine_block_timing.rs @@ -0,0 +1,127 @@ +use azalea_client::{mining::StartMiningBlockEvent, test_utils::prelude::*}; +use azalea_core::{ + direction::Direction, + position::{BlockPos, ChunkPos, Vec3}, + resource_location::ResourceLocation, +}; +use azalea_entity::LookDirection; +use azalea_protocol::{ + common::movements::{PositionMoveRotation, RelativeMovements}, + packets::{ + ConnectionProtocol, Packet, + game::{ + ClientboundBlockUpdate, ClientboundPlayerPosition, ServerboundGamePacket, + ServerboundPlayerAction, ServerboundSwing, s_interact::InteractionHand, + s_player_action, + }, + }, +}; +use azalea_registry::{Block, DataRegistry, DimensionType}; + +#[test] +fn test_mine_block_timing() { + init_tracing(); + + let mut simulation = Simulation::new(ConnectionProtocol::Game); + let sent_packets = SentPackets::new(&mut simulation); + simulation.receive_packet(make_basic_login_packet( + DimensionType::new_raw(0), + ResourceLocation::new("azalea:overworld"), + )); + + simulation.receive_packet(make_basic_empty_chunk(ChunkPos::new(0, 0), (384 + 64) / 16)); + simulation.tick(); + + let pos = BlockPos::new(0, 2, 0); + simulation.receive_packet(ClientboundBlockUpdate { + pos, + block_state: Block::Stone.into(), + }); + simulation.receive_packet(ClientboundPlayerPosition { + id: 1, + change: PositionMoveRotation { + pos: pos.up(1).center_bottom(), + delta: Vec3::ZERO, + look_direction: LookDirection::default(), + }, + relative: RelativeMovements::all_absolute(), + }); + simulation.tick(); + assert_eq!(simulation.get_block_state(pos), Some(Block::Stone.into())); + println!("set serverside stone"); + simulation.with_component_mut::<LookDirection>(|look| { + // look down + look.update_x_rot(90.); + }); + + simulation.tick(); + simulation.tick(); + simulation.tick(); + + simulation.send_event(StartMiningBlockEvent { + entity: simulation.entity, + position: pos, + }); + sent_packets.clear(); + simulation.tick(); + sent_packets.expect("ServerboundPlayerAction", |p| { + p == &ServerboundGamePacket::PlayerAction(ServerboundPlayerAction { + action: s_player_action::Action::StartDestroyBlock, + pos, + direction: Direction::Up, + seq: 1, + }) + }); + sent_packets.expect("Swing 1", |p| { + p == &ServerboundGamePacket::Swing(ServerboundSwing { + hand: InteractionHand::MainHand, + }) + .into_variant() + }); + sent_packets.expect("Swing 2", |p| { + p == &ServerboundGamePacket::Swing(ServerboundSwing { + hand: InteractionHand::MainHand, + }) + .into_variant() + }); + sent_packets.expect_tick_end(); + sent_packets.expect_empty(); + + for i in 3..=151 { + simulation.tick(); + sent_packets.expect(&format!("Swing {i}"), |p| { + p == &ServerboundGamePacket::Swing(ServerboundSwing { + hand: InteractionHand::MainHand, + }) + .into_variant() + }); + sent_packets.maybe_expect(|p| matches!(p, ServerboundGamePacket::MovePlayerPos(_))); + sent_packets.expect_tick_end(); + sent_packets.expect_empty(); + } + + simulation.tick(); + sent_packets.expect( + "ServerboundPlayerAction { action: StopDestroyBlock }", + |p| { + matches!( + p, + ServerboundGamePacket::PlayerAction(p) + if p.action == s_player_action::Action::StopDestroyBlock + ) + }, + ); + sent_packets.expect("Last swing", |p| { + p == &ServerboundGamePacket::Swing(ServerboundSwing { + hand: InteractionHand::MainHand, + }) + .into_variant() + }); + + for _ in 0..3 { + sent_packets.maybe_expect(|p| matches!(p, ServerboundGamePacket::MovePlayerPos(_))); + sent_packets.expect_tick_end(); + sent_packets.expect_empty(); + simulation.tick(); + } +} |
