azalea_entity/
lib.rs

1#![doc = include_str!("../README.md")]
2#![allow(clippy::derived_hash_with_manual_eq)]
3
4pub mod attributes;
5mod data;
6pub mod dimensions;
7mod effects;
8pub mod inventory;
9#[cfg(feature = "bevy_ecs")]
10pub mod metadata;
11pub mod mining;
12pub mod particle;
13#[cfg(feature = "bevy_ecs")]
14mod plugin;
15pub mod vec_delta_codec;
16
17use std::{
18    f64::consts::PI,
19    fmt::{self, Debug},
20    hash::{Hash, Hasher},
21};
22
23pub use attributes::Attributes;
24use azalea_block::fluid_state::FluidKind;
25use azalea_buf::AzBuf;
26use azalea_core::{
27    aabb::Aabb,
28    math,
29    position::{BlockPos, ChunkPos, Vec3},
30};
31use azalea_registry::builtin::EntityKind;
32pub use data::*;
33use derive_more::{Deref, DerefMut};
34pub use effects::{ActiveEffects, MobEffectData};
35use uuid::Uuid;
36use vec_delta_codec::VecDeltaCodec;
37
38use self::attributes::AttributeInstance;
39use crate::dimensions::EntityDimensions;
40#[cfg(feature = "bevy_ecs")]
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/// The Minecraft UUID of the entity.
88///
89/// For players, this is their actual player UUID, and for other entities it's
90/// just random.
91#[cfg_attr(feature = "bevy_ecs", derive(bevy_ecs::component::Component))]
92#[derive(Clone, Copy, Default, Deref, DerefMut, PartialEq)]
93pub struct EntityUuid(Uuid);
94impl EntityUuid {
95    pub fn new(uuid: Uuid) -> Self {
96        Self(uuid)
97    }
98}
99impl Debug for EntityUuid {
100    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101        (self.0).fmt(f)
102    }
103}
104
105/// The position of the entity right now.
106///
107/// If this is being used as an ECS component then you are free to modify it,
108/// because there are systems that will update the indexes automatically.
109///
110/// Its value is set to a default of [`Vec3::ZERO`] when it receives the login
111/// packet, its true position may be set ticks later.
112#[cfg_attr(feature = "bevy_ecs", derive(bevy_ecs::component::Component))]
113#[derive(Clone, Copy, Debug, Default, Deref, DerefMut, PartialEq)]
114pub struct Position(Vec3);
115impl Position {
116    pub fn new(pos: Vec3) -> Self {
117        Self(pos)
118    }
119}
120impl From<&Position> for Vec3 {
121    fn from(value: &Position) -> Self {
122        value.0
123    }
124}
125impl From<Position> for ChunkPos {
126    fn from(value: Position) -> Self {
127        ChunkPos::from(&value.0)
128    }
129}
130impl From<Position> for BlockPos {
131    fn from(value: Position) -> Self {
132        BlockPos::from(&value.0)
133    }
134}
135impl From<&Position> for ChunkPos {
136    fn from(value: &Position) -> Self {
137        ChunkPos::from(value.0)
138    }
139}
140impl From<&Position> for BlockPos {
141    fn from(value: &Position) -> Self {
142        BlockPos::from(value.0)
143    }
144}
145
146/// The direction that an entity is looking, in degrees.
147///
148/// To avoid flagging anticheats, consider using [`Self::update`] when updating
149/// the values of this struct.
150#[cfg_attr(feature = "bevy_ecs", derive(bevy_ecs::component::Component))]
151#[derive(AzBuf, Clone, Copy, Debug, Default, PartialEq)]
152pub struct LookDirection {
153    /// Left and right. AKA yaw. In degrees.
154    y_rot: f32,
155    /// Up and down. AKA pitch. In degrees.
156    x_rot: f32,
157}
158
159impl LookDirection {
160    /// Create a new look direction and clamp the `x_rot` to the allowed values.
161    pub fn new(y_rot: f32, x_rot: f32) -> Self {
162        Self { y_rot, x_rot }.clamped()
163    }
164    /// Returns yaw (left and right) in degrees.
165    ///
166    /// Minecraft allows this to go outside of ±360°, so it won't necessarily be
167    /// in any range.
168    pub fn y_rot(&self) -> f32 {
169        self.y_rot
170    }
171    /// Returns pitch (up and down) in degrees.
172    ///
173    /// Clamped to ±90°.
174    pub fn x_rot(&self) -> f32 {
175        self.x_rot
176    }
177
178    /// Update this look direction to the new value.
179    ///
180    /// This handles relative rotations correctly and with the default Minecraft
181    /// sensitivity to avoid triggering anticheats.
182    pub fn update(&mut self, new: LookDirection) {
183        self.update_with_sensitivity(new, 1.);
184    }
185    /// Update the `y_rot` to the given value, in degrees.
186    ///
187    /// This is a shortcut for [`Self::update`] while keeping the `x_rot` the
188    /// same.
189    pub fn update_y_rot(&mut self, new_y_rot: f32) {
190        self.update(LookDirection {
191            y_rot: new_y_rot,
192            x_rot: self.x_rot,
193        });
194    }
195    /// Update the `x_rot` to the given value, in degrees.
196    ///
197    /// This is a shortcut for [`Self::update`] while keeping the `y_rot` the
198    /// same.
199    pub fn update_x_rot(&mut self, new_x_rot: f32) {
200        self.update(LookDirection {
201            y_rot: self.y_rot,
202            x_rot: new_x_rot,
203        });
204    }
205
206    /// Update this look direction to the new value, using the given
207    /// sensitivity value.
208    ///
209    /// Consider using [`Self::update`] instead, which uses 1.0 as the
210    /// sensitivity (equivalent to 100% sensitivity in Minecraft).
211    pub fn update_with_sensitivity(&mut self, new: LookDirection, sensitivity: f32) {
212        let mut delta_y_rot = new.y_rot.rem_euclid(360.) - self.y_rot.rem_euclid(360.);
213        let delta_x_rot = new.x_rot - self.x_rot;
214
215        if delta_y_rot > 180. {
216            delta_y_rot -= 360.;
217        } else if delta_y_rot < -180. {
218            delta_y_rot += 360.;
219        }
220
221        let sensitivity = sensitivity * 0.15;
222        let delta_y_rot = (delta_y_rot / sensitivity).round() * sensitivity;
223        let delta_x_rot = (delta_x_rot / sensitivity).round() * sensitivity;
224
225        self.y_rot += delta_y_rot;
226        self.x_rot += delta_x_rot;
227    }
228
229    /// Force the [`Self::x_rot`] to be between -90 and 90 degrees, and return
230    /// the new look direction.
231    #[must_use]
232    pub fn clamped(mut self) -> Self {
233        self.x_rot = self.x_rot.clamp(-90., 90.);
234        self
235    }
236}
237
238impl From<LookDirection> for (f32, f32) {
239    fn from(value: LookDirection) -> Self {
240        (value.y_rot, value.x_rot)
241    }
242}
243impl From<(f32, f32)> for LookDirection {
244    fn from((y_rot, x_rot): (f32, f32)) -> Self {
245        Self { y_rot, x_rot }
246    }
247}
248
249impl Hash for LookDirection {
250    fn hash<H: Hasher>(&self, state: &mut H) {
251        self.y_rot.to_bits().hash(state);
252        self.x_rot.to_bits().hash(state);
253    }
254}
255impl Eq for LookDirection {}
256
257/// The physics data relating to the entity, such as position, velocity, and
258/// bounding box.
259#[cfg_attr(feature = "bevy_ecs", derive(bevy_ecs::component::Component))]
260#[derive(Clone, Debug, Default)]
261pub struct Physics {
262    /// How fast the entity is moving. Sometimes referred to as the delta
263    /// movement.
264    ///
265    /// Note that our Y velocity will be approximately -0.0784 when we're on the
266    /// ground due to how Minecraft applies gravity.
267    pub velocity: Vec3,
268    pub vec_delta_codec: VecDeltaCodec,
269
270    /// The position of the entity before it moved this tick.
271    ///
272    /// This is set immediately before physics is done.
273    pub old_position: Vec3,
274
275    /// The acceleration here is the force that will be attempted to be added to
276    /// the entity's velocity next tick.
277    ///
278    /// You should typically not set this yourself, since it's controlled by how
279    /// the entity is trying to move.
280    pub x_acceleration: f32,
281    pub y_acceleration: f32,
282    pub z_acceleration: f32,
283
284    on_ground: bool,
285    last_on_ground: bool,
286
287    /// The number of ticks until we jump again, if the jump key is being held.
288    ///
289    /// This must be 0 for us to be able to jump. Sets to 10 when we do a jump
290    /// and sets to 0 if we're not trying to jump.
291    pub no_jump_delay: u32,
292
293    /// The bounding box of the entity.
294    ///
295    /// This is more than just width and height, unlike dimensions.
296    pub bounding_box: Aabb,
297
298    pub has_impulse: bool,
299
300    pub horizontal_collision: bool,
301    // TODO: implement minor_horizontal_collision
302    pub minor_horizontal_collision: bool,
303    pub vertical_collision: bool,
304
305    pub water_fluid_height: f64,
306    pub lava_fluid_height: f64,
307    pub was_touching_water: bool,
308
309    pub fall_distance: f64,
310    // TODO: implement remaining_fire_ticks
311    pub remaining_fire_ticks: i32,
312}
313
314impl Physics {
315    pub fn new(dimensions: &EntityDimensions, pos: Vec3) -> Self {
316        Self {
317            velocity: Vec3::ZERO,
318            vec_delta_codec: VecDeltaCodec::new(pos),
319
320            old_position: pos,
321
322            x_acceleration: 0.,
323            y_acceleration: 0.,
324            z_acceleration: 0.,
325
326            on_ground: false,
327            last_on_ground: false,
328
329            no_jump_delay: 0,
330
331            bounding_box: dimensions.make_bounding_box(pos),
332
333            has_impulse: false,
334
335            horizontal_collision: false,
336            minor_horizontal_collision: false,
337            vertical_collision: false,
338
339            water_fluid_height: 0.,
340            lava_fluid_height: 0.,
341            was_touching_water: false,
342
343            fall_distance: 0.,
344            remaining_fire_ticks: 0,
345        }
346    }
347
348    pub fn on_ground(&self) -> bool {
349        self.on_ground
350    }
351    /// Updates [`Self::on_ground`] and [`Self::last_on_ground`].
352    pub fn set_on_ground(&mut self, on_ground: bool) {
353        self.last_on_ground = self.on_ground;
354        self.on_ground = on_ground;
355    }
356
357    /// The last value of the on_ground value.
358    ///
359    /// This is used by Azalea internally for physics, it might not work as you
360    /// expect since it can be influenced by packets sent by the server.
361    pub fn last_on_ground(&self) -> bool {
362        self.last_on_ground
363    }
364    pub fn set_last_on_ground(&mut self, last_on_ground: bool) {
365        self.last_on_ground = last_on_ground;
366    }
367
368    pub fn reset_fall_distance(&mut self) {
369        self.fall_distance = 0.;
370    }
371    pub fn clear_fire(&mut self) {
372        self.remaining_fire_ticks = 0;
373    }
374
375    pub fn is_in_water(&self) -> bool {
376        self.was_touching_water
377    }
378    pub fn is_in_lava(&self) -> bool {
379        // TODO: also check `!this.firstTick &&`
380        self.lava_fluid_height > 0.
381    }
382
383    pub fn set_old_pos(&mut self, pos: Position) {
384        self.old_position = *pos;
385    }
386}
387
388impl Attributes {
389    pub fn new(_entity_kind: EntityKind) -> Self {
390        // TODO: do the correct defaults for everything, some
391        // entities have different defaults
392        Attributes {
393            movement_speed: AttributeInstance::new(0.1f32 as f64),
394            sneaking_speed: AttributeInstance::new(0.3),
395            attack_speed: AttributeInstance::new(4.0),
396            water_movement_efficiency: AttributeInstance::new(0.0),
397            mining_efficiency: AttributeInstance::new(0.0),
398            block_interaction_range: AttributeInstance::new(4.5),
399            entity_interaction_range: AttributeInstance::new(3.0),
400            step_height: AttributeInstance::new(0.6),
401        }
402    }
403}
404
405/// The abilities that a player has, such as flying or being able to instantly
406/// break blocks.
407///
408/// This should only be present on local players.
409#[cfg_attr(feature = "bevy_ecs", derive(bevy_ecs::component::Component))]
410#[derive(Clone, Debug, Default)]
411pub struct PlayerAbilities {
412    pub invulnerable: bool,
413    pub flying: bool,
414    pub can_fly: bool,
415    /// Whether the player can instantly break blocks and can duplicate blocks
416    /// in their inventory.
417    pub instant_break: bool,
418
419    pub flying_speed: f32,
420    /// Used for the fov
421    pub walking_speed: f32,
422}
423
424/// The type of fluid that is at an entity's eye position, while also accounting
425/// for fluid height.
426#[cfg_attr(feature = "bevy_ecs", derive(bevy_ecs::component::Component))]
427#[derive(Clone, Copy, Debug, Deref, DerefMut, PartialEq)]
428pub struct FluidOnEyes(FluidKind);