Skip to main content

ghast_protocol/
packet.rs

1//! Packet traits, raw packets and the per-id interest filter.
2
3use bytes::{Bytes, BytesMut};
4
5use crate::codec::{Decode, DecodeResult, Encode, EncodeResult, McWrite, decode_exact};
6use crate::error::DecodeError;
7
8/// Connection state.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10pub enum State {
11    /// The first packet of every connection.
12    Handshaking,
13    /// Server list ping.
14    Status,
15    /// Login (encryption, compression, login success).
16    Login,
17    /// In game.
18    Play,
19}
20
21/// Packet direction.
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
23pub enum Direction {
24    /// Server to client.
25    Clientbound,
26    /// Client to server.
27    Serverbound,
28}
29
30/// A typed packet of one version, state and direction.
31pub trait Packet: Encode + Decode {
32    /// Packet id in its state and direction.
33    const ID: i32;
34    /// minecraft-data name (e.g. `"map_chunk"`).
35    const NAME: &'static str;
36    /// Connection state the packet belongs to.
37    const STATE: State;
38    /// Direction of the packet.
39    const DIRECTION: Direction;
40}
41
42/// All packets of one version, state and direction as an enum (generated).
43pub trait PacketSet: Sized {
44    /// Connection state of this set.
45    const STATE: State;
46    /// Direction of this set.
47    const DIRECTION: Direction;
48
49    /// Decodes a raw packet into the matching variant (strict: no trailing bytes).
50    fn decode_raw(raw: &RawPacket) -> DecodeResult<Self>;
51    /// Packet id of this value.
52    fn id(&self) -> i32;
53    /// minecraft-data name of this value.
54    fn name(&self) -> &'static str;
55    /// Encodes the packet body (without the id).
56    fn encode_body(&self, w: &mut BytesMut) -> EncodeResult;
57    /// minecraft-data name for a packet id, if the id is known.
58    fn name_of(id: i32) -> Option<&'static str>;
59}
60
61/// A packet whose body has not been decoded yet.
62///
63/// The body is a zero-copy slice of the received (and, if needed, inflated) frame.
64#[derive(Debug, Clone, PartialEq, Eq)]
65pub struct RawPacket {
66    /// Packet id.
67    pub id: i32,
68    /// Packet body after the id.
69    pub body: Bytes,
70}
71
72impl RawPacket {
73    /// Wraps an id and body.
74    #[inline]
75    pub fn new(id: i32, body: Bytes) -> Self {
76        RawPacket { id, body }
77    }
78
79    /// Decodes the body as packet type `P`; checks the id and that the whole body is used.
80    #[inline]
81    pub fn decode_as<P: Packet>(&self) -> DecodeResult<P> {
82        if self.id != P::ID {
83            return Err(DecodeError::IdMismatch {
84                expected: P::ID,
85                actual: self.id,
86            });
87        }
88        decode_exact(&self.body)
89    }
90
91    /// Encodes a typed packet into a raw packet.
92    pub fn from_packet<P: Packet>(p: &P) -> Result<Self, crate::error::EncodeError> {
93        let mut w = BytesMut::new();
94        p.encode(&mut w)?;
95        Ok(RawPacket::new(P::ID, w.freeze()))
96    }
97}
98
99/// Writes `id` + body of a typed packet (the uncompressed, unframed packet data).
100#[inline]
101pub fn write_packet<P: Packet>(p: &P, w: &mut BytesMut) -> EncodeResult {
102    w.put_varint(P::ID);
103    p.encode(w)
104}
105
106/// Set of packet ids a client wants to receive.
107///
108/// The frame decoder drops packets whose id is not in the filter *before* decoding them
109/// and — when compression is on — after inflating only the few bytes that hold the id.
110/// Ids above 255 are never used by the supported versions and are always accepted.
111#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
112pub struct PacketFilter {
113    bits: [u64; 4],
114}
115
116impl Default for PacketFilter {
117    fn default() -> Self {
118        Self::all()
119    }
120}
121
122impl PacketFilter {
123    /// Accepts every packet.
124    pub const fn all() -> Self {
125        PacketFilter {
126            bits: [u64::MAX; 4],
127        }
128    }
129
130    /// Accepts no packet (ids above 255 excepted).
131    pub const fn none() -> Self {
132        PacketFilter { bits: [0; 4] }
133    }
134
135    /// Returns the filter with `id` accepted.
136    #[must_use]
137    pub const fn with(mut self, id: i32) -> Self {
138        if id >= 0 && id < 256 {
139            self.bits[(id as usize) >> 6] |= 1 << (id & 63);
140        }
141        self
142    }
143
144    /// Returns the filter with `id` dropped.
145    #[must_use]
146    pub const fn without(mut self, id: i32) -> Self {
147        if id >= 0 && id < 256 {
148            self.bits[(id as usize) >> 6] &= !(1 << (id & 63));
149        }
150        self
151    }
152
153    /// Accepts `id`.
154    pub fn insert(&mut self, id: i32) {
155        *self = self.with(id);
156    }
157
158    /// Drops `id`.
159    pub fn remove(&mut self, id: i32) {
160        *self = self.without(id);
161    }
162
163    /// Whether packets with this id are wanted.
164    #[inline]
165    pub const fn contains(&self, id: i32) -> bool {
166        if id < 0 || id >= 256 {
167            return true;
168        }
169        self.bits[(id as usize) >> 6] & (1 << (id & 63)) != 0
170    }
171
172    /// Whether every id is accepted (lets the decoder skip the peek).
173    #[inline]
174    pub const fn is_all(&self) -> bool {
175        self.bits[0] == u64::MAX
176            && self.bits[1] == u64::MAX
177            && self.bits[2] == u64::MAX
178            && self.bits[3] == u64::MAX
179    }
180}
181
182/// A protocol version: its number and the packet sets of every state and direction.
183///
184/// Implemented by the generated marker types (e.g. [`crate::v1_8::V1_8`]). A
185/// version-agnostic layer can be written generically over `V: Version` and map each
186/// version's packet enums into common events.
187pub trait Version: Copy + Send + Sync + 'static {
188    /// Protocol number sent in the handshake (47 for 1.8.x, 754 for 1.16.5).
189    const PROTOCOL: i32;
190    /// Human-readable version name (e.g. `"1.8.9"`).
191    const NAME: &'static str;
192    /// Bit layout of packed block positions.
193    const POSITION_LAYOUT: crate::types::PositionLayout;
194
195    /// Serverbound handshaking packets.
196    type HandshakingServerbound: PacketSet;
197    /// Clientbound status packets.
198    type StatusClientbound: PacketSet;
199    /// Serverbound status packets.
200    type StatusServerbound: PacketSet;
201    /// Clientbound login packets.
202    type LoginClientbound: PacketSet;
203    /// Serverbound login packets.
204    type LoginServerbound: PacketSet;
205    /// Clientbound play packets.
206    type PlayClientbound: PacketSet;
207    /// Serverbound play packets.
208    type PlayServerbound: PacketSet;
209}
210
211#[cfg(test)]
212mod tests {
213    use super::*;
214
215    #[test]
216    fn filter() {
217        let f = PacketFilter::none().with(0x21).with(200);
218        assert!(f.contains(0x21) && f.contains(200) && !f.contains(0x22));
219        assert!(f.contains(300) && f.contains(-1));
220        assert!(PacketFilter::all().is_all() && !f.is_all());
221        assert!(!PacketFilter::all().without(5).contains(5));
222    }
223}