azalea_entity/
lib.rs

1#![allow(clippy::derived_hash_with_manual_eq)]
2
3pub mod attributes;
4mod data;
5pub mod dimensions;
6mod effects;
7mod enchantments;
8pub mod metadata;
9pub mod mining;
10pub mod particle;
11mod plugin;
12pub mod vec_delta_codec;
13
14use std::{
15    f64::consts::PI,
16    fmt::{self, Debug},
17    hash::{Hash, Hasher},
18};
19
20pub use attributes::Attributes;
21use azalea_block::{BlockState, fluid_state::FluidKind};
22use azalea_buf::AzBuf;
23use azalea_core::{
24    aabb::Aabb,
25    identifier::Identifier,
26    math,
27    position::{BlockPos, ChunkPos, Vec3},
28};
29use azalea_registry::EntityKind;
30use azalea_world::{ChunkStorage, InstanceName};
31use bevy_ecs::{bundle::Bundle, component::Component};
32pub use data::*;
33use derive_more::{Deref, DerefMut};
34pub use effects::{ActiveEffects, MobEffectData};
35use plugin::indexing::EntityChunkPos;
36use uuid::Uuid;
37use vec_delta_codec::VecDeltaCodec;
38
39use self::attributes::AttributeInstance;
40use crate::dimensions::EntityDimensions;
41pub use crate::plugin::*;
42
43pub fn move_relative(
44    physics: &mut Physics,
45    direction: LookDirection,
46    speed: f32,
47    acceleration: Vec3,
48) {
49    let input_vector = input_vector(direction, speed, acceleration);
50    physics.velocity += input_vector;
51}
52
53pub fn input_vector(direction: LookDirection, speed: f32, acceleration: Vec3) -> Vec3 {
54    let distance = acceleration.length_squared();
55    if distance < 1.0e-7 {
56        return Vec3::ZERO;
57    }
58    let acceleration = if distance > 1. {
59        acceleration.normalize()
60    } else {
61        acceleration
62    }
63    .scale(speed as f64);
64    let y_rot = math::sin(direction.y_rot * (PI / 180.) as f32);
65    let x_rot = math::cos(direction.y_rot * (PI / 180.) as f32);
66    Vec3 {
67        x: acceleration.x * (x_rot as f64) - acceleration.z * (y_rot as f64),
68        y: acceleration.y,
69        z: acceleration.z * (x_rot as f64) + acceleration.x * (y_rot as f64),
70    }
71}
72
73pub fn view_vector(look_direction: LookDirection) -> Vec3 {
74    let x_rot = look_direction.x_rot * 0.017453292;
75    let y_rot = -look_direction.y_rot * 0.017453292;
76    let y_rot_cos = math::cos(y_rot);
77    let y_rot_sin = math::sin(y_rot);
78    let x_rot_cos = math::cos(x_rot);
79    let x_rot_sin = math::sin(x_rot);
80    Vec3 {
81        x: (y_rot_sin * x_rot_cos) as f64,
82        y: (-x_rot_sin) as f64,
83        z: (y_rot_cos * x_rot_cos) as f64,
84    }
85}
86
87/// Get the position of the block below the entity, but a little lower.
88pub fn on_pos_legacy(chunk_storage: &ChunkStorage, position: Position) -> BlockPos {
89    on_pos(0.2, chunk_storage, position)
90}
91
92// int x = Mth.floor(this.position.x);
93// int y = Mth.floor(this.position.y - (double)var1);
94// int z = Mth.floor(this.position.z);
95// BlockPos var5 = new BlockPos(x, y, z);
96// if (this.level.getBlockState(var5).isAir()) {
97//    BlockPos var6 = var5.below();
98//    BlockState var7 = this.level.getBlockState(var6);
99//    if (var7.is(BlockTags.FENCES) || var7.is(BlockTags.WALLS) ||
100// var7.getBlock() instanceof FenceGateBlock) {       return var6;
101//    }
102// }
103// return var5;
104pub fn on_pos(offset: f32, chunk_storage: &ChunkStorage, pos: Position) -> BlockPos {
105    let x = pos.x.floor() as i32;
106    let y = (pos.y - offset as f64).floor() as i32;
107    let z = pos.z.floor() as i32;
108    let pos = BlockPos { x, y, z };
109
110    // TODO: check if block below is a fence, wall, or fence gate
111    let block_pos = pos.down(1);
112    let block_state = chunk_storage.get_block_state(block_pos);
113    if block_state == Some(BlockState::AIR) {
114        let block_pos_below = block_pos.down(1);
115        let block_state_below = chunk_storage.get_block_state(block_pos_below);
116        if let Some(_block_state_below) = block_state_below {
117            // if block_state_below.is_fence()
118            //     || block_state_below.is_wall()
119            //     || block_state_below.is_fence_gate()
120            // {
121            //     return block_pos_below;
122            // }
123        }
124    }
125
126    pos
127}
128
129/// The Minecraft UUID of the entity.
130///
131/// For players, this is their actual player UUID, and for other entities it's
132/// just random.
133#[derive(Component, Deref, DerefMut, Clone, Copy, Default, PartialEq)]
134pub struct EntityUuid(Uuid);
135impl EntityUuid {
136    pub fn new(uuid: Uuid) -> Self {
137        Self(uuid)
138    }
139}
140impl Debug for EntityUuid {
141    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142        (self.0).fmt(f)
143    }
144}
145
146/// The position of the entity right now.
147///
148/// You are free to change the value of this component; there's systems that
149/// update the indexes automatically.
150///
151/// Its value is set to a default of [`Vec3::ZERO`] when it receives the login
152/// packet, its true position may be set ticks later.
153#[derive(Component, Clone, Copy, Debug, Default, PartialEq, Deref, DerefMut)]
154pub struct Position(Vec3);
155impl Position {
156    pub fn new(pos: Vec3) -> Self {
157        Self(pos)
158    }
159}
160impl From<&Position> for Vec3 {
161    fn from(value: &Position) -> Self {
162        value.0
163    }
164}
165impl From<Position> for ChunkPos {
166    fn from(value: Position) -> Self {
167        ChunkPos::from(&value.0)
168    }
169}
170impl From<Position> for BlockPos {
171    fn from(value: Position) -> Self {
172        BlockPos::from(&value.0)
173    }
174}
175impl From<&Position> for ChunkPos {
176    fn from(value: &Position) -> Self {
177        ChunkPos::from(value.0)
178    }
179}
180impl From<&Position> for BlockPos {
181    fn from(value: &Position) -> Self {
182        BlockPos::from(value.0)
183    }
184}
185
186/// The second most recent position of the entity that was sent over the
187/// network.
188///
189/// This is currently only updated for our own local player entities.
190#[derive(Component, Clone, Copy, Debug, Default, PartialEq, Deref, DerefMut)]
191pub struct LastSentPosition(Vec3);
192impl From<&LastSentPosition> for Vec3 {
193    fn from(value: &LastSentPosition) -> Self {
194        value.0
195    }
196}
197impl From<LastSentPosition> for ChunkPos {
198    fn from(value: LastSentPosition) -> Self {
199        ChunkPos::from(&value.0)
200    }
201}
202impl From<LastSentPosition> for BlockPos {
203    fn from(value: LastSentPosition) -> Self {
204        BlockPos::from(&value.0)
205    }
206}
207impl From<&LastSentPosition> for ChunkPos {
208    fn from(value: &LastSentPosition) -> Self {
209        ChunkPos::from(value.0)
210    }
211}
212impl From<&LastSentPosition> for BlockPos {
213    fn from(value: &LastSentPosition) -> Self {
214        BlockPos::from(value.0)
215    }
216}
217
218/// A component for entities that can jump.
219///
220/// If this is true, the entity will try to jump every tick. It's equivalent to
221/// the space key being held in vanilla.
222#[derive(Debug, Component, Copy, Clone, Deref, DerefMut, Default, PartialEq, Eq)]
223pub struct Jumping(pub bool);
224
225/// A component that contains the direction an entity is looking, in degrees.
226///
227/// To avoid flagging anticheats, consider using [`Self::update`] when updating
228/// the values of this struct.
229#[derive(Debug, Component, Copy, Clone, Default, PartialEq, AzBuf)]
230pub struct LookDirection {
231    /// Left and right. AKA yaw. In degrees.
232    y_rot: f32,
233    /// Up and down. AKA pitch. In degrees.
234    x_rot: f32,
235}
236
237impl LookDirection {
238    /// Create a new look direction and clamp the `x_rot` to the allowed values.
239    pub fn new(y_rot: f32, x_rot: f32) -> Self {
240        apply_clamp_look_direction(Self { y_rot, x_rot })
241    }
242    /// Returns yaw (left and right) in degrees.
243    ///
244    /// Minecraft allows this to go outside of ±360°, so it won't necessarily be
245    /// in any range.
246    pub fn y_rot(&self) -> f32 {
247        self.y_rot
248    }
249    /// Returns pitch (up and down) in degrees.
250    ///
251    /// Clamped to ±90°.
252    pub fn x_rot(&self) -> f32 {
253        self.x_rot
254    }
255
256    /// Update this look direction to the new value.
257    ///
258    /// This handles relative rotations correctly and with the default Minecraft
259    /// sensitivity to avoid triggering anticheats.
260    pub fn update(&mut self, new: LookDirection) {
261        self.update_with_sensitivity(new, 1.);
262    }
263    /// Update the `y_rot` to the given value, in degrees.
264    ///
265    /// This is a shortcut for [`Self::update`] while keeping the `x_rot` the
266    /// same.
267    pub fn update_y_rot(&mut self, new_y_rot: f32) {
268        self.update(LookDirection {
269            y_rot: new_y_rot,
270            x_rot: self.x_rot,
271        });
272    }
273    /// Update the `x_rot` to the given value, in degrees.
274    ///
275    /// This is a shortcut for [`Self::update`] while keeping the `y_rot` the
276    /// same.
277    pub fn update_x_rot(&mut self, new_x_rot: f32) {
278        self.update(LookDirection {
279            y_rot: self.y_rot,
280            x_rot: new_x_rot,
281        });
282    }
283
284    /// Update this look direction to the new value, using the given
285    /// sensitivity value.
286    ///
287    /// Consider using [`Self::update`] instead, which uses 1.0 as the
288    /// sensitivity (equivalent to 100% sensitivity in Minecraft).
289    pub fn update_with_sensitivity(&mut self, new: LookDirection, sensitivity: f32) {
290        let mut delta_y_rot = new.y_rot.rem_euclid(360.) - self.y_rot.rem_euclid(360.);
291        let delta_x_rot = new.x_rot - self.x_rot;
292
293        if delta_y_rot > 180. {
294            delta_y_rot -= 360.;
295        } else if delta_y_rot < -180. {
296            delta_y_rot += 360.;
297        }
298
299        let sensitivity = sensitivity * 0.15;
300        let delta_y_rot = (delta_y_rot / sensitivity).round() * sensitivity;
301        let delta_x_rot = (delta_x_rot / sensitivity).round() * sensitivity;
302
303        self.y_rot += delta_y_rot;
304        self.x_rot += delta_x_rot;
305    }
306}
307
308impl From<LookDirection> for (f32, f32) {
309    fn from(value: LookDirection) -> Self {
310        (value.y_rot, value.x_rot)
311    }
312}
313impl From<(f32, f32)> for LookDirection {
314    fn from((y_rot, x_rot): (f32, f32)) -> Self {
315        Self { y_rot, x_rot }
316    }
317}
318
319impl Hash for LookDirection {
320    fn hash<H: Hasher>(&self, state: &mut H) {
321        self.y_rot.to_bits().hash(state);
322        self.x_rot.to_bits().hash(state);
323    }
324}
325impl Eq for LookDirection {}
326
327/// The physics data relating to the entity, such as position, velocity, and
328/// bounding box.
329#[derive(Debug, Component, Clone, Default)]
330pub struct Physics {
331    /// How fast the entity is moving. Sometimes referred to as the delta
332    /// movement.
333    ///
334    /// Note that our Y velocity will be approximately -0.0784 when we're on the
335    /// ground due to how Minecraft applies gravity.
336    pub velocity: Vec3,
337    pub vec_delta_codec: VecDeltaCodec,
338
339    /// The position of the entity before it moved this tick.
340    ///
341    /// This is set immediately before physics is done.
342    pub old_position: Vec3,
343
344    /// The acceleration here is the force that will be attempted to be added to
345    /// the entity's velocity next tick.
346    ///
347    /// You should typically not set this yourself, since it's controlled by how
348    /// the entity is trying to move.
349    pub x_acceleration: f32,
350    pub y_acceleration: f32,
351    pub z_acceleration: f32,
352
353    on_ground: bool,
354    last_on_ground: bool,
355
356    /// The number of ticks until we jump again, if the jump key is being held.
357    ///
358    /// This must be 0 for us to be able to jump. Sets to 10 when we do a jump
359    /// and sets to 0 if we're not trying to jump.
360    pub no_jump_delay: u32,
361
362    /// The bounding box of the entity.
363    ///
364    /// This is more than just width and height, unlike dimensions.
365    pub bounding_box: Aabb,
366
367    pub has_impulse: bool,
368
369    pub horizontal_collision: bool,
370    // TODO: implement minor_horizontal_collision
371    pub minor_horizontal_collision: bool,
372    pub vertical_collision: bool,
373
374    pub water_fluid_height: f64,
375    pub lava_fluid_height: f64,
376    pub was_touching_water: bool,
377
378    pub fall_distance: f64,
379    // TODO: implement remaining_fire_ticks
380    pub remaining_fire_ticks: i32,
381}
382
383impl Physics {
384    pub fn new(dimensions: &EntityDimensions, pos: Vec3) -> Self {
385        Self {
386            velocity: Vec3::ZERO,
387            vec_delta_codec: VecDeltaCodec::new(pos),
388
389            old_position: pos,
390
391            x_acceleration: 0.,
392            y_acceleration: 0.,
393            z_acceleration: 0.,
394
395            on_ground: false,
396            last_on_ground: false,
397
398            no_jump_delay: 0,
399
400            bounding_box: dimensions.make_bounding_box(pos),
401
402            has_impulse: false,
403
404            horizontal_collision: false,
405            minor_horizontal_collision: false,
406            vertical_collision: false,
407
408            water_fluid_height: 0.,
409            lava_fluid_height: 0.,
410            was_touching_water: false,
411
412            fall_distance: 0.,
413            remaining_fire_ticks: 0,
414        }
415    }
416
417    pub fn on_ground(&self) -> bool {
418        self.on_ground
419    }
420    /// Updates [`Self::on_ground`] and [`Self::last_on_ground`].
421    pub fn set_on_ground(&mut self, on_ground: bool) {
422        self.last_on_ground = self.on_ground;
423        self.on_ground = on_ground;
424    }
425
426    /// The last value of the on_ground value.
427    ///
428    /// This is used by Azalea internally for physics, it might not work as you
429    /// expect since it can be influenced by packets sent by the server.
430    pub fn last_on_ground(&self) -> bool {
431        self.last_on_ground
432    }
433    pub fn set_last_on_ground(&mut self, last_on_ground: bool) {
434        self.last_on_ground = last_on_ground;
435    }
436
437    pub fn reset_fall_distance(&mut self) {
438        self.fall_distance = 0.;
439    }
440    pub fn clear_fire(&mut self) {
441        self.remaining_fire_ticks = 0;
442    }
443
444    pub fn is_in_water(&self) -> bool {
445        self.was_touching_water
446    }
447    pub fn is_in_lava(&self) -> bool {
448        // TODO: also check `!this.firstTick &&`
449        self.lava_fluid_height > 0.
450    }
451
452    pub fn set_old_pos(&mut self, pos: Position) {
453        self.old_position = *pos;
454    }
455}
456
457/// Marker component for entities that are dead.
458///
459/// "Dead" means that the entity has 0 health.
460#[derive(Component, Copy, Clone, Default)]
461pub struct Dead;
462
463/// A component NewType for [`azalea_registry::EntityKind`].
464///
465/// Most of the time, you should be using `azalea_registry::EntityKind`
466/// directly instead.
467#[derive(Component, Clone, Copy, Debug, PartialEq, Deref)]
468pub struct EntityKindComponent(pub azalea_registry::EntityKind);
469
470/// A bundle of components that every entity has.
471///
472/// This doesn't contain metadata; that has to be added separately.
473#[derive(Bundle)]
474pub struct EntityBundle {
475    pub kind: EntityKindComponent,
476    pub uuid: EntityUuid,
477    pub world_name: InstanceName,
478    pub position: Position,
479    pub last_sent_position: LastSentPosition,
480
481    pub chunk_pos: EntityChunkPos,
482
483    pub physics: Physics,
484    pub direction: LookDirection,
485    pub dimensions: EntityDimensions,
486    pub attributes: Attributes,
487    pub jumping: Jumping,
488    pub crouching: Crouching,
489    pub fluid_on_eyes: FluidOnEyes,
490    pub on_climbable: OnClimbable,
491    pub active_effects: ActiveEffects,
492}
493
494impl EntityBundle {
495    pub fn new(
496        uuid: Uuid,
497        pos: Vec3,
498        kind: azalea_registry::EntityKind,
499        world_name: Identifier,
500    ) -> Self {
501        let dimensions = EntityDimensions::from(kind);
502
503        Self {
504            kind: EntityKindComponent(kind),
505            uuid: EntityUuid(uuid),
506            world_name: InstanceName(world_name),
507            position: Position(pos),
508            chunk_pos: EntityChunkPos(ChunkPos::from(&pos)),
509            last_sent_position: LastSentPosition(pos),
510            physics: Physics::new(&dimensions, pos),
511            dimensions,
512            direction: LookDirection::default(),
513
514            attributes: default_attributes(EntityKind::Player),
515
516            jumping: Jumping(false),
517            crouching: Crouching(false),
518            fluid_on_eyes: FluidOnEyes(FluidKind::Empty),
519            on_climbable: OnClimbable(false),
520            active_effects: ActiveEffects::default(),
521        }
522    }
523}
524
525pub fn default_attributes(_entity_kind: EntityKind) -> Attributes {
526    // TODO: do the correct defaults for everything, some
527    // entities have different defaults
528    Attributes {
529        movement_speed: AttributeInstance::new(0.1f32 as f64),
530        sneaking_speed: AttributeInstance::new(0.3),
531        attack_speed: AttributeInstance::new(4.0),
532        water_movement_efficiency: AttributeInstance::new(0.0),
533        block_interaction_range: AttributeInstance::new(4.5),
534        entity_interaction_range: AttributeInstance::new(3.0),
535        step_height: AttributeInstance::new(0.6),
536    }
537}
538
539/// A marker component that signifies that this entity is "local" and shouldn't
540/// be updated by other clients.
541///
542/// If this is for a client then all of our clients will have this.
543///
544/// This component is not removed from clients when they disconnect.
545#[derive(Component, Clone, Copy, Debug, Default)]
546pub struct LocalEntity;
547
548#[derive(Component, Clone, Copy, Debug, PartialEq, Deref, DerefMut)]
549pub struct FluidOnEyes(FluidKind);
550
551impl FluidOnEyes {
552    pub fn new(fluid: FluidKind) -> Self {
553        Self(fluid)
554    }
555}
556
557#[derive(Component, Clone, Copy, Debug, PartialEq, Deref, DerefMut)]
558pub struct OnClimbable(bool);
559
560/// A component that indicates whether the player is currently sneaking.
561///
562/// If the entity isn't a local player, then this is just a shortcut for
563/// checking if the [`Pose`] is `Crouching`.
564///
565/// If you need to modify this value, use
566/// `azalea_client::PhysicsState::trying_to_crouch` or `Client::set_crouching`
567/// instead.
568#[derive(Component, Clone, Copy, Deref, DerefMut, Default)]
569pub struct Crouching(bool);
570
571/// A component that contains the abilities the player has, like flying
572/// or instantly breaking blocks.
573///
574/// This is only present on local players.
575#[derive(Clone, Debug, Component, Default)]
576pub struct PlayerAbilities {
577    pub invulnerable: bool,
578    pub flying: bool,
579    pub can_fly: bool,
580    /// Whether the player can instantly break blocks and can duplicate blocks
581    /// in their inventory.
582    pub instant_break: bool,
583
584    pub flying_speed: f32,
585    /// Used for the fov
586    pub walking_speed: f32,
587}