Skip to main content

steel_registry/data_components/components/
mob_visibility.rs

1//! Vanilla `minecraft:mob_visibility` item component.
2
3use std::io::{Cursor, Result, Write};
4
5use simdnbt::owned::{NbtCompound, NbtTag};
6use simdnbt::{FromNbtTag, ToNbtTag};
7use steel_utils::hash::{ComponentHasher, HashComponent};
8use steel_utils::nbt::NbtNumeric as _;
9use steel_utils::serial::{ReadFrom, WriteTo};
10
11use crate::RegistryHolderSet;
12use crate::entity_type::EntityType;
13
14/// Reduces how far the targeting entity types can see the wearer.
15///
16/// Vanilla: `ExtraCodecs.floatRange(0.0F, 10.0F).fieldOf("visibility")`.
17#[derive(Debug, Clone, PartialEq)]
18pub struct MobVisibility {
19    targeting_entity_types: RegistryHolderSet<EntityType>,
20    visibility: f32,
21}
22
23impl MobVisibility {
24    pub const MIN_VISIBILITY: f32 = 0.0;
25    pub const MAX_VISIBILITY: f32 = 10.0;
26
27    #[must_use]
28    pub const fn new(
29        targeting_entity_types: RegistryHolderSet<EntityType>,
30        visibility: f32,
31    ) -> Self {
32        Self {
33            targeting_entity_types,
34            visibility,
35        }
36    }
37
38    #[must_use]
39    pub const fn targeting_entity_types(&self) -> &RegistryHolderSet<EntityType> {
40        &self.targeting_entity_types
41    }
42
43    #[must_use]
44    pub const fn visibility(&self) -> f32 {
45        self.visibility
46    }
47}
48
49impl WriteTo for MobVisibility {
50    fn write(&self, writer: &mut impl Write) -> Result<()> {
51        self.targeting_entity_types.write(writer)?;
52        self.visibility.write(writer)
53    }
54}
55
56impl ReadFrom for MobVisibility {
57    fn read(data: &mut Cursor<&[u8]>) -> Result<Self> {
58        Ok(Self {
59            targeting_entity_types: RegistryHolderSet::read(data)?,
60            visibility: f32::read(data)?,
61        })
62    }
63}
64
65impl ToNbtTag for MobVisibility {
66    fn to_nbt_tag(self) -> NbtTag {
67        let mut compound = NbtCompound::new();
68        compound.insert(
69            "targeting_entity_types",
70            self.targeting_entity_types.to_nbt_tag(),
71        );
72        compound.insert("visibility", self.visibility);
73        NbtTag::Compound(compound)
74    }
75}
76
77impl FromNbtTag for MobVisibility {
78    fn from_nbt_tag(tag: simdnbt::borrow::NbtTag<'_, '_>) -> Option<Self> {
79        let compound = tag.compound()?;
80        let targeting_entity_types =
81            RegistryHolderSet::from_nbt_tag(compound.get("targeting_entity_types")?)?;
82        let visibility = compound.get("visibility")?.codec_f32()?;
83        (Self::MIN_VISIBILITY..=Self::MAX_VISIBILITY)
84            .contains(&visibility)
85            .then(|| Self::new(targeting_entity_types, visibility))
86    }
87}
88
89impl HashComponent for MobVisibility {
90    fn hash_component(&self, hasher: &mut ComponentHasher) {
91        self.clone().to_nbt_tag().hash_component(hasher);
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use std::io::Cursor;
98
99    use simdnbt::borrow::read_tag;
100    use simdnbt::{FromNbtTag, ToNbtTag as _};
101    use steel_utils::serial::{ReadFrom as _, WriteTo as _};
102
103    use super::MobVisibility;
104    use crate::RegistryHolderSet;
105    use crate::init_vanilla_registry;
106    use crate::vanilla_entities;
107
108    fn parse(tag: simdnbt::owned::NbtTag) -> Option<MobVisibility> {
109        let mut bytes = Vec::new();
110        tag.write(&mut bytes);
111        let borrowed = read_tag(&mut Cursor::new(bytes.as_slice())).ok()?;
112        MobVisibility::from_nbt_tag(borrowed.as_tag())
113    }
114
115    #[test]
116    fn round_trips_both_codecs() {
117        init_vanilla_registry();
118        let value = MobVisibility::new(
119            RegistryHolderSet::Direct(vec![&vanilla_entities::SKELETON]),
120            10.0,
121        );
122
123        assert_eq!(parse(value.clone().to_nbt_tag()), Some(value.clone()));
124
125        let mut bytes = Vec::new();
126        value.write(&mut bytes).expect("component should write");
127        assert_eq!(
128            MobVisibility::read(&mut Cursor::new(bytes.as_slice())).expect("component should read"),
129            value
130        );
131    }
132
133    #[test]
134    fn persistent_codec_rejects_out_of_range_visibility() {
135        init_vanilla_registry();
136        let mut compound = simdnbt::owned::NbtCompound::new();
137        compound.insert(
138            "targeting_entity_types",
139            simdnbt::owned::NbtTag::String("minecraft:skeleton".into()),
140        );
141        compound.insert("visibility", 11.0_f32);
142        assert_eq!(parse(simdnbt::owned::NbtTag::Compound(compound)), None);
143    }
144}