Skip to main content

steel_protocol/packets/game/
c_player_look_at.rs

1use glam::DVec3;
2use steel_macros::{ClientPacket, WriteTo};
3use steel_registry::packets::play::C_PLAYER_LOOK_AT;
4
5/// Entity position used as one endpoint of a player look-at operation.
6#[derive(Clone, Copy, Debug, PartialEq, Eq, WriteTo)]
7#[write(as = VarInt)]
8pub enum LookAtAnchor {
9    /// The entity's base position.
10    Feet = 0,
11    /// The entity's eye position.
12    Eyes = 1,
13}
14
15#[derive(Clone, Debug, WriteTo)]
16struct LookAtEntity {
17    #[write(as = VarInt)]
18    entity_id: i32,
19    anchor: LookAtAnchor,
20}
21
22/// Rotates the receiving player toward a position or tracked entity.
23#[derive(ClientPacket, Clone, Debug, WriteTo)]
24#[packet_id(Play = C_PLAYER_LOOK_AT)]
25pub struct CPlayerLookAt {
26    from_anchor: LookAtAnchor,
27    fallback_position: DVec3,
28    target: Option<LookAtEntity>,
29}
30
31impl CPlayerLookAt {
32    #[must_use]
33    pub const fn position(from_anchor: LookAtAnchor, position: DVec3) -> Self {
34        Self {
35            from_anchor,
36            fallback_position: position,
37            target: None,
38        }
39    }
40
41    #[must_use]
42    pub const fn entity(
43        from_anchor: LookAtAnchor,
44        fallback_position: DVec3,
45        entity_id: i32,
46        target_anchor: LookAtAnchor,
47    ) -> Self {
48        Self {
49            from_anchor,
50            fallback_position,
51            target: Some(LookAtEntity {
52                entity_id,
53                anchor: target_anchor,
54            }),
55        }
56    }
57}
58
59#[cfg(test)]
60mod tests {
61    use glam::DVec3;
62    use steel_utils::serial::WriteTo as _;
63
64    use super::{CPlayerLookAt, LookAtAnchor};
65
66    #[test]
67    fn position_target_encodes_without_entity_suffix() {
68        let packet = CPlayerLookAt::position(LookAtAnchor::Eyes, DVec3::new(1.0, 2.0, 3.0));
69        let mut encoded = Vec::new();
70        assert!(packet.write(&mut encoded).is_ok());
71
72        assert_eq!(encoded.len(), 26);
73        assert_eq!(encoded[0], 1);
74        assert_eq!(encoded[25], 0);
75    }
76
77    #[test]
78    fn entity_target_encodes_id_and_anchor_after_presence_flag() {
79        let packet = CPlayerLookAt::entity(
80            LookAtAnchor::Feet,
81            DVec3::new(1.0, 2.0, 3.0),
82            42,
83            LookAtAnchor::Eyes,
84        );
85        let mut encoded = Vec::new();
86        assert!(packet.write(&mut encoded).is_ok());
87
88        assert_eq!(encoded.len(), 28);
89        assert_eq!(encoded[0], 0);
90        assert_eq!(&encoded[25..], &[1, 42, 1]);
91    }
92}