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