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