steel_protocol/packets/game/
c_sound.rs1use glam::{DVec3, IVec3};
2use steel_macros::{ClientPacket, WriteTo};
3use steel_registry::packets::play::C_SOUND;
4use steel_registry::sound_event::{SoundEventHolder, SoundEventRef};
5use steel_utils::codec::VarInt;
6use steel_utils::serial::WriteTo as WriteToTrait;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10#[repr(u8)]
11pub enum SoundSource {
12 Master = 0,
13 Music = 1,
14 Records = 2,
15 Weather = 3,
16 Blocks = 4,
17 Hostile = 5,
18 Neutral = 6,
19 Players = 7,
20 Ambient = 8,
21 Voice = 9,
22 Ui = 10,
23}
24
25impl SoundSource {
26 pub const VALUES: [SoundSource; 11] = [
27 SoundSource::Master,
28 SoundSource::Music,
29 SoundSource::Records,
30 SoundSource::Weather,
31 SoundSource::Blocks,
32 SoundSource::Hostile,
33 SoundSource::Neutral,
34 SoundSource::Players,
35 SoundSource::Ambient,
36 SoundSource::Voice,
37 SoundSource::Ui,
38 ];
39
40 #[must_use]
42 pub const fn name(self) -> &'static str {
43 match self {
44 Self::Master => "master",
45 Self::Music => "music",
46 Self::Records => "record",
47 Self::Weather => "weather",
48 Self::Blocks => "block",
49 Self::Hostile => "hostile",
50 Self::Neutral => "neutral",
51 Self::Players => "player",
52 Self::Ambient => "ambient",
53 Self::Voice => "voice",
54 Self::Ui => "ui",
55 }
56 }
57
58 #[must_use]
60 pub const fn as_varint(self) -> i32 {
61 self as i32
62 }
63}
64
65impl WriteToTrait for SoundSource {
66 fn write(&self, writer: &mut impl std::io::Write) -> std::io::Result<()> {
67 VarInt(*self as i32).write(writer)
68 }
69}
70
71#[derive(WriteTo, ClientPacket, Clone, Debug)]
76#[packet_id(Play = C_SOUND)]
77pub struct CSound {
78 pub sound: SoundEventHolder,
80 pub source: SoundSource,
82 pub pos: IVec3,
84 pub volume: f32,
86 pub pitch: f32,
88 pub seed: i64,
90}
91
92impl CSound {
93 #[must_use]
103 pub fn new(
104 sound: SoundEventRef,
105 source: SoundSource,
106 pos: DVec3,
107 volume: f32,
108 pitch: f32,
109 seed: i64,
110 ) -> Self {
111 Self::new_holder(
112 SoundEventHolder::registry(sound),
113 source,
114 pos,
115 volume,
116 pitch,
117 seed,
118 )
119 }
120
121 #[must_use]
123 pub fn new_holder(
124 sound: SoundEventHolder,
125 source: SoundSource,
126 pos: DVec3,
127 volume: f32,
128 pitch: f32,
129 seed: i64,
130 ) -> Self {
131 Self {
132 sound,
133 source,
134 pos: IVec3::new(
135 (pos.x * 8.0) as i32,
136 (pos.y * 8.0) as i32,
137 (pos.z * 8.0) as i32,
138 ),
139 volume,
140 pitch,
141 seed,
142 }
143 }
144
145 #[must_use]
154 pub fn block_sound(
155 sound: SoundEventRef,
156 pos: steel_utils::BlockPos,
157 volume: f32,
158 pitch: f32,
159 seed: i64,
160 ) -> Self {
161 Self::new(
162 sound,
163 SoundSource::Blocks,
164 pos.0.as_dvec3().map(|v| v + 0.5),
165 volume,
166 pitch,
167 seed,
168 )
169 }
170}
171
172#[cfg(test)]
173mod tests {
174 use steel_registry::init_vanilla_registry;
175 use steel_registry::{RegistryEntry, sound_events};
176 use steel_utils::BlockPos;
177
178 use super::CSound;
179
180 #[test]
181 fn registered_sound_packet_uses_holder_id() {
182 init_vanilla_registry();
183
184 let packet = CSound::block_sound(
185 &sound_events::BLOCK_WOODEN_BUTTON_CLICK_ON,
186 BlockPos::ZERO,
187 1.0,
188 1.0,
189 0,
190 );
191
192 let expected_holder_id = sound_events::BLOCK_WOODEN_BUTTON_CLICK_ON.id() as i32 + 1;
193 assert_eq!(
194 sound_events::BLOCK_WOODEN_BUTTON_CLICK_ON.packet_holder_id(),
195 expected_holder_id
196 );
197 assert!(matches!(
198 packet.sound,
199 steel_registry::sound_event::SoundEventHolder::Registry(sound)
200 if sound.packet_holder_id() == expected_holder_id
201 ));
202 }
203}