steel_registry/data_components/registry/
codecs.rs1use super::{
2 BorrowedNbtTag, Component, ComponentData, Cursor, DowncastType, DowncastTypeKey, FromNbtTag,
3 HashComponent, Identifier, OwnedNbtTag, ReadFrom, Result, ToNbtTag, WriteTo, read_tag,
4};
5
6pub type NetworkReader = fn(&mut Cursor<&[u8]>) -> Result<ComponentData>;
7
8pub type NetworkWriter = fn(&ComponentData, &mut Vec<u8>) -> Result<()>;
10
11pub type NbtReader = fn(BorrowedNbtTag) -> Option<ComponentData>;
13
14pub type NbtWriter = fn(&ComponentData) -> Result<OwnedNbtTag>;
16
17pub(super) type ComponentHash = fn(&ComponentData) -> Result<i32>;
19pub(super) type ComponentValidator = fn(&ComponentData) -> Result<()>;
20pub(super) type PersistentCodecFns = (
21 NbtReader,
22 NbtWriter,
23 ComponentHash,
24 Option<ComponentValidator>,
25);
26
27pub(crate) trait ValidatePersistentComponent {
29 fn validate_persistent(&self) -> Result<()>;
30}
31
32pub(super) fn hash_component<T: DowncastType + HashComponent>(data: &ComponentData) -> Result<i32> {
33 let Some(value) = data.downcast_ref::<T>() else {
34 return Err(std::io::Error::other("Component type mismatch"));
35 };
36 Ok(value.compute_hash())
37}
38
39pub(super) fn validate_component<T: DowncastType + ValidatePersistentComponent>(
40 data: &ComponentData,
41) -> Result<()> {
42 let Some(value) = data.downcast_ref::<T>() else {
43 return Err(std::io::Error::other("Component type mismatch"));
44 };
45 value.validate_persistent()
46}
47
48pub(super) fn read_typed_network<T: Component + ReadFrom>(
49 cursor: &mut Cursor<&[u8]>,
50) -> Result<ComponentData> {
51 Ok(ComponentData::new(T::read(cursor)?))
52}
53
54pub(super) fn write_typed_network<T: DowncastType + WriteTo>(
55 data: &ComponentData,
56 writer: &mut Vec<u8>,
57) -> Result<()> {
58 let Some(value) = data.downcast_ref::<T>() else {
59 return Err(std::io::Error::other("Component type mismatch"));
60 };
61 value.write(writer)
62}
63
64pub(super) fn read_typed_nbt<T: Component + FromNbtTag>(
65 tag: BorrowedNbtTag,
66) -> Option<ComponentData> {
67 T::from_nbt_tag(tag).map(ComponentData::new)
68}
69
70pub(super) fn write_typed_nbt<T: DowncastType + ToNbtTag + Clone>(
71 data: &ComponentData,
72) -> Result<OwnedNbtTag> {
73 let Some(value) = data.downcast_ref::<T>() else {
74 return Err(std::io::Error::other("Component type mismatch"));
75 };
76 Ok(value.clone().to_nbt_tag())
77}
78
79struct NetworkCodecs {
80 reader: NetworkReader,
81 writer: NetworkWriter,
82}
83
84struct PersistentCodecs {
85 reader: NbtReader,
86 writer: NbtWriter,
87 hash: ComponentHash,
88 validator: Option<fn(&ComponentData) -> Result<()>>,
89}
90
91struct ComponentCodecs {
92 expected_type_key: DowncastTypeKey,
93 network: NetworkCodecs,
94 persistent: Option<PersistentCodecs>,
95}
96
97pub struct ComponentEntry {
102 pub key: Identifier,
104 codecs: ComponentCodecs,
105 ignore_swap_animation: bool,
106}
107
108impl ComponentEntry {
109 #[must_use]
110 pub(super) fn implemented(
111 key: Identifier,
112 expected_type_key: DowncastTypeKey,
113 network_reader: NetworkReader,
114 network_writer: NetworkWriter,
115 persistent_codecs: Option<PersistentCodecFns>,
116 ignore_swap_animation: bool,
117 ) -> Self {
118 Self {
119 key,
120 codecs: ComponentCodecs {
121 expected_type_key,
122 network: NetworkCodecs {
123 reader: network_reader,
124 writer: network_writer,
125 },
126 persistent: persistent_codecs.map(|(reader, writer, hash, validator)| {
127 PersistentCodecs {
128 reader,
129 writer,
130 hash,
131 validator,
132 }
133 }),
134 },
135 ignore_swap_animation,
136 }
137 }
138
139 #[must_use]
144 pub fn validates(&self, data: &ComponentData) -> bool {
145 data.type_key() == self.codecs.expected_type_key
146 }
147
148 pub fn read_network(&self, data: &mut Cursor<&[u8]>) -> Result<ComponentData> {
150 let ComponentCodecs {
151 network,
152 expected_type_key,
153 ..
154 } = &self.codecs;
155 let value = (network.reader)(data)?;
156 if value.type_key() != *expected_type_key {
157 return Err(std::io::Error::other(format!(
158 "Network codec returned the wrong value type for {}",
159 self.key
160 )));
161 }
162 Ok(value)
163 }
164
165 pub fn write_network(&self, data: &ComponentData, writer: &mut Vec<u8>) -> Result<()> {
167 if !self.validates(data) {
168 return Err(std::io::Error::other(format!(
169 "Component value type does not match {}",
170 self.key
171 )));
172 }
173 (self.codecs.network.writer)(data, writer)
174 }
175
176 #[must_use]
178 pub fn read_nbt(&self, tag: BorrowedNbtTag) -> Option<ComponentData> {
179 let Some(persistent) = &self.codecs.persistent else {
180 return None;
181 };
182 let value = (persistent.reader)(tag)?;
183 (value.type_key() == self.codecs.expected_type_key).then_some(value)
184 }
185
186 pub fn write_nbt(&self, data: &ComponentData) -> Result<OwnedNbtTag> {
188 if !self.validates(data) {
189 return Err(std::io::Error::other(format!(
190 "Component value type does not match {}",
191 self.key
192 )));
193 }
194 let Some(persistent) = &self.codecs.persistent else {
195 return Err(std::io::Error::other(format!(
196 "Transient component {} has no persistent codec",
197 self.key
198 )));
199 };
200 (persistent.writer)(data)
201 }
202
203 pub fn validate_persistent_encoding(&self, data: &ComponentData) -> Result<OwnedNbtTag> {
206 if let Some(validator) = self
207 .codecs
208 .persistent
209 .as_ref()
210 .and_then(|persistent| persistent.validator)
211 {
212 validator(data)?;
213 }
214 let tag = self.write_nbt(data)?;
215 if self.read_nbt_owned(&tag).is_none() {
216 return Err(std::io::Error::other(format!(
217 "Persistent codec for component {} rejected its encoded value",
218 self.key
219 )));
220 }
221 Ok(tag)
222 }
223
224 pub fn compute_hash(&self, data: &ComponentData) -> Result<i32> {
226 if !self.validates(data) {
227 return Err(std::io::Error::other(format!(
228 "Component value type does not match {}",
229 self.key
230 )));
231 }
232 if !self.is_persistent() {
233 return Err(std::io::Error::other(format!(
234 "Transient component {} has no persistent hash codec",
235 self.key
236 )));
237 }
238 let Some(persistent) = &self.codecs.persistent else {
239 return Err(std::io::Error::other(format!(
240 "Transient component {} has no persistent hash codec",
241 self.key
242 )));
243 };
244 self.validate_persistent_encoding(data)?;
245 (persistent.hash)(data)
246 }
247
248 #[must_use]
250 pub const fn is_persistent(&self) -> bool {
251 self.codecs.persistent.is_some()
252 }
253
254 #[must_use]
256 pub const fn ignore_swap_animation(&self) -> bool {
257 self.ignore_swap_animation
258 }
259
260 #[must_use]
262 pub fn read_nbt_owned(&self, tag: &OwnedNbtTag) -> Option<ComponentData> {
263 if !self.is_persistent() {
264 return None;
265 }
266 let mut bytes = Vec::new();
267 tag.write(&mut bytes);
268 let borrowed = read_tag(&mut Cursor::new(bytes.as_slice())).ok()?;
269 self.read_nbt(borrowed.as_tag())
270 }
271}
272
273pub type ComponentEntryRef = &'static ComponentEntry;