aboutsummaryrefslogtreecommitdiff
path: root/azalea-physics/src
diff options
context:
space:
mode:
authormat <27899617+mat-1@users.noreply.github.com>2025-08-14 20:40:13 -0500
committerGitHub <noreply@github.com>2025-08-14 20:40:13 -0500
commite74ed047dbaf3877db4a89a2d589e992abd0bb11 (patch)
tree0a728c8be167a1d59a5492ed9df666f41cf12e57 /azalea-physics/src
parent6695132ddb31780786c67b8b9ff5df8ab3891438 (diff)
downloadazalea-drasl-e74ed047dbaf3877db4a89a2d589e992abd0bb11.tar.xz
Sneaking (#237)
* start implementing sneaking * fix horizontal_collision being inverted and cleanup * clippy * change dimensions and eye height based on pose * proper support for automatically crouching in certain cases * fix anticheat issues * add line to changelog and update a comment
Diffstat (limited to 'azalea-physics/src')
-rw-r--r--azalea-physics/src/collision/mod.rs226
-rw-r--r--azalea-physics/src/lib.rs117
-rw-r--r--azalea-physics/src/local_player.rs67
-rw-r--r--azalea-physics/src/travel.rs246
4 files changed, 382 insertions, 274 deletions
diff --git a/azalea-physics/src/collision/mod.rs b/azalea-physics/src/collision/mod.rs
index dc439f7b..d8ac3a95 100644
--- a/azalea-physics/src/collision/mod.rs
+++ b/azalea-physics/src/collision/mod.rs
@@ -11,9 +11,13 @@ use azalea_block::{BlockState, fluid_state::FluidState};
use azalea_core::{
aabb::AABB,
direction::Axis,
- math::EPSILON,
+ math::{self, EPSILON},
position::{BlockPos, Vec3},
};
+use azalea_entity::{
+ Attributes, Jumping, LookDirection, OnClimbable, Physics, PlayerAbilities, Pose, Position,
+ metadata::Sprinting,
+};
use azalea_world::{ChunkStorage, Instance, MoveEntityError};
use bevy_ecs::{entity::Entity, world::Mut};
pub use blocks::BlockWithShape;
@@ -23,6 +27,7 @@ pub use shape::*;
use tracing::warn;
use self::world_collisions::get_block_collisions;
+use crate::{local_player::PhysicsState, travel::no_collision};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MoverType {
@@ -34,22 +39,16 @@ pub enum MoverType {
}
// Entity.collide
-fn collide(
- movement: Vec3,
- world: &Instance,
- physics: &azalea_entity::Physics,
- source_entity: Option<Entity>,
- physics_query: &PhysicsQuery,
- collidable_entity_query: &CollidableEntityQuery,
-) -> Vec3 {
- let entity_bounding_box = physics.bounding_box;
+fn collide(ctx: &MoveCtx, movement: Vec3) -> Vec3 {
+ let entity_bounding_box = ctx.physics.bounding_box;
let entity_collisions = get_entity_collisions(
- world,
+ ctx.world,
&entity_bounding_box.expand_towards(movement),
- source_entity,
- physics_query,
- collidable_entity_query,
+ Some(ctx.source_entity),
+ ctx.physics_query,
+ ctx.collidable_entity_query,
);
+ let world = ctx.world;
let collided_delta = if movement.length_squared() == 0.0 {
movement
} else {
@@ -60,7 +59,7 @@ fn collide(
let y_collision = movement.y != collided_delta.y;
let z_collision = movement.z != collided_delta.z;
- let on_ground = physics.on_ground() || y_collision && movement.y < 0.;
+ let on_ground = ctx.physics.on_ground() || y_collision && movement.y < 0.;
let max_up_step = 0.6;
if max_up_step > 0. && on_ground && (x_collision || z_collision) {
@@ -106,20 +105,30 @@ fn collide(
collided_delta
}
+pub struct MoveCtx<'world, 'state, 'a, 'b> {
+ pub mover_type: MoverType,
+ pub world: &'a Instance,
+ pub position: Mut<'a, Position>,
+ pub physics: &'a mut Physics,
+ pub source_entity: Entity,
+ pub physics_query: &'a PhysicsQuery<'world, 'state, 'b>,
+ pub collidable_entity_query: &'a CollidableEntityQuery<'world, 'state>,
+ pub physics_state: Option<&'a PhysicsState>,
+ pub attributes: &'a Attributes,
+ pub abilities: Option<&'a PlayerAbilities>,
+
+ pub direction: LookDirection,
+ pub sprinting: Sprinting,
+ pub on_climbable: OnClimbable,
+ pub pose: Option<Pose>,
+ pub jumping: Jumping,
+}
+
/// Move an entity by a given delta, checking for collisions.
///
/// In Mojmap, this is `Entity.move`.
#[allow(clippy::too_many_arguments)]
-pub fn move_colliding(
- _mover_type: MoverType,
- movement: Vec3,
- world: &Instance,
- position: &mut Mut<azalea_entity::Position>,
- physics: &mut azalea_entity::Physics,
- source_entity: Option<Entity>,
- physics_query: &PhysicsQuery,
- collidable_entity_query: &CollidableEntityQuery,
-) -> Result<(), MoveEntityError> {
+pub fn move_colliding(ctx: &mut MoveCtx, mut movement: Vec3) -> Result<(), MoveEntityError> {
// TODO: do all these
// if self.no_physics {
@@ -139,20 +148,16 @@ pub fn move_colliding(
// this.setDeltaMovement(Vec3.ZERO);
// }
- // movement = this.maybeBackOffFromEdge(movement, moverType);
+ movement = maybe_back_off_from_edge(ctx, movement);
+ let collide_result = collide(ctx, movement);
- let collide_result = collide(
- movement,
- world,
- physics,
- source_entity,
- physics_query,
- collidable_entity_query,
- );
+ let move_distance_sqr = collide_result.length_squared();
- let move_distance = collide_result.length_squared();
+ let position = &mut ctx.position;
+ let physics = &mut *ctx.physics;
+ let world = ctx.world;
- if move_distance > EPSILON {
+ if move_distance_sqr > EPSILON || movement.length_squared() - move_distance_sqr < EPSILON {
// TODO: fall damage
let new_pos = {
@@ -168,8 +173,8 @@ pub fn move_colliding(
}
}
- let x_collision = movement.x != collide_result.x;
- let z_collision = movement.z != collide_result.z;
+ let x_collision = !math::equal(movement.x, collide_result.x);
+ let z_collision = !math::equal(movement.z, collide_result.z);
let horizontal_collision = x_collision || z_collision;
physics.horizontal_collision = horizontal_collision;
@@ -180,14 +185,15 @@ pub fn move_colliding(
// TODO: minecraft checks for a "minor" horizontal collision here
- let _block_pos_below = azalea_entity::on_pos_legacy(&world.chunks, **position);
- // let _block_state_below = self
- // .world
- // .get_block_state(&block_pos_below)
- // .expect("Couldn't get block state below");
+ let block_pos_below = azalea_entity::on_pos_legacy(&world.chunks, **position);
+ let block_state_below = world.get_block_state(block_pos_below).unwrap_or_default();
- // self.check_fall_damage(collide_result.y, on_ground, block_state_below,
- // block_pos_below);
+ check_fall_damage(
+ physics,
+ collide_result.y,
+ block_state_below,
+ block_pos_below,
+ );
// if self.isRemoved() { return; }
@@ -238,6 +244,136 @@ pub fn move_colliding(
Ok(())
}
+fn check_fall_damage(
+ physics: &mut Physics,
+ delta_y: f64,
+ _block_state_below: BlockState,
+ _block_pos_below: BlockPos,
+) {
+ if !physics.is_in_water() && delta_y < 0. {
+ physics.fall_distance -= delta_y as f32 as f64;
+ }
+
+ if physics.on_ground() {
+ // vanilla calls block.fallOn here but it's not relevant for us
+
+ physics.fall_distance = 0.;
+ }
+}
+
+fn maybe_back_off_from_edge(move_ctx: &mut MoveCtx, mut movement: Vec3) -> Vec3 {
+ let is_staying_on_ground_surface = move_ctx.physics_state.is_some_and(|s| s.trying_to_crouch);
+ let max_up_step = get_max_up_step(move_ctx.attributes);
+
+ let fall_ctx = CanFallAtLeastCtx {
+ physics: move_ctx.physics,
+ world: move_ctx.world,
+ source_entity: move_ctx.source_entity,
+ physics_query: move_ctx.physics_query,
+ collidable_entity_query: move_ctx.collidable_entity_query,
+ };
+
+ let Some(abilities) = move_ctx.abilities else {
+ return movement;
+ };
+
+ let is_backing_off = !abilities.flying
+ && movement.y <= 0.
+ && matches!(move_ctx.mover_type, MoverType::Own | MoverType::Player)
+ && is_staying_on_ground_surface
+ && is_above_ground(&fall_ctx, max_up_step);
+ if !is_backing_off {
+ return movement;
+ }
+
+ let min_movement = 0.05;
+ let min_movement_x = movement.x.signum() * min_movement;
+ let min_movement_z = movement.z.signum() * min_movement;
+
+ while movement.x != 0. && can_fall_at_least(&fall_ctx, movement.x, 0., max_up_step as f64) {
+ if movement.x.abs() <= min_movement {
+ movement.x = 0.;
+ break;
+ }
+
+ movement.x -= min_movement_x
+ }
+ while movement.z != 0. && can_fall_at_least(&fall_ctx, 0., movement.z, max_up_step as f64) {
+ if movement.z.abs() <= min_movement {
+ movement.z = 0.;
+ break;
+ }
+
+ movement.z -= min_movement_z
+ }
+ while movement.x != 0.0
+ && movement.z != 0.0
+ && can_fall_at_least(&fall_ctx, movement.x, movement.z, max_up_step as f64)
+ {
+ if movement.x.abs() <= min_movement {
+ movement.x = 0.;
+ } else {
+ movement.x -= min_movement_x;
+ }
+ if movement.z.abs() <= min_movement {
+ movement.z = 0.;
+ } else {
+ movement.z -= min_movement_z;
+ }
+ }
+
+ movement
+}
+
+fn get_max_up_step(attributes: &Attributes) -> f32 {
+ // this would be different if we were riding an entity
+ attributes.step_height.calculate() as f32
+}
+
+fn is_above_ground(ctx: &CanFallAtLeastCtx, max_up_step: f32) -> bool {
+ ctx.physics.on_ground()
+ && ctx.physics.fall_distance < max_up_step as f64
+ && !can_fall_at_least(ctx, 0., 0., max_up_step as f64 - ctx.physics.fall_distance)
+}
+
+pub struct CanFallAtLeastCtx<'world, 'state, 'a, 'b> {
+ physics: &'a Physics,
+ world: &'a Instance,
+ source_entity: Entity,
+ physics_query: &'a PhysicsQuery<'world, 'state, 'b>,
+ collidable_entity_query: &'a CollidableEntityQuery<'world, 'state>,
+}
+
+fn can_fall_at_least(
+ ctx: &CanFallAtLeastCtx,
+ delta_x: f64,
+ delta_z: f64,
+ max_up_step: f64,
+) -> bool {
+ let aabb = ctx.physics.bounding_box;
+ let aabb = AABB {
+ min: Vec3 {
+ x: aabb.min.x + EPSILON + delta_x,
+ y: aabb.min.y - max_up_step - EPSILON,
+ z: aabb.min.z + EPSILON + delta_z,
+ },
+ max: Vec3 {
+ x: aabb.max.x - EPSILON + delta_x,
+ y: aabb.min.y,
+ z: aabb.max.z - EPSILON + delta_z,
+ },
+ };
+ no_collision(
+ ctx.world,
+ Some(ctx.source_entity),
+ ctx.physics_query,
+ ctx.collidable_entity_query,
+ ctx.physics,
+ &aabb,
+ false,
+ )
+}
+
fn collide_bounding_box(
movement: Vec3,
entity_bounding_box: &AABB,
diff --git a/azalea-physics/src/lib.rs b/azalea-physics/src/lib.rs
index 27250f61..fb62b48b 100644
--- a/azalea-physics/src/lib.rs
+++ b/azalea-physics/src/lib.rs
@@ -4,6 +4,7 @@
pub mod clip;
pub mod collision;
pub mod fluids;
+pub mod local_player;
pub mod travel;
use std::collections::HashSet;
@@ -16,18 +17,17 @@ use azalea_core::{
};
use azalea_entity::{
Attributes, EntityKindComponent, HasClientLoaded, Jumping, LocalEntity, LookDirection,
- OnClimbable, Physics, Pose, Position, metadata::Sprinting, move_relative,
+ OnClimbable, Physics, Pose, Position, dimensions::EntityDimensions, metadata::Sprinting,
+ move_relative,
};
use azalea_registry::{Block, EntityKind};
use azalea_world::{Instance, InstanceContainer, InstanceName};
use bevy_app::{App, Plugin};
use bevy_ecs::prelude::*;
use clip::box_traverse_blocks;
-use collision::{
- BLOCK_SHAPE, BlockWithShape, MoverType, VoxelShape,
- entity_collisions::{CollidableEntityQuery, PhysicsQuery},
- move_colliding,
-};
+use collision::{BLOCK_SHAPE, BlockWithShape, VoxelShape, move_colliding};
+
+use crate::collision::MoveCtx;
/// A Bevy [`SystemSet`] for running physics that makes entities do things.
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
@@ -161,12 +161,12 @@ fn jump_in_liquid(physics: &mut Physics) {
#[allow(clippy::type_complexity)]
pub fn apply_effects_from_blocks(
mut query: Query<
- (&mut Physics, &Position, &InstanceName),
+ (&mut Physics, &Position, &EntityDimensions, &InstanceName),
(With<LocalEntity>, With<HasClientLoaded>),
>,
instance_container: Res<InstanceContainer>,
) {
- for (mut physics, position, world_name) in &mut query {
+ for (mut physics, position, dimensions, world_name) in &mut query {
let Some(world_lock) = instance_container.get(world_name) else {
continue;
};
@@ -189,12 +189,13 @@ pub fn apply_effects_from_blocks(
to: **position,
}];
- check_inside_blocks(&mut physics, &world, &movement_this_tick);
+ check_inside_blocks(&mut physics, dimensions, &world, &movement_this_tick);
}
}
fn check_inside_blocks(
physics: &mut Physics,
+ dimensions: &EntityDimensions,
world: &Instance,
movements: &[EntityMovement],
) -> Vec<BlockState> {
@@ -202,8 +203,7 @@ fn check_inside_blocks(
let mut visited_blocks = HashSet::<BlockState>::new();
for movement in movements {
- let bounding_box_at_target = physics
- .dimensions
+ let bounding_box_at_target = dimensions
.make_bounding_box(movement.to)
.deflate_all(1.0E-5);
@@ -242,7 +242,7 @@ fn check_inside_blocks(
movement.to,
traversed_block,
entity_inside_collision_shape,
- physics,
+ dimensions,
)
{
continue;
@@ -262,9 +262,9 @@ fn collided_with_shape_moving_from(
to: Vec3,
traversed_block: BlockPos,
entity_inside_collision_shape: &VoxelShape,
- physics: &Physics,
+ dimensions: &EntityDimensions,
) -> bool {
- let bounding_box_from = physics.dimensions.make_bounding_box(from);
+ let bounding_box_from = dimensions.make_bounding_box(from);
let delta = to - from;
bounding_box_from.collided_along_vector(
delta,
@@ -367,63 +367,27 @@ fn get_block_pos_below_that_affects_movement(position: Position) -> BlockPos {
)
}
-/// Options for [`handle_relative_friction_and_calculate_movement`]
-struct HandleRelativeFrictionAndCalculateMovementOpts<'a, 'b, 'world, 'state> {
- block_friction: f32,
- world: &'a Instance,
- physics: &'a mut Physics,
- direction: LookDirection,
- position: Mut<'a, Position>,
- attributes: &'a Attributes,
- is_sprinting: bool,
- on_climbable: OnClimbable,
- pose: Option<Pose>,
- jumping: Jumping,
- entity: Entity,
- physics_query: &'a PhysicsQuery<'world, 'state, 'b>,
- collidable_entity_query: &'a CollidableEntityQuery<'world, 'state>,
-}
-fn handle_relative_friction_and_calculate_movement(
- HandleRelativeFrictionAndCalculateMovementOpts {
- block_friction,
- world,
- physics,
- direction,
- mut position,
- attributes,
- is_sprinting,
- on_climbable,
- pose,
- jumping,
- entity,
- physics_query,
- collidable_entity_query,
- }: HandleRelativeFrictionAndCalculateMovementOpts<'_, '_, '_, '_>,
-) -> Vec3 {
+fn handle_relative_friction_and_calculate_movement(ctx: &mut MoveCtx, block_friction: f32) -> Vec3 {
move_relative(
- physics,
- direction,
- get_friction_influenced_speed(physics, attributes, block_friction, is_sprinting),
+ ctx.physics,
+ ctx.direction,
+ get_friction_influenced_speed(ctx.physics, ctx.attributes, block_friction, ctx.sprinting),
Vec3::new(
- physics.x_acceleration as f64,
- physics.y_acceleration as f64,
- physics.z_acceleration as f64,
+ ctx.physics.x_acceleration as f64,
+ ctx.physics.y_acceleration as f64,
+ ctx.physics.z_acceleration as f64,
),
);
- physics.velocity = handle_on_climbable(physics.velocity, on_climbable, *position, world, pose);
-
- move_colliding(
- MoverType::Own,
- physics.velocity,
- world,
- &mut position,
- physics,
- Some(entity),
- physics_query,
- collidable_entity_query,
- )
- .expect("Entity should exist");
+ ctx.physics.velocity = handle_on_climbable(
+ ctx.physics.velocity,
+ ctx.on_climbable,
+ *ctx.position,
+ ctx.world,
+ ctx.pose,
+ );
+
+ move_colliding(ctx, ctx.physics.velocity).expect("Entity should exist");
// let delta_movement = entity.delta;
// ladders
// if ((entity.horizontalCollision || entity.jumping) && (entity.onClimbable()
@@ -431,19 +395,20 @@ fn handle_relative_friction_and_calculate_movement(
// PowderSnowBlock.canEntityWalkOnPowderSnow(entity))) { var3 = new
// Vec3(var3.x, 0.2D, var3.z); }
- if physics.horizontal_collision || *jumping {
- let block_at_feet: Block = world
+ if ctx.physics.horizontal_collision || *ctx.jumping {
+ let block_at_feet: Block = ctx
+ .world
.chunks
- .get_block_state((*position).into())
+ .get_block_state(BlockPos::from(*ctx.position))
.unwrap_or_default()
.into();
- if *on_climbable || block_at_feet == Block::PowderSnow {
- physics.velocity.y = 0.2;
+ if *ctx.on_climbable || block_at_feet == Block::PowderSnow {
+ ctx.physics.velocity.y = 0.2;
}
}
- physics.velocity
+ ctx.physics.velocity
}
fn handle_on_climbable(
@@ -467,7 +432,7 @@ fn handle_on_climbable(
// sneaking on ladders/vines
if y < 0.0
- && pose == Some(Pose::Sneaking)
+ && pose == Some(Pose::Crouching)
&& azalea_registry::Block::from(
world
.chunks
@@ -488,15 +453,15 @@ fn get_friction_influenced_speed(
physics: &Physics,
attributes: &Attributes,
friction: f32,
- is_sprinting: bool,
+ sprinting: Sprinting,
) -> f32 {
// TODO: have speed & flying_speed fields in entity
if physics.on_ground() {
- let speed: f32 = attributes.speed.calculate() as f32;
+ let speed = attributes.movement_speed.calculate() as f32;
speed * (0.21600002f32 / (friction * friction * friction))
} else {
// entity.flying_speed
- if is_sprinting { 0.025999999f32 } else { 0.02 }
+ if *sprinting { 0.025999999f32 } else { 0.02 }
}
}
diff --git a/azalea-physics/src/local_player.rs b/azalea-physics/src/local_player.rs
new file mode 100644
index 00000000..6ce196b6
--- /dev/null
+++ b/azalea-physics/src/local_player.rs
@@ -0,0 +1,67 @@
+use azalea_core::position::Vec2;
+use bevy_ecs::component::Component;
+
+/// Component for entities that can move and sprint. Usually only in
+/// [`LocalEntity`]s.
+///
+/// [`LocalEntity`]: azalea_entity::LocalEntity
+#[derive(Default, Component, Clone)]
+pub struct PhysicsState {
+ /// Minecraft only sends a movement packet either after 20 ticks or if the
+ /// player moved enough. This is that tick counter.
+ pub position_remainder: u32,
+ pub was_sprinting: bool,
+ // Whether we're going to try to start sprinting this tick. Equivalent to
+ // holding down ctrl for a tick.
+ pub trying_to_sprint: bool,
+
+ /// Whether our player is currently trying to sneak.
+ ///
+ /// This is distinct from
+ /// [`AbstractEntityShiftKeyDown`](azalea_entity::metadata::AbstractEntityShiftKeyDown),
+ /// which is a metadata value that is controlled by the server and affects
+ /// how the nametags of other entities are displayed.
+ ///
+ /// To check whether we're actually sneaking, you can check the
+ /// [`Crouching`] or [`Pose`] components.
+ pub trying_to_crouch: bool,
+
+ pub move_direction: WalkDirection,
+ pub move_vector: Vec2,
+}
+
+/// A direction that a player can walk in, including none.
+///
+/// Superset of [`SprintDirection`].
+#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
+pub enum WalkDirection {
+ #[default]
+ None,
+ Forward,
+ Backward,
+ Left,
+ Right,
+ ForwardRight,
+ ForwardLeft,
+ BackwardRight,
+ BackwardLeft,
+}
+
+/// The directions that a player can sprint in. It's a subset of
+/// [`WalkDirection`].
+#[derive(Clone, Copy, Debug)]
+pub enum SprintDirection {
+ Forward,
+ ForwardRight,
+ ForwardLeft,
+}
+
+impl From<SprintDirection> for WalkDirection {
+ fn from(d: SprintDirection) -> Self {
+ match d {
+ SprintDirection::Forward => WalkDirection::Forward,
+ SprintDirection::ForwardRight => WalkDirection::ForwardRight,
+ SprintDirection::ForwardLeft => WalkDirection::ForwardLeft,
+ }
+ }
+}
diff --git a/azalea-physics/src/travel.rs b/azalea-physics/src/travel.rs
index 80c289d9..1c473a45 100644
--- a/azalea-physics/src/travel.rs
+++ b/azalea-physics/src/travel.rs
@@ -4,20 +4,21 @@ use azalea_core::{
position::{BlockPos, Vec3},
};
use azalea_entity::{
- Attributes, HasClientLoaded, Jumping, LocalEntity, LookDirection, OnClimbable, Physics, Pose,
- Position, metadata::Sprinting, move_relative,
+ Attributes, HasClientLoaded, Jumping, LocalEntity, LookDirection, OnClimbable, Physics,
+ PlayerAbilities, Pose, Position, metadata::Sprinting, move_relative,
};
use azalea_world::{Instance, InstanceContainer, InstanceName};
use bevy_ecs::prelude::*;
use crate::{
- HandleRelativeFrictionAndCalculateMovementOpts,
collision::{
- MoverType, Shapes,
+ MoveCtx, MoverType, Shapes,
entity_collisions::{CollidableEntityQuery, PhysicsQuery, get_entity_collisions},
move_colliding,
+ world_collisions::{get_block_and_liquid_collisions, get_block_collisions},
},
get_block_pos_below_that_affects_movement, handle_relative_friction_and_calculate_movement,
+ local_player::PhysicsState,
};
/// Move the entity with the given acceleration while handling friction,
@@ -27,15 +28,17 @@ pub fn travel(
mut query: Query<
(
Entity,
- &mut Physics,
- &mut LookDirection,
- &mut Position,
- Option<&Sprinting>,
- Option<&Pose>,
&Attributes,
&InstanceName,
&OnClimbable,
&Jumping,
+ Option<&PhysicsState>,
+ Option<&Sprinting>,
+ Option<&Pose>,
+ Option<&PlayerAbilities>,
+ &mut Physics,
+ &mut LookDirection,
+ &mut Position,
),
(With<LocalEntity>, With<HasClientLoaded>),
>,
@@ -45,15 +48,17 @@ pub fn travel(
) {
for (
entity,
- mut physics,
- direction,
- position,
- sprinting,
- pose,
attributes,
world_name,
on_climbable,
jumping,
+ physics_state,
+ sprinting,
+ pose,
+ abilities,
+ mut physics,
+ direction,
+ position,
) in &mut query
{
let Some(world_lock) = instance_container.get(world_name) else {
@@ -65,92 +70,57 @@ pub fn travel(
// TODO: elytras
- if physics.is_in_water() || physics.is_in_lava() {
+ let mut ctx = MoveCtx {
+ mover_type: MoverType::Own,
+ world: &world,
+ position,
+ physics: &mut physics,
+ source_entity: entity,
+ physics_query: &physics_query,
+ collidable_entity_query: &collidable_entity_query,
+ physics_state,
+ attributes,
+ abilities,
+ direction: *direction,
+ sprinting,
+ on_climbable: *on_climbable,
+ pose: pose.copied(),
+ jumping: *jumping,
+ };
+
+ if ctx.physics.is_in_water() || ctx.physics.is_in_lava() {
// minecraft also checks for `this.isAffectedByFluids() &&
// !this.canStandOnFluid(fluidAtBlock)` here but it doesn't matter
// for players
- travel_in_fluid(
- &world,
- entity,
- &mut physics,
- *direction,
- position,
- attributes,
- sprinting,
- on_climbable,
- &physics_query,
- &collidable_entity_query,
- );
+ travel_in_fluid(&mut ctx);
} else {
- travel_in_air(
- &world,
- entity,
- &mut physics,
- *direction,
- position,
- attributes,
- sprinting,
- *on_climbable,
- pose,
- *jumping,
- &physics_query,
- &collidable_entity_query,
- );
+ travel_in_air(&mut ctx);
}
}
}
/// The usual movement when we're not in water or using an elytra.
-#[allow(clippy::too_many_arguments)]
-fn travel_in_air(
- world: &Instance,
- entity: Entity,
- physics: &mut Physics,
- direction: LookDirection,
- position: Mut<Position>,
- attributes: &Attributes,
- sprinting: Sprinting,
- on_climbable: OnClimbable,
- pose: Option<&Pose>,
- jumping: Jumping,
- physics_query: &PhysicsQuery,
- collidable_entity_query: &CollidableEntityQuery,
-) {
+fn travel_in_air(ctx: &mut MoveCtx) {
let gravity = get_effective_gravity();
- let block_pos_below = get_block_pos_below_that_affects_movement(*position);
+ let block_pos_below = get_block_pos_below_that_affects_movement(*ctx.position);
- let block_state_below = world
+ let block_state_below = ctx
+ .world
.chunks
.get_block_state(block_pos_below)
.unwrap_or(BlockState::AIR);
let block_below: Box<dyn BlockTrait> = block_state_below.into();
let block_friction = block_below.behavior().friction;
- let inertia = if physics.on_ground() {
+ let inertia = if ctx.physics.on_ground() {
block_friction * 0.91
} else {
0.91
};
// this applies the current delta
- let mut movement = handle_relative_friction_and_calculate_movement(
- HandleRelativeFrictionAndCalculateMovementOpts {
- block_friction,
- world,
- physics,
- direction,
- position,
- attributes,
- is_sprinting: *sprinting,
- on_climbable,
- pose: pose.copied(),
- jumping,
- entity,
- physics_query,
- collidable_entity_query,
- },
- );
+ let mut movement = handle_relative_friction_and_calculate_movement(ctx, block_friction);
movement.y -= gravity;
@@ -162,9 +132,9 @@ fn travel_in_air(
// if should_discard_friction(self) {
if false {
- physics.velocity = movement;
+ ctx.physics.velocity = movement;
} else {
- physics.velocity = Vec3 {
+ ctx.physics.velocity = Vec3 {
x: movement.x * inertia as f64,
y: movement.y * 0.9800000190734863f64,
z: movement.z * inertia as f64,
@@ -172,116 +142,86 @@ fn travel_in_air(
}
}
-#[allow(clippy::too_many_arguments)]
-fn travel_in_fluid(
- world: &Instance,
- entity: Entity,
- physics: &mut Physics,
- direction: LookDirection,
- mut position: Mut<Position>,
- attributes: &Attributes,
- sprinting: Sprinting,
- on_climbable: &OnClimbable,
- physics_query: &PhysicsQuery,
- collidable_entity_query: &CollidableEntityQuery,
-) {
- let moving_down = physics.velocity.y <= 0.;
- let y = position.y;
+fn travel_in_fluid(ctx: &mut MoveCtx) {
+ let moving_down = ctx.physics.velocity.y <= 0.;
+ let y = ctx.position.y;
let gravity = get_effective_gravity();
let acceleration = Vec3::new(
- physics.x_acceleration as f64,
- physics.y_acceleration as f64,
- physics.z_acceleration as f64,
+ ctx.physics.x_acceleration as f64,
+ ctx.physics.y_acceleration as f64,
+ ctx.physics.z_acceleration as f64,
);
- if physics.was_touching_water {
- let mut water_movement_speed = if *sprinting { 0.9 } else { 0.8 };
+ if ctx.physics.was_touching_water {
+ let mut water_movement_speed = if *ctx.sprinting { 0.9 } else { 0.8 };
let mut speed = 0.02;
- let mut water_efficiency_modifier = attributes.water_movement_efficiency.calculate() as f32;
- if !physics.on_ground() {
+ let mut water_efficiency_modifier =
+ ctx.attributes.water_movement_efficiency.calculate() as f32;
+ if !ctx.physics.on_ground() {
water_efficiency_modifier *= 0.5;
}
if water_efficiency_modifier > 0. {
water_movement_speed += (0.54600006 - water_movement_speed) * water_efficiency_modifier;
- speed += (attributes.speed.calculate() as f32 - speed) * water_efficiency_modifier;
+ speed += (ctx.attributes.movement_speed.calculate() as f32 - speed)
+ * water_efficiency_modifier;
}
// if (this.hasEffect(MobEffects.DOLPHINS_GRACE)) {
// waterMovementSpeed = 0.96F;
// }
- move_relative(physics, direction, speed, acceleration);
- move_colliding(
- MoverType::Own,
- physics.velocity,
- world,
- &mut position,
- physics,
- Some(entity),
- physics_query,
- collidable_entity_query,
- )
- .expect("Entity should exist");
+ move_relative(ctx.physics, ctx.direction, speed, acceleration);
+ move_colliding(ctx, ctx.physics.velocity).expect("Entity should exist");
- let mut new_velocity = physics.velocity;
- if physics.horizontal_collision && **on_climbable {
+ let mut new_velocity = ctx.physics.velocity;
+ if ctx.physics.horizontal_collision && *ctx.on_climbable {
// underwater ladders
new_velocity.y = 0.2;
}
new_velocity.x *= water_movement_speed as f64;
new_velocity.y *= 0.8;
new_velocity.z *= water_movement_speed as f64;
- physics.velocity =
- get_fluid_falling_adjusted_movement(gravity, moving_down, new_velocity, sprinting);
+ ctx.physics.velocity =
+ get_fluid_falling_adjusted_movement(gravity, moving_down, new_velocity, ctx.sprinting);
} else {
- move_relative(physics, direction, 0.02, acceleration);
- move_colliding(
- MoverType::Own,
- physics.velocity,
- world,
- &mut position,
- physics,
- Some(entity),
- physics_query,
- collidable_entity_query,
- )
- .expect("Entity should exist");
+ move_relative(ctx.physics, ctx.direction, 0.02, acceleration);
+ move_colliding(ctx, ctx.physics.velocity).expect("Entity should exist");
- if physics.lava_fluid_height <= fluid_jump_threshold() {
- physics.velocity.x *= 0.5;
- physics.velocity.y *= 0.8;
- physics.velocity.z *= 0.5;
+ if ctx.physics.lava_fluid_height <= fluid_jump_threshold() {
+ ctx.physics.velocity.x *= 0.5;
+ ctx.physics.velocity.y *= 0.8;
+ ctx.physics.velocity.z *= 0.5;
let new_velocity = get_fluid_falling_adjusted_movement(
gravity,
moving_down,
- physics.velocity,
- sprinting,
+ ctx.physics.velocity,
+ ctx.sprinting,
);
- physics.velocity = new_velocity;
+ ctx.physics.velocity = new_velocity;
} else {
- physics.velocity *= 0.5;
+ ctx.physics.velocity *= 0.5;
}
if gravity != 0.0 {
- physics.velocity.y -= gravity / 4.0;
+ ctx.physics.velocity.y -= gravity / 4.0;
}
}
- let velocity = physics.velocity;
- if physics.horizontal_collision
+ let velocity = ctx.physics.velocity;
+ if ctx.physics.horizontal_collision
&& is_free(
- world,
- entity,
- physics_query,
- collidable_entity_query,
- physics,
- physics.bounding_box,
- velocity.up(0.6).down(position.y).up(y),
+ ctx.world,
+ ctx.source_entity,
+ ctx.physics_query,
+ ctx.collidable_entity_query,
+ ctx.physics,
+ ctx.physics.bounding_box,
+ velocity.up(0.6).down(ctx.position.y).up(y),
)
{
- physics.velocity.y = 0.3;
+ ctx.physics.velocity.y = 0.3;
}
}
@@ -316,7 +256,7 @@ fn is_free(
source_entity: Entity,
physics_query: &PhysicsQuery,
collidable_entity_query: &CollidableEntityQuery,
- entity_physics: &mut Physics,
+ entity_physics: &Physics,
bounding_box: AABB,
delta: Vec3,
) -> bool {
@@ -333,19 +273,19 @@ fn is_free(
) && !contains_any_liquid(world, bounding_box)
}
-fn no_collision(
+pub fn no_collision(
world: &Instance,
source_entity: Option<Entity>,
physics_query: &PhysicsQuery,
collidable_entity_query: &CollidableEntityQuery,
- entity_physics: &mut Physics,
+ entity_physics: &Physics,
aabb: &AABB,
include_liquid_collisions: bool,
) -> bool {
let collisions = if include_liquid_collisions {
- crate::collision::world_collisions::get_block_and_liquid_collisions(world, aabb)
+ get_block_and_liquid_collisions(world, aabb)
} else {
- crate::collision::world_collisions::get_block_collisions(world, aabb)
+ get_block_collisions(world, aabb)
};
for collision in collisions {