1use rustc_hash::FxHashMap;
10use simdnbt::{
11 FromNbtTag, ToNbtTag,
12 borrow::NbtTag as BorrowedNbtTag,
13 owned::{NbtCompound, NbtTag as OwnedNbtTag},
14};
15use std::{
16 fmt::Debug,
17 io::{Cursor, Result, Write},
18 marker::PhantomData,
19};
20
21use steel_utils::{
22 Identifier,
23 codec::VarInt,
24 serial::{ReadFrom, WriteTo},
25};
26
27use super::component_data::{Component, ComponentData, ComponentDataDiscriminant};
28use super::components::ItemEnchantments;
29use super::vanilla_components::{
30 ATTRIBUTE_MODIFIERS, BREAK_SOUND, ENCHANTMENTS, LORE, MAX_STACK_SIZE, RARITY, REPAIR_COST,
31 TOOLTIP_DISPLAY,
32};
33
34pub struct DataComponentType<T> {
49 pub key: Identifier,
50 _phantom: PhantomData<T>,
51}
52
53impl<T> Clone for DataComponentType<T> {
54 fn clone(&self) -> Self {
55 Self {
56 key: self.key.clone(),
57 _phantom: PhantomData,
58 }
59 }
60}
61
62impl<T> DataComponentType<T> {
63 #[must_use]
64 pub const fn new(key: Identifier) -> Self {
65 Self {
66 key,
67 _phantom: PhantomData,
68 }
69 }
70}
71
72pub type NetworkReader = fn(&mut Cursor<&[u8]>) -> Result<ComponentData>;
74
75pub type NetworkWriter = fn(&ComponentData, &mut Vec<u8>) -> Result<()>;
77
78pub type NbtReader = fn(BorrowedNbtTag) -> Option<ComponentData>;
80
81pub type NbtWriter = fn(&ComponentData) -> OwnedNbtTag;
83
84pub struct ComponentEntry {
89 pub key: Identifier,
91 pub expected_discriminant: ComponentDataDiscriminant,
93 pub network_reader: NetworkReader,
95 pub network_writer: NetworkWriter,
97 pub nbt_reader: NbtReader,
99 pub nbt_writer: NbtWriter,
101}
102
103impl ComponentEntry {
104 #[must_use]
106 pub fn new(
107 key: Identifier,
108 expected_discriminant: ComponentDataDiscriminant,
109 network_reader: NetworkReader,
110 network_writer: NetworkWriter,
111 nbt_reader: NbtReader,
112 nbt_writer: NbtWriter,
113 ) -> Self {
114 Self {
115 key,
116 expected_discriminant,
117 network_reader,
118 network_writer,
119 nbt_reader,
120 nbt_writer,
121 }
122 }
123
124 #[must_use]
129 pub fn validates(&self, data: &ComponentData) -> bool {
130 data.discriminant() == self.expected_discriminant
131 }
132}
133
134pub type ComponentEntryRef = &'static ComponentEntry;
135
136pub struct DataComponentRegistry {
141 entries: Vec<ComponentEntryRef>,
143 by_key: FxHashMap<Identifier, usize>,
145 allows_registering: bool,
147}
148
149impl Default for DataComponentRegistry {
150 fn default() -> Self {
151 Self::new()
152 }
153}
154
155impl DataComponentRegistry {
156 #[must_use]
157 pub fn new() -> Self {
158 Self {
159 entries: Vec::new(),
160 by_key: FxHashMap::default(),
161 allows_registering: true,
162 }
163 }
164
165 pub fn register<T>(
170 &mut self,
171 component: DataComponentType<T>,
172 expected_discriminant: ComponentDataDiscriminant,
173 ) where
174 T: 'static + Component + WriteTo + ReadFrom + ToNbtTag + FromNbtTag,
175 {
176 assert!(
177 self.allows_registering,
178 "Cannot register data components after the registry has been frozen"
179 );
180
181 fn make_network_reader<T>() -> NetworkReader
183 where
184 T: 'static + Component + ReadFrom,
185 {
186 |cursor| {
187 let value = T::read(cursor)?;
188 Ok(value.into_data())
189 }
190 }
191
192 fn make_network_writer<T>() -> NetworkWriter
193 where
194 T: 'static + Component + WriteTo,
195 {
196 |data, writer| {
197 if let Some(value) = T::from_data_ref(data) {
198 value.write(writer)
199 } else {
200 Err(std::io::Error::other("Component type mismatch"))
201 }
202 }
203 }
204
205 fn make_nbt_reader<T>() -> NbtReader
206 where
207 T: 'static + Component + FromNbtTag,
208 {
209 |tag| {
210 let value = T::from_nbt_tag(tag)?;
211 Some(value.into_data())
212 }
213 }
214
215 fn make_nbt_writer<T>() -> NbtWriter
216 where
217 T: 'static + Component + ToNbtTag + Clone,
218 {
219 |data| {
220 if let Some(value) = T::from_data_ref(data) {
221 value.clone().to_nbt_tag()
222 } else {
223 OwnedNbtTag::Compound(NbtCompound::new())
225 }
226 }
227 }
228
229 let entry = Box::leak(Box::new(ComponentEntry::new(
230 component.key.clone(),
231 expected_discriminant,
232 make_network_reader::<T>(),
233 make_network_writer::<T>(),
234 make_nbt_reader::<T>(),
235 make_nbt_writer::<T>(),
236 )));
237
238 let id = self.entries.len();
239 self.by_key.insert(component.key.clone(), id);
240 self.entries.push(entry);
241 }
242
243 pub fn register_custom_network<T>(
249 &mut self,
250 component: DataComponentType<T>,
251 expected_discriminant: ComponentDataDiscriminant,
252 network_reader: NetworkReader,
253 network_writer: NetworkWriter,
254 ) where
255 T: 'static + Component + ToNbtTag + FromNbtTag,
256 {
257 assert!(
258 self.allows_registering,
259 "Cannot register data components after the registry has been frozen"
260 );
261
262 fn make_nbt_reader<T>() -> NbtReader
263 where
264 T: 'static + Component + FromNbtTag,
265 {
266 |tag| {
267 let value = T::from_nbt_tag(tag)?;
268 Some(value.into_data())
269 }
270 }
271
272 fn make_nbt_writer<T>() -> NbtWriter
273 where
274 T: 'static + Component + ToNbtTag + Clone,
275 {
276 |data| {
277 if let Some(value) = T::from_data_ref(data) {
278 value.clone().to_nbt_tag()
279 } else {
280 OwnedNbtTag::Compound(NbtCompound::new())
281 }
282 }
283 }
284
285 let entry = Box::leak(Box::new(ComponentEntry::new(
286 component.key.clone(),
287 expected_discriminant,
288 network_reader,
289 network_writer,
290 make_nbt_reader::<T>(),
291 make_nbt_writer::<T>(),
292 )));
293
294 let id = self.entries.len();
295 self.by_key.insert(component.key.clone(), id);
296 self.entries.push(entry);
297 }
298
299 pub fn register_dynamic(
304 &mut self,
305 key: Identifier,
306 expected_discriminant: ComponentDataDiscriminant,
307 network_reader: NetworkReader,
308 network_writer: NetworkWriter,
309 nbt_reader: NbtReader,
310 nbt_writer: NbtWriter,
311 ) -> usize {
312 assert!(
313 self.allows_registering,
314 "Cannot register data components after the registry has been frozen"
315 );
316
317 let entry = Box::leak(Box::new(ComponentEntry::new(
318 key.clone(),
319 expected_discriminant,
320 network_reader,
321 network_writer,
322 nbt_reader,
323 nbt_writer,
324 )));
325
326 let id = self.entries.len();
327 self.by_key.insert(key, id);
328 self.entries.push(entry);
329 id
330 }
331
332 #[must_use]
334 pub fn get_id<T>(&self, component: DataComponentType<T>) -> Option<usize> {
335 self.by_key.get(&component.key).copied()
336 }
337
338 #[must_use]
340 pub fn get_key_by_id(&self, id: usize) -> Option<&Identifier> {
341 self.entries.get(id).map(|e| &e.key)
342 }
343}
344
345crate::impl_registry!(
346 DataComponentRegistry,
347 ComponentEntry,
348 entries,
349 by_key,
350 data_components
351);
352
353#[derive(Debug, Clone)]
357pub struct DataComponentMap {
358 map: FxHashMap<Identifier, ComponentData>,
359}
360
361impl Default for DataComponentMap {
362 fn default() -> Self {
363 Self::new()
364 }
365}
366
367impl DataComponentMap {
368 #[must_use]
369 pub fn new() -> Self {
370 Self {
371 map: FxHashMap::default(),
372 }
373 }
374
375 #[must_use]
377 pub fn common_item_components() -> Self {
378 let mut map = FxHashMap::default();
379 map.insert(MAX_STACK_SIZE.key.clone(), ComponentData::I32(64));
380 map.insert(LORE.key.clone(), ComponentData::Todo);
381 map.insert(
382 ENCHANTMENTS.key.clone(),
383 ComponentData::Enchantments(ItemEnchantments::empty()),
384 );
385 map.insert(REPAIR_COST.key.clone(), ComponentData::I32(0));
386 map.insert(ATTRIBUTE_MODIFIERS.key.clone(), ComponentData::Todo);
387 map.insert(RARITY.key.clone(), ComponentData::Todo);
388 map.insert(BREAK_SOUND.key.clone(), ComponentData::Todo);
389 map.insert(TOOLTIP_DISPLAY.key.clone(), ComponentData::Todo);
390 Self { map }
391 }
392
393 #[must_use]
395 pub fn builder_set<T: Component>(
396 mut self,
397 component: DataComponentType<T>,
398 value: Option<T>,
399 ) -> Self {
400 self.set(component, value);
401 self
402 }
403
404 pub fn set<T: Component>(&mut self, component: DataComponentType<T>, value: Option<T>) {
406 if let Some(v) = value {
407 self.map.insert(component.key.clone(), v.into_data());
408 } else {
409 self.map.remove(&component.key);
410 }
411 }
412
413 #[must_use]
415 pub fn get<T: Component>(&self, component: DataComponentType<T>) -> Option<T> {
416 let data = self.map.get(&component.key)?;
417 T::from_data(data.clone())
418 }
419
420 #[must_use]
422 pub fn get_ref<T: Component>(&self, component: DataComponentType<T>) -> Option<&T> {
423 let data = self.map.get(&component.key)?;
424 T::from_data_ref(data)
425 }
426
427 #[must_use]
429 pub fn has<T>(&self, component: DataComponentType<T>) -> bool {
430 self.map.contains_key(&component.key)
431 }
432
433 #[must_use]
435 pub fn len(&self) -> usize {
436 self.map.len()
437 }
438
439 #[must_use]
441 pub fn is_empty(&self) -> bool {
442 self.map.is_empty()
443 }
444
445 pub fn keys(&self) -> impl Iterator<Item = &Identifier> {
447 self.map.keys()
448 }
449
450 #[must_use]
452 pub fn get_raw(&self, key: &Identifier) -> Option<&ComponentData> {
453 self.map.get(key)
454 }
455
456 pub fn set_raw(&mut self, key: Identifier, data: ComponentData) -> bool {
463 use crate::{REGISTRY, RegistryExt};
464
465 if let Some(entry) = REGISTRY.data_components.by_key(&key)
467 && !entry.validates(&data)
468 {
469 return false;
470 }
471
472 self.map.insert(key, data);
473 true
474 }
475
476 pub fn remove(&mut self, key: &Identifier) -> Option<ComponentData> {
478 self.map.remove(key)
479 }
480}
481
482#[derive(Debug, Clone)]
484pub enum ComponentPatchEntry {
485 Set(ComponentData),
487 Removed,
489}
490
491impl PartialEq for ComponentPatchEntry {
492 fn eq(&self, other: &Self) -> bool {
493 match (self, other) {
494 (Self::Removed, Self::Removed) => true,
495 (Self::Set(a), Self::Set(b)) => a == b,
496 _ => false,
497 }
498 }
499}
500
501#[derive(Debug, Default, Clone, PartialEq)]
507pub struct DataComponentPatch {
508 entries: FxHashMap<Identifier, ComponentPatchEntry>,
509}
510
511impl DataComponentPatch {
512 #[must_use]
513 pub fn new() -> Self {
514 Self {
515 entries: FxHashMap::default(),
516 }
517 }
518
519 #[must_use]
520 pub fn is_empty(&self) -> bool {
521 self.entries.is_empty()
522 }
523
524 #[must_use]
525 pub fn len(&self) -> usize {
526 self.entries.len()
527 }
528
529 pub fn set<T: Component>(&mut self, component: DataComponentType<T>, value: T) {
531 self.entries.insert(
532 component.key.clone(),
533 ComponentPatchEntry::Set(value.into_data()),
534 );
535 }
536
537 pub fn set_raw(&mut self, key: Identifier, data: ComponentData) -> bool {
544 use crate::{REGISTRY, RegistryExt};
545
546 if let Some(entry) = REGISTRY.data_components.by_key(&key)
548 && !entry.validates(&data)
549 {
550 return false;
551 }
552
553 self.entries.insert(key, ComponentPatchEntry::Set(data));
554 true
555 }
556
557 pub fn remove<T>(&mut self, component: DataComponentType<T>) {
559 self.entries
560 .insert(component.key.clone(), ComponentPatchEntry::Removed);
561 }
562
563 pub fn clear<T>(&mut self, component: DataComponentType<T>) {
565 self.entries.remove(&component.key);
566 }
567
568 #[must_use]
570 pub fn get_entry(&self, key: &Identifier) -> Option<&ComponentPatchEntry> {
571 self.entries.get(key)
572 }
573
574 #[must_use]
576 pub fn is_removed(&self, key: &Identifier) -> bool {
577 matches!(self.entries.get(key), Some(ComponentPatchEntry::Removed))
578 }
579
580 #[must_use]
582 pub fn count_set(&self) -> usize {
583 self.entries
584 .values()
585 .filter(|e| matches!(e, ComponentPatchEntry::Set(_)))
586 .count()
587 }
588
589 #[must_use]
591 pub fn count_removed(&self) -> usize {
592 self.entries
593 .values()
594 .filter(|e| matches!(e, ComponentPatchEntry::Removed))
595 .count()
596 }
597
598 pub fn iter(&self) -> impl Iterator<Item = (&Identifier, &ComponentPatchEntry)> {
600 self.entries.iter()
601 }
602
603 pub fn iter_removed(&self) -> impl Iterator<Item = &Identifier> {
605 self.entries.iter().filter_map(|(k, v)| {
606 if matches!(v, ComponentPatchEntry::Removed) {
607 Some(k)
608 } else {
609 None
610 }
611 })
612 }
613
614 #[must_use]
616 pub fn to_nbt_tag_ref(&self) -> OwnedNbtTag {
617 use crate::{REGISTRY, RegistryExt};
618
619 let mut compound = NbtCompound::new();
620
621 for (key, entry) in &self.entries {
622 match entry {
623 ComponentPatchEntry::Set(data) => {
624 if let Some(entry) = REGISTRY.data_components.by_key(key) {
625 let nbt = (entry.nbt_writer)(data);
626 compound.insert(key.to_string(), nbt);
627 }
628 }
629 ComponentPatchEntry::Removed => {
630 compound.insert(format!("!{key}"), NbtCompound::new());
631 }
632 }
633 }
634
635 OwnedNbtTag::Compound(compound)
636 }
637}
638
639impl WriteTo for DataComponentPatch {
640 fn write(&self, writer: &mut impl Write) -> Result<()> {
641 use crate::{REGISTRY, RegistryExt};
642
643 let mut added: Vec<(&Identifier, &ComponentData)> = Vec::new();
644 let mut removed: Vec<&Identifier> = Vec::new();
645
646 for (key, entry) in &self.entries {
647 match entry {
648 ComponentPatchEntry::Set(data) => added.push((key, data)),
649 ComponentPatchEntry::Removed => removed.push(key),
650 }
651 }
652
653 VarInt(added.len() as i32).write(writer)?;
654 VarInt(removed.len() as i32).write(writer)?;
655
656 for (key, data) in added {
658 let id = REGISTRY
659 .data_components
660 .id_from_key(key)
661 .ok_or_else(|| std::io::Error::other(format!("Unknown component key: {key:?}")))?;
662
663 let entry = REGISTRY
664 .data_components
665 .by_id(id)
666 .ok_or_else(|| std::io::Error::other(format!("No entry for component id: {id}")))?;
667
668 VarInt(id as i32).write(writer)?;
669
670 let mut buf = Vec::new();
671 (entry.network_writer)(data, &mut buf)?;
672 writer.write_all(&buf)?;
673 }
674
675 for key in removed {
677 let id = REGISTRY
678 .data_components
679 .id_from_key(key)
680 .ok_or_else(|| std::io::Error::other(format!("Unknown component key: {key:?}")))?;
681 VarInt(id as i32).write(writer)?;
682 }
683
684 Ok(())
685 }
686}
687
688impl ReadFrom for DataComponentPatch {
689 fn read(data: &mut Cursor<&[u8]>) -> Result<Self> {
690 use crate::{REGISTRY, RegistryExt};
691
692 let added_count = VarInt::read(data)?.0 as usize;
693 let removed_count = VarInt::read(data)?.0 as usize;
694
695 log::info!("Reading DataComponentPatch: added={added_count}, removed={removed_count}");
696
697 let mut patch = Self::new();
698
699 for i in 0..added_count {
701 let pos_before = data.position();
702 let type_id = VarInt::read(data)?.0 as usize;
703
704 let key = REGISTRY
705 .data_components
706 .get_key_by_id(type_id)
707 .ok_or_else(|| {
708 std::io::Error::other(format!("Unknown component type ID: {type_id}"))
709 })?
710 .clone();
711
712 log::info!(" [{i}] Reading component {key} (id={type_id}) at pos {pos_before}");
713
714 let entry = REGISTRY
715 .data_components
716 .by_id(type_id)
717 .ok_or_else(|| std::io::Error::other(format!("No entry for component: {key}")))?;
718
719 let component_data = (entry.network_reader)(data).map_err(|e| {
720 log::error!(" Failed to read component {key}: {e}");
721 e
722 })?;
723
724 let pos_after = data.position();
725 log::info!(" Read {} bytes for {key}", pos_after - pos_before);
726
727 patch
728 .entries
729 .insert(key, ComponentPatchEntry::Set(component_data));
730 }
731
732 for _ in 0..removed_count {
734 let type_id = VarInt::read(data)?.0 as usize;
735
736 let key = REGISTRY
737 .data_components
738 .get_key_by_id(type_id)
739 .ok_or_else(|| {
740 std::io::Error::other(format!("Unknown component type ID: {type_id}"))
741 })?
742 .clone();
743
744 patch.entries.insert(key, ComponentPatchEntry::Removed);
745 }
746
747 Ok(patch)
748 }
749}
750
751impl DataComponentPatch {
752 pub fn read_delimited(data: &mut Cursor<&[u8]>) -> Result<Self> {
757 use crate::{REGISTRY, RegistryExt};
758 use std::io::Read;
759
760 let added_count = VarInt::read(data)?.0 as usize;
761 let removed_count = VarInt::read(data)?.0 as usize;
762
763 const MAX_COMPONENTS: usize = 65_536;
764 const MAX_COMPONENT_BYTES: usize = 2 * 1024 * 1024;
765
766 if added_count.saturating_add(removed_count) > MAX_COMPONENTS {
767 return Err(std::io::Error::other(format!(
768 "Component patch too large: {added_count} added + {removed_count} removed > {MAX_COMPONENTS}"
769 )));
770 }
771
772 let mut patch = Self::new();
773
774 for _ in 0..added_count {
775 let type_id = VarInt::read(data)?.0 as usize;
776 let byte_len = VarInt::read(data)?.0 as usize;
777
778 if byte_len > MAX_COMPONENT_BYTES {
779 return Err(std::io::Error::other(format!(
780 "Component data too large: {byte_len} bytes > {MAX_COMPONENT_BYTES}"
781 )));
782 }
783
784 let key = REGISTRY
785 .data_components
786 .get_key_by_id(type_id)
787 .ok_or_else(|| {
788 std::io::Error::other(format!("Unknown component type ID: {type_id}"))
789 })?
790 .clone();
791
792 let entry = REGISTRY.data_components.by_id(type_id);
793
794 let mut buf = vec![0u8; byte_len];
796 data.read_exact(&mut buf)?;
797
798 if let Some(entry) = entry {
799 let mut sub_cursor = Cursor::new(buf.as_slice());
800 match (entry.network_reader)(&mut sub_cursor) {
801 Ok(component_data) => {
802 patch
803 .entries
804 .insert(key, ComponentPatchEntry::Set(component_data));
805 }
806 Err(e) => {
807 log::warn!("Failed to read delimited component {key}: {e}");
808 }
809 }
810 }
811 }
812
813 for _ in 0..removed_count {
814 let type_id = VarInt::read(data)?.0 as usize;
815 let key = REGISTRY
816 .data_components
817 .get_key_by_id(type_id)
818 .ok_or_else(|| {
819 std::io::Error::other(format!("Unknown component type ID: {type_id}"))
820 })?
821 .clone();
822 patch.entries.insert(key, ComponentPatchEntry::Removed);
823 }
824
825 Ok(patch)
826 }
827}
828
829impl ToNbtTag for DataComponentPatch {
830 fn to_nbt_tag(self) -> OwnedNbtTag {
831 self.to_nbt_tag_ref()
832 }
833}
834
835impl FromNbtTag for DataComponentPatch {
836 fn from_nbt_tag(tag: BorrowedNbtTag) -> Option<Self> {
837 use crate::{REGISTRY, RegistryExt};
838
839 let compound = tag.compound()?;
840 let mut patch = Self::new();
841
842 for (key, value) in compound.iter() {
843 let key_str = key.to_str();
844
845 if let Some(stripped) = key_str.strip_prefix('!') {
846 if let Ok(id) = stripped.parse::<Identifier>() {
848 patch.entries.insert(id, ComponentPatchEntry::Removed);
849 }
850 } else {
851 if let Ok(id) = key_str.parse::<Identifier>()
853 && let Some(entry) = REGISTRY.data_components.by_key(&id)
854 && let Some(component_data) = (entry.nbt_reader)(value)
855 {
856 patch
857 .entries
858 .insert(id, ComponentPatchEntry::Set(component_data));
859 }
860 }
861 }
862
863 Some(patch)
864 }
865}
866
867#[must_use]
869pub fn component_try_into<T: Component>(
870 data: &ComponentData,
871 _component: DataComponentType<T>,
872) -> Option<&T> {
873 T::from_data_ref(data)
874}