steel_registry/data_components/components/
mob_visibility.rs1use 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#[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}