1
2
Fork 0
mirror of https://github.com/mat-1/azalea.git synced 2025-08-02 14:26:04 +00:00

KnockbackEvent and rename Physics::delta to velocity

This commit is contained in:
mat 2023-10-12 22:39:29 -05:00
parent 79ad1e93bf
commit eeec59adab
12 changed files with 112 additions and 58 deletions

View file

@ -15,7 +15,7 @@ use derive_more::{Deref, DerefMut};
use crate::{ use crate::{
interact::SwingArmEvent, interact::SwingArmEvent,
local_player::{LocalGameMode, SendPacketEvent}, local_player::{LocalGameMode, SendPacketEvent},
movement::walk_listener, movement::MoveEventsSet,
respawn::perform_respawn, respawn::perform_respawn,
Client, Client,
}; };
@ -28,7 +28,7 @@ impl Plugin for AttackPlugin {
Update, Update,
handle_attack_event handle_attack_event
.before(update_bounding_box) .before(update_bounding_box)
.before(walk_listener) .before(MoveEventsSet)
.after(perform_respawn), .after(perform_respawn),
) )
.add_systems( .add_systems(
@ -106,7 +106,7 @@ pub fn handle_attack_event(
ticks_since_last_attack.0 = 0; 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; **sprinting = false;
} }
} }

View file

@ -37,6 +37,7 @@ use crate::{
local_player::{ local_player::{
handle_send_packet_event, LocalGameMode, PermissionLevel, PlayerAbilities, SendPacketEvent, handle_send_packet_event, LocalGameMode, PermissionLevel, PlayerAbilities, SendPacketEvent,
}, },
movement::MoveEventsSet,
respawn::perform_respawn, respawn::perform_respawn,
Client, Client,
}; };
@ -62,7 +63,7 @@ impl Plugin for InteractPlugin {
.chain(), .chain(),
update_modifiers_for_held_item update_modifiers_for_held_item
.after(InventorySet) .after(InventorySet)
.after(crate::movement::walk_listener), .after(MoveEventsSet),
), ),
); );
} }

View file

