From eeec59adabb8c084c8b6b847a31130eb7c37d2ee Mon Sep 17 00:00:00 2001 From: mat Date: Thu, 12 Oct 2023 22:39:29 -0500 Subject: [PATCH] KnockbackEvent and rename Physics::delta to velocity --- azalea-client/src/attack.rs | 6 +- azalea-client/src/interact.rs | 3 +- azalea-client/src/mining.rs | 2 + azalea-client/src/movement.rs | 55 ++++++++++++++++--- azalea-client/src/packet_handling/game.rs | 44 +++++++++------ azalea-entity/src/lib.rs | 7 ++- azalea-physics/src/collision/mod.rs | 6 +- azalea-physics/src/lib.rs | 30 +++++----- .../clientbound_command_suggestions_packet.rs | 5 +- azalea/src/auto_tool.rs | 2 +- azalea/src/pathfinder/mod.rs | 8 +-- azalea/src/pathfinder/moves/basic.rs | 2 +- 12 files changed, 112 insertions(+), 58 deletions(-) diff --git a/azalea-client/src/attack.rs b/azalea-client/src/attack.rs index 644af5de6..56f3b1d0c 100644 --- a/azalea-client/src/attack.rs +++ b/azalea-client/src/attack.rs @@ -15,7 +15,7 @@ use derive_more::{Deref, DerefMut}; use crate::{ interact::SwingArmEvent, local_player::{LocalGameMode, SendPacketEvent}, - movement::walk_listener, + movement::MoveEventsSet, respawn::perform_respawn, Client, }; @@ -28,7 +28,7 @@ impl Plugin for AttackPlugin { Update, handle_attack_event .before(update_bounding_box) - .before(walk_listener) + .before(MoveEventsSet) .after(perform_respawn), ) .add_systems( @@ -106,7 +106,7 @@ pub fn handle_attack_event( ticks_since_last_attack.0 = 0; - physics.delta = physics.delta.multiply(0.6, 1.0, 0.6); + physics.velocity = physics.velocity.multiply(0.6, 1.0, 0.6); **sprinting = false; } } diff --git a/azalea-client/src/interact.rs b/azalea-client/src/interact.rs index b7b47ec66..32b68925c 100644 --- a/azalea-client/src/interact.rs +++ b/azalea-client/src/interact.rs @@ -37,6 +37,7 @@ use crate::{ local_player::{ handle_send_packet_event, LocalGameMode, PermissionLevel, PlayerAbilities, SendPacketEvent, }, + movement::MoveEventsSet, respawn::perform_respawn, Client, }; @@ -62,7 +63,7 @@ impl Plugin for InteractPlugin { .chain(), update_modifiers_for_held_item .after(InventorySet) - .after(crate::movement::walk_listener), + .after(MoveEventsSet), ), ); } diff --git a/azalea-client/src/mining.rs b/azalea-client/src/mining.rs index 806a7b91d..e1193f734 100644 --- a/azalea-client/src/mining.rs +++ b/azalea-client/src/mining.rs @@ -18,6 +18,7 @@ use crate::{ }, inventory::{InventoryComponent, InventorySet}, local_player::{LocalGameMode, PermissionLevel, PlayerAbilities, SendPacketEvent}, + movement::MoveEventsSet, Client, }; @@ -43,6 +44,7 @@ impl Plugin for MinePlugin { .chain() .in_set(MiningSet) .after(InventorySet) + .after(MoveEventsSet) .before(azalea_entity::update_bounding_box) .after(azalea_entity::update_fluid_on_eyes) .after(crate::interact::update_hit_result_component) diff --git a/azalea-client/src/movement.rs b/azalea-client/src/movement.rs index 286281159..f4bb88360 100644 --- a/azalea-client/src/movement.rs +++ b/azalea-client/src/movement.rs @@ -1,5 +1,6 @@ use crate::client::Client; use crate::local_player::SendPacketEvent; +use azalea_core::position::Vec3; use azalea_entity::{metadata::Sprinting, Attributes, Jumping}; use azalea_entity::{InLoadedChunk, LastSentPosition, LookDirection, Physics, Position}; use azalea_physics::{ai_step, PhysicsSet}; @@ -13,6 +14,7 @@ use azalea_protocol::packets::game::{ use azalea_world::{MinecraftEntityId, MoveEntityError}; use bevy_app::{App, FixedUpdate, Plugin, Update}; use bevy_ecs::prelude::{Event, EventWriter}; +use bevy_ecs::schedule::SystemSet; use bevy_ecs::{ component::Component, entity::Entity, event::EventReader, query::With, schedule::IntoSystemConfigs, system::Query, @@ -44,7 +46,13 @@ impl Plugin for PlayerMovePlugin { fn build(&self, app: &mut App) { app.add_event::() .add_event::() - .add_systems(Update, (sprint_listener, walk_listener).chain()) + .add_event::() + .add_systems( + Update, + (handle_sprint, handle_walk, handle_knockback) + .chain() + .in_set(MoveEventsSet), + ) .add_systems( FixedUpdate, ( @@ -60,6 +68,9 @@ impl Plugin for PlayerMovePlugin { } } +#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)] +pub struct MoveEventsSet; + impl Client { /// Set whether we're jumping. This acts as if you held space in /// vanilla. If you want to jump once, use the `jump` function. @@ -391,10 +402,9 @@ pub struct StartWalkEvent { pub direction: WalkDirection, } -/// Start walking in the given direction. To sprint, use -/// [`Client::sprint`]. To stop walking, call walk with -/// `WalkDirection::None`. -pub fn walk_listener( +/// The system that makes the player start walking when they receive a +/// [`StartWalkEvent`]. +pub fn handle_walk( mut events: EventReader, mut query: Query<(&mut PhysicsState, &mut Sprinting, &mut Attributes)>, ) { @@ -415,8 +425,9 @@ pub struct StartSprintEvent { pub entity: Entity, pub direction: SprintDirection, } -/// Start sprinting in the given direction. -pub fn sprint_listener( +/// The system that makes the player start sprinting when they receive a +/// [`StartSprintEvent`]. +pub fn handle_sprint( mut query: Query<&mut PhysicsState>, mut events: EventReader, ) { @@ -459,6 +470,36 @@ fn has_enough_impulse_to_start_sprinting(physics_state: &PhysicsState) -> bool { // } } +/// An event sent by the server that sets or adds to our velocity. Usually +/// `KnockbackKind::Set` is used for normal knockback and `KnockbackKind::Add` +/// is used for explosions, but some servers (notably Hypixel) use explosions +/// for knockback. +#[derive(Event)] +pub struct KnockbackEvent { + pub entity: Entity, + pub kind: KnockbackType, +} + +pub enum KnockbackType { + Set(Vec3), + Add(Vec3), +} + +pub fn handle_knockback(mut query: Query<&mut Physics>, mut events: EventReader) { + for event in events.iter() { + if let Ok(mut physics) = query.get_mut(event.entity) { + match event.kind { + KnockbackType::Set(velocity) => { + physics.velocity = velocity; + } + KnockbackType::Add(velocity) => { + physics.velocity += velocity; + } + } + } + } +} + #[derive(Clone, Copy, Debug, Default)] pub enum WalkDirection { #[default] diff --git a/azalea-client/src/packet_handling/game.rs b/azalea-client/src/packet_handling/game.rs index 6e1385653..1d8c6cc9e 100644 --- a/azalea-client/src/packet_handling/game.rs +++ b/azalea-client/src/packet_handling/game.rs @@ -44,6 +44,7 @@ use crate::{ GameProfileComponent, Hunger, InstanceHolder, LocalGameMode, PlayerAbilities, SendPacketEvent, TabList, }, + movement::{KnockbackEvent, KnockbackType}, raw_connection::RawConnection, ClientInformation, PlayerInfo, ReceivedRegistries, }; @@ -422,7 +423,7 @@ pub fn process_packet_events(ecs: &mut World) { continue; }; - let delta_movement = physics.delta; + let delta_movement = physics.velocity; let is_x_relative = p.relative_arguments.x; let is_y_relative = p.relative_arguments.y; @@ -459,7 +460,7 @@ pub fn process_packet_events(ecs: &mut World) { y_rot += direction.y_rot; } - physics.delta = Vec3 { + physics.velocity = Vec3 { x: delta_x, y: delta_y, z: delta_z, @@ -797,15 +798,21 @@ pub fn process_packet_events(ecs: &mut World) { continue; }; + // this is to make sure the same entity velocity update doesn't get sent + // multiple times when in swarms commands.entity(entity).add(RelativeEntityUpdate { partial_world: instance_holder.partial_instance.clone(), - update: Box::new(move |entity| { - let mut physics = entity.get_mut::().unwrap(); - physics.delta = Vec3 { - x: p.xa as f64 / 8000., - y: p.ya as f64 / 8000., - z: p.za as f64 / 8000., - }; + update: Box::new(move |entity_mut| { + entity_mut.world_scope(|world| { + world.send_event(KnockbackEvent { + entity, + kind: KnockbackType::Set(Vec3 { + x: p.xa as f64 / 8000., + y: p.ya as f64 / 8000., + z: p.za as f64 / 8000., + }), + }) + }); }), }); @@ -1186,15 +1193,18 @@ pub fn process_packet_events(ecs: &mut World) { ClientboundGamePacket::DeleteChat(_) => {} ClientboundGamePacket::Explode(p) => { trace!("Got explode packet {p:?}"); - let mut system_state: SystemState> = SystemState::new(ecs); - let mut query = system_state.get_mut(ecs); - let mut physics = query.get_mut(player_entity).unwrap(); + let mut system_state: SystemState> = + SystemState::new(ecs); + let mut knockback_events = system_state.get_mut(ecs); - physics.delta += Vec3 { - x: p.knockback_x as f64, - y: p.knockback_y as f64, - z: p.knockback_z as f64, - }; + knockback_events.send(KnockbackEvent { + entity: player_entity, + kind: KnockbackType::Set(Vec3 { + x: p.knockback_x as f64, + y: p.knockback_y as f64, + z: p.knockback_z as f64, + }), + }); system_state.apply(ecs); } diff --git a/azalea-entity/src/lib.rs b/azalea-entity/src/lib.rs index 9b6739455..bf3dfc823 100644 --- a/azalea-entity/src/lib.rs +++ b/azalea-entity/src/lib.rs @@ -35,7 +35,7 @@ pub fn move_relative( acceleration: &Vec3, ) { let input_vector = input_vector(direction, speed, acceleration); - physics.delta += input_vector; + physics.velocity += input_vector; } pub fn input_vector(direction: &LookDirection, speed: f32, acceleration: &Vec3) -> Vec3 { @@ -208,7 +208,8 @@ pub struct LookDirection { /// bounding box. #[derive(Debug, Component, Clone)] pub struct Physics { - pub delta: Vec3, + /// How fast the entity is moving. + pub velocity: Vec3, /// X acceleration. pub xxa: f32, @@ -232,7 +233,7 @@ pub struct Physics { impl Physics { pub fn new(dimensions: EntityDimensions, pos: &Vec3) -> Self { Self { - delta: Vec3::default(), + velocity: Vec3::default(), xxa: 0., yya: 0., diff --git a/azalea-physics/src/collision/mod.rs b/azalea-physics/src/collision/mod.rs index 1a8f44414..2c739b24c 100644 --- a/azalea-physics/src/collision/mod.rs +++ b/azalea-physics/src/collision/mod.rs @@ -201,8 +201,8 @@ pub fn move_colliding( // if self.isRemoved() { return; } if horizontal_collision { - let delta_movement = &physics.delta; - physics.delta = Vec3 { + let delta_movement = &physics.velocity; + physics.velocity = Vec3 { x: if x_collision { 0. } else { delta_movement.x }, y: delta_movement.y, z: if z_collision { 0. } else { delta_movement.z }, @@ -213,7 +213,7 @@ pub fn move_colliding( // blockBelow.updateEntityAfterFallOn(this.level, this); // the default implementation of updateEntityAfterFallOn sets the y movement to // 0 - physics.delta.y = 0.; + physics.velocity.y = 0.; } if on_ground { diff --git a/azalea-physics/src/lib.rs b/azalea-physics/src/lib.rs index 8c88b97a5..615cc3ad9 100644 --- a/azalea-physics/src/lib.rs +++ b/azalea-physics/src/lib.rs @@ -113,9 +113,9 @@ fn travel( // if should_discard_friction(self) { if false { - physics.delta = movement; + physics.velocity = movement; } else { - physics.delta = Vec3 { + physics.velocity = Vec3 { x: movement.x * inertia as f64, y: movement.y * 0.9800000190734863f64, z: movement.z * inertia as f64, @@ -145,14 +145,14 @@ pub fn ai_step( // vanilla does movement interpolation here, doesn't really matter much for a // bot though - if physics.delta.x.abs() < 0.003 { - physics.delta.x = 0.; + if physics.velocity.x.abs() < 0.003 { + physics.velocity.x = 0.; } - if physics.delta.y.abs() < 0.003 { - physics.delta.y = 0.; + if physics.velocity.y.abs() < 0.003 { + physics.velocity.y = 0.; } - if physics.delta.z.abs() < 0.003 { - physics.delta.z = 0.; + if physics.velocity.z.abs() < 0.003 { + physics.velocity.z = 0.; } if let Some(jumping) = jumping { @@ -194,8 +194,8 @@ pub fn jump_from_ground( let world = world_lock.read(); let jump_power: f64 = jump_power(&world, position) as f64 + jump_boost_power(); - let old_delta_movement = physics.delta; - physics.delta = Vec3 { + let old_delta_movement = physics.velocity; + physics.velocity = Vec3 { x: old_delta_movement.x, y: jump_power, z: old_delta_movement.z, @@ -203,7 +203,7 @@ pub fn jump_from_ground( if **sprinting { // sprint jumping gives some extra velocity let y_rot = look_direction.y_rot * 0.017453292; - physics.delta += Vec3 { + physics.velocity += Vec3 { x: (-math::sin(y_rot) * 0.2) as f64, y: 0., z: (math::cos(y_rot) * 0.2) as f64, @@ -245,7 +245,7 @@ fn handle_relative_friction_and_calculate_movement( // entity.delta = entity.handle_on_climbable(entity.delta); move_colliding( &MoverType::Own, - &physics.delta.clone(), + &physics.velocity.clone(), world, position, physics, @@ -259,7 +259,7 @@ fn handle_relative_friction_and_calculate_movement( // Vec3(var3.x, 0.2D, var3.z); } // TODO: powdered snow - physics.delta + physics.velocity } // private float getFrictionInfluencedSpeed(float friction) { @@ -400,7 +400,7 @@ mod tests { // delta is applied before gravity, so the first tick only sets the delta assert_eq!(entity_pos.y, 70.); let entity_physics = app.world.get::(entity).unwrap(); - assert!(entity_physics.delta.y < 0.); + assert!(entity_physics.velocity.y < 0.); } app.world.run_schedule(FixedUpdate); app.update(); @@ -463,7 +463,7 @@ mod tests { // delta will change, but it won't move until next tick assert_eq!(entity_pos.y, 70.); let entity_physics = app.world.get::(entity).unwrap(); - assert!(entity_physics.delta.y < 0.); + assert!(entity_physics.velocity.y < 0.); } app.world.run_schedule(FixedUpdate); app.update(); diff --git a/azalea-protocol/src/packets/game/clientbound_command_suggestions_packet.rs b/azalea-protocol/src/packets/game/clientbound_command_suggestions_packet.rs index d1ad12405..13887331a 100755 --- a/azalea-protocol/src/packets/game/clientbound_command_suggestions_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_command_suggestions_packet.rs @@ -1,13 +1,12 @@ use azalea_brigadier::suggestion::Suggestions; use azalea_buf::McBuf; -use azalea_chat::FormattedText; use azalea_protocol_macros::ClientboundGamePacket; #[derive(Clone, Debug, McBuf, ClientboundGamePacket)] pub struct ClientboundCommandSuggestionsPacket { #[var] pub id: u32, - pub suggestions: Suggestions, + pub suggestions: Suggestions, } #[cfg(test)] @@ -24,7 +23,7 @@ mod tests { vec![Suggestion::new_with_tooltip( StringRange::new(1, 4), "foo", - FormattedText::from("bar".to_string()), + "bar".to_string(), )], ); let mut buf = Vec::new(); diff --git a/azalea/src/auto_tool.rs b/azalea/src/auto_tool.rs index d7501f044..55ec69248 100644 --- a/azalea/src/auto_tool.rs +++ b/azalea/src/auto_tool.rs @@ -35,7 +35,7 @@ pub fn best_tool_in_hotbar_for_block(block: BlockState, menu: &Menu) -> BestTool menu, &Physics { on_ground: true, - delta: Default::default(), + velocity: Default::default(), xxa: Default::default(), yya: Default::default(), zza: Default::default(), diff --git a/azalea/src/pathfinder/mod.rs b/azalea/src/pathfinder/mod.rs index 73ce2967d..e82b01790 100644 --- a/azalea/src/pathfinder/mod.rs +++ b/azalea/src/pathfinder/mod.rs @@ -25,7 +25,7 @@ use crate::pathfinder::moves::PathfinderCtx; use crate::pathfinder::world::CachedWorld; use azalea_client::chat::SendChatEvent; use azalea_client::inventory::{InventoryComponent, InventorySet}; -use azalea_client::movement::walk_listener; +use azalea_client::movement::MoveEventsSet; use azalea_client::{StartSprintEvent, StartWalkEvent}; use azalea_core::position::{BlockPos, Vec3}; use azalea_entity::metadata::Player; @@ -85,7 +85,7 @@ impl Plugin for PathfinderPlugin { handle_stop_pathfinding_event, ) .chain() - .before(walk_listener) + .before(MoveEventsSet) .before(InventorySet), ); } @@ -462,8 +462,8 @@ fn check_node_reached( && BlockPos::from(position) == movement.target // adding the delta like this isn't a perfect solution but it helps to make // sure we don't keep going if our delta is high - && (x_difference_from_center + physics.delta.x).abs() < 0.2 - && (z_difference_from_center + physics.delta.z).abs() < 0.2 + && (x_difference_from_center + physics.velocity.x).abs() < 0.2 + && (z_difference_from_center + physics.velocity.z).abs() < 0.2 } else { true }; diff --git a/azalea/src/pathfinder/moves/basic.rs b/azalea/src/pathfinder/moves/basic.rs index 4780798c1..957e24c60 100644 --- a/azalea/src/pathfinder/moves/basic.rs +++ b/azalea/src/pathfinder/moves/basic.rs @@ -97,7 +97,7 @@ fn execute_ascend_move(mut ctx: ExecuteCtx) { let side_distance = z_axis as f64 * (target_center.x - position.x).abs() + x_axis as f64 * (target_center.z - position.z).abs(); - let lateral_motion = x_axis as f64 * physics.delta.z + z_axis as f64 * physics.delta.x; + let lateral_motion = x_axis as f64 * physics.velocity.z + z_axis as f64 * physics.velocity.x; if lateral_motion > 0.1 { return; }