azalea_client/plugins/
movement.rs

1use azalea_core::{
2    game_type::GameMode,
3    position::{Vec2, Vec3},
4    tick::GameTick,
5};
6use azalea_entity::{
7    Attributes, Crouching, HasClientLoaded, Jumping, LastSentPosition, LocalEntity, LookDirection,
8    Physics, PlayerAbilities, Pose, Position,
9    dimensions::calculate_dimensions,
10    metadata::{self, Sprinting},
11    update_bounding_box,
12};
13use azalea_physics::{
14    PhysicsSystems, ai_step,
15    collision::entity_collisions::{AabbQuery, CollidableEntityQuery, update_last_bounding_box},
16    local_player::{PhysicsState, SprintDirection, WalkDirection},
17    travel::{no_collision, travel},
18};
19use azalea_protocol::{
20    common::movements::MoveFlags,
21    packets::{
22        Packet,
23        game::{
24            ServerboundPlayerCommand, ServerboundPlayerInput,
25            s_move_player_pos::ServerboundMovePlayerPos,
26            s_move_player_pos_rot::ServerboundMovePlayerPosRot,
27            s_move_player_rot::ServerboundMovePlayerRot,
28            s_move_player_status_only::ServerboundMovePlayerStatusOnly,
29        },
30    },
31};
32use azalea_registry::EntityKind;
33use azalea_world::{Instance, MinecraftEntityId};
34use bevy_app::{App, Plugin, Update};
35use bevy_ecs::prelude::*;
36
37use crate::{
38    client::Client,
39    local_player::{Hunger, InstanceHolder, LocalGameMode},
40    packet::game::SendGamePacketEvent,
41};
42
43pub struct MovementPlugin;
44
45impl Plugin for MovementPlugin {
46    fn build(&self, app: &mut App) {
47        app.add_message::<StartWalkEvent>()
48            .add_message::<StartSprintEvent>()
49            .add_message::<KnockbackEvent>()
50            .add_systems(
51                Update,
52                (handle_sprint, handle_walk, handle_knockback)
53                    .chain()
54                    .in_set(MoveEventsSystems)
55                    .after(update_bounding_box)
56                    .after(update_last_bounding_box),
57            )
58            .add_systems(
59                GameTick,
60                (
61                    (tick_controls, local_player_ai_step, update_pose)
62                        .chain()
63                        .in_set(PhysicsSystems)
64                        .before(ai_step)
65                        .before(azalea_physics::fluids::update_in_water_state_and_do_fluid_pushing),
66                    send_player_input_packet,
67                    send_sprinting_if_needed
68                        .after(azalea_entity::update_in_loaded_chunk)
69                        .after(travel),
70                    send_position.after(PhysicsSystems),
71                )
72                    .chain(),
73            );
74    }
75}
76
77#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
78pub struct MoveEventsSystems;
79
80impl Client {
81    /// Set whether we're jumping. This acts as if you held space in
82    /// vanilla.
83    ///
84    /// If you want to jump once, use the `jump` function in `azalea`.
85    ///
86    /// If you're making a realistic client, calling this function every tick is
87    /// recommended.
88    pub fn set_jumping(&self, jumping: bool) {
89        self.query_self::<&mut Jumping, _>(|mut j| **j = jumping);
90    }
91
92    /// Returns whether the player will try to jump next tick.
93    pub fn jumping(&self) -> bool {
94        *self.component::<Jumping>()
95    }
96
97    pub fn set_crouching(&self, crouching: bool) {
98        self.query_self::<&mut PhysicsState, _>(|mut p| p.trying_to_crouch = crouching);
99    }
100
101    /// Whether the client is currently trying to sneak.
102    ///
103    /// You may want to check the [`Pose`] instead.
104    pub fn crouching(&self) -> bool {
105        self.query_self::<&PhysicsState, _>(|p| p.trying_to_crouch)
106    }
107
108    /// Sets the direction the client is looking.
109    ///
110    /// `y_rot` is yaw (looking to the side, between -180 to 180), and `x_rot`
111    /// is pitch (looking up and down, between -90 to 90).
112    ///
113    /// You can get these numbers from the vanilla f3 screen.
114    pub fn set_direction(&self, y_rot: f32, x_rot: f32) {
115        self.query_self::<&mut LookDirection, _>(|mut ld| {
116            ld.update(LookDirection::new(y_rot, x_rot));
117        });
118    }
119
120    /// Returns the direction the client is looking.
121    ///
122    /// See [`Self::set_direction`] for more details.
123    pub fn direction(&self) -> (f32, f32) {
124        let look_direction: LookDirection = self.component::<LookDirection>();
125        (look_direction.y_rot(), look_direction.x_rot())
126    }
127}
128
129/// A component that contains the look direction that was last sent over the
130/// network.
131#[derive(Debug, Component, Clone, Default)]
132pub struct LastSentLookDirection {
133    pub x_rot: f32,
134    pub y_rot: f32,
135}
136
137#[allow(clippy::type_complexity)]
138pub fn send_position(
139    mut query: Query<
140        (
141            Entity,
142            &Position,
143            &LookDirection,
144            &mut PhysicsState,
145            &mut LastSentPosition,
146            &mut Physics,
147            &mut LastSentLookDirection,
148        ),
149        With<HasClientLoaded>,
150    >,
151    mut commands: Commands,
152) {
153    for (
154        entity,
155        position,
156        direction,
157        mut physics_state,
158        mut last_sent_position,
159        mut physics,
160        mut last_direction,
161    ) in query.iter_mut()
162    {
163        let packet = {
164            // TODO: the camera being able to be controlled by other entities isn't
165            // implemented yet if !self.is_controlled_camera() { return };
166
167            let x_delta = position.x - last_sent_position.x;
168            let y_delta = position.y - last_sent_position.y;
169            let z_delta = position.z - last_sent_position.z;
170            let y_rot_delta = (direction.y_rot() - last_direction.y_rot) as f64;
171            let x_rot_delta = (direction.x_rot() - last_direction.x_rot) as f64;
172
173            physics_state.position_remainder += 1;
174
175            // boolean sendingPosition = Mth.lengthSquared(xDelta, yDelta, zDelta) >
176            // Mth.square(2.0E-4D) || this.positionReminder >= 20;
177            let is_delta_large_enough =
178                (x_delta.powi(2) + y_delta.powi(2) + z_delta.powi(2)) > 2.0e-4f64.powi(2);
179            let sending_position = is_delta_large_enough || physics_state.position_remainder >= 20;
180            let sending_direction = y_rot_delta != 0.0 || x_rot_delta != 0.0;
181
182            // if self.is_passenger() {
183            //   TODO: posrot packet for being a passenger
184            // }
185            let flags = MoveFlags {
186                on_ground: physics.on_ground(),
187                horizontal_collision: physics.horizontal_collision,
188            };
189            let packet = if sending_position && sending_direction {
190                Some(
191                    ServerboundMovePlayerPosRot {
192                        pos: **position,
193                        look_direction: *direction,
194                        flags,
195                    }
196                    .into_variant(),
197                )
198            } else if sending_position {
199                Some(
200                    ServerboundMovePlayerPos {
201                        pos: **position,
202                        flags,
203                    }
204                    .into_variant(),
205                )
206            } else if sending_direction {
207                Some(
208                    ServerboundMovePlayerRot {
209                        look_direction: *direction,
210                        flags,
211                    }
212                    .into_variant(),
213                )
214            } else if physics.last_on_ground() != physics.on_ground() {
215                Some(ServerboundMovePlayerStatusOnly { flags }.into_variant())
216            } else {
217                None
218            };
219
220            if sending_position {
221                **last_sent_position = **position;
222                physics_state.position_remainder = 0;
223            }
224            if sending_direction {
225                last_direction.y_rot = direction.y_rot();
226                last_direction.x_rot = direction.x_rot();
227            }
228
229            let on_ground = physics.on_ground();
230            physics.set_last_on_ground(on_ground);
231            // minecraft checks for autojump here, but also autojump is bad so
232
233            packet
234        };
235
236        if let Some(packet) = packet {
237            commands.trigger(SendGamePacketEvent {
238                sent_by: entity,
239                packet,
240            });
241        }
242    }
243}
244
245#[derive(Debug, Default, Component, Clone, PartialEq, Eq)]
246pub struct LastSentInput(pub ServerboundPlayerInput);
247pub fn send_player_input_packet(
248    mut query: Query<(Entity, &PhysicsState, &Jumping, Option<&LastSentInput>)>,
249    mut commands: Commands,
250) {
251    for (entity, physics_state, jumping, last_sent_input) in query.iter_mut() {
252        let dir = physics_state.move_direction;
253        type D = WalkDirection;
254        let input = ServerboundPlayerInput {
255            forward: matches!(dir, D::Forward | D::ForwardLeft | D::ForwardRight),
256            backward: matches!(dir, D::Backward | D::BackwardLeft | D::BackwardRight),
257            left: matches!(dir, D::Left | D::ForwardLeft | D::BackwardLeft),
258            right: matches!(dir, D::Right | D::ForwardRight | D::BackwardRight),
259            jump: **jumping,
260            shift: physics_state.trying_to_crouch,
261            sprint: physics_state.trying_to_sprint,
262        };
263
264        // if LastSentInput isn't present, we default to assuming we're not pressing any
265        // keys and insert it anyways every time it changes
266        let last_sent_input = last_sent_input.cloned().unwrap_or_default();
267
268        if input != last_sent_input.0 {
269            commands.trigger(SendGamePacketEvent {
270                sent_by: entity,
271                packet: input.clone().into_variant(),
272            });
273            commands.entity(entity).insert(LastSentInput(input));
274        }
275    }
276}
277
278pub fn send_sprinting_if_needed(
279    mut query: Query<(Entity, &MinecraftEntityId, &Sprinting, &mut PhysicsState)>,
280    mut commands: Commands,
281) {
282    for (entity, minecraft_entity_id, sprinting, mut physics_state) in query.iter_mut() {
283        let was_sprinting = physics_state.was_sprinting;
284        if **sprinting != was_sprinting {
285            let sprinting_action = if **sprinting {
286                azalea_protocol::packets::game::s_player_command::Action::StartSprinting
287            } else {
288                azalea_protocol::packets::game::s_player_command::Action::StopSprinting
289            };
290            commands.trigger(SendGamePacketEvent::new(
291                entity,
292                ServerboundPlayerCommand {
293                    id: *minecraft_entity_id,
294                    action: sprinting_action,
295                    data: 0,
296                },
297            ));
298            physics_state.was_sprinting = **sprinting;
299        }
300    }
301}
302
303/// Updates the [`PhysicsState::move_vector`] based on the
304/// [`PhysicsState::move_direction`].
305pub(crate) fn tick_controls(mut query: Query<&mut PhysicsState>) {
306    for mut physics_state in query.iter_mut() {
307        let mut forward_impulse: f32 = 0.;
308        let mut left_impulse: f32 = 0.;
309        let move_direction = physics_state.move_direction;
310        match move_direction {
311            WalkDirection::Forward | WalkDirection::ForwardRight | WalkDirection::ForwardLeft => {
312                forward_impulse += 1.;
313            }
314            WalkDirection::Backward
315            | WalkDirection::BackwardRight
316            | WalkDirection::BackwardLeft => {
317                forward_impulse -= 1.;
318            }
319            _ => {}
320        };
321        match move_direction {
322            WalkDirection::Right | WalkDirection::ForwardRight | WalkDirection::BackwardRight => {
323                left_impulse += 1.;
324            }
325            WalkDirection::Left | WalkDirection::ForwardLeft | WalkDirection::BackwardLeft => {
326                left_impulse -= 1.;
327            }
328            _ => {}
329        };
330
331        let move_vector = Vec2::new(left_impulse, forward_impulse).normalized();
332        physics_state.move_vector = move_vector;
333    }
334}
335
336/// Makes the bot do one physics tick.
337///
338/// This is handled automatically by the client.
339#[allow(clippy::type_complexity)]
340pub fn local_player_ai_step(
341    mut query: Query<
342        (
343            Entity,
344            &PhysicsState,
345            &PlayerAbilities,
346            &metadata::Swimming,
347            &metadata::SleepingPos,
348            &InstanceHolder,
349            &Position,
350            Option<&Hunger>,
351            Option<&LastSentInput>,
352            &mut Physics,
353            &mut Sprinting,
354            &mut Crouching,
355            &mut Attributes,
356        ),
357        (With<HasClientLoaded>, With<LocalEntity>),
358    >,
359    aabb_query: AabbQuery,
360    collidable_entity_query: CollidableEntityQuery,
361) {
362    for (
363        entity,
364        physics_state,
365        abilities,
366        swimming,
367        sleeping_pos,
368        instance_holder,
369        position,
370        hunger,
371        last_sent_input,
372        mut physics,
373        mut sprinting,
374        mut crouching,
375        mut attributes,
376    ) in query.iter_mut()
377    {
378        // server ai step
379
380        let is_swimming = **swimming;
381        // TODO: implement passengers
382        let is_passenger = false;
383        let is_sleeping = sleeping_pos.is_some();
384
385        let world = instance_holder.instance.read();
386        let ctx = CanPlayerFitCtx {
387            world: &world,
388            entity,
389            position: *position,
390            aabb_query: &aabb_query,
391            collidable_entity_query: &collidable_entity_query,
392            physics: &physics,
393        };
394
395        let new_crouching = !abilities.flying
396            && !is_swimming
397            && !is_passenger
398            && can_player_fit_within_blocks_and_entities_when(&ctx, Pose::Crouching)
399            && (last_sent_input.is_some_and(|i| i.0.shift)
400                || !is_sleeping
401                    && !can_player_fit_within_blocks_and_entities_when(&ctx, Pose::Standing));
402        if **crouching != new_crouching {
403            **crouching = new_crouching;
404        }
405
406        // TODO: food data and abilities
407        // let has_enough_food_to_sprint = self.food_data().food_level ||
408        // self.abilities().may_fly;
409        let has_enough_food_to_sprint = hunger.is_none_or(Hunger::is_enough_to_sprint);
410
411        // TODO: double tapping w to sprint i think
412
413        let trying_to_sprint = physics_state.trying_to_sprint;
414
415        // TODO: swimming
416        let is_underwater = false;
417        let is_in_water = physics.is_in_water();
418        // TODO: elytra
419        let is_fall_flying = false;
420        // TODO: passenger
421        let is_passenger = false;
422        // TODO: using items
423        let using_item = false;
424        // TODO: status effects
425        let has_blindness = false;
426
427        let has_enough_impulse = has_enough_impulse_to_start_sprinting(physics_state);
428
429        // LocalPlayer.canStartSprinting
430        let can_start_sprinting = !**sprinting
431            && has_enough_impulse
432            && has_enough_food_to_sprint
433            && !using_item
434            && !has_blindness
435            && (!is_passenger || is_underwater)
436            && (!is_fall_flying || is_underwater)
437            && (!is_moving_slowly(&crouching) || is_underwater)
438            && (!is_in_water || is_underwater);
439        if trying_to_sprint && can_start_sprinting {
440            set_sprinting(true, &mut sprinting, &mut attributes);
441        }
442
443        if **sprinting {
444            // TODO: swimming
445
446            let vehicle_can_sprint = false;
447            // shouldStopRunSprinting
448            let should_stop_sprinting = has_blindness
449                || (is_passenger && !vehicle_can_sprint)
450                || !has_enough_impulse
451                || !has_enough_food_to_sprint
452                || (physics.horizontal_collision && !physics.minor_horizontal_collision)
453                || (is_in_water && !is_underwater);
454            if should_stop_sprinting {
455                set_sprinting(false, &mut sprinting, &mut attributes);
456            }
457        }
458
459        // TODO: replace those booleans when using items and passengers are properly
460        // implemented
461        let move_vector = modify_input(
462            physics_state.move_vector,
463            false,
464            false,
465            **crouching,
466            &attributes,
467        );
468        physics.x_acceleration = move_vector.x;
469        physics.z_acceleration = move_vector.y;
470    }
471}
472
473fn is_moving_slowly(crouching: &Crouching) -> bool {
474    **crouching
475}
476
477// LocalPlayer.modifyInput
478fn modify_input(
479    mut move_vector: Vec2,
480    is_using_item: bool,
481    is_passenger: bool,
482    moving_slowly: bool,
483    attributes: &Attributes,
484) -> Vec2 {
485    if move_vector.length_squared() == 0. {
486        return move_vector;
487    }
488
489    move_vector *= 0.98;
490    if is_using_item && !is_passenger {
491        move_vector *= 0.2;
492    }
493
494    if moving_slowly {
495        let sneaking_speed = attributes.sneaking_speed.calculate() as f32;
496        move_vector *= sneaking_speed;
497    }
498
499    modify_input_speed_for_square_movement(move_vector)
500}
501fn modify_input_speed_for_square_movement(move_vector: Vec2) -> Vec2 {
502    let length = move_vector.length();
503    if length == 0. {
504        return move_vector;
505    }
506    let scaled_to_inverse_length = move_vector * (1. / length);
507    let dist = distance_to_unit_square(scaled_to_inverse_length);
508    let scale = (length * dist).min(1.);
509    scaled_to_inverse_length * scale
510}
511fn distance_to_unit_square(v: Vec2) -> f32 {
512    let x = v.x.abs();
513    let y = v.y.abs();
514    let ratio = if y > x { x / y } else { y / x };
515    (1. + ratio * ratio).sqrt()
516}
517
518impl Client {
519    /// Start walking in the given direction.
520    ///
521    /// To sprint, use [`Client::sprint`]. To stop walking, call walk with
522    /// [`WalkDirection::None`].
523    ///
524    /// # Example
525    ///
526    /// ```rust,no_run
527    /// # use azalea_client::{Client, WalkDirection};
528    /// # use std::time::Duration;
529    /// # async fn example(mut bot: Client) {
530    /// // walk for one second
531    /// bot.walk(WalkDirection::Forward);
532    /// tokio::time::sleep(Duration::from_secs(1)).await;
533    /// bot.walk(WalkDirection::None);
534    /// # }
535    /// ```
536    pub fn walk(&self, direction: WalkDirection) {
537        let mut ecs = self.ecs.lock();
538        ecs.write_message(StartWalkEvent {
539            entity: self.entity,
540            direction,
541        });
542    }
543
544    /// Start sprinting in the given direction.
545    ///
546    /// o stop moving, call [`bot.walk(WalkDirection::None)`](Self::walk)
547    ///
548    /// # Example
549    ///
550    /// ```rust,no_run
551    /// # use azalea_client::{Client, WalkDirection, SprintDirection};
552    /// # use std::time::Duration;
553    /// # async fn example(mut bot: Client) {
554    /// // sprint for one second
555    /// bot.sprint(SprintDirection::Forward);
556    /// tokio::time::sleep(Duration::from_secs(1)).await;
557    /// bot.walk(WalkDirection::None);
558    /// # }
559    /// ```
560    pub fn sprint(&self, direction: SprintDirection) {
561        let mut ecs = self.ecs.lock();
562        ecs.write_message(StartSprintEvent {
563            entity: self.entity,
564            direction,
565        });
566    }
567}
568
569/// An event sent when the client starts walking.
570///
571/// This does not get sent for non-local entities.
572///
573/// To stop walking or sprinting, send this event with `WalkDirection::None`.
574#[derive(Message, Debug)]
575pub struct StartWalkEvent {
576    pub entity: Entity,
577    pub direction: WalkDirection,
578}
579
580/// The system that makes the player start walking when they receive a
581/// [`StartWalkEvent`].
582pub fn handle_walk(
583    mut events: MessageReader<StartWalkEvent>,
584    mut query: Query<(&mut PhysicsState, &mut Sprinting, &mut Attributes)>,
585) {
586    for event in events.read() {
587        if let Ok((mut physics_state, mut sprinting, mut attributes)) = query.get_mut(event.entity)
588        {
589            physics_state.move_direction = event.direction;
590            physics_state.trying_to_sprint = false;
591            set_sprinting(false, &mut sprinting, &mut attributes);
592        }
593    }
594}
595
596/// An event sent when the client starts sprinting.
597///
598/// This does not get sent for non-local entities.
599#[derive(Message)]
600pub struct StartSprintEvent {
601    pub entity: Entity,
602    pub direction: SprintDirection,
603}
604/// The system that makes the player start sprinting when they receive a
605/// [`StartSprintEvent`].
606pub fn handle_sprint(
607    mut query: Query<&mut PhysicsState>,
608    mut events: MessageReader<StartSprintEvent>,
609) {
610    for event in events.read() {
611        if let Ok(mut physics_state) = query.get_mut(event.entity) {
612            physics_state.move_direction = WalkDirection::from(event.direction);
613            physics_state.trying_to_sprint = true;
614        }
615    }
616}
617
618/// Change whether we're sprinting by adding an attribute modifier to the
619/// player.
620///
621/// You should use the [`Client::walk`] and [`Client::sprint`] functions
622/// instead.
623///
624/// Returns true if the operation was successful.
625fn set_sprinting(
626    sprinting: bool,
627    currently_sprinting: &mut Sprinting,
628    attributes: &mut Attributes,
629) -> bool {
630    **currently_sprinting = sprinting;
631    if sprinting {
632        attributes
633            .movement_speed
634            .try_insert(azalea_entity::attributes::sprinting_modifier())
635            .is_ok()
636    } else {
637        attributes
638            .movement_speed
639            .remove(&azalea_entity::attributes::sprinting_modifier().id)
640            .is_none()
641    }
642}
643
644// Whether the player is moving fast enough to be able to start sprinting.
645fn has_enough_impulse_to_start_sprinting(physics_state: &PhysicsState) -> bool {
646    // if self.underwater() {
647    //     self.has_forward_impulse()
648    // } else {
649    physics_state.move_vector.y > 0.8
650    // }
651}
652
653/// An event sent by the server that sets or adds to our velocity.
654///
655/// Usually `KnockbackKind::Set` is used for normal knockback and
656/// `KnockbackKind::Add` is used for explosions, but some servers (notably
657/// Hypixel) use explosions for knockback.
658#[derive(Message)]
659pub struct KnockbackEvent {
660    pub entity: Entity,
661    pub knockback: KnockbackType,
662}
663
664pub enum KnockbackType {
665    Set(Vec3),
666    Add(Vec3),
667}
668
669pub fn handle_knockback(mut query: Query<&mut Physics>, mut events: MessageReader<KnockbackEvent>) {
670    for event in events.read() {
671        if let Ok(mut physics) = query.get_mut(event.entity) {
672            match event.knockback {
673                KnockbackType::Set(velocity) => {
674                    physics.velocity = velocity;
675                }
676                KnockbackType::Add(velocity) => {
677                    physics.velocity += velocity;
678                }
679            }
680        }
681    }
682}
683
684pub fn update_pose(
685    mut query: Query<(
686        Entity,
687        &mut Pose,
688        &Physics,
689        &PhysicsState,
690        &LocalGameMode,
691        &InstanceHolder,
692        &Position,
693    )>,
694    aabb_query: AabbQuery,
695    collidable_entity_query: CollidableEntityQuery,
696) {
697    for (entity, mut pose, physics, physics_state, game_mode, instance_holder, position) in
698        query.iter_mut()
699    {
700        let world = instance_holder.instance.read();
701        let world = &*world;
702        let ctx = CanPlayerFitCtx {
703            world,
704            entity,
705            position: *position,
706            aabb_query: &aabb_query,
707            collidable_entity_query: &collidable_entity_query,
708            physics,
709        };
710
711        if !can_player_fit_within_blocks_and_entities_when(&ctx, Pose::Swimming) {
712            continue;
713        }
714
715        // TODO: implement everything else from getDesiredPose: sleeping, swimming,
716        // fallFlying, spinAttack
717        let desired_pose = if physics_state.trying_to_crouch {
718            Pose::Crouching
719        } else {
720            Pose::Standing
721        };
722
723        // TODO: passengers
724        let is_passenger = false;
725
726        // canPlayerFitWithinBlocksAndEntitiesWhen
727        let new_pose = if game_mode.current == GameMode::Spectator
728            || is_passenger
729            || can_player_fit_within_blocks_and_entities_when(&ctx, desired_pose)
730        {
731            desired_pose
732        } else if can_player_fit_within_blocks_and_entities_when(&ctx, Pose::Crouching) {
733            Pose::Crouching
734        } else {
735            Pose::Swimming
736        };
737
738        // avoid triggering change detection
739        if new_pose != *pose {
740            *pose = new_pose;
741        }
742    }
743}
744
745struct CanPlayerFitCtx<'world, 'state, 'a, 'b> {
746    world: &'a Instance,
747    entity: Entity,
748    position: Position,
749    aabb_query: &'a AabbQuery<'world, 'state, 'b>,
750    collidable_entity_query: &'a CollidableEntityQuery<'world, 'state>,
751    physics: &'a Physics,
752}
753fn can_player_fit_within_blocks_and_entities_when(ctx: &CanPlayerFitCtx, pose: Pose) -> bool {
754    // return this.level().noCollision(this,
755    // this.getDimensions(var1).makeBoundingBox(this.position()).deflate(1.0E-7));
756    no_collision(
757        ctx.world,
758        Some(ctx.entity),
759        ctx.aabb_query,
760        ctx.collidable_entity_query,
761        ctx.physics,
762        &calculate_dimensions(EntityKind::Player, pose).make_bounding_box(*ctx.position),
763        false,
764    )
765}