azalea_entity/
lib.rs

1#![allow(clippy::derived_hash_with_manual_eq)]
2
3pub mod attributes;
4mod data;
5mod 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    fmt::Debug,
16    hash::{Hash, Hasher},
17};
18
19pub use attributes::Attributes;
20use azalea_block::{BlockState, fluid_state::FluidKind};
21use azalea_buf::AzBuf;
22use azalea_core::{
23    aabb::AABB,
24    math,
25    position::{BlockPos, ChunkPos, Vec3},
26    resource_location::ResourceLocation,
27};
28use azalea_world::{ChunkStorage, InstanceName};
29use bevy_ecs::{bundle::Bundle, component::Component};
30pub use data::*;
31use derive_more::{Deref, DerefMut};
32pub use dimensions::EntityDimensions;
33use plugin::indexing::EntityChunkPos;
34use uuid::Uuid;
35use vec_delta_codec::VecDeltaCodec;
36
37use self::attributes::AttributeInstance;
38pub use crate::plugin::*;
39
40pub fn move_relative(
41    physics: &mut Physics,
42    direction: &LookDirection,
43    speed: f32,
44    acceleration: &Vec3,
45) {
46    let input_vector = input_vector(direction, speed, acceleration);
47    physics.velocity += input_vector;
48}
49
50pub fn input_vector(direction: &LookDirection, speed: f32, acceleration: &Vec3) -> Vec3 {
51    let distance = acceleration.length_squared();
52    if distance < 1.0E-7 {
53        return Vec3::ZERO;
54    }
55    let acceleration = if distance > 1.0 {
56        acceleration.normalize()
57    } else {
58        *acceleration
59    }
60    .scale(speed as f64);
61    let y_rot = math::sin(direction.y_rot * 0.017453292f32);
62    let x_rot = math::cos(direction.y_rot * 0.017453292f32);
63    Vec3 {
64        x: acceleration.x * (x_rot as f64) - acceleration.z * (y_rot as f64),
65        y: acceleration.y,
66        z: acceleration.z * (x_rot as f64) + acceleration.x * (y_rot as f64),
67    }
68}
69
70pub fn view_vector(look_direction: &LookDirection) -> Vec3 {
71    let x_rot = look_direction.x_rot * 0.017453292;
72    let y_rot = -look_direction.y_rot * 0.017453292;
73    let y_rot_cos = math::cos(y_rot);
74    let y_rot_sin = math::sin(y_rot);
75    let x_rot_cos = math::cos(x_rot);
76    let x_rot_sin = math::sin(x_rot);
77    Vec3 {
78        x: (y_rot_sin * x_rot_cos) as f64,
79        y: (-x_rot_sin) as f64,
80        z: (y_rot_cos * x_rot_cos) as f64,
81    }
82}
83
84/// Get the position of the block below the entity, but a little lower.
85pub fn on_pos_legacy(chunk_storage: &ChunkStorage, position: &Position) -> BlockPos {
86    on_pos(0.2, chunk_storage, position)
87}
88
89// int x = Mth.floor(this.position.x);
90// int y = Mth.floor(this.position.y - (double)var1);
91// int z = Mth.floor(this.position.z);
92// BlockPos var5 = new BlockPos(x, y, z);
93// if (this.level.getBlockState(var5).isAir()) {
94//    BlockPos var6 = var5.below();
95//    BlockState var7 = this.level.getBlockState(var6);
96//    if (var7.is(BlockTags.FENCES) || var7.is(BlockTags.WALLS) ||
97// var7.getBlock() instanceof FenceGateBlock) {       return var6;
98//    }
99// }
100// return var5;
101pub fn on_pos(offset: f32, chunk_storage: &ChunkStorage, pos: &Position) -> BlockPos {
102    let x = pos.x.floor() as i32;
103    let y = (pos.y - offset as f64).floor() as i32;
104    let z = pos.z.floor() as i32;
105    let pos = BlockPos { x, y, z };
106
107    // TODO: check if block below is a fence, wall, or fence gate
108    let block_pos = pos.down(1);
109    let block_state = chunk_storage.get_block_state(&block_pos);
110    if block_state == Some(BlockState::AIR) {
111        let block_pos_below = block_pos.down(1);
112        let block_state_below = chunk_storage.get_block_state(&block_pos_below);
113        if let Some(_block_state_below) = block_state_below {
114            // if block_state_below.is_fence()
115            //     || block_state_below.is_wall()
116            //     || block_state_below.is_fence_gate()
117            // {
118            //     return block_pos_below;
119            // }
120        }
121    }
122
123    pos
124}
125
126/// The Minecraft UUID of the entity. For players, this is their actual player
127/// UUID, and for other entities it's just random.
128#[derive(Component, Deref, DerefMut, Clone, Copy, Default)]
129pub struct EntityUuid(Uuid);
130impl EntityUuid {
131    pub fn new(uuid: Uuid) -> Self {
132        Self(uuid)
133    }
134}
135impl Debug for EntityUuid {
136    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137        (self.0).fmt(f)
138    }
139}
140
141/// The position of the entity right now.
142///
143/// You are free to change the value of this component; there's systems that
144/// update the indexes automatically.
145///
146/// Its value is set to a default of [`Vec3::ZERO`] when it receives the login
147/// packet, its true position may be set ticks later.
148#[derive(Component, Clone, Copy, Debug, Default, PartialEq, Deref, DerefMut)]
149pub struct Position(Vec3);
150impl Position {
151    pub fn new(pos: Vec3) -> Self {
152        Self(pos)
153    }
154}
155impl From<&Position> for Vec3 {
156    fn from(value: &Position) -> Self {
157        value.0
158    }
159}
160impl From<Position> for ChunkPos {
161    fn from(value: Position) -> Self {
162        ChunkPos::from(&value.0)
163    }
164}
165impl From<Position> for BlockPos {
166    fn from(value: Position) -> Self {
167        BlockPos::from(&value.0)
168    }
169}
170impl From<&Position> for ChunkPos {
171    fn from(value: &Position) -> Self {
172        ChunkPos::from(value.0)
173    }
174}
175impl From<&Position> for BlockPos {
176    fn from(value: &Position) -> Self {
177        BlockPos::from(value.0)
178    }
179}
180
181/// The second most recent position of the entity that was sent over the
182/// network. This is currently only updated for our own local player entities.
183#[derive(Component, Clone, Copy, Debug, Default, PartialEq, Deref, DerefMut)]
184pub struct LastSentPosition(Vec3);
185impl From<&LastSentPosition> for Vec3 {
186    fn from(value: &LastSentPosition) -> Self {
187        value.0
188    }
189}
190impl From<LastSentPosition> for ChunkPos {
191    fn from(value: LastSentPosition) -> Self {
192        ChunkPos::from(&value.0)
193    }
194}
195impl From<LastSentPosition> for BlockPos {
196    fn from(value: LastSentPosition) -> Self {
197        BlockPos::from(&value.0)
198    }
199}
200impl From<&LastSentPosition> for ChunkPos {
201    fn from(value: &LastSentPosition) -> Self {
202        ChunkPos::from(value.0)
203    }
204}
205impl From<&LastSentPosition> for BlockPos {
206    fn from(value: &LastSentPosition) -> Self {
207        BlockPos::from(value.0)
208    }
209}
210
211/// A component for entities that can jump.
212///
213/// If this is true, the entity will try to jump every tick. It's equivalent to
214/// the space key being held in vanilla.
215#[derive(Debug, Component, Copy, Clone, Deref, DerefMut, Default, PartialEq, Eq)]
216pub struct Jumping(pub bool);
217
218/// A component that contains the direction an entity is looking.
219#[derive(Debug, Component, Copy, Clone, Default, PartialEq, AzBuf)]
220pub struct LookDirection {
221    /// Left and right. Aka yaw.
222    pub y_rot: f32,
223    /// Up and down. Aka pitch.
224    pub x_rot: f32,
225}
226
227impl LookDirection {
228    pub fn new(y_rot: f32, x_rot: f32) -> Self {
229        Self { y_rot, x_rot }
230    }
231}
232
233impl From<LookDirection> for (f32, f32) {
234    fn from(value: LookDirection) -> Self {
235        (value.y_rot, value.x_rot)
236    }
237}
238impl From<(f32, f32)> for LookDirection {
239    fn from((y_rot, x_rot): (f32, f32)) -> Self {
240        Self { y_rot, x_rot }
241    }
242}
243
244impl Hash for LookDirection {
245    fn hash<H: Hasher>(&self, state: &mut H) {
246        self.y_rot.to_bits().hash(state);
247        self.x_rot.to_bits().hash(state);
248    }
249}
250impl Eq for LookDirection {}
251
252/// The physics data relating to the entity, such as position, velocity, and
253/// bounding box.
254#[derive(Debug, Component, Clone, Default)]
255pub struct Physics {
256    /// How fast the entity is moving.
257    ///
258    /// Sometimes referred to as the delta movement.
259    pub velocity: Vec3,
260    pub vec_delta_codec: VecDeltaCodec,
261
262    /// The position of the entity before it moved this tick.
263    ///
264    /// This is set immediately before physics is done.
265    pub old_position: Vec3,
266
267    /// The acceleration here is the force that will be attempted to be added to
268    /// the entity's velocity next tick.
269    ///
270    /// You should typically not set this yourself, since it's controlled by how
271    /// the entity is trying to move.
272    pub x_acceleration: f32,
273    pub y_acceleration: f32,
274    pub z_acceleration: f32,
275
276    on_ground: bool,
277    last_on_ground: bool,
278
279    /// The number of ticks until we jump again, if the jump key is being held.
280    ///
281    /// This must be 0 for us to be able to jump. Sets to 10 when we do a jump
282    /// and sets to 0 if we're not trying to jump.
283    pub no_jump_delay: u32,
284
285    /// The width and height of the entity.
286    pub dimensions: EntityDimensions,
287    /// The bounding box of the entity. This is more than just width and height,
288    /// unlike dimensions.
289    pub bounding_box: AABB,
290
291    pub has_impulse: bool,
292
293    pub horizontal_collision: bool,
294    // pub minor_horizontal_collision: bool,
295    pub vertical_collision: bool,
296
297    pub water_fluid_height: f64,
298    pub lava_fluid_height: f64,
299    pub was_touching_water: bool,
300
301    // TODO: implement fall_distance
302    pub fall_distance: f32,
303    // TODO: implement remaining_fire_ticks
304    pub remaining_fire_ticks: i32,
305}
306
307impl Physics {
308    pub fn new(dimensions: EntityDimensions, pos: Vec3) -> Self {
309        Self {
310            velocity: Vec3::ZERO,
311            vec_delta_codec: VecDeltaCodec::new(pos),
312
313            old_position: pos,
314
315            x_acceleration: 0.,
316            y_acceleration: 0.,
317            z_acceleration: 0.,
318
319            on_ground: false,
320            last_on_ground: false,
321
322            no_jump_delay: 0,
323
324            bounding_box: dimensions.make_bounding_box(&pos),
325            dimensions,
326
327            has_impulse: false,
328
329            horizontal_collision: false,
330            vertical_collision: false,
331
332            water_fluid_height: 0.,
333            lava_fluid_height: 0.,
334            was_touching_water: false,
335
336            fall_distance: 0.,
337            remaining_fire_ticks: 0,
338        }
339    }
340
341    pub fn on_ground(&self) -> bool {
342        self.on_ground
343    }
344    /// Updates [`Self::on_ground`] and [`Self::last_on_ground`].
345    pub fn set_on_ground(&mut self, on_ground: bool) {
346        self.last_on_ground = self.on_ground;
347        self.on_ground = on_ground;
348    }
349
350    /// The last value of the on_ground value.
351    ///
352    /// This is used by Azalea internally for physics, it might not work as you
353    /// expect since it can be influenced by packets sent by the server.
354    pub fn last_on_ground(&self) -> bool {
355        self.last_on_ground
356    }
357    pub fn set_last_on_ground(&mut self, last_on_ground: bool) {
358        self.last_on_ground = last_on_ground;
359    }
360
361    pub fn reset_fall_distance(&mut self) {
362        self.fall_distance = 0.;
363    }
364    pub fn clear_fire(&mut self) {
365        self.remaining_fire_ticks = 0;
366    }
367
368    pub fn is_in_water(&self) -> bool {
369        self.was_touching_water
370    }
371    pub fn is_in_lava(&self) -> bool {
372        // TODO: also check `!this.firstTick &&`
373        self.lava_fluid_height > 0.
374    }
375
376    pub fn set_old_pos(&mut self, pos: &Position) {
377        self.old_position = **pos;
378    }
379}
380
381/// Marker component for entities that are dead.
382///
383/// "Dead" means that the entity has 0 health.
384#[derive(Component, Copy, Clone, Default)]
385pub struct Dead;
386
387/// A component that contains the offset of the entity's eyes from the entity
388/// coordinates.
389///
390/// This is used to calculate the camera position for players, when spectating
391/// an entity, and when raycasting from the entity.
392#[derive(Component, Clone, Copy, Debug, PartialEq, Deref, DerefMut)]
393pub struct EyeHeight(f32);
394impl EyeHeight {
395    pub fn new(height: f32) -> Self {
396        Self(height)
397    }
398}
399impl From<EyeHeight> for f32 {
400    fn from(value: EyeHeight) -> Self {
401        value.0
402    }
403}
404impl From<EyeHeight> for f64 {
405    fn from(value: EyeHeight) -> Self {
406        value.0 as f64
407    }
408}
409impl From<&EyeHeight> for f32 {
410    fn from(value: &EyeHeight) -> Self {
411        value.0
412    }
413}
414impl From<&EyeHeight> for f64 {
415    fn from(value: &EyeHeight) -> Self {
416        value.0 as f64
417    }
418}
419
420/// A component NewType for [`azalea_registry::EntityKind`].
421///
422/// Most of the time, you should be using `azalea_registry::EntityKind`
423/// directly instead.
424#[derive(Component, Clone, Copy, Debug, PartialEq, Deref)]
425pub struct EntityKind(pub azalea_registry::EntityKind);
426
427/// A bundle of components that every entity has. This doesn't contain metadata,
428/// that has to be added separately.
429#[derive(Bundle)]
430pub struct EntityBundle {
431    pub kind: EntityKind,
432    pub uuid: EntityUuid,
433    pub world_name: InstanceName,
434    pub position: Position,
435    pub last_sent_position: LastSentPosition,
436
437    pub chunk_pos: EntityChunkPos,
438
439    pub physics: Physics,
440    pub direction: LookDirection,
441    pub eye_height: EyeHeight,
442    pub attributes: Attributes,
443    pub jumping: Jumping,
444    pub fluid_on_eyes: FluidOnEyes,
445    pub on_climbable: OnClimbable,
446}
447
448impl EntityBundle {
449    pub fn new(
450        uuid: Uuid,
451        pos: Vec3,
452        kind: azalea_registry::EntityKind,
453        world_name: ResourceLocation,
454    ) -> Self {
455        let dimensions = EntityDimensions::from(kind);
456        let eye_height = dimensions.height * 0.85;
457
458        Self {
459            kind: EntityKind(kind),
460            uuid: EntityUuid(uuid),
461            world_name: InstanceName(world_name),
462            position: Position(pos),
463            chunk_pos: EntityChunkPos(ChunkPos::from(&pos)),
464            last_sent_position: LastSentPosition(pos),
465            physics: Physics::new(dimensions, pos),
466            eye_height: EyeHeight(eye_height),
467            direction: LookDirection::default(),
468
469            attributes: Attributes {
470                // TODO: do the correct defaults for everything, some
471                // entities have different defaults
472                speed: AttributeInstance::new(0.1),
473                attack_speed: AttributeInstance::new(4.0),
474                water_movement_efficiency: AttributeInstance::new(0.0),
475            },
476
477            jumping: Jumping(false),
478            fluid_on_eyes: FluidOnEyes(FluidKind::Empty),
479            on_climbable: OnClimbable(false),
480        }
481    }
482}
483
484/// A marker component that signifies that this entity is "local" and shouldn't
485/// be updated by other clients.
486///
487/// If this is for a client then all of our clients will have this.
488///
489/// This component is not removed from clients when they disconnect.
490#[derive(Component, Clone, Debug, Default)]
491pub struct LocalEntity;
492
493#[derive(Component, Clone, Debug, PartialEq, Deref, DerefMut)]
494pub struct FluidOnEyes(FluidKind);
495
496impl FluidOnEyes {
497    pub fn new(fluid: FluidKind) -> Self {
498        Self(fluid)
499    }
500}
501
502#[derive(Component, Clone, Debug, PartialEq, Deref, DerefMut)]
503pub struct OnClimbable(bool);