Skip to main content

steel_protocol/packets/game/
c_entity_position_sync.rs

1//! Clientbound entity position sync packet - authoritative entity position update.
2
3use std::io::{self, Write};
4
5use glam::DVec3;
6use steel_macros::{ClientPacket, WriteTo};
7use steel_registry::packets::play::C_ENTITY_POSITION_SYNC;
8use steel_utils::codec::VarInt;
9use steel_utils::serial::{PrefixedWrite, WriteTo};
10
11/// One position along a stepped movement path.
12#[derive(Clone, Copy, Debug, PartialEq, WriteTo)]
13pub struct PositionStep {
14    /// Position reached at this step.
15    pub position: DVec3,
16    /// Ticks elapsed since the previous step.
17    #[write(as = VarInt)]
18    pub tick_offset: i32,
19}
20
21/// The path an entity took to reach its current position.
22#[derive(Clone, Debug, PartialEq)]
23pub enum PositionPath {
24    /// The entity moved straight to a single position.
25    Linear(DVec3),
26    /// The entity moved through intermediate positions the client replays in order.
27    Stepped(Vec<PositionStep>),
28}
29
30impl PositionPath {
31    /// Returns the position the entity ends up at.
32    #[must_use]
33    pub fn end_position(&self) -> Option<DVec3> {
34        match self {
35            Self::Linear(position) => Some(*position),
36            Self::Stepped(steps) => steps.last().map(|step| step.position),
37        }
38    }
39}
40
41impl WriteTo for PositionPath {
42    fn write(&self, writer: &mut impl Write) -> io::Result<()> {
43        match self {
44            Self::Linear(position) => {
45                VarInt(0).write(writer)?;
46                position.write(writer)
47            }
48            Self::Stepped(steps) => {
49                VarInt(1).write(writer)?;
50                steps.write_prefixed::<VarInt>(writer)
51            }
52        }
53    }
54}
55
56/// Synchronizes an entity's absolute position and rotation.
57#[derive(ClientPacket, WriteTo, Clone, Debug)]
58#[packet_id(Play = C_ENTITY_POSITION_SYNC)]
59pub struct CEntityPositionSync {
60    /// The entity being synchronized.
61    #[write(as = VarInt)]
62    pub entity_id: i32,
63    /// The path the entity took to its current position.
64    pub path: PositionPath,
65    /// Rotation around the Y axis, in degrees.
66    pub y_rot: f32,
67    /// Rotation around the X axis, in degrees.
68    pub x_rot: f32,
69    /// Whether the entity is on the ground.
70    pub on_ground: bool,
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    fn encode(packet: &CEntityPositionSync) -> Vec<u8> {
78        let mut bytes = Vec::new();
79        packet.write(&mut bytes).expect("packet should encode");
80        bytes
81    }
82
83    #[test]
84    fn linear_path_writes_type_then_position() {
85        let bytes = encode(&CEntityPositionSync {
86            entity_id: 12,
87            path: PositionPath::Linear(DVec3::new(1.0, 2.0, 3.0)),
88            y_rot: 0.0,
89            x_rot: 0.0,
90            on_ground: true,
91        });
92
93        let mut expected = vec![12, 0];
94        for value in [1.0f64, 2.0, 3.0] {
95            expected.extend_from_slice(&value.to_be_bytes());
96        }
97        expected.extend_from_slice(&0.0f32.to_be_bytes());
98        expected.extend_from_slice(&0.0f32.to_be_bytes());
99        expected.push(1);
100
101        assert_eq!(bytes, expected);
102    }
103
104    #[test]
105    fn stepped_path_writes_prefixed_steps() {
106        let bytes = encode(&CEntityPositionSync {
107            entity_id: 12,
108            path: PositionPath::Stepped(vec![PositionStep {
109                position: DVec3::new(1.0, 2.0, 3.0),
110                tick_offset: 2,
111            }]),
112            y_rot: 0.0,
113            x_rot: 0.0,
114            on_ground: false,
115        });
116
117        let mut expected = vec![12, 1, 1];
118        for value in [1.0f64, 2.0, 3.0] {
119            expected.extend_from_slice(&value.to_be_bytes());
120        }
121        expected.push(2);
122        expected.extend_from_slice(&0.0f32.to_be_bytes());
123        expected.extend_from_slice(&0.0f32.to_be_bytes());
124        expected.push(0);
125
126        assert_eq!(bytes, expected);
127    }
128}