use crate::{EnumRepr, TypeCodec};
use mcproto_codec::{
error::{CodecError, CodecKind, CodecOperation, InvalidEncodingReason},
io::{read_exact_counted, write_all_counted},
varint::{VarIntRead, VarIntWrite},
varlong::{VarLongRead, VarLongWrite},
};
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as _};
use std::{
fmt,
io::{Read, Write},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Boolean(
pub bool,
);
impl TypeCodec for Boolean {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
let byte = if self.0 { 1u8 } else { 0u8 };
write_all_counted(writer, &[byte], CodecKind::Boolean, 0)
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError>
where
Self: Sized,
{
let mut buf = [0u8; 1];
read_exact_counted(reader, &mut buf, CodecKind::Boolean, 0)?;
match buf[0] {
0 => Ok(Boolean(false)),
1 => Ok(Boolean(true)),
_ => Err(CodecError::invalid_encoding(
CodecKind::Boolean,
1,
InvalidEncodingReason::InvalidBooleanValue { value: buf[0] },
)),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Byte(
pub i8,
);
impl TypeCodec for Byte {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::Byte, 0)
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
let mut bytes = [0; 1];
read_exact_counted(reader, &mut bytes, CodecKind::Byte, 0)?;
Ok(Self(i8::from_be_bytes(bytes)))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct UnsignedByte(
pub u8,
);
impl TypeCodec for UnsignedByte {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::UnsignedByte, 0)
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
let mut bytes = [0; 1];
read_exact_counted(reader, &mut bytes, CodecKind::UnsignedByte, 0)?;
Ok(Self(u8::from_be_bytes(bytes)))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Short(
pub i16,
);
impl TypeCodec for Short {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::Short, 0)
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
let mut bytes = [0; 2];
read_exact_counted(reader, &mut bytes, CodecKind::Short, 0)?;
Ok(Self(i16::from_be_bytes(bytes)))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct UnsignedShort(
pub u16,
);
impl TypeCodec for UnsignedShort {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::UnsignedShort, 0)
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
let mut bytes = [0; 2];
read_exact_counted(reader, &mut bytes, CodecKind::UnsignedShort, 0)?;
Ok(Self(u16::from_be_bytes(bytes)))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Int(
pub i32,
);
impl TypeCodec for Int {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::Int, 0)
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
let mut bytes = [0; 4];
read_exact_counted(reader, &mut bytes, CodecKind::Int, 0)?;
Ok(Self(i32::from_be_bytes(bytes)))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Long(
pub i64,
);
impl TypeCodec for Long {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::Long, 0)
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
let mut bytes = [0; 8];
read_exact_counted(reader, &mut bytes, CodecKind::Long, 0)?;
Ok(Self(i64::from_be_bytes(bytes)))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct Float(
pub f32,
);
impl TypeCodec for Float {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::Float, 0)
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
let mut bytes = [0; 4];
read_exact_counted(reader, &mut bytes, CodecKind::Float, 0)?;
Ok(Self(f32::from_be_bytes(bytes)))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct Double(
pub f64,
);
impl TypeCodec for Double {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::Double, 0)
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
let mut bytes = [0; 8];
read_exact_counted(reader, &mut bytes, CodecKind::Double, 0)?;
Ok(Self(f64::from_be_bytes(bytes)))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
pub struct PrefixedString(
pub String,
);
impl PrefixedString {
pub const MAX_UTF16_CODE_UNITS: usize = 0x7fff;
pub const MAX_BYTES: usize = Self::MAX_UTF16_CODE_UNITS * 3;
fn encode_value(value: &str, writer: &mut impl Write) -> Result<(), CodecError> {
encode_prefixed_string(
value,
writer,
CodecKind::String,
Self::MAX_BYTES,
Self::MAX_UTF16_CODE_UNITS,
)
}
fn decode_value(reader: &mut impl Read) -> Result<(String, usize), CodecError> {
decode_prefixed_string(
reader,
CodecKind::String,
Self::MAX_BYTES,
Self::MAX_UTF16_CODE_UNITS,
)
}
}
pub(crate) fn encode_prefixed_string(
value: &str,
writer: &mut impl Write,
codec: CodecKind,
max_bytes: usize,
max_code_units: usize,
) -> Result<(), CodecError> {
if value.len() > max_bytes {
return Err(CodecError::invalid_encoding_for_operation(
codec,
CodecOperation::Write,
0,
InvalidEncodingReason::StringTooLong { max_bytes },
));
}
if value.encode_utf16().count() > max_code_units {
return Err(CodecError::invalid_encoding_for_operation(
codec,
CodecOperation::Write,
0,
InvalidEncodingReason::TooManyUtf16CodeUnits { max_code_units },
));
}
let bytes = value.as_bytes();
let prefix_size = writer
.write_varint_with_size(bytes.len() as i32)
.map_err(|error| error.with_context(codec))?;
write_all_counted(writer, bytes, codec, prefix_size)
}
pub(crate) fn decode_prefixed_string(
reader: &mut impl Read,
codec: CodecKind,
max_bytes: usize,
max_code_units: usize,
) -> Result<(String, usize), CodecError> {
let (byte_length, prefix_size) = reader
.read_varint_with_size()
.map_err(|error| error.with_context(codec))?;
let byte_length = usize::try_from(byte_length).map_err(|_| {
CodecError::invalid_encoding(
codec,
prefix_size,
InvalidEncodingReason::NegativeLength { value: byte_length },
)
})?;
if byte_length > max_bytes {
return Err(CodecError::invalid_encoding(
codec,
prefix_size,
InvalidEncodingReason::StringTooLong { max_bytes },
));
}
let mut bytes = vec![0; byte_length];
read_exact_counted(reader, &mut bytes, codec, prefix_size)?;
let bytes_processed = prefix_size + byte_length;
let value = String::from_utf8(bytes).map_err(|error| {
let utf8_error = error.utf8_error();
CodecError::invalid_encoding(
codec,
bytes_processed,
InvalidEncodingReason::InvalidUtf8 {
valid_up_to: utf8_error.valid_up_to(),
error_len: utf8_error.error_len(),
},
)
})?;
if value.len() > max_bytes {
return Err(CodecError::invalid_encoding(
codec,
bytes_processed,
InvalidEncodingReason::StringTooLong { max_bytes },
));
}
if value.encode_utf16().count() > max_code_units {
return Err(CodecError::invalid_encoding(
codec,
bytes_processed,
InvalidEncodingReason::TooManyUtf16CodeUnits { max_code_units },
));
}
Ok((value, bytes_processed))
}
impl TypeCodec for PrefixedString {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
Self::encode_value(&self.0, writer)
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
Self::decode_value(reader).map(|(value, _)| Self(value))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Identifier(String);
impl Identifier {
pub const MAX_UTF16_CODE_UNITS: usize = PrefixedString::MAX_UTF16_CODE_UNITS;
pub const MAX_BYTES: usize = PrefixedString::MAX_BYTES;
pub const MAX_ENCODED_BYTES: usize = Self::MAX_BYTES + 3;
pub fn new(value: impl Into<String>) -> Result<Self, InvalidIdentifier> {
let value = value.into();
validate_identifier(&value)?;
Ok(Self(value))
}
pub fn as_str(&self) -> &str {
&self.0
}
pub fn into_inner(self) -> String {
self.0
}
}
impl fmt::Display for Identifier {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(&self.0)
}
}
impl Serialize for Identifier {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.0)
}
}
impl<'de> Deserialize<'de> for Identifier {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Self::new(String::deserialize(deserializer)?).map_err(D::Error::custom)
}
}
impl TypeCodec for Identifier {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
PrefixedString::encode_value(&self.0, writer)
.map_err(|error| error.with_context(CodecKind::Identifier))
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
let (value, bytes_processed) = PrefixedString::decode_value(reader)
.map_err(|error| error.with_context(CodecKind::Identifier))?;
Self::new(value).map_err(|_| {
CodecError::invalid_encoding(
CodecKind::Identifier,
bytes_processed,
InvalidEncodingReason::InvalidIdentifier,
)
})
}
}
pub(crate) fn is_valid_identifier(value: &str) -> bool {
let (namespace, path) = match value.split_once(':') {
Some((namespace, path)) => (namespace, path),
None => ("minecraft", value),
};
let namespace_is_valid = !namespace.is_empty()
&& namespace.bytes().all(|byte| {
byte.is_ascii_lowercase() || byte.is_ascii_digit() || b"_.-".contains(&byte)
});
let path_is_valid = !path.is_empty()
&& path.bytes().all(|byte| {
byte.is_ascii_lowercase() || byte.is_ascii_digit() || b"/._-".contains(&byte)
});
namespace_is_valid && path_is_valid && !path.contains(':')
}
fn validate_identifier(value: &str) -> Result<(), InvalidIdentifier> {
if is_valid_identifier(value) {
Ok(())
} else {
Err(InvalidIdentifier)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct InvalidIdentifier;
impl fmt::Display for InvalidIdentifier {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("invalid Minecraft identifier")
}
}
impl std::error::Error for InvalidIdentifier {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct VarInt(
pub i32,
);
impl TypeCodec for VarInt {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
writer.write_varint(self.0)
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
reader.read_varint().map(Self)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct VarLong(
pub i64,
);
impl TypeCodec for VarLong {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
writer.write_varlong(self.0)
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
reader.read_varlong().map(Self)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Position {
pub x: i32,
pub y: i16,
pub z: i32,
}
impl TypeCodec for Position {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
let value = ((self.x as i64 & 0x3ff_ffff) << 38)
| ((self.z as i64 & 0x3ff_ffff) << 12)
| (self.y as i64 & 0xfff);
write_all_counted(writer, &value.to_be_bytes(), CodecKind::Position, 0)
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
let mut bytes = [0; 8];
read_exact_counted(reader, &mut bytes, CodecKind::Position, 0)?;
let value = i64::from_be_bytes(bytes);
Ok(Self {
x: (value >> 38) as i32,
y: ((value << 52) >> 52) as i16,
z: ((value << 26) >> 38) as i32,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Angle(
pub u8,
);
impl Angle {
pub fn to_degrees(&self) -> f64 {
f64::from(self.0) * 360.0 / 256.0
}
pub fn to_radians(&self) -> f64 {
f64::from(self.0) * std::f64::consts::TAU / 256.0
}
}
impl TypeCodec for Angle {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
write_all_counted(writer, &[self.0], CodecKind::Angle, 0)
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
let mut bytes = [0; 1];
read_exact_counted(reader, &mut bytes, CodecKind::Angle, 0)?;
Ok(Self(bytes[0]))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct LpVec3 {
pub x: f64,
pub y: f64,
pub z: f64,
}
impl LpVec3 {
pub const MAX_QUANTIZED_VALUE: f64 = 32766.0;
pub const ZERO_THRESHOLD: f64 = 1.0 / Self::MAX_QUANTIZED_VALUE;
pub const MAX_SCALE_FACTOR: u64 = (u32::MAX as u64) << 2 | 0x03;
const CONTINUATION_FLAG: u64 = 0x04;
const SCALE_BITS: u64 = 0x03;
#[must_use]
pub const fn new(x: f64, y: f64, z: f64) -> Self {
Self { x, y, z }
}
fn pack(value: f64) -> u64 {
((value * 0.5 + 0.5) * Self::MAX_QUANTIZED_VALUE).round() as u64
}
fn unpack(value: u64) -> f64 {
((value & 32767) as f64).min(Self::MAX_QUANTIZED_VALUE) * 2.0 / Self::MAX_QUANTIZED_VALUE
- 1.0
}
}
impl TypeCodec for LpVec3 {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
let contains_nan = self.x.is_nan() || self.y.is_nan() || self.z.is_nan();
let max_coordinate = self.x.abs().max(self.y.abs()).max(self.z.abs());
if contains_nan || max_coordinate < Self::ZERO_THRESHOLD {
return write_all_counted(writer, &[0], CodecKind::LpVec3, 0);
}
let scale_factor = max_coordinate.ceil() as u64;
if scale_factor > Self::MAX_SCALE_FACTOR {
return Err(CodecError::invalid_encoding_for_operation(
CodecKind::LpVec3,
CodecOperation::Write,
0,
InvalidEncodingReason::LpVec3ScaleOutOfRange {
scale_factor,
max: Self::MAX_SCALE_FACTOR,
},
));
}
let need_continuation = scale_factor & Self::SCALE_BITS != scale_factor;
let packed_scale = if need_continuation {
scale_factor & Self::SCALE_BITS | Self::CONTINUATION_FLAG
} else {
scale_factor
};
let scale = scale_factor as f64;
let packed = Self::pack(self.x / scale) << 3
| Self::pack(self.y / scale) << 18
| Self::pack(self.z / scale) << 33
| packed_scale;
let upper = ((packed >> 16) as u32).to_be_bytes();
let bytes = [
packed as u8,
(packed >> 8) as u8,
upper[0],
upper[1],
upper[2],
upper[3],
];
write_all_counted(writer, &bytes, CodecKind::LpVec3, 0)?;
if need_continuation {
writer
.write_varint((scale_factor >> 2) as u32 as i32)
.map_err(|error| error.with_context(CodecKind::LpVec3))?;
}
Ok(())
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
let mut first = [0; 1];
read_exact_counted(reader, &mut first, CodecKind::LpVec3, 0)?;
if first[0] == 0 {
return Ok(Self::default());
}
let mut remaining = [0; 5];
read_exact_counted(reader, &mut remaining, CodecKind::LpVec3, 1)?;
let upper = u32::from_be_bytes([remaining[1], remaining[2], remaining[3], remaining[4]]);
let packed = u64::from(upper) << 16 | u64::from(remaining[0]) << 8 | u64::from(first[0]);
let mut scale_factor = u64::from(first[0]) & Self::SCALE_BITS;
if first[0] & Self::CONTINUATION_FLAG as u8 != 0 {
let continuation = reader
.read_varint()
.map_err(|error| error.with_context(CodecKind::LpVec3))?;
scale_factor |= u64::from(continuation as u32) << 2;
}
let scale = scale_factor as f64;
Ok(Self {
x: Self::unpack(packed >> 3) * scale,
y: Self::unpack(packed >> 18) * scale,
z: Self::unpack(packed >> 33) * scale,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Uuid(
pub uuid::Uuid,
);
impl Uuid {
pub fn from_bytes(bytes: [u8; 16]) -> Self {
Self(uuid::Uuid::from_bytes(bytes))
}
pub fn into_bytes(self) -> [u8; 16] {
self.0.into_bytes()
}
}
impl TypeCodec for Uuid {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
write_all_counted(writer, &self.0.into_bytes(), CodecKind::Uuid, 0)
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
let mut bytes = [0; 16];
read_exact_counted(reader, &mut bytes, CodecKind::Uuid, 0)?;
Ok(Self::from_bytes(bytes))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
pub struct BitSet(
pub Vec<u64>,
);
impl BitSet {
pub fn contains(&self, index: usize) -> bool {
match self.0.get(index / 64) {
Some(word) => (word & (1 << (index % 64))) != 0,
None => false,
}
}
}
impl TypeCodec for BitSet {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
let prefix_size = writer
.write_varint_with_size(self.0.len() as i32)
.map_err(|error| error.with_context(CodecKind::BitSet))?;
let mut bytes_processed = prefix_size;
for word in &self.0 {
write_all_counted(
writer,
&word.to_be_bytes(),
CodecKind::BitSet,
bytes_processed,
)?;
bytes_processed += 8;
}
Ok(())
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
let (length, prefix_size) = reader
.read_varint_with_size()
.map_err(|error| error.with_context(CodecKind::BitSet))?;
let length = usize::try_from(length).map_err(|_| {
CodecError::invalid_encoding(
CodecKind::BitSet,
prefix_size,
InvalidEncodingReason::NegativeLength { value: length },
)
})?;
let mut words = Vec::with_capacity(length);
let mut bytes_processed = prefix_size;
for _ in 0..length {
let mut bytes = [0; 8];
read_exact_counted(reader, &mut bytes, CodecKind::BitSet, bytes_processed)?;
words.push(u64::from_be_bytes(bytes));
bytes_processed += 8;
}
Ok(Self(words))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
pub struct FixedBitSet<const N: usize>(
pub Vec<u8>,
);
impl<const N: usize> FixedBitSet<N> {
pub const BYTE_LEN: usize = N.div_ceil(8);
pub fn contains(&self, index: usize) -> bool {
index < N
&& self
.0
.get(index / 8)
.is_some_and(|byte| (byte & (1 << (index % 8))) != 0)
}
fn validate(
&self,
operation: CodecOperation,
bytes_processed: usize,
) -> Result<(), CodecError> {
if self.0.len() != Self::BYTE_LEN {
return Err(CodecError::invalid_encoding_for_operation(
CodecKind::FixedBitSet,
operation,
bytes_processed,
InvalidEncodingReason::InvalidFixedBitSetLength {
expected: Self::BYTE_LEN,
actual: self.0.len(),
},
));
}
if let Some(last_byte) = self.0.last()
&& N % 8 != 0
{
let allowed_mask = (1u8 << (N % 8)) - 1;
if last_byte & !allowed_mask != 0 {
return Err(CodecError::invalid_encoding_for_operation(
CodecKind::FixedBitSet,
operation,
bytes_processed,
InvalidEncodingReason::ValueOutOfRange {
terminal_byte: *last_byte,
allowed_mask,
},
));
}
}
Ok(())
}
}
impl<const N: usize> TypeCodec for FixedBitSet<N> {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
self.validate(CodecOperation::Write, 0)?;
write_all_counted(writer, &self.0, CodecKind::FixedBitSet, 0)
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
let mut bytes = vec![0; Self::BYTE_LEN];
read_exact_counted(reader, &mut bytes, CodecKind::FixedBitSet, 0)?;
let value = Self(bytes);
value.validate(CodecOperation::Read, Self::BYTE_LEN)?;
Ok(value)
}
}
macro_rules! impl_enum_repr {
($type:ident, $primitive:ty) => {
impl EnumRepr for $type {
fn from_discriminant(value: i128) -> Option<Self> {
<$primitive>::try_from(value).ok().map(Self)
}
fn discriminant(&self) -> i128 {
self.0 as i128
}
}
};
}
impl EnumRepr for Boolean {
fn from_discriminant(value: i128) -> Option<Self> {
match value {
0 => Some(Self(false)),
1 => Some(Self(true)),
_ => None,
}
}
fn discriminant(&self) -> i128 {
if self.0 { 1 } else { 0 }
}
}
impl_enum_repr!(Byte, i8);
impl_enum_repr!(UnsignedByte, u8);
impl_enum_repr!(Short, i16);
impl_enum_repr!(UnsignedShort, u16);
impl_enum_repr!(Int, i32);
impl_enum_repr!(Long, i64);
impl_enum_repr!(VarInt, i32);
impl_enum_repr!(VarLong, i64);
impl_enum_repr!(Angle, u8);