azalea_protocol/common/
movements.rs

1use std::{
2    io::{self, Cursor, Write},
3    ops::Add,
4};
5
6use azalea_buf::{AzBuf, AzaleaRead, AzaleaWrite, BufReadError};
7use azalea_core::{bitset::FixedBitSet, math, position::Vec3};
8use azalea_entity::{LookDirection, Physics, Position};
9
10/// The updated position, velocity, and rotations for an entity.
11///
12/// Often, this field comes alongside a [`RelativeMovements`] field, which
13/// specifies which parts of this struct should be treated as relative.
14#[derive(AzBuf, Clone, Debug)]
15pub struct PositionMoveRotation {
16    pub pos: Vec3,
17    /// The updated delta movement (velocity).
18    pub delta: Vec3,
19    pub look_direction: LookDirection,
20}
21
22#[derive(Debug, Clone, Default)]
23pub struct RelativeMovements {
24    pub x: bool,
25    pub y: bool,
26    pub z: bool,
27    pub y_rot: bool,
28    pub x_rot: bool,
29    pub delta_x: bool,
30    pub delta_y: bool,
31    pub delta_z: bool,
32    pub rotate_delta: bool,
33}
34impl RelativeMovements {
35    pub fn all_absolute() -> Self {
36        RelativeMovements::default()
37    }
38    pub fn all_relative() -> Self {
39        RelativeMovements {
40            x: true,
41            y: true,
42            z: true,
43            y_rot: true,
44            x_rot: true,
45            delta_x: true,
46            delta_y: true,
47            delta_z: true,
48            rotate_delta: true,
49        }
50    }
51
52    pub fn apply(
53        &self,
54        change: &PositionMoveRotation,
55        position: &mut Position,
56        direction: &mut LookDirection,
57        physics: &mut Physics,
58    ) {
59        let new_position = Vec3::new(
60            apply_change(position.x, self.x, change.pos.x),
61            apply_change(position.y, self.y, change.pos.y),
62            apply_change(position.z, self.z, change.pos.z),
63        );
64
65        let new_look_direction = LookDirection::new(
66            apply_change(direction.y_rot(), self.y_rot, change.look_direction.y_rot()),
67            apply_change(direction.x_rot(), self.x_rot, change.look_direction.x_rot()),
68        );
69
70        let mut new_delta = physics.velocity;
71        if self.rotate_delta {
72            let y_rot_delta = direction.y_rot() - new_look_direction.y_rot();
73            let x_rot_delta = direction.x_rot() - new_look_direction.x_rot();
74            new_delta = new_delta
75                .x_rot(math::to_radians(x_rot_delta as f64) as f32)
76                .y_rot(math::to_radians(y_rot_delta as f64) as f32);
77        }
78        let new_delta = Vec3::new(
79            apply_change(new_delta.x, self.delta_x, change.delta.x),
80            apply_change(new_delta.y, self.delta_y, change.delta.y),
81            apply_change(new_delta.z, self.delta_z, change.delta.z),
82        );
83
84        **position = new_position;
85        *direction = new_look_direction;
86        physics.velocity = new_delta;
87    }
88}
89
90fn apply_change<T: Add<Output = T>>(base: T, condition: bool, change: T) -> T {
91    if condition { base + change } else { change }
92}
93
94impl AzaleaRead for RelativeMovements {
95    fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
96        // yes minecraft seriously wastes that many bits, smh
97        let set = u32::azalea_read(buf)?;
98        let set = FixedBitSet::<32>::new_with_data(set.swap_bytes().to_be_bytes());
99        Ok(RelativeMovements {
100            x: set.index(0),
101            y: set.index(1),
102            z: set.index(2),
103            y_rot: set.index(3),
104            x_rot: set.index(4),
105            delta_x: set.index(5),
106            delta_y: set.index(6),
107            delta_z: set.index(7),
108            rotate_delta: set.index(8),
109        })
110    }
111}
112
113impl AzaleaWrite for RelativeMovements {
114    fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
115        let mut set = FixedBitSet::<32>::new();
116        let mut set_bit = |index: usize, value: bool| {
117            if value {
118                set.set(index);
119            }
120        };
121
122        set_bit(0, self.x);
123        set_bit(1, self.y);
124        set_bit(2, self.z);
125        set_bit(3, self.y_rot);
126        set_bit(4, self.x_rot);
127        set_bit(5, self.delta_x);
128        set_bit(6, self.delta_y);
129        set_bit(7, self.delta_z);
130        set_bit(8, self.rotate_delta);
131
132        set.azalea_write(buf)
133    }
134}
135
136#[derive(Clone, Copy, Debug, Default, PartialEq)]
137pub struct MoveFlags {
138    pub on_ground: bool,
139    pub horizontal_collision: bool,
140}
141impl AzaleaWrite for MoveFlags {
142    fn azalea_write(&self, buf: &mut impl io::Write) -> Result<(), io::Error> {
143        let mut bitset = FixedBitSet::<8>::new();
144        if self.on_ground {
145            bitset.set(0);
146        }
147        if self.horizontal_collision {
148            bitset.set(1);
149        }
150        bitset.azalea_write(buf)?;
151        Ok(())
152    }
153}
154impl AzaleaRead for MoveFlags {
155    fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
156        let bitset = FixedBitSet::<8>::azalea_read(buf)?;
157        let on_ground = bitset.index(0);
158        let horizontal_collision = bitset.index(1);
159        Ok(Self {
160            on_ground,
161            horizontal_collision,
162        })
163    }
164}