chunkedge_protocol/
lib.rs

1#![doc = include_str!("../README.md")]
2#![allow(deprecated)] // TODO: update aes library
3
4/// Used only by macros. Not public API.
5#[doc(hidden)]
6pub mod __private {
7    pub use anyhow::{anyhow, bail, ensure, Context, Result};
8
9    pub use crate::Packet;
10}
11
12extern crate self as chunkedge_protocol;
13
14mod biome_pos;
15pub mod block_pos;
16pub mod chunk_pos;
17pub mod chunk_section_pos;
18pub mod decode;
19mod difficulty;
20mod direction;
21pub mod encode;
22pub mod game_mode;
23mod global_pos;
24mod hand;
25pub mod movement_flags;
26pub mod packets;
27pub mod profile;
28pub mod sound;
29mod velocity;
30
31use std::io::Write;
32
33use anyhow::Context;
34pub use biome_pos::BiomePos;
35pub use block::{BlockKind, BlockState};
36pub use block_pos::BlockPos;
37pub use chunk_pos::ChunkPos;
38pub use chunk_section_pos::ChunkSectionPos;
39pub use chunkedge_binary::array::FixedArray;
40pub use chunkedge_binary::bit_set::{FixedBitSet, VariableBitSet};
41pub use chunkedge_binary::byte_angle::ByteAngle;
42use chunkedge_binary::Encode;
43pub use chunkedge_binary::{
44    IDSet, IdOr, IntoTextComponent, TextComponent, VarInt, VarIntDecodeError, VarLong,
45};
46pub use chunkedge_generated::registry_id::RegistryId;
47pub use chunkedge_generated::{block, packet_id, status_effects};
48pub use chunkedge_ident::Ident;
49pub use chunkedge_item::{ItemKind, ItemStack};
50use chunkedge_protocol_macros::Packet;
51pub use decode::PacketDecoder;
52use derive_more::{From, Into};
53pub use difficulty::Difficulty;
54pub use direction::Direction;
55pub use encode::{PacketEncoder, WritePacket};
56pub use game_mode::GameMode;
57pub use global_pos::GlobalPos;
58pub use hand::Hand;
59pub use ident::ident;
60pub use packets::play::level_particles_s2c::Particle;
61use serde::{Deserialize, Serialize};
62pub use sound::Sound;
63pub use text::{JsonText, Text};
64pub use velocity::Velocity;
65pub use {
66    anyhow, bytes, chunkedge_ident as ident, chunkedge_math as math, chunkedge_nbt as nbt,
67    chunkedge_text as text, uuid,
68};
69
70/// The maximum number of bytes in a single Minecraft packet.
71pub const MAX_PACKET_SIZE: i32 = 2_i32.pow(21) - 1; // (the maximum that can be sent in a 3-byte VarInt)
72
73/// The Minecraft protocol version this library currently targets.
74pub const PROTOCOL_VERSION: i32 = 770;
75
76/// The stringified name of the Minecraft version this library currently
77/// targets.
78pub const MINECRAFT_VERSION: &str = "1.21.5";
79
80/// How large a packet should be before it is compressed by the packet encoder.
81///
82/// If the inner value is >= 0, then packets with encoded lengths >= to this
83/// value will be compressed. If the value is negative, then compression is
84/// disabled and no packets are compressed.
85#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, From, Into)]
86pub struct CompressionThreshold(pub i32);
87
88impl CompressionThreshold {
89    /// No compression.
90    pub const DEFAULT: Self = Self(-1);
91}
92
93/// No compression.
94impl Default for CompressionThreshold {
95    fn default() -> Self {
96        Self::DEFAULT
97    }
98}
99
100/// Types considered to be Minecraft packets.
101///
102/// In serialized form, a packet begins with a [`VarInt`] packet ID followed by
103/// the body of the packet. If present, the implementations of [`Encode`] and
104/// [`chunkedge_binary::Decode`] on `Self` are expected to only encode/decode the
105/// _body_ of this packet without the leading ID.
106pub trait Packet: std::fmt::Debug {
107    /// The leading `VarInt` ID of this packet.
108    const ID: i32;
109    /// The name of this packet for debugging purposes.
110    const NAME: &'static str;
111    /// The side this packet is intended for.
112    const SIDE: PacketSide;
113    /// The state in which this packet is used.
114    const STATE: PacketState;
115
116    /// Encodes this packet's `VarInt` ID first, followed by the packet's body.
117    fn encode_with_id(&self, mut w: impl Write) -> anyhow::Result<()>
118    where
119        Self: Encode,
120    {
121        VarInt(Self::ID)
122            .encode(&mut w)
123            .context("failed to encode packet ID")?;
124
125        self.encode(w)
126    }
127}
128
129/// The side a packet is intended for.
130#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
131pub enum PacketSide {
132    /// Server -> Client
133    Clientbound,
134    /// Client -> Server
135    Serverbound,
136}
137
138/// The state in  which a packet is used.
139#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
140pub enum PacketState {
141    Handshake,
142    Status,
143    Login,
144    Configuration,
145    Play,
146}
147
148#[allow(dead_code)]
149#[cfg(test)]
150mod tests {
151    use std::borrow::Cow;
152
153    use bytes::BytesMut;
154    // use crate::{Packet, PacketSide};
155    use chunkedge_binary::{Decode, Encode, VarInt, VarLong};
156    use chunkedge_item::{ItemKind, ItemStack};
157    use chunkedge_protocol_macros::Packet;
158
159    use super::*;
160    use crate::block_pos::BlockPos;
161    use crate::decode::PacketDecoder;
162    use crate::encode::PacketEncoder;
163    use crate::hand::Hand;
164    use crate::text::{IntoText, Text};
165    use crate::Ident;
166
167    #[derive(Encode, Decode, Packet, Debug)]
168    #[packet(id = 1, side = PacketSide::Clientbound)]
169    struct RegularStruct {
170        foo: i32,
171        bar: bool,
172        baz: f64,
173    }
174
175    #[derive(Encode, Decode, Packet, Debug)]
176    #[packet(id = 2, side = PacketSide::Clientbound)]
177    struct UnitStruct;
178
179    #[derive(Encode, Decode, Packet, Debug)]
180    #[packet(id = 3, side = PacketSide::Clientbound)]
181    struct EmptyStruct;
182
183    #[derive(Encode, Decode, Packet, Debug)]
184    #[packet(id = 4, side = PacketSide::Clientbound)]
185    struct TupleStruct(i32, bool, f64);
186
187    #[derive(Encode, Decode, Packet, Debug)]
188    #[packet(id = 5, side = PacketSide::Clientbound)]
189    struct StructWithGenerics<'z, T = ()> {
190        foo: &'z str,
191        bar: T,
192    }
193
194    #[derive(Encode, Decode, Packet, Debug)]
195    #[packet(id = 6, side = PacketSide::Clientbound)]
196    struct TupleStructWithGenerics<'z, T = ()>(&'z str, i32, T);
197
198    #[allow(unconditional_recursion, clippy::extra_unused_type_parameters)]
199    fn assert_has_impls<'a, T>()
200    where
201        T: Encode + Decode<'a> + Packet,
202    {
203        assert_has_impls::<RegularStruct>();
204        assert_has_impls::<UnitStruct>();
205        assert_has_impls::<EmptyStruct>();
206        assert_has_impls::<TupleStruct>();
207        assert_has_impls::<StructWithGenerics>();
208        assert_has_impls::<TupleStructWithGenerics>();
209    }
210
211    #[test]
212    fn packet_name() {
213        assert_eq!(RegularStruct::NAME, "RegularStruct");
214        assert_eq!(UnitStruct::NAME, "UnitStruct");
215        assert_eq!(StructWithGenerics::<()>::NAME, "StructWithGenerics");
216    }
217
218    #[cfg(feature = "encryption")]
219    const CRYPT_KEY: [u8; 16] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
220
221    #[derive(PartialEq, Debug, Encode, Decode, Packet)]
222    #[packet(id = 42, side = PacketSide::Clientbound)]
223    struct TestPacket<'a> {
224        a: bool,
225        b: u8,
226        c: i32,
227        d: f32,
228        e: f64,
229        f: BlockPos,
230        g: Hand,
231        h: Ident<Cow<'a, str>>,
232        i: ItemStack,
233        j: Text,
234        k: VarInt,
235        l: VarLong,
236        m: &'a str,
237        n: &'a [u8; 10],
238        o: [u128; 3],
239    }
240
241    impl<'a> TestPacket<'a> {
242        fn new(string: &'a str) -> Self {
243            Self {
244                a: true,
245                b: 12,
246                c: -999,
247                d: 5.001,
248                e: 1e10,
249                f: BlockPos::new(1, 2, 3),
250                g: Hand::Off,
251                h: Ident::new("minecraft:whatever").unwrap(),
252                i: ItemStack::new(ItemKind::WoodenSword, 12),
253                j: "my ".into_text() + "fancy".italic() + " text",
254                k: VarInt(123),
255                l: VarLong(456),
256                m: string,
257                n: &[7; 10],
258                o: [123456789; 3],
259            }
260        }
261    }
262
263    fn check_test_packet(dec: &mut PacketDecoder, string: &str) {
264        let frame = dec.try_next_packet().unwrap().unwrap();
265
266        let pkt = frame.decode::<TestPacket>().unwrap();
267
268        assert_eq!(&pkt, &TestPacket::new(string));
269    }
270
271    #[test]
272    fn packets_round_trip() {
273        let mut buf = BytesMut::new();
274
275        let mut enc = PacketEncoder::new();
276
277        enc.append_packet(&TestPacket::new("first")).unwrap();
278        #[cfg(feature = "compression")]
279        enc.set_compression(0.into());
280        enc.append_packet(&TestPacket::new("second")).unwrap();
281        buf.unsplit(enc.take());
282        #[cfg(feature = "encryption")]
283        enc.enable_encryption(&CRYPT_KEY);
284        enc.append_packet(&TestPacket::new("third")).unwrap();
285        enc.prepend_packet(&TestPacket::new("fourth")).unwrap();
286
287        buf.unsplit(enc.take());
288
289        let mut dec = PacketDecoder::new();
290
291        dec.queue_bytes(buf);
292
293        check_test_packet(&mut dec, "first");
294
295        #[cfg(feature = "compression")]
296        dec.set_compression(0.into());
297
298        check_test_packet(&mut dec, "second");
299
300        #[cfg(feature = "encryption")]
301        dec.enable_encryption(&CRYPT_KEY);
302
303        check_test_packet(&mut dec, "fourth");
304        check_test_packet(&mut dec, "third");
305    }
306}