Skip to main content

steel_protocol/packets/game/
c_level_particles.rs

1use steel_macros::{ClientPacket, WriteTo};
2use steel_registry::{packets::play::C_LEVEL_PARTICLES, particle_type::ParticleData};
3
4/// Vanilla's `ClientboundLevelParticlesPacket.RandomizationType`: controls how
5/// the client jitters particle position/velocity around the given distribution.
6#[derive(WriteTo, Clone, Copy, Debug, Default, PartialEq, Eq)]
7#[write(as = i8)]
8pub enum RandomizationType {
9    #[default]
10    Default = 0,
11    Alternative = 1,
12    AlternativeWithSpeed = 2,
13}
14
15/// Sent to create particles on the client.
16///
17/// The client samples particle positions and velocities from the supplied
18/// distribution. A count of zero has Vanilla's special single-particle
19/// velocity behavior.
20#[derive(ClientPacket, WriteTo, Clone, Debug)]
21#[packet_id(Play = C_LEVEL_PARTICLES)]
22pub struct CLevelParticles {
23    pub particle: ParticleData,
24    pub override_limiter: bool,
25    pub always_show: bool,
26    pub x: f64,
27    pub y: f64,
28    pub z: f64,
29    pub x_dist: f32,
30    pub y_dist: f32,
31    pub z_dist: f32,
32    pub x_max_speed: f32,
33    pub y_max_speed: f32,
34    pub z_max_speed: f32,
35    #[write(as = VarInt)]
36    pub count: i32,
37    pub randomization_type: RandomizationType,
38}
39
40#[cfg(test)]
41mod tests {
42
43    use steel_registry::init_vanilla_registry;
44    use steel_registry::{RegistryEntry, vanilla_particle_types};
45    use steel_utils::{codec::VarInt, serial::WriteTo};
46
47    use super::{CLevelParticles, RandomizationType};
48
49    #[test]
50    fn writes_fields_in_vanilla_wire_order() {
51        init_vanilla_registry();
52
53        let packet = CLevelParticles {
54            particle: steel_registry::particle_type::ParticleData::simple(
55                &vanilla_particle_types::FLAME,
56            ),
57            override_limiter: true,
58            always_show: false,
59            x: 1.25,
60            y: -2.5,
61            z: 3.75,
62            x_dist: 0.5,
63            y_dist: -1.0,
64            z_dist: 2.0,
65            x_max_speed: 0.125,
66            y_max_speed: 0.25,
67            z_max_speed: 0.5,
68            count: -7,
69            randomization_type: RandomizationType::Alternative,
70        };
71
72        let mut encoded = Vec::new();
73        let Ok(()) = packet.write(&mut encoded) else {
74            panic!("level particles packet should encode");
75        };
76
77        let mut expected = Vec::new();
78        let Ok(flame_id) = i32::try_from(vanilla_particle_types::FLAME.id()) else {
79            panic!("flame particle id should fit in i32");
80        };
81        let Ok(()) = VarInt(flame_id).write(&mut expected) else {
82            panic!("flame particle id should encode");
83        };
84        expected.extend_from_slice(&[1, 0]);
85        expected.extend_from_slice(&1.25_f64.to_be_bytes());
86        expected.extend_from_slice(&(-2.5_f64).to_be_bytes());
87        expected.extend_from_slice(&3.75_f64.to_be_bytes());
88        expected.extend_from_slice(&0.5_f32.to_be_bytes());
89        expected.extend_from_slice(&(-1.0_f32).to_be_bytes());
90        expected.extend_from_slice(&2.0_f32.to_be_bytes());
91        expected.extend_from_slice(&0.125_f32.to_be_bytes());
92        expected.extend_from_slice(&0.25_f32.to_be_bytes());
93        expected.extend_from_slice(&0.5_f32.to_be_bytes());
94        let Ok(()) = VarInt(-7).write(&mut expected) else {
95            panic!("count should encode");
96        };
97        expected.push(1);
98
99        assert_eq!(encoded, expected);
100    }
101}