From 37f9f1c6feda676be30bef31291eaed2a5fc82ce Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 2 Oct 2022 14:52:48 -0500 Subject: [PATCH] add jumping --- azalea-block/src/behavior.rs | 18 ++++++- azalea-client/src/client.rs | 6 +++ azalea-client/src/movement.rs | 32 +++++++++++- azalea-physics/src/collision/mod.rs | 2 +- azalea-physics/src/lib.rs | 79 +++++++++++++++++++++++++++++ azalea-world/src/entity/mod.rs | 6 +++ bot/src/main.rs | 6 ++- 7 files changed, 145 insertions(+), 4 deletions(-) diff --git a/azalea-block/src/behavior.rs b/azalea-block/src/behavior.rs index db357632..18854fff 100644 --- a/azalea-block/src/behavior.rs +++ b/azalea-block/src/behavior.rs @@ -1,7 +1,17 @@ -#[derive(Default)] pub struct BlockBehavior { pub has_collision: bool, pub friction: f32, + pub jump_factor: f32, +} + +impl Default for BlockBehavior { + fn default() -> Self { + Self { + has_collision: true, + friction: 0.6, + jump_factor: 1.0, + } + } } impl BlockBehavior { @@ -16,4 +26,10 @@ impl BlockBehavior { self.friction = friction; self } + + #[inline] + pub fn jump_factor(mut self, jump_factor: f32) -> Self { + self.jump_factor = jump_factor; + self + } } diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs index cca932c0..2b721206 100644 --- a/azalea-client/src/client.rs +++ b/azalea-client/src/client.rs @@ -85,6 +85,12 @@ pub struct PhysicsState { pub move_direction: MoveDirection, pub forward_impulse: f32, pub left_impulse: f32, + + /// Whether we will jump next tick. This is purely to help with bots, + /// realistic clients should change the `jumping` field in the player entity. + /// + /// TODO: have a convenient way to change the `jumping` field in the player entity. + pub jumping_once: bool, } /// Whether we should ignore errors when decoding packets. diff --git a/azalea-client/src/movement.rs b/azalea-client/src/movement.rs index 193f2017..ab324370 100644 --- a/azalea-client/src/movement.rs +++ b/azalea-client/src/movement.rs @@ -166,9 +166,16 @@ impl Client { // server ai step { - let physics_state = self.physics_state.lock().unwrap(); + let mut physics_state = self.physics_state.lock().unwrap(); player_entity.xxa = physics_state.left_impulse; player_entity.zza = physics_state.forward_impulse; + + // handle jumping_once + if physics_state.jumping_once { + player_entity.jumping = true; + } else if player_entity.jumping { + physics_state.jumping_once = false; + } } player_entity.ai_step(); @@ -210,10 +217,33 @@ impl Client { } } + /// Start walking in the given direction. pub fn walk(&mut self, direction: MoveDirection) { let mut physics_state = self.physics_state.lock().unwrap(); physics_state.move_direction = direction; } + + /// Jump once next tick. This acts as if you pressed space for one tick in + /// vanilla. If you want to jump continuously, use `set_jumping`. + pub fn jump(&mut self) { + let mut physics_state = self.physics_state.lock().unwrap(); + physics_state.jumping_once = true; + } + + /// Toggle whether we're jumping. This acts as if you held space in + /// vanilla. If you want to jump once, use the `jump` function. + /// + /// If you're making a realistic client, calling this function every tick is + /// recommended. + pub fn set_jumping(&mut self, jumping: bool) { + let player_lock = self.player.lock().unwrap(); + let mut dimension_lock = self.dimension.lock().unwrap(); + let mut player_entity = player_lock + .entity_mut(&mut dimension_lock) + .expect("Player must exist"); + + player_entity.jumping = jumping; + } } #[derive(Clone, Copy, Debug, Default)] diff --git a/azalea-physics/src/collision/mod.rs b/azalea-physics/src/collision/mod.rs index a18440b7..f08e48e3 100644 --- a/azalea-physics/src/collision/mod.rs +++ b/azalea-physics/src/collision/mod.rs @@ -133,7 +133,7 @@ impl MovableEntity for EntityMut<'_> { let horizontal_collision = x_collision || z_collision; let vertical_collision = movement.y != collide_result.y; let on_ground = vertical_collision && movement.y < 0.; - // self.on_ground = on_ground; + self.on_ground = on_ground; // TODO: minecraft checks for a "minor" horizontal collision here diff --git a/azalea-physics/src/lib.rs b/azalea-physics/src/lib.rs index 56923577..fe75c71e 100644 --- a/azalea-physics/src/lib.rs +++ b/azalea-physics/src/lib.rs @@ -11,6 +11,8 @@ use collision::{MovableEntity, MoverType}; pub trait HasPhysics { fn travel(&mut self, acceleration: &Vec3); fn ai_step(&mut self); + + fn jump_from_ground(&mut self); } impl HasPhysics for EntityMut<'_> { @@ -85,6 +87,14 @@ impl HasPhysics for EntityMut<'_> { self.delta.z = 0.; } + if self.jumping { + // TODO: jumping in liquids and jump delay + + if self.on_ground { + self.jump_from_ground(); + } + } + self.xxa *= 0.98; self.zza *= 0.98; @@ -97,6 +107,27 @@ impl HasPhysics for EntityMut<'_> { // pushEntities // drowning damage } + + fn jump_from_ground(&mut self) { + let jump_power: f64 = jump_power(self) as f64 + jump_boost_power(self); + let old_delta_movement = self.delta; + self.delta = Vec3 { + x: old_delta_movement.x, + y: jump_power, + z: old_delta_movement.z, + }; + // if self.sprinting { + // let y_rot = self.y_rot * 0.017453292; + // self.delta = self.delta + // + Vec3 { + // x: (-f32::sin(y_rot) * 0.2) as f64, + // y: 0., + // z: (f32::cos(y_rot) * 0.2) as f64, + // }; + // } + + // self.has_impulse = true; + } } fn get_block_pos_below_that_affects_movement(entity: &EntityData) -> BlockPos { @@ -141,6 +172,54 @@ fn get_speed(entity: &EntityData, friction: f32) -> f32 { } } +/// Returns the what the entity's jump should be multiplied by based on the +/// block they're standing on. +fn block_jump_factor(entity: &EntityMut) -> f32 { + let block_at_pos = entity.dimension.get_block_state(&entity.pos().into()); + let block_below = entity + .dimension + .get_block_state(&get_block_pos_below_that_affects_movement(entity)); + + let block_at_pos_jump_factor = if let Some(block) = block_at_pos { + Box::::from(block).behavior().jump_factor + } else { + 1. + }; + if block_at_pos_jump_factor != 1. { + return block_at_pos_jump_factor; + } + + if let Some(block) = block_below { + Box::::from(block).behavior().jump_factor + } else { + 1. + } +} + +// protected float getJumpPower() { +// return 0.42F * this.getBlockJumpFactor(); +// } +// public double getJumpBoostPower() { +// return this.hasEffect(MobEffects.JUMP) ? (double)(0.1F * (float)(this.getEffect(MobEffects.JUMP).getAmplifier() + 1)) : 0.0D; +// } +fn jump_power(entity: &EntityMut) -> f32 { + 0.42 * block_jump_factor(entity) +} + +fn jump_boost_power(_entity: &EntityMut) -> f64 { + // TODO: potion effects + // if let Some(effects) = entity.effects() { + // if let Some(jump_effect) = effects.get(&Effect::Jump) { + // 0.1 * (jump_effect.amplifier + 1) as f32 + // } else { + // 0. + // } + // } else { + // 0. + // } + 0. +} + #[cfg(test)] mod tests { use super::*; diff --git a/azalea-world/src/entity/mod.rs b/azalea-world/src/entity/mod.rs index b9c273f0..37474d1d 100644 --- a/azalea-world/src/entity/mod.rs +++ b/azalea-world/src/entity/mod.rs @@ -260,6 +260,10 @@ pub struct EntityData { pub dimensions: EntityDimensions, /// The bounding box of the entity. This is more than just width and height, unlike dimensions. pub bounding_box: AABB, + + /// Whether the entity will try to jump every tick + /// (equivalent to the space key being held down in vanilla). + pub jumping: bool, } impl EntityData { @@ -291,6 +295,8 @@ impl EntityData { // TODO: have this be based on the entity type bounding_box: dimensions.make_bounding_box(&pos), dimensions, + + jumping: false, } } diff --git a/bot/src/main.rs b/bot/src/main.rs index 379b3af3..9b2eea1f 100644 --- a/bot/src/main.rs +++ b/bot/src/main.rs @@ -18,12 +18,16 @@ async fn handle_event(event: Event, mut bot: Client) -> anyhow::Result<()> { match event { Event::Login => { // tokio::time::sleep(std::time::Duration::from_secs(1)).await; - bot.walk(MoveDirection::Forward); + // bot.walk(MoveDirection::Forward); + // loop { // tokio::time::sleep(std::time::Duration::from_secs(2)).await; // } // bot.walk(MoveDirection::None); } + Event::GameTick => { + bot.set_jumping(true); + } Event::Packet(_packet) => {} _ => {} }