Skip to main content

mcproto_types/
basic.rs

1//! Basic Minecraft protocol types and their wire encodings.
2//!
3//! This module includes primitive numeric values, booleans, variable-length
4//! integers, length-prefixed strings, and resource identifiers.
5
6use crate::{EnumRepr, TypeCodec};
7use mcproto_codec::{
8    error::{CodecError, CodecKind, CodecOperation, InvalidEncodingReason},
9    io::{read_exact_counted, write_all_counted},
10    varint::{VarIntRead, VarIntWrite},
11    varlong::{VarLongRead, VarLongWrite},
12};
13use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as _};
14use std::{
15    fmt,
16    io::{Read, Write},
17};
18
19/// A boolean encoded as `0x00` for false or `0x01` for true.
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
21pub struct Boolean(
22    /// The boolean value.
23    pub bool,
24);
25
26impl TypeCodec for Boolean {
27    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
28        let byte = if self.0 { 1u8 } else { 0u8 };
29        write_all_counted(writer, &[byte], CodecKind::Boolean, 0)
30    }
31
32    fn decode(reader: &mut impl Read) -> Result<Self, CodecError>
33    where
34        Self: Sized,
35    {
36        let mut buf = [0u8; 1];
37        read_exact_counted(reader, &mut buf, CodecKind::Boolean, 0)?;
38        match buf[0] {
39            0 => Ok(Boolean(false)),
40            1 => Ok(Boolean(true)),
41            _ => Err(CodecError::invalid_encoding(
42                CodecKind::Boolean,
43                1,
44                InvalidEncodingReason::InvalidBooleanValue { value: buf[0] },
45            )),
46        }
47    }
48}
49
50/// A two's-complement signed 8-bit integer from -128 through 127.
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
52pub struct Byte(
53    /// The integer value.
54    pub i8,
55);
56
57impl TypeCodec for Byte {
58    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
59        write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::Byte, 0)
60    }
61
62    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
63        let mut bytes = [0; 1];
64        read_exact_counted(reader, &mut bytes, CodecKind::Byte, 0)?;
65        Ok(Self(i8::from_be_bytes(bytes)))
66    }
67}
68
69/// An unsigned 8-bit integer from 0 through 255.
70#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
71pub struct UnsignedByte(
72    /// The integer value.
73    pub u8,
74);
75
76impl TypeCodec for UnsignedByte {
77    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
78        write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::UnsignedByte, 0)
79    }
80
81    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
82        let mut bytes = [0; 1];
83        read_exact_counted(reader, &mut bytes, CodecKind::UnsignedByte, 0)?;
84        Ok(Self(u8::from_be_bytes(bytes)))
85    }
86}
87
88/// A two's-complement signed 16-bit integer from -32,768 through 32,767.
89#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
90pub struct Short(
91    /// The integer value.
92    pub i16,
93);
94
95impl TypeCodec for Short {
96    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
97        write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::Short, 0)
98    }
99
100    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
101        let mut bytes = [0; 2];
102        read_exact_counted(reader, &mut bytes, CodecKind::Short, 0)?;
103        Ok(Self(i16::from_be_bytes(bytes)))
104    }
105}
106
107/// An unsigned 16-bit integer from 0 through 65,535.
108#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
109pub struct UnsignedShort(
110    /// The integer value.
111    pub u16,
112);
113
114impl TypeCodec for UnsignedShort {
115    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
116        write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::UnsignedShort, 0)
117    }
118
119    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
120        let mut bytes = [0; 2];
121        read_exact_counted(reader, &mut bytes, CodecKind::UnsignedShort, 0)?;
122        Ok(Self(u16::from_be_bytes(bytes)))
123    }
124}
125
126/// A two's-complement signed 32-bit integer from -2,147,483,648 through
127/// 2,147,483,647.
128#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
129pub struct Int(
130    /// The integer value.
131    pub i32,
132);
133
134impl TypeCodec for Int {
135    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
136        write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::Int, 0)
137    }
138
139    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
140        let mut bytes = [0; 4];
141        read_exact_counted(reader, &mut bytes, CodecKind::Int, 0)?;
142        Ok(Self(i32::from_be_bytes(bytes)))
143    }
144}
145
146/// A two's-complement signed 64-bit integer from -9,223,372,036,854,775,808
147/// through 9,223,372,036,854,775,807.
148#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
149pub struct Long(
150    /// The integer value.
151    pub i64,
152);
153
154impl TypeCodec for Long {
155    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
156        write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::Long, 0)
157    }
158
159    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
160        let mut bytes = [0; 8];
161        read_exact_counted(reader, &mut bytes, CodecKind::Long, 0)?;
162        Ok(Self(i64::from_be_bytes(bytes)))
163    }
164}
165
166/// A big-endian IEEE-754 single-precision floating-point number.
167///
168/// All bit patterns, including infinities and NaN values, are preserved.
169///
170/// # Examples
171///
172/// ```
173/// use mcproto_types::{Float, TypeCodec};
174///
175/// let mut encoded = Vec::new();
176/// Float(1.5).encode(&mut encoded)?;
177/// assert_eq!(encoded, [0x3f, 0xc0, 0x00, 0x00]);
178///
179/// let mut input = encoded.as_slice();
180/// assert_eq!(Float::decode(&mut input)?, Float(1.5));
181/// # Ok::<(), mcproto_codec::error::CodecError>(())
182/// ```
183#[derive(Debug, Clone, Copy, PartialEq, Default)]
184pub struct Float(
185    /// The floating-point value.
186    pub f32,
187);
188
189impl TypeCodec for Float {
190    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
191        write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::Float, 0)
192    }
193
194    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
195        let mut bytes = [0; 4];
196        read_exact_counted(reader, &mut bytes, CodecKind::Float, 0)?;
197        Ok(Self(f32::from_be_bytes(bytes)))
198    }
199}
200
201/// A big-endian IEEE-754 double-precision floating-point number.
202#[derive(Debug, Clone, Copy, PartialEq, Default)]
203pub struct Double(
204    /// The floating-point value.
205    pub f64,
206);
207
208impl TypeCodec for Double {
209    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
210        write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::Double, 0)
211    }
212
213    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
214        let mut bytes = [0; 8];
215        read_exact_counted(reader, &mut bytes, CodecKind::Double, 0)?;
216        Ok(Self(f64::from_be_bytes(bytes)))
217    }
218}
219
220/// A UTF-8 string prefixed by its byte length as a VarInt.
221///
222/// The protocol limits both the UTF-8 payload size and the number of UTF-16
223/// code units. Supplementary [Unicode scalar values] count as two UTF-16
224/// code units. The general protocol limit is 32,767 UTF-16 code units and
225/// three UTF-8 bytes per permitted code unit; a particular field may impose
226/// a lower limit.
227///
228/// [Unicode scalar values]: https://www.unicode.org/glossary/#unicode_scalar_value
229#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
230pub struct PrefixedString(
231    /// The string value.
232    pub String,
233);
234
235impl PrefixedString {
236    /// The maximum number of UTF-16 code units permitted in the string.
237    pub const MAX_UTF16_CODE_UNITS: usize = 0x7fff;
238    /// The maximum size of the UTF-8 payload, excluding its VarInt length prefix.
239    pub const MAX_BYTES: usize = Self::MAX_UTF16_CODE_UNITS * 3;
240
241    fn encode_value(value: &str, writer: &mut impl Write) -> Result<(), CodecError> {
242        encode_prefixed_string(
243            value,
244            writer,
245            CodecKind::String,
246            Self::MAX_BYTES,
247            Self::MAX_UTF16_CODE_UNITS,
248        )
249    }
250
251    fn decode_value(reader: &mut impl Read) -> Result<(String, usize), CodecError> {
252        decode_prefixed_string(
253            reader,
254            CodecKind::String,
255            Self::MAX_BYTES,
256            Self::MAX_UTF16_CODE_UNITS,
257        )
258    }
259}
260
261/// Encodes `value` as a VarInt-length-prefixed string.
262///
263/// `codec` identifies the codec in errors, `max_bytes` limits the UTF-8
264/// payload, and `max_code_units` limits the number of UTF-16 code units.
265/// Supplementary Unicode scalar values count as two UTF-16 code units.
266///
267/// # Errors
268///
269/// Returns a [`CodecError`] if the string exceeds `max_bytes` or
270/// `max_code_units`, or if the underlying writer fails while writing the length
271/// prefix or payload.
272pub(crate) fn encode_prefixed_string(
273    value: &str,
274    writer: &mut impl Write,
275    codec: CodecKind,
276    max_bytes: usize,
277    max_code_units: usize,
278) -> Result<(), CodecError> {
279    if value.len() > max_bytes {
280        return Err(CodecError::invalid_encoding_for_operation(
281            codec,
282            CodecOperation::Write,
283            0,
284            InvalidEncodingReason::StringTooLong { max_bytes },
285        ));
286    }
287    if value.encode_utf16().count() > max_code_units {
288        return Err(CodecError::invalid_encoding_for_operation(
289            codec,
290            CodecOperation::Write,
291            0,
292            InvalidEncodingReason::TooManyUtf16CodeUnits { max_code_units },
293        ));
294    }
295
296    let bytes = value.as_bytes();
297    let prefix_size = writer
298        .write_varint_with_size(bytes.len() as i32)
299        .map_err(|error| error.with_context(codec))?;
300    write_all_counted(writer, bytes, codec, prefix_size)
301}
302
303/// Decodes a VarInt-length-prefixed string and returns its value and the total
304/// number of bytes processed, including the length prefix.
305///
306/// `codec` is the codec in errors, `max_bytes` limits the UTF-8 payload, and
307/// `max_code_units` limits the number of UTF-16 code units.
308///
309/// # Errors
310///
311/// Returns a [`CodecError`] if the length prefix is negative, the payload
312/// exceeds `max_bytes`, the payload contains invalid UTF-8 or more than
313/// `max_code_units` UTF-16 code units, or the reader reaches an unexpected end
314/// of input.
315pub(crate) fn decode_prefixed_string(
316    reader: &mut impl Read,
317    codec: CodecKind,
318    max_bytes: usize,
319    max_code_units: usize,
320) -> Result<(String, usize), CodecError> {
321    let (byte_length, prefix_size) = reader
322        .read_varint_with_size()
323        .map_err(|error| error.with_context(codec))?;
324    let byte_length = usize::try_from(byte_length).map_err(|_| {
325        CodecError::invalid_encoding(
326            codec,
327            prefix_size,
328            InvalidEncodingReason::NegativeLength { value: byte_length },
329        )
330    })?;
331
332    if byte_length > max_bytes {
333        return Err(CodecError::invalid_encoding(
334            codec,
335            prefix_size,
336            InvalidEncodingReason::StringTooLong { max_bytes },
337        ));
338    }
339
340    let mut bytes = vec![0; byte_length];
341    read_exact_counted(reader, &mut bytes, codec, prefix_size)?;
342    let bytes_processed = prefix_size + byte_length;
343    let value = String::from_utf8(bytes).map_err(|error| {
344        let utf8_error = error.utf8_error();
345        CodecError::invalid_encoding(
346            codec,
347            bytes_processed,
348            InvalidEncodingReason::InvalidUtf8 {
349                valid_up_to: utf8_error.valid_up_to(),
350                error_len: utf8_error.error_len(),
351            },
352        )
353    })?;
354    if value.len() > max_bytes {
355        return Err(CodecError::invalid_encoding(
356            codec,
357            bytes_processed,
358            InvalidEncodingReason::StringTooLong { max_bytes },
359        ));
360    }
361    if value.encode_utf16().count() > max_code_units {
362        return Err(CodecError::invalid_encoding(
363            codec,
364            bytes_processed,
365            InvalidEncodingReason::TooManyUtf16CodeUnits { max_code_units },
366        ));
367    }
368    Ok((value, bytes_processed))
369}
370
371impl TypeCodec for PrefixedString {
372    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
373        Self::encode_value(&self.0, writer)
374    }
375
376    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
377        Self::decode_value(reader).map(|(value, _)| Self(value))
378    }
379}
380
381/// A resource identifier encoded as a [`PrefixedString`].
382///
383/// The namespace permits `[a-z0-9._-]`; the value permits
384/// `[a-z0-9._/-]`. See the protocol's [identifier format] for details.
385///
386/// [identifier format]: https://minecraft.wiki/w/Java_Edition_protocol/Packets#Identifier
387#[derive(Debug, Clone, PartialEq, Eq, Hash)]
388pub struct Identifier(String);
389
390impl Identifier {
391    /// The maximum number of UTF-16 code units permitted in the identifier.
392    pub const MAX_UTF16_CODE_UNITS: usize = PrefixedString::MAX_UTF16_CODE_UNITS;
393    /// The maximum size of the UTF-8 payload, excluding its VarInt length prefix.
394    pub const MAX_BYTES: usize = PrefixedString::MAX_BYTES;
395    /// The maximum encoded size, including the VarInt length prefix.
396    pub const MAX_ENCODED_BYTES: usize = Self::MAX_BYTES + 3;
397
398    /// Creates an identifier after validating its namespace and path.
399    ///
400    /// An identifier without an explicit namespace is validated as belonging
401    /// to the `minecraft` namespace, but its original spelling is preserved.
402    ///
403    /// # Errors
404    ///
405    /// Returns [`InvalidIdentifier`] if the namespace or path is empty or
406    /// contains a character not permitted by the identifier format.
407    pub fn new(value: impl Into<String>) -> Result<Self, InvalidIdentifier> {
408        let value = value.into();
409        validate_identifier(&value)?;
410        Ok(Self(value))
411    }
412
413    /// Returns the identifier as a string slice.
414    pub fn as_str(&self) -> &str {
415        &self.0
416    }
417
418    /// Returns the owned identifier string.
419    pub fn into_inner(self) -> String {
420        self.0
421    }
422}
423
424impl fmt::Display for Identifier {
425    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
426        formatter.write_str(&self.0)
427    }
428}
429
430impl Serialize for Identifier {
431    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
432    where
433        S: Serializer,
434    {
435        serializer.serialize_str(&self.0)
436    }
437}
438
439impl<'de> Deserialize<'de> for Identifier {
440    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
441    where
442        D: Deserializer<'de>,
443    {
444        Self::new(String::deserialize(deserializer)?).map_err(D::Error::custom)
445    }
446}
447
448impl TypeCodec for Identifier {
449    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
450        PrefixedString::encode_value(&self.0, writer)
451            .map_err(|error| error.with_context(CodecKind::Identifier))
452    }
453
454    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
455        let (value, bytes_processed) = PrefixedString::decode_value(reader)
456            .map_err(|error| error.with_context(CodecKind::Identifier))?;
457        Self::new(value).map_err(|_| {
458            CodecError::invalid_encoding(
459                CodecKind::Identifier,
460                bytes_processed,
461                InvalidEncodingReason::InvalidIdentifier,
462            )
463        })
464    }
465}
466
467/// Returns whether a string is a valid Minecraft resource identifier.
468///
469/// An identifier without an explicit namespace is validated as belonging to
470/// the `minecraft` namespace. The namespace permits `[a-z0-9._-]`; the path
471/// permits `[a-z0-9._/-]`. See the protocol's [identifier format].
472///
473/// [identifier format]: https://minecraft.wiki/w/Java_Edition_protocol/Packets#Identifier
474pub(crate) fn is_valid_identifier(value: &str) -> bool {
475    let (namespace, path) = match value.split_once(':') {
476        Some((namespace, path)) => (namespace, path),
477        None => ("minecraft", value),
478    };
479    let namespace_is_valid = !namespace.is_empty()
480        && namespace.bytes().all(|byte| {
481            byte.is_ascii_lowercase() || byte.is_ascii_digit() || b"_.-".contains(&byte)
482        });
483    let path_is_valid = !path.is_empty()
484        && path.bytes().all(|byte| {
485            byte.is_ascii_lowercase() || byte.is_ascii_digit() || b"/._-".contains(&byte)
486        });
487    namespace_is_valid && path_is_valid && !path.contains(':')
488}
489
490fn validate_identifier(value: &str) -> Result<(), InvalidIdentifier> {
491    if is_valid_identifier(value) {
492        Ok(())
493    } else {
494        Err(InvalidIdentifier)
495    }
496}
497
498/// An error returned when a string is not a valid Minecraft resource identifier.
499#[derive(Debug, Clone, Copy, PartialEq, Eq)]
500pub struct InvalidIdentifier;
501
502impl fmt::Display for InvalidIdentifier {
503    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
504        formatter.write_str("invalid Minecraft identifier")
505    }
506}
507
508impl std::error::Error for InvalidIdentifier {}
509
510/// A variable-length two's-complement signed 32-bit integer.
511///
512/// VarInts use one to five bytes on the wire. Each byte carries seven payload
513/// bits; the most-significant bit is set on every byte except the last.
514///
515/// # Examples
516///
517/// ```
518/// use mcproto_types::{TypeCodec, basic::VarInt};
519///
520/// let mut encoded = Vec::new();
521/// VarInt(25565).encode(&mut encoded)?;
522/// assert_eq!(encoded, [0xdd, 0xc7, 0x01]);
523///
524/// let mut input = encoded.as_slice();
525/// assert_eq!(VarInt::decode(&mut input)?, VarInt(25565));
526/// assert!(input.is_empty());
527/// # Ok::<(), mcproto_codec::error::CodecError>(())
528/// ```
529#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
530pub struct VarInt(
531    /// The integer value.
532    pub i32,
533);
534
535impl TypeCodec for VarInt {
536    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
537        writer.write_varint(self.0)
538    }
539
540    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
541        reader.read_varint().map(Self)
542    }
543}
544
545/// A variable-length two's-complement signed 64-bit integer.
546///
547/// VarLongs use one to ten bytes on the wire. Each byte carries seven payload
548/// bits; the most-significant bit is set on every byte except the last.
549///
550/// # Examples
551///
552/// ```
553/// use mcproto_types::{TypeCodec, basic::VarLong};
554///
555/// let mut encoded = Vec::new();
556/// VarLong(9_223_372_036_854_775_000).encode(&mut encoded)?;
557///
558/// let mut input = encoded.as_slice();
559/// assert_eq!(
560///     VarLong::decode(&mut input)?,
561///     VarLong(9_223_372_036_854_775_000),
562/// );
563/// assert!(input.is_empty());
564/// # Ok::<(), mcproto_codec::error::CodecError>(())
565/// ```
566#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
567pub struct VarLong(
568    /// The integer value.
569    pub i64,
570);
571
572impl TypeCodec for VarLong {
573    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
574        writer.write_varlong(self.0)
575    }
576
577    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
578        reader.read_varlong().map(Self)
579    }
580}
581
582/// A Minecraft protocol block position packed into a 64-bit value.
583///
584/// The wire format stores `x` in the 26 most-significant bits, `z` in the
585/// middle 26 bits, and `y` in the 12 least-significant bits. Each component is
586/// a signed two's-complement integer:
587///
588/// ```text
589/// x: 26 bits | z: 26 bits | y: 12 bits
590/// ```
591///
592/// The value is packed as:
593///
594/// ```text
595/// ((x & 0x3FFFFFF) << 38) | ((z & 0x3FFFFFF) << 12) | (y & 0xFFF)
596/// ```
597///
598/// # Examples
599///
600/// ```
601/// use mcproto_types::{TypeCodec, basic::Position};
602///
603/// let position = Position {
604///     x: 18_357_644,
605///     y: 831,
606///     z: -20_882_616,
607/// };
608///
609/// let mut encoded = Vec::new();
610/// position.encode(&mut encoded)?;
611/// assert_eq!(
612///     encoded,
613///     [0x46, 0x07, 0x63, 0x2c, 0x15, 0xb4, 0x83, 0x3f]
614/// );
615///
616/// let mut input = encoded.as_slice();
617/// assert_eq!(Position::decode(&mut input)?, position);
618/// assert!(input.is_empty());
619/// # Ok::<(), mcproto_codec::error::CodecError>(())
620/// ```
621#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
622pub struct Position {
623    /// The x coordinate, from -33,554,432 through 33,554,431.
624    pub x: i32,
625    /// The y coordinate, from -2,048 through 2,047.
626    pub y: i16,
627    /// The z coordinate, from -33,554,432 through 33,554,431.
628    pub z: i32,
629}
630
631impl TypeCodec for Position {
632    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
633        let value = ((self.x as i64 & 0x3ff_ffff) << 38)
634            | ((self.z as i64 & 0x3ff_ffff) << 12)
635            | (self.y as i64 & 0xfff);
636        write_all_counted(writer, &value.to_be_bytes(), CodecKind::Position, 0)
637    }
638
639    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
640        let mut bytes = [0; 8];
641        read_exact_counted(reader, &mut bytes, CodecKind::Position, 0)?;
642        let value = i64::from_be_bytes(bytes);
643
644        Ok(Self {
645            x: (value >> 38) as i32,
646            y: ((value << 52) >> 52) as i16,
647            z: ((value << 26) >> 38) as i32,
648        })
649    }
650}
651
652/// A rotation angle encoded in steps of 1/256 of a full turn.
653///
654/// The wire value is a single byte. Because 256 steps represent one full turn,
655/// the value wraps around and its signedness does not matter.
656///
657/// # Examples
658///
659/// ```
660/// use mcproto_types::{TypeCodec, basic::Angle};
661///
662/// let angle = Angle(64);
663///
664/// let mut encoded = Vec::new();
665/// angle.encode(&mut encoded)?;
666/// assert_eq!(encoded, [64]);
667///
668/// let mut input = encoded.as_slice();
669/// assert_eq!(Angle::decode(&mut input)?, angle);
670///
671/// assert_eq!(angle.to_degrees(), 90.0);
672/// assert!((angle.to_radians() - std::f64::consts::FRAC_PI_2).abs() < 1e-12);
673/// # Ok::<(), mcproto_codec::error::CodecError>(())
674/// ```
675#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
676pub struct Angle(
677    /// The raw angle step count, from 0 through 255.
678    pub u8,
679);
680
681impl Angle {
682    /// Returns the angle in degrees.
683    ///
684    /// A full turn of 256 steps is equivalent to 360 degrees.
685    pub fn to_degrees(&self) -> f64 {
686        f64::from(self.0) * 360.0 / 256.0
687    }
688
689    /// Returns the angle in radians.
690    ///
691    /// A full turn of 256 steps is equivalent to 2π radians.
692    pub fn to_radians(&self) -> f64 {
693        f64::from(self.0) * std::f64::consts::TAU / 256.0
694    }
695}
696
697impl TypeCodec for Angle {
698    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
699        write_all_counted(writer, &[self.0], CodecKind::Angle, 0)
700    }
701
702    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
703        let mut bytes = [0; 1];
704        read_exact_counted(reader, &mut bytes, CodecKind::Angle, 0)?;
705        Ok(Self(bytes[0]))
706    }
707}
708
709/// Three doubles compressed into a shared-scale, low-precision vector.
710///
711/// `LpVec3` normally occupies six bytes. The coordinates are divided by their
712/// rounded-up maximum absolute value, quantized into three unsigned 15-bit
713/// values, and packed with a shared scale factor:
714///
715/// ```text
716/// X: 15 bits | Y: 15 bits | Z: 15 bits | continuation: 1 bit | scale: 2 bits
717/// ```
718///
719/// The first two packed bytes are written in little-endian order, while the
720/// remaining four bytes are written in big-endian order. If the scale factor
721/// is greater than three, its upper bits follow as a VarInt. A vector whose
722/// greatest absolute coordinate is below `1 / 32766`, or which contains NaN,
723/// is encoded as the single byte `0x00` and decodes as zero.
724///
725/// This format is used by the Java Edition `Spawn Entity` and
726/// `Set Entity Velocity` packets.
727///
728/// # Examples
729///
730/// ```
731/// use mcproto_types::{TypeCodec, basic::LpVec3};
732///
733/// let value = LpVec3::new(10.0, 0.2, -5.0);
734/// let mut encoded = Vec::new();
735/// value.encode(&mut encoded)?;
736/// assert_eq!(encoded, [0xf6, 0xff, 0x40, 0x01, 0x05, 0x1f, 0x02]);
737///
738/// let mut input = encoded.as_slice();
739/// let decoded = LpVec3::decode(&mut input)?;
740/// assert!((decoded.x - value.x).abs() < 0.001);
741/// assert!((decoded.y - value.y).abs() < 0.001);
742/// assert!((decoded.z - value.z).abs() < 0.001);
743/// assert!(input.is_empty());
744/// # Ok::<(), mcproto_codec::error::CodecError>(())
745/// ```
746#[derive(Debug, Clone, Copy, PartialEq, Default)]
747pub struct LpVec3 {
748    /// The x coordinate.
749    pub x: f64,
750    /// The y coordinate.
751    pub y: f64,
752    /// The z coordinate.
753    pub z: f64,
754}
755
756impl LpVec3 {
757    /// Greatest quantized coordinate value stored in a 15-bit field.
758    pub const MAX_QUANTIZED_VALUE: f64 = 32766.0;
759    /// Coordinates below this absolute maximum use the one-byte zero form.
760    pub const ZERO_THRESHOLD: f64 = 1.0 / Self::MAX_QUANTIZED_VALUE;
761    /// Greatest shared scale factor representable by two low bits and a
762    /// 32-bit VarInt continuation.
763    pub const MAX_SCALE_FACTOR: u64 = (u32::MAX as u64) << 2 | 0x03;
764
765    const CONTINUATION_FLAG: u64 = 0x04;
766    const SCALE_BITS: u64 = 0x03;
767
768    /// Creates a low-precision vector from its coordinates.
769    #[must_use]
770    pub const fn new(x: f64, y: f64, z: f64) -> Self {
771        Self { x, y, z }
772    }
773
774    fn pack(value: f64) -> u64 {
775        ((value * 0.5 + 0.5) * Self::MAX_QUANTIZED_VALUE).round() as u64
776    }
777
778    fn unpack(value: u64) -> f64 {
779        ((value & 32767) as f64).min(Self::MAX_QUANTIZED_VALUE) * 2.0 / Self::MAX_QUANTIZED_VALUE
780            - 1.0
781    }
782}
783
784impl TypeCodec for LpVec3 {
785    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
786        let contains_nan = self.x.is_nan() || self.y.is_nan() || self.z.is_nan();
787        let max_coordinate = self.x.abs().max(self.y.abs()).max(self.z.abs());
788        if contains_nan || max_coordinate < Self::ZERO_THRESHOLD {
789            return write_all_counted(writer, &[0], CodecKind::LpVec3, 0);
790        }
791
792        let scale_factor = max_coordinate.ceil() as u64;
793        if scale_factor > Self::MAX_SCALE_FACTOR {
794            return Err(CodecError::invalid_encoding_for_operation(
795                CodecKind::LpVec3,
796                CodecOperation::Write,
797                0,
798                InvalidEncodingReason::LpVec3ScaleOutOfRange {
799                    scale_factor,
800                    max: Self::MAX_SCALE_FACTOR,
801                },
802            ));
803        }
804
805        let need_continuation = scale_factor & Self::SCALE_BITS != scale_factor;
806        let packed_scale = if need_continuation {
807            scale_factor & Self::SCALE_BITS | Self::CONTINUATION_FLAG
808        } else {
809            scale_factor
810        };
811        let scale = scale_factor as f64;
812        let packed = Self::pack(self.x / scale) << 3
813            | Self::pack(self.y / scale) << 18
814            | Self::pack(self.z / scale) << 33
815            | packed_scale;
816        let upper = ((packed >> 16) as u32).to_be_bytes();
817        let bytes = [
818            packed as u8,
819            (packed >> 8) as u8,
820            upper[0],
821            upper[1],
822            upper[2],
823            upper[3],
824        ];
825        write_all_counted(writer, &bytes, CodecKind::LpVec3, 0)?;
826
827        if need_continuation {
828            writer
829                .write_varint((scale_factor >> 2) as u32 as i32)
830                .map_err(|error| error.with_context(CodecKind::LpVec3))?;
831        }
832        Ok(())
833    }
834
835    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
836        let mut first = [0; 1];
837        read_exact_counted(reader, &mut first, CodecKind::LpVec3, 0)?;
838        if first[0] == 0 {
839            return Ok(Self::default());
840        }
841
842        let mut remaining = [0; 5];
843        read_exact_counted(reader, &mut remaining, CodecKind::LpVec3, 1)?;
844        let upper = u32::from_be_bytes([remaining[1], remaining[2], remaining[3], remaining[4]]);
845        let packed = u64::from(upper) << 16 | u64::from(remaining[0]) << 8 | u64::from(first[0]);
846        let mut scale_factor = u64::from(first[0]) & Self::SCALE_BITS;
847        if first[0] & Self::CONTINUATION_FLAG as u8 != 0 {
848            let continuation = reader
849                .read_varint()
850                .map_err(|error| error.with_context(CodecKind::LpVec3))?;
851            scale_factor |= u64::from(continuation as u32) << 2;
852        }
853
854        let scale = scale_factor as f64;
855        Ok(Self {
856            x: Self::unpack(packed >> 3) * scale,
857            y: Self::unpack(packed >> 18) * scale,
858            z: Self::unpack(packed >> 33) * scale,
859        })
860    }
861}
862
863/// A 128-bit universally unique identifier.
864///
865/// Encoded as an unsigned 128-bit integer (or two unsigned 64-bit integers: the
866/// most significant 64 bits and then the least significant 64 bits).
867///
868/// See [Universally unique identifier](https://en.wikipedia.org/wiki/Universally_unique_identifier).
869///
870/// # Examples
871///
872/// ```
873/// use mcproto_types::{TypeCodec, basic::Uuid};
874///
875/// let value = Uuid::from_bytes([
876///     0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0,
877///     0x0f, 0xed, 0xcb, 0xa9, 0x87, 0x65, 0x43, 0x21,
878/// ]);
879///
880/// let mut encoded = Vec::new();
881/// value.encode(&mut encoded)?;
882/// assert_eq!(
883///     encoded,
884///     [
885///         0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0,
886///         0x0f, 0xed, 0xcb, 0xa9, 0x87, 0x65, 0x43, 0x21,
887///     ]
888/// );
889///
890/// let mut input = encoded.as_slice();
891/// assert_eq!(Uuid::decode(&mut input)?, value);
892/// assert!(input.is_empty());
893/// # Ok::<(), mcproto_codec::error::CodecError>(())
894/// ```
895#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
896pub struct Uuid(
897    /// The UUID value.
898    pub uuid::Uuid,
899);
900
901impl Uuid {
902    /// Creates a UUID from 16 bytes in big-endian order.
903    pub fn from_bytes(bytes: [u8; 16]) -> Self {
904        Self(uuid::Uuid::from_bytes(bytes))
905    }
906
907    /// Returns the UUID as 16 bytes in big-endian order.
908    pub fn into_bytes(self) -> [u8; 16] {
909        self.0.into_bytes()
910    }
911}
912
913impl TypeCodec for Uuid {
914    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
915        write_all_counted(writer, &self.0.into_bytes(), CodecKind::Uuid, 0)
916    }
917
918    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
919        let mut bytes = [0; 16];
920        read_exact_counted(reader, &mut bytes, CodecKind::Uuid, 0)?;
921        Ok(Self::from_bytes(bytes))
922    }
923}
924
925/// A length-prefixed bit set.
926///
927/// The wire representation is a VarInt length prefix followed by that many
928/// 64-bit words, encoded in big-endian order. The `i`th bit is set when:
929///
930/// ```text
931/// (data[i / 64] & (1 << (i % 64))) != 0
932/// ```
933///
934/// # Examples
935///
936/// ```
937/// use mcproto_types::{TypeCodec, basic::BitSet};
938///
939/// let bits = BitSet(vec![0b0000_0101]);
940///
941/// let mut encoded = Vec::new();
942/// bits.encode(&mut encoded)?;
943/// assert_eq!(
944///     encoded,
945///     [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05]
946/// );
947///
948/// let mut input = encoded.as_slice();
949/// assert_eq!(BitSet::decode(&mut input)?, bits);
950/// assert!(input.is_empty());
951///
952/// assert!(bits.contains(0));
953/// assert!(!bits.contains(1));
954/// assert!(bits.contains(2));
955/// # Ok::<(), mcproto_codec::error::CodecError>(())
956/// ```
957#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
958pub struct BitSet(
959    /// The packed 64-bit words, in little-endian bit order.
960    pub Vec<u64>,
961);
962
963impl BitSet {
964    /// Returns whether the bit at `index` is set.
965    pub fn contains(&self, index: usize) -> bool {
966        match self.0.get(index / 64) {
967            Some(word) => (word & (1 << (index % 64))) != 0,
968            None => false,
969        }
970    }
971}
972
973impl TypeCodec for BitSet {
974    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
975        let prefix_size = writer
976            .write_varint_with_size(self.0.len() as i32)
977            .map_err(|error| error.with_context(CodecKind::BitSet))?;
978
979        let mut bytes_processed = prefix_size;
980        for word in &self.0 {
981            write_all_counted(
982                writer,
983                &word.to_be_bytes(),
984                CodecKind::BitSet,
985                bytes_processed,
986            )?;
987            bytes_processed += 8;
988        }
989
990        Ok(())
991    }
992
993    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
994        let (length, prefix_size) = reader
995            .read_varint_with_size()
996            .map_err(|error| error.with_context(CodecKind::BitSet))?;
997        let length = usize::try_from(length).map_err(|_| {
998            CodecError::invalid_encoding(
999                CodecKind::BitSet,
1000                prefix_size,
1001                InvalidEncodingReason::NegativeLength { value: length },
1002            )
1003        })?;
1004
1005        let mut words = Vec::with_capacity(length);
1006        let mut bytes_processed = prefix_size;
1007        for _ in 0..length {
1008            let mut bytes = [0; 8];
1009            read_exact_counted(reader, &mut bytes, CodecKind::BitSet, bytes_processed)?;
1010            words.push(u64::from_be_bytes(bytes));
1011            bytes_processed += 8;
1012        }
1013
1014        Ok(Self(words))
1015    }
1016}
1017
1018/// A bit set with a fixed length of `N` bits.
1019///
1020/// A fixed bit set is encoded as exactly `ceil(N / 8)` bytes, without a
1021/// length prefix. This differs from [`BitSet`], which is prefixed by a VarInt
1022/// and stores packed 64-bit words. The packed bytes follow the same bit order
1023/// as Java's `BitSet.toByteArray`: bit `i` is set when the following expression
1024/// is non-zero:
1025///
1026/// ```text
1027/// (data[i / 8] & (1 << (i % 8))) != 0
1028/// ```
1029///
1030/// The final byte is padded with zero bits when `N` is not divisible by eight.
1031///
1032/// # Examples
1033///
1034/// ```
1035/// use mcproto_types::{TypeCodec, basic::FixedBitSet};
1036///
1037/// // Bits 0, 7, and 8 are set in a nine-bit set.
1038/// let bits = FixedBitSet::<9>(vec![0b1000_0001, 0b0000_0001]);
1039///
1040/// let mut encoded = Vec::new();
1041/// bits.encode(&mut encoded)?;
1042/// assert_eq!(encoded, [0b1000_0001, 0b0000_0001]);
1043///
1044/// let mut input = encoded.as_slice();
1045/// assert_eq!(FixedBitSet::<9>::decode(&mut input)?, bits);
1046/// assert!(bits.contains(0));
1047/// assert!(bits.contains(7));
1048/// assert!(bits.contains(8));
1049/// assert!(!bits.contains(9));
1050/// # Ok::<(), mcproto_codec::error::CodecError>(())
1051/// ```
1052#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
1053pub struct FixedBitSet<const N: usize>(
1054    /// The packed bytes, in little-endian bit order within each byte.
1055    pub Vec<u8>,
1056);
1057
1058impl<const N: usize> FixedBitSet<N> {
1059    /// The number of packed bytes used by this fixed bit set.
1060    pub const BYTE_LEN: usize = N.div_ceil(8);
1061
1062    /// Returns whether the bit at `index` is set.
1063    ///
1064    /// Indices outside this set's fixed length return `false`.
1065    pub fn contains(&self, index: usize) -> bool {
1066        index < N
1067            && self
1068                .0
1069                .get(index / 8)
1070                .is_some_and(|byte| (byte & (1 << (index % 8))) != 0)
1071    }
1072
1073    fn validate(
1074        &self,
1075        operation: CodecOperation,
1076        bytes_processed: usize,
1077    ) -> Result<(), CodecError> {
1078        if self.0.len() != Self::BYTE_LEN {
1079            return Err(CodecError::invalid_encoding_for_operation(
1080                CodecKind::FixedBitSet,
1081                operation,
1082                bytes_processed,
1083                InvalidEncodingReason::InvalidFixedBitSetLength {
1084                    expected: Self::BYTE_LEN,
1085                    actual: self.0.len(),
1086                },
1087            ));
1088        }
1089
1090        if let Some(last_byte) = self.0.last()
1091            && N % 8 != 0
1092        {
1093            let allowed_mask = (1u8 << (N % 8)) - 1;
1094            if last_byte & !allowed_mask != 0 {
1095                return Err(CodecError::invalid_encoding_for_operation(
1096                    CodecKind::FixedBitSet,
1097                    operation,
1098                    bytes_processed,
1099                    InvalidEncodingReason::ValueOutOfRange {
1100                        terminal_byte: *last_byte,
1101                        allowed_mask,
1102                    },
1103                ));
1104            }
1105        }
1106
1107        Ok(())
1108    }
1109}
1110
1111impl<const N: usize> TypeCodec for FixedBitSet<N> {
1112    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
1113        self.validate(CodecOperation::Write, 0)?;
1114        write_all_counted(writer, &self.0, CodecKind::FixedBitSet, 0)
1115    }
1116
1117    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
1118        let mut bytes = vec![0; Self::BYTE_LEN];
1119        read_exact_counted(reader, &mut bytes, CodecKind::FixedBitSet, 0)?;
1120
1121        let value = Self(bytes);
1122        value.validate(CodecOperation::Read, Self::BYTE_LEN)?;
1123        Ok(value)
1124    }
1125}
1126
1127macro_rules! impl_enum_repr {
1128    ($type:ident, $primitive:ty) => {
1129        impl EnumRepr for $type {
1130            fn from_discriminant(value: i128) -> Option<Self> {
1131                <$primitive>::try_from(value).ok().map(Self)
1132            }
1133
1134            fn discriminant(&self) -> i128 {
1135                self.0 as i128
1136            }
1137        }
1138    };
1139}
1140
1141impl EnumRepr for Boolean {
1142    fn from_discriminant(value: i128) -> Option<Self> {
1143        match value {
1144            0 => Some(Self(false)),
1145            1 => Some(Self(true)),
1146            _ => None,
1147        }
1148    }
1149
1150    fn discriminant(&self) -> i128 {
1151        if self.0 { 1 } else { 0 }
1152    }
1153}
1154
1155impl_enum_repr!(Byte, i8);
1156impl_enum_repr!(UnsignedByte, u8);
1157impl_enum_repr!(Short, i16);
1158impl_enum_repr!(UnsignedShort, u16);
1159impl_enum_repr!(Int, i32);
1160impl_enum_repr!(Long, i64);
1161impl_enum_repr!(VarInt, i32);
1162impl_enum_repr!(VarLong, i64);
1163impl_enum_repr!(Angle, u8);