aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormat <git@matdoes.dev>2025-07-24 04:42:52 -0530
committermat <git@matdoes.dev>2025-07-24 04:42:52 -0530
commit302752860c7c10f02479727fe3d6d3b086177604 (patch)
tree5088f051ed9a2e816b17966513c6494e69626728
parent45f89b48e425262974a993adc0774dcbcad397fa (diff)
downloadazalea-drasl-302752860c7c10f02479727fe3d6d3b086177604.tar.xz
update movement code for 1.21.5 changes
fixes grim flags
-rw-r--r--azalea-client/src/plugins/movement.rs73
-rw-r--r--azalea-core/src/bitset.rs4
-rw-r--r--azalea-core/src/position.rs60
-rw-r--r--azalea-entity/src/attributes.rs3
-rw-r--r--azalea-entity/src/lib.rs5
-rw-r--r--azalea-physics/src/lib.rs39
-rw-r--r--azalea-protocol/src/common/movements.rs3
7 files changed, 156 insertions, 31 deletions
diff --git a/azalea-client/src/plugins/movement.rs b/azalea-client/src/plugins/movement.rs
index e97d9ec1..e4778be1 100644
--- a/azalea-client/src/plugins/movement.rs
+++ b/azalea-client/src/plugins/movement.rs
@@ -1,6 +1,9 @@
use std::{backtrace::Backtrace, io};
-use azalea_core::{position::Vec3, tick::GameTick};
+use azalea_core::{
+ position::{Vec2, Vec3},
+ tick::GameTick,
+};
use azalea_entity::{
Attributes, InLoadedChunk, Jumping, LastSentPosition, LookDirection, Physics, Position,
metadata::Sprinting,
@@ -137,8 +140,7 @@ pub struct PhysicsState {
pub trying_to_sprint: bool,
pub move_direction: WalkDirection,
- pub forward_impulse: f32,
- pub left_impulse: f32,
+ pub move_vector: Vec2,
}
#[allow(clippy::type_complexity)]
@@ -308,12 +310,10 @@ pub fn send_sprinting_if_needed(
}
}
-/// Update the impulse from self.move_direction. The multiplier is used for
-/// sneaking.
+/// Updates the [`PhysicsState::move_vector`] based on the
+/// [`PhysicsState::move_direction`].
pub(crate) fn tick_controls(mut query: Query<&mut PhysicsState>) {
for mut physics_state in query.iter_mut() {
- let multiplier: Option<f32> = None;
-
let mut forward_impulse: f32 = 0.;
let mut left_impulse: f32 = 0.;
let move_direction = physics_state.move_direction;
@@ -337,13 +337,9 @@ pub(crate) fn tick_controls(mut query: Query<&mut PhysicsState>) {
}
_ => {}
};
- physics_state.forward_impulse = forward_impulse;
- physics_state.left_impulse = left_impulse;
- if let Some(multiplier) = multiplier {
- physics_state.forward_impulse *= multiplier;
- physics_state.left_impulse *= multiplier;
- }
+ let move_vector = Vec2::new(left_impulse, forward_impulse).normalized();
+ physics_state.move_vector = move_vector;
}
}
@@ -357,8 +353,12 @@ pub fn local_player_ai_step(
) {
for (physics_state, mut physics, mut sprinting, mut attributes) in query.iter_mut() {
// server ai step
- physics.x_acceleration = physics_state.left_impulse;
- physics.z_acceleration = physics_state.forward_impulse;
+
+ // TODO: replace those booleans when using items, passengers, and sneaking are
+ // properly implemented
+ let move_vector = modify_input(physics_state.move_vector, false, false, false, &attributes);
+ physics.x_acceleration = move_vector.x;
+ physics.z_acceleration = move_vector.y;
// TODO: food data and abilities
// let has_enough_food_to_sprint = self.food_data().food_level ||
@@ -385,6 +385,47 @@ pub fn local_player_ai_step(
}
}
+// LocalPlayer.modifyInput
+fn modify_input(
+ mut move_vector: Vec2,
+ is_using_item: bool,
+ is_passenger: bool,
+ moving_slowly: bool,
+ attributes: &Attributes,
+) -> Vec2 {
+ if move_vector.length_squared() == 0. {
+ return move_vector;
+ }
+
+ move_vector *= 0.98;
+ if is_using_item && !is_passenger {
+ move_vector *= 0.2;
+ }
+
+ if moving_slowly {
+ let sneaking_speed = attributes.sneaking_speed.calculate() as f32;
+ move_vector *= sneaking_speed;
+ }
+
+ modify_input_speed_for_square_movement(move_vector)
+}
+fn modify_input_speed_for_square_movement(move_vector: Vec2) -> Vec2 {
+ let length = move_vector.length();
+ if length == 0. {
+ return move_vector;
+ }
+ let scaled_to_inverse_length = move_vector * (1. / length);
+ let dist = distance_to_unit_square(scaled_to_inverse_length);
+ let scale = (length * dist).min(1.);
+ scaled_to_inverse_length * scale
+}
+fn distance_to_unit_square(v: Vec2) -> f32 {
+ let x = v.x.abs();
+ let y = v.y.abs();
+ let ratio = if y > x { x / y } else { y / x };
+ (1.0 + ratio * ratio).sqrt()
+}
+
impl Client {
/// Start walking in the given direction. To sprint, use
/// [`Client::sprint`]. To stop walking, call walk with
@@ -508,7 +549,7 @@ fn has_enough_impulse_to_start_sprinting(physics_state: &PhysicsState) -> bool {
// if self.underwater() {
// self.has_forward_impulse()
// } else {
- physics_state.forward_impulse > 0.8
+ physics_state.move_vector.y > 0.8
// }
}
diff --git a/azalea-core/src/bitset.rs b/azalea-core/src/bitset.rs
index 2a3e5b51..fbb12ee0 100644
--- a/azalea-core/src/bitset.rs
+++ b/azalea-core/src/bitset.rs
@@ -151,6 +151,10 @@ where
}
}
+ pub const fn new_with_data(data: [u8; bits_to_bytes(N)]) -> Self {
+ FixedBitSet { data }
+ }
+
#[inline]
pub fn index(&self, index: usize) -> bool {
(self.data[index / 8] & (1u8 << (index % 8))) != 0
diff --git a/azalea-core/src/position.rs b/azalea-core/src/position.rs
index c0c25639..dd6a37e0 100644
--- a/azalea-core/src/position.rs
+++ b/azalea-core/src/position.rs
@@ -818,6 +818,66 @@ impl fmt::Display for Vec3 {
}
}
+/// A 2D vector.
+#[derive(Clone, Copy, Debug, Default, PartialEq, AzBuf)]
+#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
+pub struct Vec2 {
+ pub x: f32,
+ pub y: f32,
+}
+impl Vec2 {
+ const ZERO: Vec2 = Vec2 { x: 0.0, y: 0.0 };
+
+ #[inline]
+ pub fn new(x: f32, y: f32) -> Self {
+ Vec2 { x, y }
+ }
+ #[inline]
+ pub fn scale(&self, amount: f32) -> Self {
+ Vec2 {
+ x: self.x * amount,
+ y: self.y * amount,
+ }
+ }
+ #[inline]
+ pub fn dot(&self, other: Vec2) -> f32 {
+ self.x * other.x + self.y * other.y
+ }
+ #[inline]
+ pub fn normalized(&self) -> Self {
+ let length = (self.x * self.x + self.y * self.y).sqrt();
+ if length < 1e-4 {
+ return Vec2::ZERO;
+ }
+ Vec2 {
+ x: self.x / length,
+ y: self.y / length,
+ }
+ }
+ #[inline]
+ pub fn length_squared(&self) -> f32 {
+ self.x * self.x + self.y * self.y
+ }
+ #[inline]
+ pub fn length(&self) -> f32 {
+ self.length_squared().sqrt()
+ }
+}
+impl Mul<f32> for Vec2 {
+ type Output = Self;
+
+ #[inline]
+ fn mul(self, rhs: f32) -> Self::Output {
+ self.scale(rhs)
+ }
+}
+impl MulAssign<f32> for Vec2 {
+ #[inline]
+ fn mul_assign(&mut self, rhs: f32) {
+ *self = self.scale(rhs);
+ }
+}
+
const PACKED_X_LENGTH: u64 = 1 + 25; // minecraft does something a bit more complicated to get this 25
const PACKED_Z_LENGTH: u64 = PACKED_X_LENGTH;
const PACKED_Y_LENGTH: u64 = 64 - PACKED_X_LENGTH - PACKED_Z_LENGTH;
diff --git a/azalea-entity/src/attributes.rs b/azalea-entity/src/attributes.rs
index 7af845f8..c2a22e9b 100644
--- a/azalea-entity/src/attributes.rs
+++ b/azalea-entity/src/attributes.rs
@@ -10,6 +10,7 @@ use thiserror::Error;
#[derive(Clone, Debug, Component)]
pub struct Attributes {
pub speed: AttributeInstance,
+ pub sneaking_speed: AttributeInstance,
pub attack_speed: AttributeInstance,
pub water_movement_efficiency: AttributeInstance,
@@ -92,7 +93,7 @@ pub enum AttributeModifierOperation {
pub fn sprinting_modifier() -> AttributeModifier {
AttributeModifier {
id: ResourceLocation::new("sprinting"),
- amount: 0.30000001192092896,
+ amount: 0.3f32 as f64,
operation: AttributeModifierOperation::MultiplyTotal,
}
}
diff --git a/azalea-entity/src/lib.rs b/azalea-entity/src/lib.rs
index 340b6f25..18e9e1a1 100644
--- a/azalea-entity/src/lib.rs
+++ b/azalea-entity/src/lib.rs
@@ -50,10 +50,10 @@ pub fn move_relative(
pub fn input_vector(direction: LookDirection, speed: f32, acceleration: Vec3) -> Vec3 {
let distance = acceleration.length_squared();
- if distance < 1.0E-7 {
+ if distance < 1.0e-7 {
return Vec3::ZERO;
}
- let acceleration = if distance > 1.0 {
+ let acceleration = if distance > 1. {
acceleration.normalize()
} else {
acceleration
@@ -492,6 +492,7 @@ pub fn default_attributes(_entity_kind: EntityKind) -> Attributes {
// entities have different defaults
Attributes {
speed: AttributeInstance::new(0.1),
+ sneaking_speed: AttributeInstance::new(0.3),
attack_speed: AttributeInstance::new(4.0),
water_movement_efficiency: AttributeInstance::new(0.0),
block_interaction_range: AttributeInstance::new(4.5),
diff --git a/azalea-physics/src/lib.rs b/azalea-physics/src/lib.rs
index 772b9b86..9481cef7 100644
--- a/azalea-physics/src/lib.rs
+++ b/azalea-physics/src/lib.rs
@@ -15,10 +15,10 @@ use azalea_core::{
tick::GameTick,
};
use azalea_entity::{
- Attributes, InLoadedChunk, Jumping, LocalEntity, LookDirection, OnClimbable, Physics, Pose,
- Position, metadata::Sprinting, move_relative,
+ Attributes, EntityKindComponent, InLoadedChunk, Jumping, LocalEntity, LookDirection,
+ OnClimbable, Physics, Pose, Position, metadata::Sprinting, move_relative,
};
-use azalea_registry::Block;
+use azalea_registry::{Block, EntityKind};
use azalea_world::{Instance, InstanceContainer, InstanceName};
use bevy_app::{App, Plugin};
use bevy_ecs::prelude::*;
@@ -66,12 +66,17 @@ pub fn ai_step(
&LookDirection,
&Sprinting,
&InstanceName,
+ &EntityKindComponent,
),
(With<LocalEntity>, With<InLoadedChunk>),
>,
instance_container: Res<InstanceContainer>,
) {
- for (mut physics, jumping, position, look_direction, sprinting, instance_name) in &mut query {
+ for (mut physics, jumping, position, look_direction, sprinting, instance_name, entity_kind) in
+ &mut query
+ {
+ let is_player = **entity_kind == EntityKind::Player;
+
// vanilla does movement interpolation here, doesn't really matter much for a
// bot though
@@ -79,14 +84,29 @@ pub fn ai_step(
physics.no_jump_delay -= 1;
}
- if physics.velocity.x.abs() < 0.003 {
- physics.velocity.x = 0.;
+ if is_player {
+ if physics.velocity.horizontal_distance_squared() < 9.0e-6 {
+ physics.velocity.x = 0.;
+ physics.velocity.z = 0.;
+ }
+ } else {
+ if physics.velocity.x.abs() < 0.003 {
+ physics.velocity.x = 0.;
+ }
+ if physics.velocity.z.abs() < 0.003 {
+ physics.velocity.z = 0.;
+ }
}
+
if physics.velocity.y.abs() < 0.003 {
physics.velocity.y = 0.;
}
- if physics.velocity.z.abs() < 0.003 {
- physics.velocity.z = 0.;
+
+ if is_player {
+ // handled in local_player_ai_step
+ } else {
+ physics.x_acceleration *= 0.98;
+ physics.z_acceleration *= 0.98;
}
if jumping == Some(&Jumping(true)) {
@@ -128,9 +148,6 @@ pub fn ai_step(
physics.no_jump_delay = 0;
}
- physics.x_acceleration *= 0.98;
- physics.z_acceleration *= 0.98;
-
// TODO: freezing, pushEntities, drowning damage (in their own systems,
// after `travel`)
}
diff --git a/azalea-protocol/src/common/movements.rs b/azalea-protocol/src/common/movements.rs
index e88fb87e..6475b46f 100644
--- a/azalea-protocol/src/common/movements.rs
+++ b/azalea-protocol/src/common/movements.rs
@@ -94,7 +94,8 @@ fn apply_change<T: Add<Output = T>>(base: T, condition: bool, change: T) -> T {
impl AzaleaRead for RelativeMovements {
fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
// yes minecraft seriously wastes that many bits, smh
- let set = FixedBitSet::<32>::azalea_read(buf)?;
+ let set = u32::azalea_read(buf)?;
+ let set = FixedBitSet::<32>::new_with_data(set.swap_bytes().to_be_bytes());
Ok(RelativeMovements {
x: set.index(0),
y: set.index(1),