Skip to main content

mcproto_types/
basic.rs

1use crate::TypeCodec;
2use mcproto_codec::{
3    error::{CodecError, CodecKind, CodecOperation, InvalidEncodingReason},
4    io::{read_exact_counted, write_all_counted},
5    varint::{VarIntRead, VarIntWrite},
6};
7use std::{
8    fmt,
9    io::{Read, Write},
10};
11
12/// True is encoded as 0x01, false as 0x00.
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
14pub struct Boolean(pub bool);
15
16impl TypeCodec for Boolean {
17    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
18        let byte = if self.0 { 1u8 } else { 0u8 };
19        write_all_counted(writer, &[byte], CodecKind::Boolean, 0)
20    }
21
22    fn decode(reader: &mut impl Read) -> Result<Self, CodecError>
23    where
24        Self: Sized,
25    {
26        let mut buf = [0u8; 1];
27        read_exact_counted(reader, &mut buf, CodecKind::Boolean, 0)?;
28        match buf[0] {
29            0 => Ok(Boolean(false)),
30            1 => Ok(Boolean(true)),
31            _ => Err(CodecError::invalid_encoding(
32                CodecKind::Boolean,
33                1,
34                InvalidEncodingReason::InvalidBooleanValue { value: buf[0] },
35            )),
36        }
37    }
38}
39
40/// Signed 8-bit integer, two's complement.
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
42pub struct Byte(pub i8);
43
44impl TypeCodec for Byte {
45    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
46        write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::Byte, 0)
47    }
48
49    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
50        let mut bytes = [0; 1];
51        read_exact_counted(reader, &mut bytes, CodecKind::Byte, 0)?;
52        Ok(Self(i8::from_be_bytes(bytes)))
53    }
54}
55
56/// Unsigned 8-bit integer.
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
58pub struct UnsignedByte(pub u8);
59
60impl TypeCodec for UnsignedByte {
61    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
62        write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::UnsignedByte, 0)
63    }
64
65    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
66        let mut bytes = [0; 1];
67        read_exact_counted(reader, &mut bytes, CodecKind::UnsignedByte, 0)?;
68        Ok(Self(u8::from_be_bytes(bytes)))
69    }
70}
71
72/// Signed 16-bit integer, big-endian two's complement.
73#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
74pub struct Short(pub i16);
75
76impl TypeCodec for Short {
77    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
78        write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::Short, 0)
79    }
80
81    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
82        let mut bytes = [0; 2];
83        read_exact_counted(reader, &mut bytes, CodecKind::Short, 0)?;
84        Ok(Self(i16::from_be_bytes(bytes)))
85    }
86}
87
88/// Unsigned 16-bit integer, big-endian.
89#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
90pub struct UnsignedShort(pub u16);
91
92impl TypeCodec for UnsignedShort {
93    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
94        write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::UnsignedShort, 0)
95    }
96
97    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
98        let mut bytes = [0; 2];
99        read_exact_counted(reader, &mut bytes, CodecKind::UnsignedShort, 0)?;
100        Ok(Self(u16::from_be_bytes(bytes)))
101    }
102}
103
104/// Signed 32-bit integer, big-endian two's complement.
105#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
106pub struct Int(pub i32);
107
108impl TypeCodec for Int {
109    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
110        write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::Int, 0)
111    }
112
113    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
114        let mut bytes = [0; 4];
115        read_exact_counted(reader, &mut bytes, CodecKind::Int, 0)?;
116        Ok(Self(i32::from_be_bytes(bytes)))
117    }
118}
119
120/// Signed 64-bit integer, big-endian two's complement.
121#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
122pub struct Long(pub i64);
123
124impl TypeCodec for Long {
125    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
126        write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::Long, 0)
127    }
128
129    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
130        let mut bytes = [0; 8];
131        read_exact_counted(reader, &mut bytes, CodecKind::Long, 0)?;
132        Ok(Self(i64::from_be_bytes(bytes)))
133    }
134}
135
136/// A standard UTF-8 string prefixed by its byte length as a VarInt.
137#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
138pub struct PrefixedString(pub String);
139
140impl PrefixedString {
141    pub const MAX_UTF16_CODE_UNITS: usize = 0x7fff;
142    pub const MAX_BYTES: usize = Self::MAX_UTF16_CODE_UNITS * 3;
143
144    fn validate_value(
145        value: &str,
146        operation: CodecOperation,
147        bytes_processed: usize,
148    ) -> Result<(), CodecError> {
149        let bytes = value.as_bytes();
150        if bytes.len() > Self::MAX_BYTES {
151            return Err(CodecError::invalid_encoding_for_operation(
152                CodecKind::String,
153                operation,
154                bytes_processed,
155                InvalidEncodingReason::StringTooLong {
156                    max_bytes: Self::MAX_BYTES,
157                },
158            ));
159        }
160
161        if value.encode_utf16().count() > Self::MAX_UTF16_CODE_UNITS {
162            return Err(CodecError::invalid_encoding_for_operation(
163                CodecKind::String,
164                operation,
165                bytes_processed,
166                InvalidEncodingReason::TooManyUtf16CodeUnits {
167                    max_code_units: Self::MAX_UTF16_CODE_UNITS,
168                },
169            ));
170        }
171
172        Ok(())
173    }
174
175    fn encode_value(value: &str, writer: &mut impl Write) -> Result<(), CodecError> {
176        Self::validate_value(value, CodecOperation::Write, 0)?;
177
178        let bytes = value.as_bytes();
179        let prefix_size = writer
180            .write_varint_with_size(bytes.len() as i32)
181            .map_err(|error| error.with_context(CodecKind::String))?;
182        write_all_counted(writer, bytes, CodecKind::String, prefix_size)
183    }
184
185    fn decode_value(reader: &mut impl Read) -> Result<(String, usize), CodecError> {
186        let (byte_length, prefix_size) = reader
187            .read_varint_with_size()
188            .map_err(|error| error.with_context(CodecKind::String))?;
189        let byte_length = usize::try_from(byte_length).map_err(|_| {
190            CodecError::invalid_encoding(
191                CodecKind::String,
192                prefix_size,
193                InvalidEncodingReason::NegativeLength { value: byte_length },
194            )
195        })?;
196
197        if byte_length > Self::MAX_BYTES {
198            return Err(CodecError::invalid_encoding(
199                CodecKind::String,
200                prefix_size,
201                InvalidEncodingReason::StringTooLong {
202                    max_bytes: Self::MAX_BYTES,
203                },
204            ));
205        }
206
207        let mut bytes = vec![0; byte_length];
208        read_exact_counted(reader, &mut bytes, CodecKind::String, prefix_size)?;
209        let bytes_processed = prefix_size + byte_length;
210        let value = String::from_utf8(bytes).map_err(|error| {
211            let utf8_error = error.utf8_error();
212            CodecError::invalid_encoding(
213                CodecKind::String,
214                bytes_processed,
215                InvalidEncodingReason::InvalidUtf8 {
216                    valid_up_to: utf8_error.valid_up_to(),
217                    error_len: utf8_error.error_len(),
218                },
219            )
220        })?;
221        Self::validate_value(&value, CodecOperation::Read, bytes_processed)?;
222        Ok((value, bytes_processed))
223    }
224}
225
226impl TypeCodec for PrefixedString {
227    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
228        Self::encode_value(&self.0, writer)
229    }
230
231    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
232        Self::decode_value(reader).map(|(value, _)| Self(value))
233    }
234}
235
236/// A Minecraft resource identifier encoded as a protocol String.
237#[derive(Debug, Clone, PartialEq, Eq, Hash)]
238pub struct Identifier(String);
239
240impl Identifier {
241    pub const MAX_UTF16_CODE_UNITS: usize = PrefixedString::MAX_UTF16_CODE_UNITS;
242    pub const MAX_BYTES: usize = PrefixedString::MAX_BYTES;
243    pub const MAX_ENCODED_BYTES: usize = Self::MAX_BYTES + 3;
244
245    pub fn new(value: impl Into<String>) -> Result<Self, InvalidIdentifier> {
246        let value = value.into();
247        validate_identifier(&value)?;
248        Ok(Self(value))
249    }
250
251    pub fn as_str(&self) -> &str {
252        &self.0
253    }
254
255    pub fn into_inner(self) -> String {
256        self.0
257    }
258}
259
260impl TypeCodec for Identifier {
261    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
262        PrefixedString::encode_value(&self.0, writer)
263            .map_err(|error| error.with_context(CodecKind::Identifier))
264    }
265
266    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
267        let (value, bytes_processed) = PrefixedString::decode_value(reader)
268            .map_err(|error| error.with_context(CodecKind::Identifier))?;
269        Self::new(value).map_err(|_| {
270            CodecError::invalid_encoding(
271                CodecKind::Identifier,
272                bytes_processed,
273                InvalidEncodingReason::InvalidIdentifier,
274            )
275        })
276    }
277}
278
279fn validate_identifier(value: &str) -> Result<(), InvalidIdentifier> {
280    let (namespace, path) = match value.split_once(':') {
281        Some((namespace, path)) => (namespace, path),
282        None => ("minecraft", value),
283    };
284    let namespace_is_valid = !namespace.is_empty()
285        && namespace.bytes().all(|byte| {
286            byte.is_ascii_lowercase() || byte.is_ascii_digit() || b"_.-".contains(&byte)
287        });
288    let path_is_valid = !path.is_empty()
289        && path.bytes().all(|byte| {
290            byte.is_ascii_lowercase() || byte.is_ascii_digit() || b"/._-".contains(&byte)
291        });
292    if namespace_is_valid && path_is_valid && !path.contains(':') {
293        Ok(())
294    } else {
295        Err(InvalidIdentifier)
296    }
297}
298
299#[derive(Debug, Clone, Copy, PartialEq, Eq)]
300pub struct InvalidIdentifier;
301
302impl fmt::Display for InvalidIdentifier {
303    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
304        formatter.write_str("invalid Minecraft identifier")
305    }
306}
307
308impl std::error::Error for InvalidIdentifier {}