steel_registry/data_components/components/
jukebox_playable.rs1use std::io::{Cursor, Error, Result, Write};
4use std::str::FromStr;
5
6use simdnbt::owned::NbtTag;
7use steel_utils::Identifier;
8use steel_utils::hash::{ComponentHasher, HashComponent};
9use steel_utils::serial::{ReadFrom, WriteTo};
10
11use crate::jukebox_song::{JukeboxSong, JukeboxSongRef, JukeboxSongValue};
12use crate::{REGISTRY, RegistryExt, RegistryHolder};
13
14#[derive(Debug, Clone, PartialEq)]
17pub struct JukeboxPlayable {
18 song: RegistryHolder<JukeboxSong>,
19}
20
21impl JukeboxPlayable {
22 #[must_use]
23 pub const fn new(song: JukeboxSongRef) -> Self {
24 Self {
25 song: RegistryHolder::reference(song),
26 }
27 }
28
29 #[must_use]
30 pub const fn direct(song: JukeboxSongValue) -> Self {
31 Self {
32 song: RegistryHolder::direct(song),
33 }
34 }
35
36 #[must_use]
37 pub const fn song(&self) -> &RegistryHolder<JukeboxSong> {
38 &self.song
39 }
40
41 #[must_use]
43 pub fn from_persistent_nbt(tag: simdnbt::borrow::NbtTag) -> Option<Self> {
44 let key = Identifier::from_str(&tag.string()?.to_str()).ok()?;
45 REGISTRY.jukebox_songs.by_key(&key).map(Self::new)
46 }
47
48 pub fn to_persistent_nbt(&self) -> Result<NbtTag> {
50 let Some(song) = self.song.as_reference() else {
51 return Err(Error::other("Direct jukebox song holder is not persistent"));
52 };
53 Ok(NbtTag::String(song.key.to_string().into()))
54 }
55}
56
57impl HashComponent for JukeboxPlayable {
58 fn hash_component(&self, hasher: &mut ComponentHasher) {
59 self.song.hash_component(hasher);
60 }
61}
62
63impl ReadFrom for JukeboxPlayable {
64 fn read(data: &mut Cursor<&[u8]>) -> Result<Self> {
65 RegistryHolder::read(data).map(|song| Self { song })
66 }
67}
68
69impl WriteTo for JukeboxPlayable {
70 fn write(&self, writer: &mut impl Write) -> Result<()> {
71 self.song.write(writer)
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use std::io::Cursor;
78
79 use simdnbt::owned::NbtTag;
80 use steel_utils::serial::{ReadFrom, WriteTo};
81
82 use super::JukeboxPlayable;
83 use crate::init_vanilla_registry;
84 use crate::vanilla_jukebox_songs;
85
86 #[test]
87 fn registry_reference_round_trips_both_codecs() {
88 init_vanilla_registry();
89 let component = JukeboxPlayable::new(&vanilla_jukebox_songs::CAT);
90
91 let mut network = Vec::new();
92 component
93 .write(&mut network)
94 .expect("registry jukebox holder should encode");
95 assert_eq!(
96 JukeboxPlayable::read(&mut Cursor::new(network.as_slice()))
97 .expect("registry jukebox holder should decode"),
98 component
99 );
100
101 let nbt = component
102 .to_persistent_nbt()
103 .expect("reference is persistent");
104 assert_eq!(nbt, NbtTag::String("minecraft:cat".into()));
105 }
106
107 #[test]
108 fn direct_holder_round_trips_stream_and_is_not_persistent() {
109 use crate::jukebox_song::JukeboxSongValue;
110 use crate::sound_event::SoundEventHolder;
111 use steel_utils::Identifier;
112 use text_components::TextComponent;
113
114 init_vanilla_registry();
115 let component = JukeboxPlayable::direct(JukeboxSongValue {
116 sound_event: SoundEventHolder::Direct {
117 sound_id: Identifier::vanilla_static("custom_song"),
118 fixed_range: Some(12.0),
119 },
120 description: TextComponent::plain("Custom song"),
121 length_in_seconds: -1.0,
122 comparator_output: 99,
123 });
124 let mut network = Vec::new();
125 component
126 .write(&mut network)
127 .expect("direct holder should encode");
128 assert_eq!(
129 JukeboxPlayable::read(&mut Cursor::new(network.as_slice()))
130 .expect("direct holder should decode"),
131 component
132 );
133 assert!(component.to_persistent_nbt().is_err());
134 }
135}