More Entity Physics work
Co-Authored-By: akashbeh <112662680+akashbeh@users.noreply.github.com>
This commit is contained in:
parent
a4180e0512
commit
6e4a05d66d
7
pumpkin-protocol/src/java/server/play/custom_payload.rs
Normal file
7
pumpkin-protocol/src/java/server/play/custom_payload.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
use pumpkin_data::packet::serverbound::PLAY_CUSTOM_PAYLOAD;
|
||||||
|
use pumpkin_macros::packet;
|
||||||
|
|
||||||
|
#[packet(PLAY_CUSTOM_PAYLOAD)]
|
||||||
|
pub struct SCustomPayload {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
@ -10,6 +10,7 @@ mod close_container;
|
|||||||
mod command_suggestion;
|
mod command_suggestion;
|
||||||
mod confirm_teleport;
|
mod confirm_teleport;
|
||||||
mod cookie_response;
|
mod cookie_response;
|
||||||
|
mod custom_payload;
|
||||||
mod interact;
|
mod interact;
|
||||||
mod keep_alive;
|
mod keep_alive;
|
||||||
mod pick_item;
|
mod pick_item;
|
||||||
@ -44,6 +45,7 @@ pub use close_container::*;
|
|||||||
pub use command_suggestion::*;
|
pub use command_suggestion::*;
|
||||||
pub use confirm_teleport::*;
|
pub use confirm_teleport::*;
|
||||||
pub use cookie_response::*;
|
pub use cookie_response::*;
|
||||||
|
pub use custom_payload::*;
|
||||||
pub use interact::*;
|
pub use interact::*;
|
||||||
pub use keep_alive::*;
|
pub use keep_alive::*;
|
||||||
pub use pick_item::*;
|
pub use pick_item::*;
|
||||||
|
|||||||
@ -76,7 +76,31 @@ impl ExperienceOrbEntity {
|
|||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl EntityBase for ExperienceOrbEntity {
|
impl EntityBase for ExperienceOrbEntity {
|
||||||
async fn tick(&self, caller: Arc<dyn EntityBase>, server: &Server) {
|
async fn tick(&self, caller: Arc<dyn EntityBase>, server: &Server) {
|
||||||
self.entity.tick(caller, server).await;
|
let entity = &self.entity;
|
||||||
|
entity.tick(caller.clone(), server).await;
|
||||||
|
let bounding_box = entity.bounding_box.load();
|
||||||
|
|
||||||
|
let original_velo = entity.velocity.load();
|
||||||
|
|
||||||
|
let mut velo = original_velo;
|
||||||
|
|
||||||
|
let no_clip = !self
|
||||||
|
.entity
|
||||||
|
.world
|
||||||
|
.read()
|
||||||
|
.await
|
||||||
|
.is_space_empty(bounding_box.expand(-1.0e-7, -1.0e-7, -1.0e-7))
|
||||||
|
.await;
|
||||||
|
// TODO: isSubmergedIn
|
||||||
|
if !no_clip {
|
||||||
|
velo.y -= self.get_gravity();
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.velocity.store(velo);
|
||||||
|
|
||||||
|
entity.move_entity(caller.clone(), velo).await;
|
||||||
|
|
||||||
|
entity.tick_block_collisions(&caller, server).await;
|
||||||
|
|
||||||
let age = self.orb_age.fetch_add(1, Ordering::Relaxed);
|
let age = self.orb_age.fetch_add(1, Ordering::Relaxed);
|
||||||
if age >= 6000 {
|
if age >= 6000 {
|
||||||
@ -115,4 +139,8 @@ impl EntityBase for ExperienceOrbEntity {
|
|||||||
fn get_living_entity(&self) -> Option<&LivingEntity> {
|
fn get_living_entity(&self) -> Option<&LivingEntity> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_gravity(&self) -> f64 {
|
||||||
|
0.03
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
use pumpkin_data::potion::Effect;
|
use pumpkin_data::potion::Effect;
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::{
|
||||||
use std::sync::atomic::{AtomicU8, Ordering::Relaxed};
|
AtomicBool, AtomicU8,
|
||||||
|
Ordering::{Relaxed, SeqCst},
|
||||||
|
};
|
||||||
use std::{collections::HashMap, sync::atomic::AtomicI32};
|
use std::{collections::HashMap, sync::atomic::AtomicI32};
|
||||||
|
|
||||||
use super::{Entity, NBTStorage};
|
use super::{Entity, NBTStorage};
|
||||||
@ -64,7 +66,7 @@ pub struct LivingEntity {
|
|||||||
/// The position where the entity was last climbing, used for death messages
|
/// The position where the entity was last climbing, used for death messages
|
||||||
pub climbing_pos: AtomicCell<Option<BlockPos>>,
|
pub climbing_pos: AtomicCell<Option<BlockPos>>,
|
||||||
|
|
||||||
_water_movement_speed_multiplier: f32,
|
water_movement_speed_multiplier: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@ -102,7 +104,7 @@ impl LivingEntity {
|
|||||||
climbing_pos: AtomicCell::new(None),
|
climbing_pos: AtomicCell::new(None),
|
||||||
movement_input: AtomicCell::new(Vector3::default()),
|
movement_input: AtomicCell::new(Vector3::default()),
|
||||||
movement_speed: AtomicCell::new(default_movement_speed),
|
movement_speed: AtomicCell::new(default_movement_speed),
|
||||||
_water_movement_speed_multiplier: water_movement_speed_multiplier,
|
water_movement_speed_multiplier,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,6 +203,445 @@ impl LivingEntity {
|
|||||||
world.get_block(&block_pos).await == &Block::POWDER_SNOW
|
world.get_block(&block_pos).await == &Block::POWDER_SNOW
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_effective_gravity(&self, caller: &Arc<dyn EntityBase>) -> f64 {
|
||||||
|
let final_gravity = caller.get_gravity();
|
||||||
|
|
||||||
|
if self.entity.velocity.load().y <= 0.0
|
||||||
|
&& self.has_effect(&StatusEffect::SLOW_FALLING).await
|
||||||
|
{
|
||||||
|
final_gravity.min(0.01)
|
||||||
|
} else {
|
||||||
|
final_gravity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn tick_movement(&self, server: &Server, caller: Arc<dyn EntityBase>) {
|
||||||
|
if self.jumping_cooldown.load(Relaxed) != 0 {
|
||||||
|
self.jumping_cooldown.fetch_sub(1, Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
let should_swim_in_fluids = if let Some(player) = caller.get_player() {
|
||||||
|
!player.is_flying().await
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
};
|
||||||
|
|
||||||
|
self.entity.check_zero_velo();
|
||||||
|
|
||||||
|
let mut movement_input = self.movement_input.load();
|
||||||
|
|
||||||
|
movement_input.x *= 0.98;
|
||||||
|
|
||||||
|
movement_input.z *= 0.98;
|
||||||
|
|
||||||
|
self.movement_input.store(movement_input);
|
||||||
|
|
||||||
|
// TODO: Tick AI
|
||||||
|
|
||||||
|
if self.jumping.load(SeqCst) && should_swim_in_fluids {
|
||||||
|
let in_lava = self.entity.touching_lava.load(SeqCst);
|
||||||
|
|
||||||
|
let in_water = self.entity.touching_water.load(SeqCst);
|
||||||
|
|
||||||
|
let fluid_height = if in_lava {
|
||||||
|
self.entity.lava_height.load()
|
||||||
|
} else {
|
||||||
|
self.entity.water_height.load()
|
||||||
|
};
|
||||||
|
|
||||||
|
let swim_height = self.get_swim_height();
|
||||||
|
|
||||||
|
let on_ground = self.entity.on_ground.load(SeqCst);
|
||||||
|
|
||||||
|
if (in_water || in_lava) && (!on_ground || fluid_height > swim_height) {
|
||||||
|
// Swim upward
|
||||||
|
|
||||||
|
let mut velo = self.entity.velocity.load();
|
||||||
|
|
||||||
|
velo.y += 0.04;
|
||||||
|
|
||||||
|
self.entity.velocity.store(velo);
|
||||||
|
} else if (on_ground || in_water && fluid_height <= swim_height)
|
||||||
|
&& self.jumping_cooldown.load(SeqCst) == 0
|
||||||
|
{
|
||||||
|
self.jump().await;
|
||||||
|
|
||||||
|
self.jumping_cooldown.store(10, SeqCst);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.jumping_cooldown.store(0, SeqCst);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.has_effect(&StatusEffect::SLOW_FALLING).await
|
||||||
|
|| self.has_effect(&StatusEffect::LEVITATION).await
|
||||||
|
{
|
||||||
|
self.fall_distance.store(0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let touching_water = self.entity.touching_water.load(SeqCst);
|
||||||
|
|
||||||
|
// Strider is the only entity that has canWalkOnFluid = false
|
||||||
|
|
||||||
|
if (touching_water || self.entity.touching_lava.load(SeqCst))
|
||||||
|
&& should_swim_in_fluids
|
||||||
|
&& self.entity.entity_type != &EntityType::STRIDER
|
||||||
|
{
|
||||||
|
self.travel_in_fluid(caller.clone(), touching_water).await;
|
||||||
|
} else {
|
||||||
|
// TODO: Gliding
|
||||||
|
|
||||||
|
self.travel_in_air(caller.clone()).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
//self.entity.tick_block_underneath(&caller);
|
||||||
|
|
||||||
|
let suffocating = self.entity.tick_block_collisions(&caller, server).await;
|
||||||
|
|
||||||
|
if suffocating {
|
||||||
|
self.damage(1.0, DamageType::IN_WALL).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn travel_in_air(&self, caller: Arc<dyn EntityBase>) {
|
||||||
|
// applyMovementInput
|
||||||
|
|
||||||
|
let (speed, friction) = if self.entity.on_ground.load(SeqCst) {
|
||||||
|
// getVelocityAffectingPos
|
||||||
|
|
||||||
|
let slipperiness = f64::from(
|
||||||
|
self.entity
|
||||||
|
.get_block_with_y_offset(0.500_001)
|
||||||
|
.await
|
||||||
|
.1
|
||||||
|
.slipperiness,
|
||||||
|
);
|
||||||
|
|
||||||
|
let speed =
|
||||||
|
self.movement_speed.load() * 0.216 / (slipperiness * slipperiness * slipperiness);
|
||||||
|
|
||||||
|
(speed, slipperiness * 0.91)
|
||||||
|
} else {
|
||||||
|
let speed = if let Some(player) = caller.get_player() {
|
||||||
|
player.get_off_ground_speed().await
|
||||||
|
} else {
|
||||||
|
// TODO: If the passenger is a player, ogs = movement_speed * 0.1
|
||||||
|
|
||||||
|
0.02
|
||||||
|
};
|
||||||
|
|
||||||
|
(speed, 0.91)
|
||||||
|
};
|
||||||
|
|
||||||
|
self.entity
|
||||||
|
.update_velocity_from_input(self.movement_input.load(), speed);
|
||||||
|
|
||||||
|
self.apply_climbing_speed();
|
||||||
|
|
||||||
|
self.make_move(caller.clone()).await;
|
||||||
|
|
||||||
|
let mut velo = self.entity.velocity.load();
|
||||||
|
|
||||||
|
// TODO: Add powdered snow
|
||||||
|
|
||||||
|
if (self.entity.horizontal_collision.load(SeqCst) || self.jumping.load(SeqCst))
|
||||||
|
&& (self.climbing.load(Relaxed))
|
||||||
|
{
|
||||||
|
velo.y = 0.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
let levitation = self.get_effect(&StatusEffect::LEVITATION).await;
|
||||||
|
|
||||||
|
if let Some(lev) = levitation {
|
||||||
|
velo.y += (0.05 * f64::from(lev.amplifier + 1) - velo.y) * 0.2;
|
||||||
|
} else {
|
||||||
|
velo.y -= self.get_effective_gravity(&caller).await;
|
||||||
|
|
||||||
|
// TODO: If world is not loaded: replace effective gravity with:
|
||||||
|
|
||||||
|
// if below world's bottom y then -0.1, else 0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
// If entity has no drag: store velo and return
|
||||||
|
|
||||||
|
velo.x *= friction;
|
||||||
|
|
||||||
|
velo.z *= friction;
|
||||||
|
|
||||||
|
velo.y *= if caller.is_flutterer() {
|
||||||
|
friction
|
||||||
|
} else {
|
||||||
|
0.98
|
||||||
|
};
|
||||||
|
|
||||||
|
self.entity.velocity.store(velo);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn travel_in_fluid(&self, caller: Arc<dyn EntityBase>, water: bool) {
|
||||||
|
let movement_input = self.movement_input.load();
|
||||||
|
|
||||||
|
let y0 = self.entity.pos.load().y;
|
||||||
|
|
||||||
|
let falling = self.entity.velocity.load().y <= 0.0;
|
||||||
|
|
||||||
|
let gravity = self.get_effective_gravity(&caller).await;
|
||||||
|
|
||||||
|
if water {
|
||||||
|
let mut friction = if self.entity.sprinting.load(Relaxed) {
|
||||||
|
0.9
|
||||||
|
} else {
|
||||||
|
f64::from(self.water_movement_speed_multiplier)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut speed = 0.02;
|
||||||
|
|
||||||
|
let mut water_movement_efficiency = 0.0; // TODO: Entity attribute
|
||||||
|
|
||||||
|
if water_movement_efficiency > 0.0 {
|
||||||
|
if !self.entity.on_ground.load(SeqCst) {
|
||||||
|
water_movement_efficiency *= 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
friction += (0.546_000_06 - friction) * water_movement_efficiency;
|
||||||
|
|
||||||
|
speed += (self.movement_speed.load() - speed) * water_movement_efficiency;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.has_effect(&StatusEffect::DOLPHINS_GRACE).await {
|
||||||
|
friction = 0.96;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.entity
|
||||||
|
.update_velocity_from_input(movement_input, speed);
|
||||||
|
|
||||||
|
self.make_move(caller).await;
|
||||||
|
|
||||||
|
let mut velo = self.entity.velocity.load();
|
||||||
|
|
||||||
|
if self.entity.horizontal_collision.load(SeqCst) && self.climbing.load(Relaxed) {
|
||||||
|
velo.y = 0.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
velo = velo.multiply(friction, 0.8, friction);
|
||||||
|
|
||||||
|
self.apply_fluid_moving_speed(&mut velo.y, gravity, falling);
|
||||||
|
|
||||||
|
self.entity.velocity.store(velo);
|
||||||
|
} else {
|
||||||
|
self.entity.update_velocity_from_input(movement_input, 0.02);
|
||||||
|
|
||||||
|
self.make_move(caller).await;
|
||||||
|
|
||||||
|
let mut velo = self.entity.velocity.load();
|
||||||
|
|
||||||
|
if self.entity.lava_height.load() <= self.get_swim_height() {
|
||||||
|
velo.x *= 0.5;
|
||||||
|
|
||||||
|
velo.z *= 0.5;
|
||||||
|
|
||||||
|
velo.y *= 0.8;
|
||||||
|
|
||||||
|
self.apply_fluid_moving_speed(&mut velo.y, gravity, falling);
|
||||||
|
} else {
|
||||||
|
velo = velo * 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
if gravity != 0.0 {
|
||||||
|
velo.y -= gravity / 4.0; // Negative gravity = buoyancy
|
||||||
|
}
|
||||||
|
|
||||||
|
self.entity.velocity.store(velo);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut velo = self.entity.velocity.load();
|
||||||
|
|
||||||
|
velo.y += 0.6 - self.entity.pos.load().y + y0;
|
||||||
|
|
||||||
|
if self.entity.horizontal_collision.load(SeqCst)
|
||||||
|
&& !self
|
||||||
|
.entity
|
||||||
|
.world
|
||||||
|
.read()
|
||||||
|
.await
|
||||||
|
.check_fluid_collision(self.entity.bounding_box.load().shift(velo))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
velo.y = 0.3;
|
||||||
|
|
||||||
|
self.entity.velocity.store(velo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_fluid_moving_speed(&self, dy: &mut f64, gravity: f64, falling: bool) {
|
||||||
|
if gravity != 0.0 && !self.entity.sprinting.load(Relaxed) {
|
||||||
|
if falling && (*dy - 0.005).abs() >= 0.003 && (*dy - gravity / 16.0).abs() < 0.003 {
|
||||||
|
*dy = -0.003;
|
||||||
|
} else {
|
||||||
|
*dy -= gravity / 16.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn make_move(&self, caller: Arc<dyn EntityBase>) {
|
||||||
|
self.entity
|
||||||
|
.move_entity(caller, self.entity.velocity.load())
|
||||||
|
.await;
|
||||||
|
|
||||||
|
self.check_climbing();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_climbing(&self) {
|
||||||
|
// If spectator: return false
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// let mut pos = self.entity.block_pos.load();
|
||||||
|
|
||||||
|
// let world = self.entity.world.read().await;
|
||||||
|
|
||||||
|
// let (block, state) = world.get_block_and_state(&pos).await;
|
||||||
|
|
||||||
|
// let name = block.properties(state.id).map(|props| props.name());
|
||||||
|
|
||||||
|
// if let Some(name) = name {
|
||||||
|
// if name == "LadderLikeProperties"
|
||||||
|
// || name == "ScaffoldingLikeProperties"
|
||||||
|
// || name == "CaveVinesLikeProperties"
|
||||||
|
// || name == "CaveVinesPlantLikeProperties"
|
||||||
|
// {
|
||||||
|
// self.climbing.store(true, Relaxed);
|
||||||
|
|
||||||
|
// self.climbing_pos.store(Some(pos));
|
||||||
|
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if name == "OakTrapdoorLikeProperties" {
|
||||||
|
// let trapdoor = OakTrapdoorLikeProperties::from_state_id(state.id, &block);
|
||||||
|
|
||||||
|
// pos.0.y -= 1;
|
||||||
|
|
||||||
|
// let (down_block, down_state) = world.get_block_and_state(&pos).await;
|
||||||
|
|
||||||
|
// let is_ladder = down_block
|
||||||
|
// .properties(down_state.id)
|
||||||
|
// .is_some_and(|down_props| down_props.name() == "LadderLikeProperties");
|
||||||
|
|
||||||
|
// if is_ladder {
|
||||||
|
// let ladder = LadderLikeProperties::from_state_id(down_state.id, &down_block);
|
||||||
|
|
||||||
|
// if trapdoor.r#facing == ladder.r#facing {
|
||||||
|
// self.climbing.store(true, Relaxed);
|
||||||
|
|
||||||
|
// self.climbing_pos.store(Some(pos));
|
||||||
|
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
self.climbing.store(false, Relaxed);
|
||||||
|
|
||||||
|
if self.entity.on_ground.load(SeqCst) {
|
||||||
|
self.climbing_pos.store(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_climbing_speed(&self) {
|
||||||
|
if self.climbing.load(Relaxed) {
|
||||||
|
self.fall_distance.store(0.0);
|
||||||
|
|
||||||
|
let mut velo = self.entity.velocity.load();
|
||||||
|
|
||||||
|
let pos = 0.15;
|
||||||
|
|
||||||
|
let neg = -0.15;
|
||||||
|
|
||||||
|
if velo.x < neg {
|
||||||
|
velo.x = neg;
|
||||||
|
} else if velo.x > pos {
|
||||||
|
velo.x = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
if velo.z < neg {
|
||||||
|
velo.z = neg;
|
||||||
|
} else if velo.z > pos {
|
||||||
|
velo.z = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
velo.y = velo.y.max(neg);
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// if velo.y < 0.0
|
||||||
|
// && self.entity.entity_type == &EntityType::PLAYER
|
||||||
|
// && self.entity.sneaking.load(Relaxed)
|
||||||
|
// {
|
||||||
|
// let block = self
|
||||||
|
// .entity
|
||||||
|
// .world
|
||||||
|
// .read()
|
||||||
|
// .await
|
||||||
|
// .get_block(&self.entity.block_pos.load())
|
||||||
|
// .await;
|
||||||
|
|
||||||
|
// if let Some(props) = block.properties(block.default_state.id) {
|
||||||
|
// if props.name() == "ScaffoldingLikeProperties" {
|
||||||
|
// velo.y = 0.0;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
self.entity.velocity.store(velo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_swim_height(&self) -> f64 {
|
||||||
|
let eye_height = self.entity.standing_eye_height;
|
||||||
|
|
||||||
|
if self.entity.entity_type == &EntityType::BREEZE {
|
||||||
|
f64::from(eye_height)
|
||||||
|
} else if eye_height < 0.4 {
|
||||||
|
0.0
|
||||||
|
} else {
|
||||||
|
0.4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn jump(&self) {
|
||||||
|
let jump = self.get_jump_velocity(1.0).await;
|
||||||
|
|
||||||
|
if jump <= 1.0e-5 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut velo = self.entity.velocity.load();
|
||||||
|
|
||||||
|
velo.y = jump.max(velo.y);
|
||||||
|
|
||||||
|
if self.entity.sprinting.load(Relaxed) {
|
||||||
|
let yaw = f64::from(self.entity.yaw.load()).to_radians();
|
||||||
|
|
||||||
|
velo.x -= yaw.sin() * 0.2;
|
||||||
|
|
||||||
|
velo.y += yaw.cos() * 0.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.entity.velocity.store(velo);
|
||||||
|
|
||||||
|
self.entity.velocity_dirty.store(true, SeqCst);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_jump_velocity(&self, mut strength: f64) -> f64 {
|
||||||
|
strength *= 1.0; // TODO: Entity Attribute jump strength
|
||||||
|
|
||||||
|
strength *= f64::from(self.entity.get_jump_velocity_multiplier().await);
|
||||||
|
|
||||||
|
if let Some(effect) = self.get_effect(&StatusEffect::JUMP_BOOST).await {
|
||||||
|
strength += 0.1 * f64::from(effect.amplifier + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
strength
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn update_fall_distance(
|
pub async fn update_fall_distance(
|
||||||
&self,
|
&self,
|
||||||
height_difference: f64,
|
height_difference: f64,
|
||||||
@ -352,19 +793,6 @@ impl LivingEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn tick_move(&self, entity: &dyn EntityBase, server: &Server) {
|
|
||||||
let velo = self.entity.velocity.load();
|
|
||||||
let pos = self.entity.pos.load();
|
|
||||||
self.entity
|
|
||||||
.pos
|
|
||||||
.store(Vector3::new(pos.x + velo.x, pos.y + velo.y, pos.z + velo.z));
|
|
||||||
let multiplier = f64::from(Entity::velocity_multiplier(pos));
|
|
||||||
self.entity
|
|
||||||
.velocity
|
|
||||||
.store(velo.multiply(multiplier, 1.0, multiplier));
|
|
||||||
Entity::check_block_collision(entity, server).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn tick_effects(&self) {
|
async fn tick_effects(&self) {
|
||||||
let mut effects_to_remove = Vec::new();
|
let mut effects_to_remove = Vec::new();
|
||||||
|
|
||||||
@ -497,9 +925,19 @@ impl EntityBase for LivingEntity {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_gravity(&self) -> f64 {
|
||||||
|
const GRAVITY: f64 = 0.08;
|
||||||
|
GRAVITY
|
||||||
|
}
|
||||||
|
|
||||||
async fn tick(&self, caller: Arc<dyn EntityBase>, server: &Server) {
|
async fn tick(&self, caller: Arc<dyn EntityBase>, server: &Server) {
|
||||||
self.entity.tick(caller.clone(), server).await;
|
self.entity.tick(caller.clone(), server).await;
|
||||||
self.tick_move(caller.as_ref(), server).await;
|
self.tick_movement(server, caller.clone()).await;
|
||||||
|
// TODO
|
||||||
|
if caller.get_player().is_none() {
|
||||||
|
self.entity.send_pos_rot().await;
|
||||||
|
self.entity.send_velocity().await;
|
||||||
|
}
|
||||||
self.tick_effects().await;
|
self.tick_effects().await;
|
||||||
if self.hurt_cooldown.load(Relaxed) > 0 {
|
if self.hurt_cooldown.load(Relaxed) > 0 {
|
||||||
self.hurt_cooldown.fetch_sub(1, Relaxed);
|
self.hurt_cooldown.fetch_sub(1, Relaxed);
|
||||||
|
|||||||
@ -132,6 +132,10 @@ where
|
|||||||
fn get_living_entity(&self) -> Option<&LivingEntity> {
|
fn get_living_entity(&self) -> Option<&LivingEntity> {
|
||||||
Some(&self.get_mob_entity().living_entity)
|
Some(&self.get_mob_entity().living_entity)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_gravity(&self) -> f64 {
|
||||||
|
self.get_mob_entity().living_entity.get_gravity()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
|||||||
@ -139,6 +139,10 @@ pub trait EntityBase: Send + Sync {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_flutterer(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
async fn damage_with_context(
|
async fn damage_with_context(
|
||||||
&self,
|
&self,
|
||||||
amount: f32,
|
amount: f32,
|
||||||
@ -685,7 +689,8 @@ impl Entity {
|
|||||||
self.velocity.store(motion);
|
self.velocity.store(motion);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _tick_block_underneath(_caller: &Arc<dyn EntityBase>) {
|
#[allow(dead_code)]
|
||||||
|
fn tick_block_underneath(_caller: &Arc<dyn EntityBase>) {
|
||||||
// let world = self.world.read().await;
|
// let world = self.world.read().await;
|
||||||
|
|
||||||
// let (pos, block, state) = self.get_block_with_y_offset(0.2).await;
|
// let (pos, block, state) = self.get_block_with_y_offset(0.2).await;
|
||||||
@ -1095,8 +1100,7 @@ impl Entity {
|
|||||||
|
|
||||||
// Entity.updateVelocity in yarn
|
// Entity.updateVelocity in yarn
|
||||||
|
|
||||||
#[allow(dead_code)]
|
fn update_velocity_from_input(&self, movement_input: Vector3<f64>, speed: f64) {
|
||||||
fn _update_velocity_from_input(&self, movement_input: Vector3<f64>, speed: f64) {
|
|
||||||
let final_input = self.movement_input_to_velocity(movement_input, speed);
|
let final_input = self.movement_input_to_velocity(movement_input, speed);
|
||||||
|
|
||||||
self.velocity.store(self.velocity.load() + final_input);
|
self.velocity.store(self.velocity.load() + final_input);
|
||||||
@ -1149,7 +1153,7 @@ impl Entity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::float_cmp)]
|
#[allow(clippy::float_cmp)]
|
||||||
async fn _get_jump_velocity_multiplier(&self) -> f32 {
|
async fn get_jump_velocity_multiplier(&self) -> f32 {
|
||||||
let world = self.world.read().await;
|
let world = self.world.read().await;
|
||||||
|
|
||||||
let f = world
|
let f = world
|
||||||
@ -1644,25 +1648,6 @@ impl Entity {
|
|||||||
self.invulnerable.load(Relaxed) || self.damage_immunities.contains(damage_type)
|
self.invulnerable.load(Relaxed) || self.damage_immunities.contains(damage_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn velocity_multiplier(_pos: Vector3<f64>) -> f32 {
|
|
||||||
// let world = self.world.read().await;
|
|
||||||
// TODO: handle when player is outside world
|
|
||||||
// let block = world.get_block(&self.block_pos.load()).await;
|
|
||||||
// block.velocity_multiplier
|
|
||||||
1.0
|
|
||||||
// if velo_multiplier == 1.0 {
|
|
||||||
// const VELOCITY_OFFSET: f64 = 0.500001; // Vanilla
|
|
||||||
// let pos_with_y_offset = BlockPos(Vector3::new(
|
|
||||||
// pos.x.floor() as i32,
|
|
||||||
// (pos.y - VELOCITY_OFFSET).floor() as i32,
|
|
||||||
// pos.z.floor() as i32,
|
|
||||||
// ));
|
|
||||||
// let block = world.get_block(&pos_with_y_offset).await.unwrap();
|
|
||||||
// block.velocity_multiplier
|
|
||||||
// } else {
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn check_block_collision(entity: &dyn EntityBase, server: &Server) {
|
pub async fn check_block_collision(entity: &dyn EntityBase, server: &Server) {
|
||||||
let aabb = entity.get_entity().bounding_box.load();
|
let aabb = entity.get_entity().bounding_box.load();
|
||||||
let blockpos = BlockPos::new(
|
let blockpos = BlockPos::new(
|
||||||
|
|||||||
@ -604,6 +604,29 @@ impl Player {
|
|||||||
self.sleeping_since.store(Some(0));
|
self.sleeping_since.store(Some(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_off_ground_speed(&self) -> f64 {
|
||||||
|
let sprinting = self.get_entity().sprinting.load(Ordering::Relaxed);
|
||||||
|
|
||||||
|
if !self.get_entity().has_vehicle().await {
|
||||||
|
let fly_speed = {
|
||||||
|
let abilities = self.abilities.lock().await;
|
||||||
|
|
||||||
|
abilities.flying.then_some(f64::from(abilities.fly_speed))
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(flying) = fly_speed {
|
||||||
|
return if sprinting { flying * 2.0 } else { flying };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if sprinting { 0.025_999_999 } else { 0.02 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn is_flying(&self) -> bool {
|
||||||
|
let abilities = self.abilities.lock().await;
|
||||||
|
abilities.flying
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn wake_up(&self) {
|
pub async fn wake_up(&self) {
|
||||||
let world = self.world().await;
|
let world = self.world().await;
|
||||||
let respawn_point = self
|
let respawn_point = self
|
||||||
|
|||||||
@ -8,7 +8,7 @@ use pumpkin_config::networking::compression::CompressionInfo;
|
|||||||
use pumpkin_protocol::java::server::play::{
|
use pumpkin_protocol::java::server::play::{
|
||||||
SChangeGameMode, SChatCommand, SChatMessage, SChunkBatch, SClickSlot, SClientCommand,
|
SChangeGameMode, SChatCommand, SChatMessage, SChunkBatch, SClickSlot, SClientCommand,
|
||||||
SClientInformationPlay, SClientTickEnd, SCloseContainer, SCommandSuggestion, SConfirmTeleport,
|
SClientInformationPlay, SClientTickEnd, SCloseContainer, SCommandSuggestion, SConfirmTeleport,
|
||||||
SCookieResponse as SPCookieResponse, SInteract, SKeepAlive, SPickItemFromBlock,
|
SCookieResponse as SPCookieResponse, SCustomPayload, SInteract, SKeepAlive, SPickItemFromBlock,
|
||||||
SPlayPingRequest, SPlayerAbilities, SPlayerAction, SPlayerCommand, SPlayerInput, SPlayerLoaded,
|
SPlayPingRequest, SPlayerAbilities, SPlayerAction, SPlayerCommand, SPlayerInput, SPlayerLoaded,
|
||||||
SPlayerPosition, SPlayerPositionRotation, SPlayerRotation, SPlayerSession, SSetCommandBlock,
|
SPlayerPosition, SPlayerPositionRotation, SPlayerRotation, SPlayerSession, SSetCommandBlock,
|
||||||
SSetCreativeSlot, SSetHeldItem, SSetPlayerGround, SSwingArm, SUpdateSign, SUseItem, SUseItemOn,
|
SSetCreativeSlot, SSetHeldItem, SSetPlayerGround, SSwingArm, SUpdateSign, SUseItem, SUseItemOn,
|
||||||
@ -668,6 +668,9 @@ impl JavaClient {
|
|||||||
self.handle_chat_session_update(player, server, SPlayerSession::read(payload)?)
|
self.handle_chat_session_update(player, server, SPlayerSession::read(payload)?)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
SCustomPayload::PACKET_ID => {
|
||||||
|
// TODO: this fixes Failed to handle player packet id for now
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
log::warn!("Failed to handle player packet id {}", packet.id);
|
log::warn!("Failed to handle player packet id {}", packet.id);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user