steel_registry/data_components/components/
registry_holder_sets.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::serial::{ReadFrom, WriteTo};
9
10use crate::RegistryHolderSet;
11use crate::banner_pattern::BannerPattern;
12use crate::damage_type::{DamageType, DamageTypeRef};
13use crate::item_stack::ItemStack;
14use crate::items::Item;
15
16pub type ProvidesBannerPatterns = RegistryHolderSet<BannerPattern>;
18
19#[derive(Debug, Clone, PartialEq)]
21pub struct DamageResistant {
22 types: RegistryHolderSet<DamageType>,
23}
24
25impl DamageResistant {
26 #[must_use]
27 pub const fn new(types: RegistryHolderSet<DamageType>) -> Self {
28 Self { types }
29 }
30
31 #[must_use]
32 pub const fn types(&self) -> &RegistryHolderSet<DamageType> {
33 &self.types
34 }
35
36 #[must_use]
38 pub fn is_resistant_to(&self, damage_type: DamageTypeRef) -> bool {
39 self.types.contains(damage_type)
40 }
41}
42
43impl WriteTo for DamageResistant {
44 fn write(&self, writer: &mut impl Write) -> Result<()> {
45 self.types.write(writer)
46 }
47}
48
49impl ReadFrom for DamageResistant {
50 fn read(data: &mut Cursor<&[u8]>) -> Result<Self> {
51 Ok(Self::new(RegistryHolderSet::read(data)?))
52 }
53}
54
55impl ToNbtTag for DamageResistant {
56 fn to_nbt_tag(self) -> NbtTag {
57 let mut compound = NbtCompound::new();
58 compound.insert("types", self.types.to_nbt_tag());
59 NbtTag::Compound(compound)
60 }
61}
62
63impl FromNbtTag for DamageResistant {
64 fn from_nbt_tag(tag: simdnbt::borrow::NbtTag<'_, '_>) -> Option<Self> {
65 let compound = tag.compound()?;
66 Some(Self::new(RegistryHolderSet::from_nbt_tag(
67 compound.get("types")?,
68 )?))
69 }
70}
71
72impl HashComponent for DamageResistant {
73 fn hash_component(&self, hasher: &mut ComponentHasher) {
74 self.clone().to_nbt_tag().hash_component(hasher);
75 }
76}
77
78#[derive(Debug, Clone, PartialEq)]
80pub struct Repairable {
81 items: RegistryHolderSet<Item>,
82}
83
84impl Repairable {
85 #[must_use]
86 pub const fn new(items: RegistryHolderSet<Item>) -> Self {
87 Self { items }
88 }
89
90 #[must_use]
91 pub const fn items(&self) -> &RegistryHolderSet<Item> {
92 &self.items
93 }
94
95 #[must_use]
97 pub fn is_valid_repair_item(&self, repair_item: &ItemStack) -> bool {
98 self.items.contains(repair_item.item())
99 }
100}
101
102impl WriteTo for Repairable {
103 fn write(&self, writer: &mut impl Write) -> Result<()> {
104 self.items.write(writer)
105 }
106}
107
108impl ReadFrom for Repairable {
109 fn read(data: &mut Cursor<&[u8]>) -> Result<Self> {
110 Ok(Self::new(RegistryHolderSet::read(data)?))
111 }
112}
113
114impl ToNbtTag for Repairable {
115 fn to_nbt_tag(self) -> NbtTag {
116 let mut compound = NbtCompound::new();
117 compound.insert("items", self.items.to_nbt_tag());
118 NbtTag::Compound(compound)
119 }
120}
121
122impl FromNbtTag for Repairable {
123 fn from_nbt_tag(tag: simdnbt::borrow::NbtTag<'_, '_>) -> Option<Self> {
124 let compound = tag.compound()?;
125 Some(Self::new(RegistryHolderSet::from_nbt_tag(
126 compound.get("items")?,
127 )?))
128 }
129}
130
131impl HashComponent for Repairable {
132 fn hash_component(&self, hasher: &mut ComponentHasher) {
133 self.clone().to_nbt_tag().hash_component(hasher);
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use std::io::Cursor;
140
141 use simdnbt::borrow::read_tag;
142 use simdnbt::{FromNbtTag, ToNbtTag as _};
143 use steel_utils::hash::HashComponent as _;
144 use steel_utils::serial::{ReadFrom as _, WriteTo as _};
145
146 use super::{DamageResistant, ProvidesBannerPatterns, Repairable};
147 use crate::REGISTRY;
148 use crate::RegistryHolderSet;
149 use crate::data_components::vanilla_components::{
150 DAMAGE_RESISTANT, PROVIDES_BANNER_PATTERNS, REPAIRABLE,
151 };
152 use crate::init_vanilla_registry;
153 use crate::item_stack::ItemStack;
154 use crate::vanilla_banner_pattern_tags::BannerPatternTag;
155 use crate::vanilla_banner_patterns;
156 use crate::vanilla_damage_type_tags::DamageTypeTag;
157 use crate::vanilla_damage_types;
158 use crate::vanilla_item_tags::ItemTag;
159 use crate::vanilla_items;
160
161 fn parse<T: FromNbtTag>(tag: simdnbt::owned::NbtTag) -> Option<T> {
162 let mut bytes = Vec::new();
163 tag.write(&mut bytes);
164 let borrowed = read_tag(&mut Cursor::new(bytes.as_slice())).ok()?;
165 T::from_nbt_tag(borrowed.as_tag())
166 }
167
168 #[test]
169 fn damage_resistant_uses_the_vanilla_record_and_holder_set_codecs() {
170 init_vanilla_registry();
171
172 let component = DamageResistant::new(RegistryHolderSet::Tag(DamageTypeTag::IS_FIRE));
173 assert!(component.is_resistant_to(&vanilla_damage_types::IN_FIRE));
174 assert!(!component.is_resistant_to(&vanilla_damage_types::GENERIC));
175 assert_eq!(
176 parse::<DamageResistant>(component.clone().to_nbt_tag()),
177 Some(component.clone())
178 );
179 assert_eq!(
180 component.compute_hash(),
181 component.clone().to_nbt_tag().compute_hash()
182 );
183
184 let mut bytes = Vec::new();
185 component.write(&mut bytes).expect("component should write");
186 assert_eq!(
187 DamageResistant::read(&mut Cursor::new(bytes.as_slice()))
188 .expect("component should read"),
189 component
190 );
191 }
192
193 #[test]
194 fn repairable_accepts_tagged_and_direct_repair_items() {
195 init_vanilla_registry();
196
197 let wooden = Repairable::new(RegistryHolderSet::Tag(ItemTag::WOODEN_TOOL_MATERIALS));
198 assert!(wooden.is_valid_repair_item(&ItemStack::new(&vanilla_items::OAK_PLANKS)));
199 assert!(!wooden.is_valid_repair_item(&ItemStack::new(&vanilla_items::DIAMOND)));
200
201 let direct = Repairable::new(RegistryHolderSet::Direct(vec![
202 &vanilla_items::PHANTOM_MEMBRANE,
203 ]));
204 assert!(direct.is_valid_repair_item(&ItemStack::new(&vanilla_items::PHANTOM_MEMBRANE)));
205 assert!(!direct.is_valid_repair_item(&ItemStack::new(&vanilla_items::BREEZE_ROD)));
206 assert_eq!(
207 parse::<Repairable>(direct.clone().to_nbt_tag()),
208 Some(direct.clone())
209 );
210
211 let mut bytes = Vec::new();
212 direct.write(&mut bytes).expect("component should write");
213 assert_eq!(
214 Repairable::read(&mut Cursor::new(bytes.as_slice())).expect("component should read"),
215 direct
216 );
217 }
218
219 #[test]
220 fn provides_banner_patterns_uses_fixed_registry_holder_sets() {
221 init_vanilla_registry();
222
223 let component = ProvidesBannerPatterns::Tag(BannerPatternTag::PATTERN_ITEM_FLOWER);
224 assert!(component.contains(&vanilla_banner_patterns::FLOWER));
225 assert_eq!(
226 component.clone().to_nbt_tag(),
227 simdnbt::owned::NbtTag::String("#minecraft:pattern_item/flower".into())
228 );
229 assert_eq!(
230 parse::<ProvidesBannerPatterns>(component.clone().to_nbt_tag()),
231 Some(component.clone())
232 );
233 assert_eq!(
234 component.compute_hash(),
235 component.clone().to_nbt_tag().compute_hash()
236 );
237
238 let mut bytes = Vec::new();
239 component.write(&mut bytes).expect("component should write");
240 assert_eq!(
241 ProvidesBannerPatterns::read(&mut Cursor::new(bytes.as_slice()))
242 .expect("component should read"),
243 component
244 );
245 }
246
247 #[test]
248 fn extracted_item_prototypes_include_all_holder_set_components() {
249 init_vanilla_registry();
250
251 let damage_resistant_count = REGISTRY
252 .items
253 .iter()
254 .filter(|(_, item)| item.components.has(DAMAGE_RESISTANT))
255 .count();
256 let repairable_count = REGISTRY
257 .items
258 .iter()
259 .filter(|(_, item)| item.components.has(REPAIRABLE))
260 .count();
261 assert_eq!(damage_resistant_count, 17);
262 assert_eq!(repairable_count, 75);
263
264 let banner_pattern_count = REGISTRY
265 .items
266 .iter()
267 .filter(|(_, item)| item.components.has(PROVIDES_BANNER_PATTERNS))
268 .count();
269 assert_eq!(banner_pattern_count, 10);
270
271 let flower_pattern = ItemStack::new(&vanilla_items::FLOWER_BANNER_PATTERN);
272 assert!(
273 flower_pattern
274 .get(PROVIDES_BANNER_PATTERNS)
275 .is_some_and(|patterns| patterns.contains(&vanilla_banner_patterns::FLOWER))
276 );
277
278 let netherite = ItemStack::new(&vanilla_items::NETHERITE_INGOT);
279 assert!(!netherite.can_be_hurt_by(&vanilla_damage_types::IN_FIRE));
280 assert!(netherite.can_be_hurt_by(&vanilla_damage_types::GENERIC));
281
282 let nether_star = ItemStack::new(&vanilla_items::NETHER_STAR);
283 assert!(!nether_star.can_be_hurt_by(&vanilla_damage_types::EXPLOSION));
284 assert!(nether_star.can_be_hurt_by(&vanilla_damage_types::IN_FIRE));
285
286 let elytra = ItemStack::new(&vanilla_items::ELYTRA);
287 assert!(elytra.is_valid_repair_item(&ItemStack::new(&vanilla_items::PHANTOM_MEMBRANE)));
288 assert!(!elytra.is_valid_repair_item(&ItemStack::new(&vanilla_items::BREEZE_ROD)));
289
290 let mace = ItemStack::new(&vanilla_items::MACE);
291 assert!(mace.is_valid_repair_item(&ItemStack::new(&vanilla_items::BREEZE_ROD)));
292 assert!(!mace.is_valid_repair_item(&ItemStack::new(&vanilla_items::PHANTOM_MEMBRANE)));
293 }
294}