1use std::borrow::Cow;
2use std::io::Write;
3
4use anyhow::bail;
5use valence_generated::block::BlockState;
6use valence_math::{DVec3, Vec3};
7
8use crate::{BlockPos, Decode, Encode, ItemStack, Packet, VarInt};
9
10#[derive(Clone, Debug, Packet)]
11pub struct ParticleS2c<'a> {
12 pub particle: Cow<'a, Particle>,
13 pub long_distance: bool,
14 pub position: DVec3,
15 pub offset: Vec3,
16 pub max_speed: f32,
17 pub count: i32,
18}
19
20impl Encode for ParticleS2c<'_> {
21 fn encode(&self, mut w: impl Write) -> anyhow::Result<()> {
22 VarInt(self.particle.id()).encode(&mut w)?;
23 self.long_distance.encode(&mut w)?;
24 self.position.encode(&mut w)?;
25 self.offset.encode(&mut w)?;
26 self.max_speed.encode(&mut w)?;
27 self.count.encode(&mut w)?;
28
29 self.particle.as_ref().encode(w)
30 }
31}
32
33impl<'a> Decode<'a> for ParticleS2c<'a> {
34 fn decode(r: &mut &'a [u8]) -> anyhow::Result<Self> {
35 let particle_id = VarInt::decode(r)?.0;
36 let long_distance = bool::decode(r)?;
37 let position = Decode::decode(r)?;
38 let offset = Decode::decode(r)?;
39 let max_speed = f32::decode(r)?;
40 let particle_count = i32::decode(r)?;
41
42 Ok(Self {
43 particle: Cow::Owned(Particle::decode_with_id(particle_id, r)?),
44 long_distance,
45 position,
46 offset,
47 max_speed,
48 count: particle_count,
49 })
50 }
51}
52
53#[derive(Clone, PartialEq, Debug)]
54pub enum Particle {
55 AmbientEntityEffect,
56 AngryVillager,
57 Block(BlockState),
58 BlockMarker(BlockState),
59 Bubble,
60 Cloud,
61 Crit,
62 DamageIndicator,
63 DragonBreath,
64 DrippingLava,
65 FallingLava,
66 LandingLava,
67 DrippingWater,
68 FallingWater,
69 Dust {
70 rgb: Vec3,
71 scale: f32,
72 },
73 DustColorTransition {
74 from_rgb: Vec3,
75 scale: f32,
76 to_rgb: Vec3,
77 },
78 Effect,
79 ElderGuardian,
80 EnchantedHit,
81 Enchant,
82 EndRod,
83 EntityEffect,
84 ExplosionEmitter,
85 Explosion,
86 SonicBoom,
87 FallingDust(BlockState),
88 Firework,
89 Fishing,
90 Flame,
91 CherryLeaves,
92 SculkSoul,
93 SculkCharge {
94 roll: f32,
95 },
96 SculkChargePop,
97 SoulFireFlame,
98 Soul,
99 Flash,
100 HappyVillager,
101 Composter,
102 Heart,
103 InstantEffect,
104 Item(ItemStack),
105 VibrationBlock {
107 block_pos: BlockPos,
108 ticks: i32,
109 },
110 VibrationEntity {
112 entity_id: i32,
113 entity_eye_height: f32,
114 ticks: i32,
115 },
116 ItemSlime,
117 ItemSnowball,
118 LargeSmoke,
119 Lava,
120 Mycelium,
121 Note,
122 Poof,
123 Portal,
124 Rain,
125 Smoke,
126 Sneeze,
127 Spit,
128 SquidInk,
129 SweepAttack,
130 TotemOfUndying,
131 Underwater,
132 Splash,
133 Witch,
134 BubblePop,
135 CurrentDown,
136 BubbleColumnUp,
137 Nautilus,
138 Dolphin,
139 CampfireCosySmoke,
140 CampfireSignalSmoke,
141 DrippingHoney,
142 FallingHoney,
143 LandingHoney,
144 FallingNectar,
145 FallingSporeBlossom,
146 Ash,
147 CrimsonSpore,
148 WarpedSpore,
149 SporeBlossomAir,
150 DrippingObsidianTear,
151 FallingObsidianTear,
152 LandingObsidianTear,
153 ReversePortal,
154 WhiteAsh,
155 SmallFlame,
156 Snowflake,
157 DrippingDripstoneLava,
158 FallingDripstoneLava,
159 DrippingDripstoneWater,
160 FallingDripstoneWater,
161 GlowSquidInk,
162 Glow,
163 WaxOn,
164 WaxOff,
165 ElectricSpark,
166 Scrape,
167 Shriek {
168 delay: i32,
169 },
170 EggCrack,
171}
172
173impl Particle {
174 pub const fn id(&self) -> i32 {
175 match self {
176 Particle::AmbientEntityEffect => 0,
177 Particle::AngryVillager => 1,
178 Particle::Block(_) => 2,
179 Particle::BlockMarker(_) => 3,
180 Particle::Bubble => 4,
181 Particle::Cloud => 5,
182 Particle::Crit => 6,
183 Particle::DamageIndicator => 7,
184 Particle::DragonBreath => 8,
185 Particle::DrippingLava => 9,
186 Particle::FallingLava => 10,
187 Particle::LandingLava => 11,
188 Particle::DrippingWater => 12,
189 Particle::FallingWater => 13,
190 Particle::Dust { .. } => 14,
191 Particle::DustColorTransition { .. } => 15,
192 Particle::Effect => 16,
193 Particle::ElderGuardian => 17,
194 Particle::EnchantedHit => 18,
195 Particle::Enchant => 19,
196 Particle::EndRod => 20,
197 Particle::EntityEffect => 21,
198 Particle::ExplosionEmitter => 22,
199 Particle::Explosion => 23,
200 Particle::SonicBoom => 24,
201 Particle::FallingDust(_) => 25,
202 Particle::Firework => 26,
203 Particle::Fishing => 27,
204 Particle::Flame => 28,
205 Particle::CherryLeaves => 29,
206 Particle::SculkSoul => 30,
207 Particle::SculkCharge { .. } => 31,
208 Particle::SculkChargePop => 32,
209 Particle::SoulFireFlame => 33,
210 Particle::Soul => 34,
211 Particle::Flash => 35,
212 Particle::HappyVillager => 36,
213 Particle::Composter => 37,
214 Particle::Heart => 38,
215 Particle::InstantEffect => 39,
216 Particle::Item { .. } => 40,
217 Particle::VibrationBlock { .. } => 41,
218 Particle::VibrationEntity { .. } => 41,
219 Particle::ItemSlime => 42,
220 Particle::ItemSnowball => 43,
221 Particle::LargeSmoke => 44,
222 Particle::Lava => 45,
223 Particle::Mycelium => 46,
224 Particle::Note => 47,
225 Particle::Poof => 48,
226 Particle::Portal => 49,
227 Particle::Rain => 50,
228 Particle::Smoke => 51,
229 Particle::Sneeze => 52,
230 Particle::Spit => 53,
231 Particle::SquidInk => 54,
232 Particle::SweepAttack => 55,
233 Particle::TotemOfUndying => 56,
234 Particle::Underwater => 57,
235 Particle::Splash => 58,
236 Particle::Witch => 59,
237 Particle::BubblePop => 60,
238 Particle::CurrentDown => 61,
239 Particle::BubbleColumnUp => 62,
240 Particle::Nautilus => 63,
241 Particle::Dolphin => 64,
242 Particle::CampfireCosySmoke => 65,
243 Particle::CampfireSignalSmoke => 66,
244 Particle::DrippingHoney => 67,
245 Particle::FallingHoney => 68,
246 Particle::LandingHoney => 69,
247 Particle::FallingNectar => 70,
248 Particle::FallingSporeBlossom => 71,
249 Particle::Ash => 72,
250 Particle::CrimsonSpore => 73,
251 Particle::WarpedSpore => 74,
252 Particle::SporeBlossomAir => 75,
253 Particle::DrippingObsidianTear => 76,
254 Particle::FallingObsidianTear => 77,
255 Particle::LandingObsidianTear => 78,
256 Particle::ReversePortal => 79,
257 Particle::WhiteAsh => 80,
258 Particle::SmallFlame => 81,
259 Particle::Snowflake => 82,
260 Particle::DrippingDripstoneLava => 83,
261 Particle::FallingDripstoneLava => 84,
262 Particle::DrippingDripstoneWater => 85,
263 Particle::FallingDripstoneWater => 86,
264 Particle::GlowSquidInk => 87,
265 Particle::Glow => 88,
266 Particle::WaxOn => 89,
267 Particle::WaxOff => 80,
268 Particle::ElectricSpark => 91,
269 Particle::Scrape => 92,
270 Particle::Shriek { .. } => 93,
271 Particle::EggCrack => 94,
272 }
273 }
274
275 pub fn decode_with_id(particle_id: i32, r: &mut &[u8]) -> anyhow::Result<Self> {
277 Ok(match particle_id {
278 0 => Particle::AmbientEntityEffect,
279 1 => Particle::AngryVillager,
280 2 => Particle::Block(BlockState::decode(r)?),
281 3 => Particle::BlockMarker(BlockState::decode(r)?),
282 4 => Particle::Bubble,
283 5 => Particle::Cloud,
284 6 => Particle::Crit,
285 7 => Particle::DamageIndicator,
286 8 => Particle::DragonBreath,
287 9 => Particle::DrippingLava,
288 10 => Particle::FallingLava,
289 11 => Particle::LandingLava,
290 12 => Particle::DrippingWater,
291 13 => Particle::FallingWater,
292 14 => Particle::Dust {
293 rgb: Decode::decode(r)?,
294 scale: Decode::decode(r)?,
295 },
296 15 => Particle::DustColorTransition {
297 from_rgb: Decode::decode(r)?,
298 scale: Decode::decode(r)?,
299 to_rgb: Decode::decode(r)?,
300 },
301 16 => Particle::Effect,
302 17 => Particle::ElderGuardian,
303 18 => Particle::EnchantedHit,
304 19 => Particle::Enchant,
305 20 => Particle::EndRod,
306 21 => Particle::EntityEffect,
307 22 => Particle::ExplosionEmitter,
308 23 => Particle::Explosion,
309 24 => Particle::SonicBoom,
310 25 => Particle::FallingDust(BlockState::decode(r)?),
311 26 => Particle::Firework,
312 27 => Particle::Fishing,
313 28 => Particle::Flame,
314 29 => Particle::CherryLeaves,
315 30 => Particle::SculkSoul,
316 31 => Particle::SculkCharge {
317 roll: f32::decode(r)?,
318 },
319 32 => Particle::SculkChargePop,
320 33 => Particle::SoulFireFlame,
321 34 => Particle::Soul,
322 35 => Particle::Flash,
323 36 => Particle::HappyVillager,
324 37 => Particle::Composter,
325 38 => Particle::Heart,
326 39 => Particle::InstantEffect,
327 40 => Particle::Item(Decode::decode(r)?),
328 41 => match <&str>::decode(r)? {
329 "block" => Particle::VibrationBlock {
330 block_pos: BlockPos::decode(r)?,
331 ticks: VarInt::decode(r)?.0,
332 },
333 "entity" => Particle::VibrationEntity {
334 entity_id: VarInt::decode(r)?.0,
335 entity_eye_height: f32::decode(r)?,
336 ticks: VarInt::decode(r)?.0,
337 },
338 invalid => bail!("invalid vibration position source of \"{invalid}\""),
339 },
340 42 => Particle::ItemSlime,
341 43 => Particle::ItemSnowball,
342 44 => Particle::LargeSmoke,
343 45 => Particle::Lava,
344 46 => Particle::Mycelium,
345 47 => Particle::Note,
346 48 => Particle::Poof,
347 49 => Particle::Portal,
348 50 => Particle::Rain,
349 51 => Particle::Smoke,
350 52 => Particle::Sneeze,
351 53 => Particle::Spit,
352 54 => Particle::SquidInk,
353 55 => Particle::SweepAttack,
354 56 => Particle::TotemOfUndying,
355 57 => Particle::Underwater,
356 58 => Particle::Splash,
357 59 => Particle::Witch,
358 60 => Particle::BubblePop,
359 61 => Particle::CurrentDown,
360 62 => Particle::BubbleColumnUp,
361 63 => Particle::Nautilus,
362 64 => Particle::Dolphin,
363 65 => Particle::CampfireCosySmoke,
364 66 => Particle::CampfireSignalSmoke,
365 67 => Particle::DrippingHoney,
366 68 => Particle::FallingHoney,
367 69 => Particle::LandingHoney,
368 70 => Particle::FallingNectar,
369 71 => Particle::FallingSporeBlossom,
370 72 => Particle::Ash,
371 73 => Particle::CrimsonSpore,
372 74 => Particle::WarpedSpore,
373 75 => Particle::SporeBlossomAir,
374 76 => Particle::DrippingObsidianTear,
375 77 => Particle::FallingObsidianTear,
376 78 => Particle::LandingObsidianTear,
377 79 => Particle::ReversePortal,
378 80 => Particle::WhiteAsh,
379 81 => Particle::SmallFlame,
380 82 => Particle::Snowflake,
381 83 => Particle::DrippingDripstoneLava,
382 84 => Particle::FallingDripstoneLava,
383 85 => Particle::DrippingDripstoneWater,
384 86 => Particle::FallingDripstoneWater,
385 87 => Particle::GlowSquidInk,
386 88 => Particle::Glow,
387 89 => Particle::WaxOn,
388 90 => Particle::WaxOff,
389 91 => Particle::ElectricSpark,
390 92 => Particle::Scrape,
391 93 => Particle::Shriek {
392 delay: VarInt::decode(r)?.0,
393 },
394 94 => Particle::EggCrack,
395 id => bail!("invalid particle ID of {id}"),
396 })
397 }
398}
399
400impl Encode for Particle {
402 fn encode(&self, mut w: impl Write) -> anyhow::Result<()> {
403 match self {
404 Particle::Block(block_state) => block_state.encode(w),
405 Particle::BlockMarker(block_state) => block_state.encode(w),
406 Particle::Dust { rgb, scale } => {
407 rgb.encode(&mut w)?;
408 scale.encode(w)
409 }
410 Particle::DustColorTransition {
411 from_rgb,
412 scale,
413 to_rgb,
414 } => {
415 from_rgb.encode(&mut w)?;
416 scale.encode(&mut w)?;
417 to_rgb.encode(w)
418 }
419 Particle::FallingDust(block_state) => block_state.encode(w),
420 Particle::SculkCharge { roll } => roll.encode(w),
421 Particle::Item(stack) => stack.encode(w),
422 Particle::VibrationBlock { block_pos, ticks } => {
423 "block".encode(&mut w)?;
424 block_pos.encode(&mut w)?;
425 VarInt(*ticks).encode(w)
426 }
427 Particle::VibrationEntity {
428 entity_id,
429 entity_eye_height,
430 ticks,
431 } => {
432 "entity".encode(&mut w)?;
433 VarInt(*entity_id).encode(&mut w)?;
434 entity_eye_height.encode(&mut w)?;
435 VarInt(*ticks).encode(w)
436 }
437 Particle::Shriek { delay } => VarInt(*delay).encode(w),
438 _ => Ok(()),
439 }
440 }
441}