Skip to main content

steel_registry/
decorated_pot_pattern.rs

1//! Decorated pot pattern registry values.
2
3use std::io::{Cursor, Result as IoResult, Write};
4
5use rustc_hash::FxHashMap;
6use simdnbt::owned::{NbtCompound, NbtTag};
7use simdnbt::{FromNbtTag, ToNbtTag};
8use steel_utils::Identifier;
9use steel_utils::hash::{ComponentHasher, HashComponent, HashEntry};
10use steel_utils::serial::{ReadFrom, WriteTo};
11
12/// Complete registry-independent decorated pot pattern definition.
13///
14/// Mirrors vanilla's `DecoratedPotPattern(ResourceLocation assetId)` record.
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct DecoratedPotPatternValue {
17    asset_id: Identifier,
18}
19
20impl DecoratedPotPatternValue {
21    #[must_use]
22    pub const fn new(asset_id: Identifier) -> Self {
23        Self { asset_id }
24    }
25
26    #[must_use]
27    pub const fn asset_id(&self) -> &Identifier {
28        &self.asset_id
29    }
30
31    fn to_nbt_tag_ref(&self) -> NbtTag {
32        let mut compound = NbtCompound::new();
33        compound.insert("asset_id", self.asset_id.clone());
34        NbtTag::Compound(compound)
35    }
36}
37
38impl WriteTo for DecoratedPotPatternValue {
39    fn write(&self, writer: &mut impl Write) -> IoResult<()> {
40        self.asset_id.write(writer)
41    }
42}
43
44impl ReadFrom for DecoratedPotPatternValue {
45    fn read(data: &mut Cursor<&[u8]>) -> IoResult<Self> {
46        Ok(Self::new(Identifier::read(data)?))
47    }
48}
49
50impl ToNbtTag for DecoratedPotPatternValue {
51    fn to_nbt_tag(self) -> NbtTag {
52        self.to_nbt_tag_ref()
53    }
54}
55
56impl FromNbtTag for DecoratedPotPatternValue {
57    fn from_nbt_tag(tag: simdnbt::borrow::NbtTag) -> Option<Self> {
58        let compound = tag.compound()?;
59        let asset_id = Identifier::from_nbt_tag(compound.get("asset_id")?)?;
60        Some(Self::new(asset_id))
61    }
62}
63
64impl HashComponent for DecoratedPotPatternValue {
65    fn hash_component(&self, hasher: &mut ComponentHasher) {
66        let mut key_hasher = ComponentHasher::new();
67        key_hasher.put_string("asset_id");
68        let mut value_hasher = ComponentHasher::new();
69        self.asset_id.hash_component(&mut value_hasher);
70        let entry = HashEntry::new(key_hasher, value_hasher);
71
72        hasher.start_map();
73        hasher.put_raw_bytes(&entry.key_bytes);
74        hasher.put_raw_bytes(&entry.value_bytes);
75        hasher.end_map();
76    }
77}
78
79/// Registered decorated pot pattern definition.
80#[derive(Debug)]
81pub struct DecoratedPotPattern {
82    pub key: Identifier,
83    value: DecoratedPotPatternValue,
84}
85
86impl DecoratedPotPattern {
87    #[must_use]
88    pub const fn new(key: Identifier, value: DecoratedPotPatternValue) -> Self {
89        Self { key, value }
90    }
91
92    #[must_use]
93    pub const fn value(&self) -> &DecoratedPotPatternValue {
94        &self.value
95    }
96}
97
98impl ToNbtTag for &DecoratedPotPattern {
99    fn to_nbt_tag(self) -> NbtTag {
100        self.value.to_nbt_tag_ref()
101    }
102}
103
104pub type DecoratedPotPatternRef = &'static DecoratedPotPattern;
105
106pub struct DecoratedPotPatternRegistry {
107    patterns_by_id: Vec<DecoratedPotPatternRef>,
108    patterns_by_key: FxHashMap<Identifier, usize>,
109    allows_registering: bool,
110}
111
112impl DecoratedPotPatternRegistry {
113    #[must_use]
114    pub fn new() -> Self {
115        Self {
116            patterns_by_id: Vec::new(),
117            patterns_by_key: FxHashMap::default(),
118            allows_registering: true,
119        }
120    }
121}
122
123crate::impl_standard_methods!(
124    DecoratedPotPatternRegistry,
125    DecoratedPotPatternRef,
126    patterns_by_id,
127    patterns_by_key,
128    allows_registering
129);
130
131crate::impl_registry!(
132    DecoratedPotPatternRegistry,
133    DecoratedPotPattern,
134    patterns_by_id,
135    patterns_by_key,
136    decorated_pot_patterns
137);
138
139#[cfg(test)]
140mod tests {
141    use std::io::Cursor;
142
143    use simdnbt::borrow::read_tag;
144    use simdnbt::{FromNbtTag as _, ToNbtTag as _};
145    use steel_utils::hash::HashComponent as _;
146    use steel_utils::serial::{ReadFrom as _, WriteTo as _};
147
148    use super::DecoratedPotPatternValue;
149    use crate::init_vanilla_registry;
150    use crate::{REGISTRY, vanilla_decorated_pot_patterns};
151
152    fn parse(tag: simdnbt::owned::NbtTag) -> Option<DecoratedPotPatternValue> {
153        let mut bytes = Vec::new();
154        tag.write(&mut bytes);
155        let borrowed = read_tag(&mut Cursor::new(bytes.as_slice())).ok()?;
156        DecoratedPotPatternValue::from_nbt_tag(borrowed.as_tag())
157    }
158
159    #[test]
160    fn generated_patterns_are_registered() {
161        init_vanilla_registry();
162        assert!(
163            REGISTRY
164                .decorated_pot_patterns
165                .iter()
166                .any(|(_, pattern)| pattern.key.path.as_ref() == "angler")
167        );
168    }
169
170    #[test]
171    fn direct_codecs_and_hash_match_vanilla_shape() {
172        init_vanilla_registry();
173        let pattern = vanilla_decorated_pot_patterns::ANGLER.value().clone();
174        let mut network = Vec::new();
175        pattern.write(&mut network).expect("pattern should encode");
176        assert_eq!(
177            DecoratedPotPatternValue::read(&mut Cursor::new(network.as_slice()))
178                .expect("pattern should decode"),
179            pattern
180        );
181
182        let nbt = pattern.clone().to_nbt_tag();
183        assert_eq!(parse(nbt.clone()), Some(pattern.clone()));
184        assert_eq!(pattern.compute_hash(), nbt.compute_hash());
185    }
186}