use std::{fmt, io::Read};
use mcproto_codec::error::{CodecError, CodecKind, CodecOperation, InvalidEncodingReason};
use crate::{
Float, GlobalPosition, Position, PrefixedOptional, ProtocolEnum, TextComponent, TypeCodec,
TypeStructCodec, Uuid, VarInt,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct InvalidRegistryIdValue {
value: i64,
}
impl InvalidRegistryIdValue {
#[must_use]
pub const fn value(self) -> i64 {
self.value
}
}
impl fmt::Display for InvalidRegistryIdValue {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
formatter,
"registry ID must be between 0 and {}, got {}",
i32::MAX,
self.value
)
}
}
impl std::error::Error for InvalidRegistryIdValue {}
macro_rules! registry_id_type {
($(#[$meta:meta])* $name:ident) => {
$(#[$meta])*
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct $name(u32);
impl $name {
pub const MAX: u32 = i32::MAX as u32;
pub const fn new(value: u32) -> Result<Self, InvalidRegistryIdValue> {
if value <= Self::MAX {
Ok(Self(value))
} else {
Err(InvalidRegistryIdValue {
value: value as i64,
})
}
}
#[must_use]
pub const fn get(self) -> u32 {
self.0
}
}
impl TryFrom<u32> for $name {
type Error = InvalidRegistryIdValue;
fn try_from(value: u32) -> Result<Self, Self::Error> {
Self::new(value)
}
}
impl From<$name> for u32 {
fn from(value: $name) -> Self {
value.get()
}
}
impl TypeCodec for $name {
fn encode(
&self,
writer: &mut impl std::io::Write,
) -> Result<(), CodecError> {
VarInt(self.0 as i32)
.encode(writer)
.map_err(|error| error.with_context(CodecKind::RegistryId))
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
let value = VarInt::decode(reader)
.map_err(|error| error.with_context(CodecKind::RegistryId))?
.0;
if value < 0 {
return Err(CodecError::invalid_encoding(
CodecKind::RegistryId,
0,
InvalidEncodingReason::InvalidRegistryId {
value,
max: i32::MAX,
},
));
}
Ok(Self(value as u32))
}
}
};
}
registry_id_type!( BlockStateId);
registry_id_type!( CatVariantId);
registry_id_type!( CatSoundVariantId);
registry_id_type!( CowVariantId);
registry_id_type!( CowSoundVariantId);
registry_id_type!( WolfVariantId);
registry_id_type!( WolfSoundVariantId);
registry_id_type!( FrogVariantId);
registry_id_type!( PigVariantId);
registry_id_type!( PigSoundVariantId);
registry_id_type!( ChickenVariantId);
registry_id_type!( ChickenSoundVariantId);
registry_id_type!( ZombieNautilusVariantId);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum InvalidNonAirBlockStateId {
Air,
OutOfRange(u32),
}
impl fmt::Display for InvalidNonAirBlockStateId {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Air => formatter.write_str(
"block state ID 0 is air and cannot be represented as a present optional state",
),
Self::OutOfRange(value) => write!(
formatter,
"block state ID must be between 1 and {}, got {value}",
i32::MAX
),
}
}
}
impl std::error::Error for InvalidNonAirBlockStateId {}
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct NonAirBlockStateId(BlockStateId);
impl NonAirBlockStateId {
pub const fn new(value: u32) -> Result<Self, InvalidNonAirBlockStateId> {
if value == 0 {
Err(InvalidNonAirBlockStateId::Air)
} else if value > i32::MAX as u32 {
Err(InvalidNonAirBlockStateId::OutOfRange(value))
} else {
Ok(Self(BlockStateId(value)))
}
}
#[must_use]
pub const fn get(self) -> u32 {
self.0.get()
}
#[must_use]
pub const fn block_state_id(self) -> BlockStateId {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum OptionalBlockState {
#[default]
Absent,
Present(NonAirBlockStateId),
}
impl TypeCodec for OptionalBlockState {
fn encode(&self, writer: &mut impl std::io::Write) -> Result<(), CodecError> {
let value = match self {
Self::Absent => 0,
Self::Present(state) => state.get() as i32,
};
VarInt(value)
.encode(writer)
.map_err(|error| error.with_context(CodecKind::EntityMetadataValue))
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
let value = VarInt::decode(reader)
.map_err(|error| error.with_context(CodecKind::EntityMetadataValue))?
.0;
match value {
0 => Ok(Self::Absent),
1.. => Ok(Self::Present(NonAirBlockStateId(BlockStateId(
value as u32,
)))),
_ => Err(CodecError::invalid_encoding(
CodecKind::EntityMetadataValue,
0,
InvalidEncodingReason::InvalidRegistryId {
value,
max: i32::MAX,
},
)),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct InvalidOptionalVarIntValue {
value: i32,
}
impl InvalidOptionalVarIntValue {
#[must_use]
pub const fn value(self) -> i32 {
self.value
}
}
impl fmt::Display for InvalidOptionalVarIntValue {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
formatter,
"Optional VarInt cannot represent present value {}",
self.value
)
}
}
impl std::error::Error for InvalidOptionalVarIntValue {}
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct OptionalVarInt(Option<i32>);
impl OptionalVarInt {
#[must_use]
pub const fn none() -> Self {
Self(None)
}
pub const fn some(value: i32) -> Result<Self, InvalidOptionalVarIntValue> {
if value == -1 || value == i32::MAX {
Err(InvalidOptionalVarIntValue { value })
} else {
Ok(Self(Some(value)))
}
}
#[must_use]
pub const fn value(self) -> Option<i32> {
self.0
}
#[must_use]
pub const fn is_some(self) -> bool {
self.0.is_some()
}
#[must_use]
pub const fn is_none(self) -> bool {
self.0.is_none()
}
}
impl TryFrom<Option<i32>> for OptionalVarInt {
type Error = InvalidOptionalVarIntValue;
fn try_from(value: Option<i32>) -> Result<Self, Self::Error> {
match value {
Some(value) => Self::some(value),
None => Ok(Self::none()),
}
}
}
impl TypeCodec for OptionalVarInt {
fn encode(&self, writer: &mut impl std::io::Write) -> Result<(), CodecError> {
let selector = match self.0 {
None => 0,
Some(value) => value.checked_add(1).ok_or_else(|| {
CodecError::invalid_encoding_for_operation(
CodecKind::EntityMetadataValue,
CodecOperation::Write,
0,
InvalidEncodingReason::InvalidOptionalVarInt { value },
)
})?,
};
VarInt(selector)
.encode(writer)
.map_err(|error| error.with_context(CodecKind::EntityMetadataValue))
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
let selector = VarInt::decode(reader)
.map_err(|error| error.with_context(CodecKind::EntityMetadataValue))?
.0;
if selector == 0 {
return Ok(Self::none());
}
let value = selector.checked_sub(1).ok_or_else(|| {
CodecError::invalid_encoding(
CodecKind::EntityMetadataValue,
0,
InvalidEncodingReason::InvalidOptionalVarInt { value: selector },
)
})?;
Ok(Self(Some(value)))
}
}
macro_rules! prefixed_optional_metadata_type {
($(#[$meta:meta])* $name:ident, $inner:ty) => {
$(#[$meta])*
#[repr(transparent)]
#[derive(Debug, Clone, PartialEq, TypeStructCodec)]
#[type_struct_codec(kind = EntityMetadataValue)]
pub struct $name(PrefixedOptional<$inner>);
impl $name {
#[must_use]
pub const fn some(value: $inner) -> Self {
Self(PrefixedOptional::some(value))
}
#[must_use]
pub const fn none() -> Self {
Self(PrefixedOptional::none())
}
#[must_use]
pub fn value(&self) -> Option<&$inner> {
self.0.0.0.as_ref()
}
#[must_use]
pub fn into_option(self) -> Option<$inner> {
self.0.into_option()
}
#[must_use]
pub const fn is_some(&self) -> bool {
self.0.is_some()
}
#[must_use]
pub const fn is_none(&self) -> bool {
self.0.is_none()
}
}
impl From<Option<$inner>> for $name {
fn from(value: Option<$inner>) -> Self {
Self(value.into())
}
}
impl From<$name> for Option<$inner> {
fn from(value: $name) -> Self {
value.into_option()
}
}
impl Default for $name {
fn default() -> Self {
Self::none()
}
}
};
}
prefixed_optional_metadata_type!( OptionalTextComponent, TextComponent);
prefixed_optional_metadata_type!( OptionalPosition, Position);
prefixed_optional_metadata_type!( OptionalLivingEntityReference, Uuid);
prefixed_optional_metadata_type!( OptionalGlobalPosition, GlobalPosition);
#[derive(Debug, Clone, Copy, PartialEq, Default, TypeStructCodec)]
#[type_struct_codec(kind = EntityMetadataValue)]
pub struct Rotations {
pub x: Float,
pub y: Float,
pub z: Float,
}
#[derive(Debug, Clone, Copy, PartialEq, Default, TypeStructCodec)]
#[type_struct_codec(kind = EntityMetadataValue)]
pub struct Vector3 {
pub x: Float,
pub y: Float,
pub z: Float,
}
#[derive(Debug, Clone, Copy, PartialEq, Default, TypeStructCodec)]
#[type_struct_codec(kind = EntityMetadataValue)]
pub struct Quaternion {
pub x: Float,
pub y: Float,
pub z: Float,
pub w: Float,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ProtocolEnum)]
#[protocol_enum(repr = VarInt)]
pub enum Direction {
Down = 0,
Up = 1,
North = 2,
South = 3,
West = 4,
East = 5,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ProtocolEnum)]
#[protocol_enum(repr = VarInt)]
pub enum Pose {
Standing = 0,
FallFlying = 1,
Sleeping = 2,
Swimming = 3,
SpinAttack = 4,
Sneaking = 5,
LongJumping = 6,
Dying = 7,
Croaking = 8,
UsingTongue = 9,
Sitting = 10,
Roaring = 11,
Sniffing = 12,
Emerging = 13,
Digging = 14,
Sliding = 15,
Shooting = 16,
Inhaling = 17,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ProtocolEnum)]
#[protocol_enum(repr = VarInt)]
pub enum VillagerType {
Desert = 0,
Jungle = 1,
Plains = 2,
Savanna = 3,
Snow = 4,
Swamp = 5,
Taiga = 6,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ProtocolEnum)]
#[protocol_enum(repr = VarInt)]
pub enum VillagerProfession {
None = 0,
Armorer = 1,
Butcher = 2,
Cartographer = 3,
Cleric = 4,
Farmer = 5,
Fisherman = 6,
Fletcher = 7,
Leatherworker = 8,
Librarian = 9,
Mason = 10,
Nitwit = 11,
Shepherd = 12,
Toolsmith = 13,
Weaponsmith = 14,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, TypeStructCodec)]
#[type_struct_codec(kind = EntityMetadataValue)]
pub struct VillagerData {
pub villager_type: VillagerType,
pub profession: VillagerProfession,
pub level: VarInt,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ProtocolEnum)]
#[protocol_enum(repr = VarInt)]
pub enum SnifferState {
Idling = 0,
FeelingHappy = 1,
Scenting = 2,
Sniffing = 3,
Searching = 4,
Digging = 5,
Rising = 6,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ProtocolEnum)]
#[protocol_enum(repr = VarInt)]
pub enum ArmadilloState {
Idle = 0,
Rolling = 1,
Scared = 2,
Unrolling = 3,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ProtocolEnum)]
#[protocol_enum(repr = VarInt)]
pub enum CopperGolemState {
Idle = 0,
GettingItem = 1,
GettingNoItem = 2,
DroppingItem = 3,
DroppingNoItem = 4,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ProtocolEnum)]
#[protocol_enum(repr = VarInt)]
pub enum WeatheringCopperState {
Unaffected = 0,
Exposed = 1,
Weathered = 2,
Oxidized = 3,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ProtocolEnum)]
#[protocol_enum(repr = VarInt)]
pub enum HumanoidArm {
Left = 0,
Right = 1,
}