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