@ -18,6 +18,7 @@ use crate::{
}, },
inventory::{InventoryComponent, InventorySet}, inventory::{InventoryComponent, InventorySet},
local_player::{LocalGameMode, PermissionLevel, PlayerAbilities, SendPacketEvent}, local_player::{LocalGameMode, PermissionLevel, PlayerAbilities, SendPacketEvent},
movement::MoveEventsSet,
Client, Client,
}; };
@ -43,6 +44,7 @@ impl Plugin for MinePlugin {
.chain() .chain()
.in_set(MiningSet) .in_set(MiningSet)
.after(InventorySet) .after(InventorySet)
.after(MoveEventsSet)
.before(azalea_entity::update_bounding_box) .before(azalea_entity::update_bounding_box)
.after(azalea_entity::update_fluid_on_eyes) .after(azalea_entity::update_fluid_on_eyes)
.after(crate::interact::update_hit_result_component) .after(crate::interact::update_hit_result_component)

View file

@ -1,5 +1,6 @@
use crate::client::Client; use crate::client::Client;
use crate::local_player::SendPacketEvent; use crate::local_player::SendPacketEvent;
use azalea_core::position::Vec3;
use azalea_entity::{metadata::Sprinting, Attributes, Jumping}; use azalea_entity::{metadata::Sprinting, Attributes, Jumping};
use azalea_entity::{InLoadedChunk, LastSentPosition, LookDirection, Physics, Position}; use azalea_entity::{InLoadedChunk, LastSentPosition, LookDirection, Physics, Position};
use azalea_physics::{ai_step, PhysicsSet}; use azalea_physics::{ai_step, PhysicsSet};
@ -13,6 +14,7 @@ use azalea_protocol::packets::game::{
use azalea_world::{MinecraftEntityId, MoveEntityError}; use azalea_world::{MinecraftEntityId, MoveEntityError};
use bevy_app::{App, FixedUpdate, Plugin, Update}; use bevy_app::{App, FixedUpdate, Plugin, Update};
use bevy_ecs::prelude::{Event, EventWriter}; use bevy_ecs::prelude::{Event, EventWriter};
use bevy_ecs::schedule::SystemSet;
use bevy_ecs::{ use bevy_ecs::{
component::Component, entity::Entity, event::EventReader, query::With, component::Component, entity::Entity, event::EventReader, query::With,
schedule::IntoSystemConfigs, system::Query, schedule::IntoSystemConfigs, system::Query,
@ -44,7 +46,13 @@ impl Plugin for PlayerMovePlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_event::<StartWalkEvent>() app.add_event::<StartWalkEvent>()
.add_event::<StartSprintEvent>() .add_event::<StartSprintEvent>()
.add_systems(Update, (sprint_listener, walk_listener).chain()) .add_event::<KnockbackEvent>()
.add_systems(
Update,
(handle_sprint, handle_walk, handle_knockback)
.chain()
.in_set(MoveEventsSet),
)
.add_systems( .add_systems(
FixedUpdate, FixedUpdate,
( (
@ -60,6 +68,9 @@ impl Plugin for PlayerMovePlugin {
} }
} }
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
pub struct MoveEventsSet;
impl Client { impl Client {
/// Set whether we're jumping. This acts as if you held space in /// Set whether we're jumping. This acts as if you held space in
/// vanilla. If you want to jump once, use the `jump` function. /// vanilla. If you want to jump once, use the `jump` function.
@ -391,10 +402,9 @@ pub struct StartWalkEvent {
pub direction: WalkDirection, pub direction: WalkDirection,
} }
/// Start walking in the given direction. To sprint, use /// The system that makes the player start walking when they receive a
/// [`Client::sprint`]. To stop walking, call walk with /// [`StartWalkEvent`].
/// `WalkDirection::None`. pub fn handle_walk(
pub fn walk_listener(
mut events: EventReader<StartWalkEvent>, mut events: EventReader<StartWalkEvent>,
mut query: Query<(&mut PhysicsState, &mut Sprinting, &mut Attributes)>, mut query: Query<(&mut PhysicsState, &mut Sprinting, &mut Attributes)>,
) { ) {
@ -415,8 +425,9 @@ pub struct StartSprintEvent {
pub entity: Entity, pub entity: Entity,
pub direction: SprintDirection, pub direction: SprintDirection,
} }
/// Start sprinting in the given direction. /// The system that makes the player start sprinting when they receive a
pub fn sprint_listener( /// [`StartSprintEvent`].
pub fn handle_sprint(
mut query: Query<&mut PhysicsState>, mut query: Query<&mut PhysicsState>,
mut events: EventReader<StartSprintEvent>, mut events: EventReader<StartSprintEvent>,
) { ) {
@ -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<KnockbackEvent>) {
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)] #[derive(Clone, Copy, Debug, Default)]
pub enum WalkDirection { pub enum WalkDirection {
#[default] #[default]

View file

@ -44,6 +44,7 @@ use crate::{
GameProfileComponent, Hunger, InstanceHolder, LocalGameMode, PlayerAbilities, GameProfileComponent, Hunger, InstanceHolder, LocalGameMode, PlayerAbilities,
SendPacketEvent, TabList, SendPacketEvent, TabList,
}, },
movement::{KnockbackEvent, KnockbackType},
raw_connection::RawConnection, raw_connection::RawConnection,
ClientInformation, PlayerInfo, ReceivedRegistries, ClientInformation, PlayerInfo, ReceivedRegistries,
}; };
@ -422,7 +423,7 @@ pub fn process_packet_events(ecs: &mut World) {
continue; continue;
}; };
let delta_movement = physics.delta; let delta_movement = physics.velocity;
let is_x_relative = p.relative_arguments.x; let is_x_relative = p.relative_arguments.x;
let is_y_relative = p.relative_arguments.y; 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; y_rot += direction.y_rot;
} }
physics.delta = Vec3 { physics.velocity = Vec3 {
x: delta_x, x: delta_x,
y: delta_y, y: delta_y,
z: delta_z, z: delta_z,
@ -797,15 +798,21 @@ pub fn process_packet_events(ecs: &mut World) {
continue; 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 { commands.entity(entity).add(RelativeEntityUpdate {
partial_world: instance_holder.partial_instance.clone(), partial_world: instance_holder.partial_instance.clone(),
update: Box::new(move |entity| { update: Box::new(move |entity_mut| {
let mut physics = entity.get_mut::<Physics>().unwrap(); entity_mut.world_scope(|world| {
physics.delta = Vec3 { world.send_event(KnockbackEvent {
x: p.xa as f64 / 8000., entity,
y: p.ya as f64 / 8000., kind: KnockbackType::Set(Vec3 {
z: p.za as f64 / 8000., 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::DeleteChat(_) => {}
ClientboundGamePacket::Explode(p) => { ClientboundGamePacket::Explode(p) => {
trace!("Got explode packet {p:?}"); trace!("Got explode packet {p:?}");
let mut system_state: SystemState<Query<&mut Physics>> = SystemState::new(ecs); let mut system_state: SystemState<EventWriter<KnockbackEvent>> =
let mut query = system_state.get_mut(ecs); SystemState::new(ecs);
let mut physics = query.get_mut(player_entity).unwrap(); let mut knockback_events = system_state.get_mut(ecs);
physics.delta += Vec3 { knockback_events.send(KnockbackEvent {
x: p.knockback_x as f64, entity: player_entity,
y: p.knockback_y as f64, kind: KnockbackType::Set(Vec3 {
z: p.knockback_z as f64, x: p.knockback_x as f64,
}; y: p.knockback_y as f64,
z: p.knockback_z as f64,
}),
});
system_state.apply(ecs); system_state.apply(ecs);
} }

View file

@ -35,7 +35,7 @@ pub fn move_relative(
acceleration: &Vec3, acceleration: &Vec3,
) { ) {
let input_vector = input_vector(direction, speed, acceleration); 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 { pub fn input_vector(direction: &LookDirection, speed: f32, acceleration: &Vec3) -> Vec3 {
@ -208,7 +208,8 @@ pub struct LookDirection {
/// bounding box. /// bounding box.
#[derive(Debug, Component, Clone)] #[derive(Debug, Component, Clone)]
pub struct Physics { pub struct Physics {
pub delta: Vec3, /// How fast the entity is moving.
pub velocity: Vec3,
/// X acceleration. /// X acceleration.
pub xxa: f32, pub xxa: f32,
@ -232,7 +233,7 @@ pub struct Physics {
impl Physics { impl Physics {
pub fn new(dimensions: EntityDimensions, pos: &Vec3) -> Self { pub fn new(dimensions: EntityDimensions, pos: &Vec3) -> Self {
Self { Self {
delta: Vec3::default(), velocity: Vec3::default(),
xxa: 0., xxa: 0.,
yya: 0., yya: 0.,

View file

@ -201,8 +201,8 @@ pub fn move_colliding(
// if self.isRemoved() { return; } // if self.isRemoved() { return; }
if horizontal_collision { if horizontal_collision {
let delta_movement = &physics.delta; let delta_movement = &physics.velocity;
physics.delta = Vec3 { physics.velocity = Vec3 {
x: if x_collision { 0. } else { delta_movement.x }, x: if x_collision { 0. } else { delta_movement.x },
y: delta_movement.y, y: delta_movement.y,
z: if z_collision { 0. } else { delta_movement.z }, z: if z_collision { 0. } else { delta_movement.z },
@ -213,7 +213,7 @@ pub fn move_colliding(
// blockBelow.updateEntityAfterFallOn(this.level, this); // blockBelow.updateEntityAfterFallOn(this.level, this);
// the default implementation of updateEntityAfterFallOn sets the y movement to // the default implementation of updateEntityAfterFallOn sets the y movement to
// 0 // 0
physics.delta.y = 0.; physics.velocity.y = 0.;
} }
if on_ground { if on_ground {

View file

@ -113,9 +113,9 @@ fn travel(
// if should_discard_friction(self) { // if should_discard_friction(self) {
if false { if false {
physics.delta = movement; physics.velocity = movement;
} else { } else {
physics.delta = Vec3 { physics.velocity = Vec3 {
x: movement.x * inertia as f64, x: movement.x * inertia as f64,
y: movement.y * 0.9800000190734863f64, y: movement.y * 0.9800000190734863f64,
z: movement.z * inertia as f64, 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 // vanilla does movement interpolation here, doesn't really matter much for a
// bot though // bot though
if physics.delta.x.abs() < 0.003 { if physics.velocity.x.abs() < 0.003 {
physics.delta.x = 0.; physics.velocity.x = 0.;
} }
if physics.delta.y.abs() < 0.003 { if physics.velocity.y.abs() < 0.003 {
physics.delta.y = 0.; physics.velocity.y = 0.;
} }
if physics.delta.z.abs() < 0.003 { if physics.velocity.z.abs() < 0.003 {
physics.delta.z = 0.; physics.velocity.z = 0.;
} }
if let Some(jumping) = jumping { if let Some(jumping) = jumping {
@ -194,8 +194,8 @@ pub fn jump_from_ground(
let world = world_lock.read(); let world = world_lock.read();
let jump_power: f64 = jump_power(&world, position) as f64 + jump_boost_power(); let jump_power: f64 = jump_power(&world, position) as f64 + jump_boost_power();
let old_delta_movement = physics.delta; let old_delta_movement = physics.velocity;
physics.delta = Vec3 { physics.velocity = Vec3 {
x: old_delta_movement.x, x: old_delta_movement.x,
y: jump_power, y: jump_power,
z: old_delta_movement.z, z: old_delta_movement.z,
@ -203,7 +203,7 @@ pub fn jump_from_ground(
if **sprinting { if **sprinting {
// sprint jumping gives some extra velocity // sprint jumping gives some extra velocity
let y_rot = look_direction.y_rot * 0.017453292; let y_rot = look_direction.y_rot * 0.017453292;
physics.delta += Vec3 { physics.velocity += Vec3 {
x: (-math::sin(y_rot) * 0.2) as f64, x: (-math::sin(y_rot) * 0.2) as f64,
y: 0., y: 0.,
z: (math::cos(y_rot) * 0.2) as f64, 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); // entity.delta = entity.handle_on_climbable(entity.delta);
move_colliding( move_colliding(
&MoverType::Own, &MoverType::Own,
&physics.delta.clone(), &physics.velocity.clone(),
world, world,
position, position,
physics, physics,
@ -259,7 +259,7 @@ fn handle_relative_friction_and_calculate_movement(
// Vec3(var3.x, 0.2D, var3.z); } // Vec3(var3.x, 0.2D, var3.z); }
// TODO: powdered snow // TODO: powdered snow
physics.delta physics.velocity
} }
// private float getFrictionInfluencedSpeed(float friction) { // private float getFrictionInfluencedSpeed(float friction) {
@ -400,7 +400,7 @@ mod tests {
// delta is applied before gravity, so the first tick only sets the delta // delta is applied before gravity, so the first tick only sets the delta
assert_eq!(entity_pos.y, 70.); assert_eq!(entity_pos.y, 70.);
let entity_physics = app.world.get::<Physics>(entity).unwrap(); let entity_physics = app.world.get::<Physics>(entity).unwrap();
assert!(entity_physics.delta.y < 0.); assert!(entity_physics.velocity.y < 0.);
} }
app.world.run_schedule(FixedUpdate); app.world.run_schedule(FixedUpdate);
app.update(); app.update();
@ -463,7 +463,7 @@ mod tests {
// delta will change, but it won't move until next tick // delta will change, but it won't move until next tick
assert_eq!(entity_pos.y, 70.); assert_eq!(entity_pos.y, 70.);
let entity_physics = app.world.get::<Physics>(entity).unwrap(); let entity_physics = app.world.get::<Physics>(entity).unwrap();
assert!(entity_physics.delta.y < 0.); assert!(entity_physics.velocity.y < 0.);
} }
app.world.run_schedule(FixedUpdate); app.world.run_schedule(FixedUpdate);
app.update(); app.update();

View file

@ -1,13 +1,12 @@
use azalea_brigadier::suggestion::Suggestions; use azalea_brigadier::suggestion::Suggestions;
use azalea_buf::McBuf; use azalea_buf::McBuf;
use azalea_chat::FormattedText;
use azalea_protocol_macros::ClientboundGamePacket; use azalea_protocol_macros::ClientboundGamePacket;
#[derive(Clone, Debug, McBuf, ClientboundGamePacket)] #[derive(Clone, Debug, McBuf, ClientboundGamePacket)]
pub struct ClientboundCommandSuggestionsPacket { pub struct ClientboundCommandSuggestionsPacket {
#[var] #[var]
pub id: u32, pub id: u32,
pub suggestions: Suggestions<FormattedText>, pub suggestions: Suggestions,
} }
#[cfg(test)] #[cfg(test)]
@ -24,7 +23,7 @@ mod tests {
vec![Suggestion::new_with_tooltip( vec![Suggestion::new_with_tooltip(
StringRange::new(1, 4), StringRange::new(1, 4),
"foo", "foo",
FormattedText::from("bar".to_string()), "bar".to_string(),
)], )],
); );
let mut buf = Vec::new(); let mut buf = Vec::new();

View file

@ -35,7 +35,7 @@ pub fn best_tool_in_hotbar_for_block(block: BlockState, menu: &Menu) -> BestTool
menu, menu,
&Physics { &Physics {
on_ground: true, on_ground: true,
delta: Default::default(), velocity: Default::default(),
xxa: Default::default(), xxa: Default::default(),
yya: Default::default(), yya: Default::default(),
zza: Default::default(), zza: Default::default(),

View file

@ -25,7 +25,7 @@ use crate::pathfinder::moves::PathfinderCtx;
use crate::pathfinder::world::CachedWorld; use crate::pathfinder::world::CachedWorld;
use azalea_client::chat::SendChatEvent; use azalea_client::chat::SendChatEvent;
use azalea_client::inventory::{InventoryComponent, InventorySet}; use azalea_client::inventory::{InventoryComponent, InventorySet};
use azalea_client::movement::walk_listener; use azalea_client::movement::MoveEventsSet;
use azalea_client::{StartSprintEvent, StartWalkEvent}; use azalea_client::{StartSprintEvent, StartWalkEvent};
use azalea_core::position::{BlockPos, Vec3}; use azalea_core::position::{BlockPos, Vec3};
use azalea_entity::metadata::Player; use azalea_entity::metadata::Player;
@ -85,7 +85,7 @@ impl Plugin for PathfinderPlugin {
handle_stop_pathfinding_event, handle_stop_pathfinding_event,
) )
.chain() .chain()
.before(walk_listener) .before(MoveEventsSet)
.before(InventorySet), .before(InventorySet),
); );
} }
@ -462,8 +462,8 @@ fn check_node_reached(
&& BlockPos::from(position) == movement.target && BlockPos::from(position) == movement.target
// adding the delta like this isn't a perfect solution but it helps to make // 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 // sure we don't keep going if our delta is high
&& (x_difference_from_center + physics.delta.x).abs() < 0.2 && (x_difference_from_center + physics.velocity.x).abs() < 0.2
&& (z_difference_from_center + physics.delta.z).abs() < 0.2 && (z_difference_from_center + physics.velocity.z).abs() < 0.2
} else { } else {
true true
}; };

View file

@ -97,7 +97,7 @@ fn execute_ascend_move(mut ctx: ExecuteCtx) {
let side_distance = z_axis as f64 * (target_center.x - position.x).abs() let side_distance = z_axis as f64 * (target_center.x - position.x).abs()
+ x_axis as f64 * (target_center.z - position.z).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 { if lateral_motion > 0.1 {
return; return;
} }