Skip to main content

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