Skip to main content

mcproto_types/entity_metadata/
types.rs

1//! Structures used by entity metadata value payloads.
2
3use std::{fmt, io::Read};
4
5use mcproto_codec::error::{CodecError, CodecKind, CodecOperation, InvalidEncodingReason};
6
7use crate::{
8    Float, GlobalPosition, Position, PrefixedOptional, ProtocolEnum, TextComponent, TypeCodec,
9    TypeStructCodec, Uuid, VarInt,
10};
11
12/// Error returned when a numeric registry ID does not fit a non-negative VarInt.
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14pub struct InvalidRegistryIdValue {
15    value: i64,
16}
17
18impl InvalidRegistryIdValue {
19    /// Returns the rejected numeric value.
20    #[must_use]
21    pub const fn value(self) -> i64 {
22        self.value
23    }
24}
25
26impl fmt::Display for InvalidRegistryIdValue {
27    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
28        write!(
29            formatter,
30            "registry ID must be between 0 and {}, got {}",
31            i32::MAX,
32            self.value
33        )
34    }
35}
36
37impl std::error::Error for InvalidRegistryIdValue {}
38
39macro_rules! registry_id_type {
40    ($(#[$meta:meta])* $name:ident) => {
41        $(#[$meta])*
42        #[repr(transparent)]
43        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
44        pub struct $name(u32);
45
46        impl $name {
47            /// Greatest registry ID representable by the protocol VarInt.
48            pub const MAX: u32 = i32::MAX as u32;
49
50            /// Creates a validated registry ID.
51            pub const fn new(value: u32) -> Result<Self, InvalidRegistryIdValue> {
52                if value <= Self::MAX {
53                    Ok(Self(value))
54                } else {
55                    Err(InvalidRegistryIdValue {
56                        value: value as i64,
57                    })
58                }
59            }
60
61            /// Returns the zero-based numeric registry ID.
62            #[must_use]
63            pub const fn get(self) -> u32 {
64                self.0
65            }
66        }
67
68        impl TryFrom<u32> for $name {
69            type Error = InvalidRegistryIdValue;
70
71            fn try_from(value: u32) -> Result<Self, Self::Error> {
72                Self::new(value)
73            }
74        }
75
76        impl From<$name> for u32 {
77            fn from(value: $name) -> Self {
78                value.get()
79            }
80        }
81
82        impl TypeCodec for $name {
83            fn encode(
84                &self,
85                writer: &mut impl std::io::Write,
86            ) -> Result<(), CodecError> {
87                VarInt(self.0 as i32)
88                    .encode(writer)
89                    .map_err(|error| error.with_context(CodecKind::RegistryId))
90            }
91
92            fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
93                let value = VarInt::decode(reader)
94                    .map_err(|error| error.with_context(CodecKind::RegistryId))?
95                    .0;
96                if value < 0 {
97                    return Err(CodecError::invalid_encoding(
98                        CodecKind::RegistryId,
99                        0,
100                        InvalidEncodingReason::InvalidRegistryId {
101                            value,
102                            max: i32::MAX,
103                        },
104                    ));
105                }
106                Ok(Self(value as u32))
107            }
108        }
109    };
110}
111
112registry_id_type!(/// An ID in the `minecraft:block_state` registry.
113    BlockStateId);
114registry_id_type!(/// An ID in the `minecraft:cat_variant` registry.
115    CatVariantId);
116registry_id_type!(/// An ID in the `minecraft:cat_sound_variant` registry.
117    CatSoundVariantId);
118registry_id_type!(/// An ID in the `minecraft:cow_variant` registry.
119    CowVariantId);
120registry_id_type!(/// An ID in the `minecraft:cow_sound_variant` registry.
121    CowSoundVariantId);
122registry_id_type!(/// An ID in the `minecraft:wolf_variant` registry.
123    WolfVariantId);
124registry_id_type!(/// An ID in the `minecraft:wolf_sound_variant` registry.
125    WolfSoundVariantId);
126registry_id_type!(/// An ID in the `minecraft:frog_variant` registry.
127    FrogVariantId);
128registry_id_type!(/// An ID in the `minecraft:pig_variant` registry.
129    PigVariantId);
130registry_id_type!(/// An ID in the `minecraft:pig_sound_variant` registry.
131    PigSoundVariantId);
132registry_id_type!(/// An ID in the `minecraft:chicken_variant` registry.
133    ChickenVariantId);
134registry_id_type!(/// An ID in the `minecraft:chicken_sound_variant` registry.
135    ChickenSoundVariantId);
136registry_id_type!(/// An ID in the `minecraft:zombie_nautilus_variant` registry.
137    ZombieNautilusVariantId);
138
139/// Error returned when constructing a block-state reference that cannot be air.
140#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
141pub enum InvalidNonAirBlockStateId {
142    /// Registry ID zero denotes air and is reserved as the absent marker.
143    Air,
144    /// The ID cannot be represented by a non-negative VarInt.
145    OutOfRange(u32),
146}
147
148impl fmt::Display for InvalidNonAirBlockStateId {
149    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
150        match self {
151            Self::Air => formatter.write_str(
152                "block state ID 0 is air and cannot be represented as a present optional state",
153            ),
154            Self::OutOfRange(value) => write!(
155                formatter,
156                "block state ID must be between 1 and {}, got {value}",
157                i32::MAX
158            ),
159        }
160    }
161}
162
163impl std::error::Error for InvalidNonAirBlockStateId {}
164
165/// A non-air block state usable in the Optional Block State encoding.
166#[repr(transparent)]
167#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
168pub struct NonAirBlockStateId(BlockStateId);
169
170impl NonAirBlockStateId {
171    /// Creates a block-state ID in the inclusive range `1..=i32::MAX`.
172    pub const fn new(value: u32) -> Result<Self, InvalidNonAirBlockStateId> {
173        if value == 0 {
174            Err(InvalidNonAirBlockStateId::Air)
175        } else if value > i32::MAX as u32 {
176            Err(InvalidNonAirBlockStateId::OutOfRange(value))
177        } else {
178            Ok(Self(BlockStateId(value)))
179        }
180    }
181
182    /// Returns the zero-based block-state registry ID.
183    #[must_use]
184    pub const fn get(self) -> u32 {
185        self.0.get()
186    }
187
188    /// Returns this ID as a regular block-state ID.
189    #[must_use]
190    pub const fn block_state_id(self) -> BlockStateId {
191        self.0
192    }
193}
194
195/// A block state encoded with zero reserved for absence.
196///
197/// Unlike a prefixed optional, this value has no Boolean marker. A zero VarInt
198/// is absent; every positive VarInt is a non-air block-state registry ID.
199#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
200pub enum OptionalBlockState {
201    /// No block state. Encoded as `VarInt(0)`.
202    #[default]
203    Absent,
204    /// A non-air block state, encoded as its unmodified positive registry ID.
205    Present(NonAirBlockStateId),
206}
207
208impl TypeCodec for OptionalBlockState {
209    fn encode(&self, writer: &mut impl std::io::Write) -> Result<(), CodecError> {
210        let value = match self {
211            Self::Absent => 0,
212            Self::Present(state) => state.get() as i32,
213        };
214        VarInt(value)
215            .encode(writer)
216            .map_err(|error| error.with_context(CodecKind::EntityMetadataValue))
217    }
218
219    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
220        let value = VarInt::decode(reader)
221            .map_err(|error| error.with_context(CodecKind::EntityMetadataValue))?
222            .0;
223        match value {
224            0 => Ok(Self::Absent),
225            1.. => Ok(Self::Present(NonAirBlockStateId(BlockStateId(
226                value as u32,
227            )))),
228            _ => Err(CodecError::invalid_encoding(
229                CodecKind::EntityMetadataValue,
230                0,
231                InvalidEncodingReason::InvalidRegistryId {
232                    value,
233                    max: i32::MAX,
234                },
235            )),
236        }
237    }
238}
239
240/// Error returned for an Optional VarInt value with no unambiguous wire form.
241#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
242pub struct InvalidOptionalVarIntValue {
243    value: i32,
244}
245
246impl InvalidOptionalVarIntValue {
247    /// Returns the rejected present value.
248    #[must_use]
249    pub const fn value(self) -> i32 {
250        self.value
251    }
252}
253
254impl fmt::Display for InvalidOptionalVarIntValue {
255    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
256        write!(
257            formatter,
258            "Optional VarInt cannot represent present value {}",
259            self.value
260        )
261    }
262}
263
264impl std::error::Error for InvalidOptionalVarIntValue {}
265
266/// An optional VarInt encoded as zero for absence and `value + 1` for presence.
267///
268/// The in-memory value is the actual value, not the incremented wire selector.
269/// `-1` collides with the absent selector and `i32::MAX` cannot be incremented,
270/// so constructors reject those two present values.
271#[repr(transparent)]
272#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
273pub struct OptionalVarInt(Option<i32>);
274
275impl OptionalVarInt {
276    /// Creates an absent value.
277    #[must_use]
278    pub const fn none() -> Self {
279        Self(None)
280    }
281
282    /// Creates a present value when `value + 1` has an unambiguous VarInt form.
283    pub const fn some(value: i32) -> Result<Self, InvalidOptionalVarIntValue> {
284        if value == -1 || value == i32::MAX {
285            Err(InvalidOptionalVarIntValue { value })
286        } else {
287            Ok(Self(Some(value)))
288        }
289    }
290
291    /// Returns the actual present value, before wire incrementing.
292    #[must_use]
293    pub const fn value(self) -> Option<i32> {
294        self.0
295    }
296
297    /// Returns whether the value is present.
298    #[must_use]
299    pub const fn is_some(self) -> bool {
300        self.0.is_some()
301    }
302
303    /// Returns whether the value is absent.
304    #[must_use]
305    pub const fn is_none(self) -> bool {
306        self.0.is_none()
307    }
308}
309
310impl TryFrom<Option<i32>> for OptionalVarInt {
311    type Error = InvalidOptionalVarIntValue;
312
313    fn try_from(value: Option<i32>) -> Result<Self, Self::Error> {
314        match value {
315            Some(value) => Self::some(value),
316            None => Ok(Self::none()),
317        }
318    }
319}
320
321impl TypeCodec for OptionalVarInt {
322    fn encode(&self, writer: &mut impl std::io::Write) -> Result<(), CodecError> {
323        let selector = match self.0 {
324            None => 0,
325            Some(value) => value.checked_add(1).ok_or_else(|| {
326                CodecError::invalid_encoding_for_operation(
327                    CodecKind::EntityMetadataValue,
328                    CodecOperation::Write,
329                    0,
330                    InvalidEncodingReason::InvalidOptionalVarInt { value },
331                )
332            })?,
333        };
334        VarInt(selector)
335            .encode(writer)
336            .map_err(|error| error.with_context(CodecKind::EntityMetadataValue))
337    }
338
339    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
340        let selector = VarInt::decode(reader)
341            .map_err(|error| error.with_context(CodecKind::EntityMetadataValue))?
342            .0;
343        if selector == 0 {
344            return Ok(Self::none());
345        }
346        let value = selector.checked_sub(1).ok_or_else(|| {
347            CodecError::invalid_encoding(
348                CodecKind::EntityMetadataValue,
349                0,
350                InvalidEncodingReason::InvalidOptionalVarInt { value: selector },
351            )
352        })?;
353        Ok(Self(Some(value)))
354    }
355}
356
357macro_rules! prefixed_optional_metadata_type {
358    ($(#[$meta:meta])* $name:ident, $inner:ty) => {
359        $(#[$meta])*
360        #[repr(transparent)]
361        #[derive(Debug, Clone, PartialEq, TypeStructCodec)]
362        #[type_struct_codec(kind = EntityMetadataValue)]
363        pub struct $name(PrefixedOptional<$inner>);
364
365        impl $name {
366            /// Creates a present value.
367            #[must_use]
368            pub const fn some(value: $inner) -> Self {
369                Self(PrefixedOptional::some(value))
370            }
371
372            /// Creates an absent value.
373            #[must_use]
374            pub const fn none() -> Self {
375                Self(PrefixedOptional::none())
376            }
377
378            /// Returns the contained value by reference, if present.
379            #[must_use]
380            pub fn value(&self) -> Option<&$inner> {
381                self.0.0.0.as_ref()
382            }
383
384            /// Extracts the wrapped optional value.
385            #[must_use]
386            pub fn into_option(self) -> Option<$inner> {
387                self.0.into_option()
388            }
389
390            /// Returns whether this value is present.
391            #[must_use]
392            pub const fn is_some(&self) -> bool {
393                self.0.is_some()
394            }
395
396            /// Returns whether this value is absent.
397            #[must_use]
398            pub const fn is_none(&self) -> bool {
399                self.0.is_none()
400            }
401        }
402
403        impl From<Option<$inner>> for $name {
404            fn from(value: Option<$inner>) -> Self {
405                Self(value.into())
406            }
407        }
408
409        impl From<$name> for Option<$inner> {
410            fn from(value: $name) -> Self {
411                value.into_option()
412            }
413        }
414
415        impl Default for $name {
416            fn default() -> Self {
417                Self::none()
418            }
419        }
420    };
421}
422
423prefixed_optional_metadata_type!(/// A Boolean-prefixed optional text component.
424    OptionalTextComponent, TextComponent);
425prefixed_optional_metadata_type!(/// A Boolean-prefixed optional packed block position.
426    OptionalPosition, Position);
427prefixed_optional_metadata_type!(/// A Boolean-prefixed optional living-entity UUID reference.
428    OptionalLivingEntityReference, Uuid);
429prefixed_optional_metadata_type!(/// A Boolean-prefixed optional dimension and block position.
430    OptionalGlobalPosition, GlobalPosition);
431
432/// Three Euler rotation components encoded as consecutive Floats.
433#[derive(Debug, Clone, Copy, PartialEq, Default, TypeStructCodec)]
434#[type_struct_codec(kind = EntityMetadataValue)]
435pub struct Rotations {
436    /// Rotation around the x axis.
437    pub x: Float,
438    /// Rotation around the y axis.
439    pub y: Float,
440    /// Rotation around the z axis.
441    pub z: Float,
442}
443
444/// A three-dimensional vector encoded as consecutive Floats.
445#[derive(Debug, Clone, Copy, PartialEq, Default, TypeStructCodec)]
446#[type_struct_codec(kind = EntityMetadataValue)]
447pub struct Vector3 {
448    /// X component.
449    pub x: Float,
450    /// Y component.
451    pub y: Float,
452    /// Z component.
453    pub z: Float,
454}
455
456/// A quaternion encoded as x, y, z, and w Floats.
457#[derive(Debug, Clone, Copy, PartialEq, Default, TypeStructCodec)]
458#[type_struct_codec(kind = EntityMetadataValue)]
459pub struct Quaternion {
460    /// X component.
461    pub x: Float,
462    /// Y component.
463    pub y: Float,
464    /// Z component.
465    pub z: Float,
466    /// W component.
467    pub w: Float,
468}
469
470/// The six block-face directions used by entity metadata.
471#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ProtocolEnum)]
472#[protocol_enum(repr = VarInt)]
473pub enum Direction {
474    Down = 0,
475    Up = 1,
476    North = 2,
477    South = 3,
478    West = 4,
479    East = 5,
480}
481
482/// An entity pose from the metadata Pose value type.
483#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ProtocolEnum)]
484#[protocol_enum(repr = VarInt)]
485pub enum Pose {
486    Standing = 0,
487    FallFlying = 1,
488    Sleeping = 2,
489    Swimming = 3,
490    SpinAttack = 4,
491    Sneaking = 5,
492    LongJumping = 6,
493    Dying = 7,
494    Croaking = 8,
495    UsingTongue = 9,
496    Sitting = 10,
497    Roaring = 11,
498    Sniffing = 12,
499    Emerging = 13,
500    Digging = 14,
501    Sliding = 15,
502    Shooting = 16,
503    Inhaling = 17,
504}
505
506/// A built-in villager biome type.
507#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ProtocolEnum)]
508#[protocol_enum(repr = VarInt)]
509pub enum VillagerType {
510    Desert = 0,
511    Jungle = 1,
512    Plains = 2,
513    Savanna = 3,
514    Snow = 4,
515    Swamp = 5,
516    Taiga = 6,
517}
518
519/// A built-in villager profession.
520#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ProtocolEnum)]
521#[protocol_enum(repr = VarInt)]
522pub enum VillagerProfession {
523    None = 0,
524    Armorer = 1,
525    Butcher = 2,
526    Cartographer = 3,
527    Cleric = 4,
528    Farmer = 5,
529    Fisherman = 6,
530    Fletcher = 7,
531    Leatherworker = 8,
532    Librarian = 9,
533    Mason = 10,
534    Nitwit = 11,
535    Shepherd = 12,
536    Toolsmith = 13,
537    Weaponsmith = 14,
538}
539
540/// Villager biome type, profession, and level metadata.
541#[derive(Debug, Clone, Copy, PartialEq, Eq, TypeStructCodec)]
542#[type_struct_codec(kind = EntityMetadataValue)]
543pub struct VillagerData {
544    /// Entry in the `minecraft:villager_type` registry.
545    pub villager_type: VillagerType,
546    /// Entry in the `minecraft:villager_profession` registry.
547    pub profession: VillagerProfession,
548    /// Villager trading level.
549    pub level: VarInt,
550}
551
552/// State used by sniffer entities.
553#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ProtocolEnum)]
554#[protocol_enum(repr = VarInt)]
555pub enum SnifferState {
556    Idling = 0,
557    FeelingHappy = 1,
558    Scenting = 2,
559    Sniffing = 3,
560    Searching = 4,
561    Digging = 5,
562    Rising = 6,
563}
564
565/// State used by armadillo entities.
566#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ProtocolEnum)]
567#[protocol_enum(repr = VarInt)]
568pub enum ArmadilloState {
569    Idle = 0,
570    Rolling = 1,
571    Scared = 2,
572    Unrolling = 3,
573}
574
575/// State used by copper golem entities.
576#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ProtocolEnum)]
577#[protocol_enum(repr = VarInt)]
578pub enum CopperGolemState {
579    Idle = 0,
580    GettingItem = 1,
581    GettingNoItem = 2,
582    DroppingItem = 3,
583    DroppingNoItem = 4,
584}
585
586/// Weathering stage for copper golem metadata.
587#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ProtocolEnum)]
588#[protocol_enum(repr = VarInt)]
589pub enum WeatheringCopperState {
590    Unaffected = 0,
591    Exposed = 1,
592    Weathered = 2,
593    Oxidized = 3,
594}
595
596/// Dominant humanoid arm.
597#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ProtocolEnum)]
598#[protocol_enum(repr = VarInt)]
599pub enum HumanoidArm {
600    Left = 0,
601    Right = 1,
602}