valence_protocol/packets/play/
particle_s2c.rs

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    /// The 'Block' variant of the 'Vibration' particle
106    VibrationBlock {
107        block_pos: BlockPos,
108        ticks: i32,
109    },
110    /// The 'Entity' variant of the 'Vibration' particle
111    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    /// Decodes the particle assuming the given particle ID.
276    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
400/// Encodes the particle without an ID.
401impl 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}