steel_protocol/packets/game/
c_entity_position_sync.rs1use 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#[derive(Clone, Copy, Debug, PartialEq, WriteTo)]
13pub struct PositionStep {
14 pub position: DVec3,
16 #[write(as = VarInt)]
18 pub tick_offset: i32,
19}
20
21#[derive(Clone, Debug, PartialEq)]
23pub enum PositionPath {
24 Linear(DVec3),
26 Stepped(Vec<PositionStep>),
28}
29
30impl PositionPath {
31 #[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#[derive(ClientPacket, WriteTo, Clone, Debug)]
58#[packet_id(Play = C_ENTITY_POSITION_SYNC)]
59pub struct CEntityPositionSync {
60 #[write(as = VarInt)]
62 pub entity_id: i32,
63 pub path: PositionPath,
65 pub y_rot: f32,
67 pub x_rot: f32,
69 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}