Skip to main content

steel_registry/entity_data/
vanilla_serializers.rs

1//! Vanilla entity data serializer registration.
2//!
3//! This module registers all vanilla entity data serializers in the exact order
4//! they appear in vanilla's `EntityDataSerializers.java`. The registration order
5//! determines the serializer ID used in the network protocol.
6#![cfg_attr(
7    test,
8    expect(
9        clippy::unwrap_used,
10        reason = "vanilla entity data serializer tests unwrap generated registry invariants"
11    )
12)]
13
14use std::io;
15
16use steel_utils::{
17    PackedBlockPos,
18    codec::{VarInt, VarLong},
19    serial::{PrefixedWrite, WriteTo},
20};
21
22use steel_utils::Identifier;
23
24use super::{EntityData, EntityDataSerializerRegistry, ParticleData};
25
26/// Simple serializer: extract value and call `.write(buf)`.
27macro_rules! ser_write {
28    ($name:ident, $variant:ident) => {
29        fn $name(data: &EntityData, buf: &mut Vec<u8>) -> io::Result<()> {
30            match data {
31                EntityData::$variant(v) => v.write(buf),
32                _ => Err(io::Error::other(concat!("Expected ", stringify!($variant)))),
33            }
34        }
35    };
36}
37
38/// Serializer that wraps value in `VarInt`.
39macro_rules! ser_varint {
40    ($name:ident, $variant:ident) => {
41        fn $name(data: &EntityData, buf: &mut Vec<u8>) -> io::Result<()> {
42            match data {
43                EntityData::$variant(v) => VarInt(*v).write(buf),
44                _ => Err(io::Error::other(concat!("Expected ", stringify!($variant)))),
45            }
46        }
47    };
48}
49
50/// Serializer that casts enum to i32 then writes as `VarInt`.
51macro_rules! ser_enum_varint {
52    ($name:ident, $variant:ident) => {
53        fn $name(data: &EntityData, buf: &mut Vec<u8>) -> io::Result<()> {
54            match data {
55                EntityData::$variant(v) => VarInt(*v as i32).write(buf),
56                _ => Err(io::Error::other(concat!("Expected ", stringify!($variant)))),
57            }
58        }
59    };
60}
61
62// Simple write serializers
63ser_write!(ser_byte, Byte);
64ser_write!(ser_float, Float);
65ser_write!(ser_component, Component);
66ser_write!(ser_item_stack, ItemStack);
67ser_write!(ser_boolean, Boolean);
68ser_write!(ser_block_state, BlockState);
69
70// Plain i32 VarInt serializers
71ser_varint!(ser_int, Int);
72ser_varint!(ser_copper_golem_state, CopperGolemState);
73ser_varint!(ser_weathering_copper_state, WeatheringCopperState);
74
75// Holder/registry reference serializers resolve protocol IDs at the network edge.
76ser_write!(ser_cat_variant, CatVariant);
77ser_write!(ser_cat_sound_variant, CatSoundVariant);
78ser_write!(ser_cow_variant, CowVariant);
79ser_write!(ser_cow_sound_variant, CowSoundVariant);
80ser_write!(ser_wolf_variant, WolfVariant);
81ser_write!(ser_wolf_sound_variant, WolfSoundVariant);
82ser_write!(ser_frog_variant, FrogVariant);
83ser_write!(ser_pig_variant, PigVariant);
84ser_write!(ser_pig_sound_variant, PigSoundVariant);
85ser_write!(ser_chicken_variant, ChickenVariant);
86ser_write!(ser_chicken_sound_variant, ChickenSoundVariant);
87ser_write!(ser_zombie_nautilus_variant, ZombieNautilusVariant);
88ser_write!(ser_painting_variant, PaintingVariant);
89
90// Enum as VarInt serializers
91ser_enum_varint!(ser_direction, Direction);
92ser_enum_varint!(ser_pose, Pose);
93ser_enum_varint!(ser_sniffer_state, SnifferState);
94ser_enum_varint!(ser_armadillo_state, ArmadilloState);
95
96fn ser_long(data: &EntityData, buf: &mut Vec<u8>) -> io::Result<()> {
97    match data {
98        EntityData::Long(v) => VarLong(*v).write(buf),
99        _ => Err(io::Error::other("Expected Long")),
100    }
101}
102
103fn ser_string(data: &EntityData, buf: &mut Vec<u8>) -> io::Result<()> {
104    match data {
105        EntityData::String(v) => v.write_prefixed::<VarInt>(buf),
106        _ => Err(io::Error::other("Expected String")),
107    }
108}
109
110fn ser_optional_component(data: &EntityData, buf: &mut Vec<u8>) -> io::Result<()> {
111    match data {
112        EntityData::OptionalComponent(v) => match v {
113            Some(comp) => {
114                true.write(buf)?;
115                comp.write(buf)
116            }
117            None => false.write(buf),
118        },
119        _ => Err(io::Error::other("Expected OptionalComponent")),
120    }
121}
122
123fn ser_rotations(data: &EntityData, buf: &mut Vec<u8>) -> io::Result<()> {
124    match data {
125        EntityData::Rotations(v) => {
126            v.x.write(buf)?;
127            v.y.write(buf)?;
128            v.z.write(buf)
129        }
130        _ => Err(io::Error::other("Expected Rotations")),
131    }
132}
133
134fn ser_block_pos(data: &EntityData, buf: &mut Vec<u8>) -> io::Result<()> {
135    match data {
136        EntityData::BlockPos(v) => PackedBlockPos::from(*v).write(buf),
137        _ => Err(io::Error::other("Expected BlockPos")),
138    }
139}
140
141fn ser_optional_block_pos(data: &EntityData, buf: &mut Vec<u8>) -> io::Result<()> {
142    match data {
143        EntityData::OptionalBlockPos(v) => match v {
144            Some(pos) => {
145                true.write(buf)?;
146                PackedBlockPos::from(*pos).write(buf)
147            }
148            None => false.write(buf),
149        },
150        _ => Err(io::Error::other("Expected OptionalBlockPos")),
151    }
152}
153
154fn ser_optional_living_entity_reference(data: &EntityData, buf: &mut Vec<u8>) -> io::Result<()> {
155    match data {
156        EntityData::OptionalLivingEntityRef(v) => match v {
157            Some(uuid) => {
158                true.write(buf)?;
159                uuid.write(buf)
160            }
161            None => false.write(buf),
162        },
163        _ => Err(io::Error::other("Expected OptionalLivingEntityRef")),
164    }
165}
166
167fn ser_optional_block_state(data: &EntityData, buf: &mut Vec<u8>) -> io::Result<()> {
168    match data {
169        EntityData::OptionalBlockState(v) => {
170            // Vanilla encodes: 0 = absent, otherwise raw block state ID
171            match v {
172                Some(state) => VarInt(i32::from(state.0)).write(buf),
173                None => VarInt(0).write(buf),
174            }
175        }
176        _ => Err(io::Error::other("Expected OptionalBlockState")),
177    }
178}
179
180fn write_particle(particle: &ParticleData, buf: &mut Vec<u8>) -> io::Result<()> {
181    particle.write(buf)
182}
183
184fn ser_particle(data: &EntityData, buf: &mut Vec<u8>) -> io::Result<()> {
185    match data {
186        EntityData::Particle(v) => write_particle(v, buf),
187        _ => Err(io::Error::other("Expected Particle")),
188    }
189}
190
191fn ser_particles(data: &EntityData, buf: &mut Vec<u8>) -> io::Result<()> {
192    match data {
193        EntityData::Particles(v) => {
194            VarInt(v.particles.len() as i32).write(buf)?;
195            for particle in &v.particles {
196                write_particle(particle, buf)?;
197            }
198            Ok(())
199        }
200        _ => Err(io::Error::other("Expected Particles")),
201    }
202}
203
204fn ser_villager_data(data: &EntityData, buf: &mut Vec<u8>) -> io::Result<()> {
205    match data {
206        EntityData::VillagerData(v) => {
207            VarInt(v.villager_type).write(buf)?;
208            VarInt(v.profession).write(buf)?;
209            VarInt(v.level).write(buf)
210        }
211        _ => Err(io::Error::other("Expected VillagerData")),
212    }
213}
214
215fn ser_optional_unsigned_int(data: &EntityData, buf: &mut Vec<u8>) -> io::Result<()> {
216    match data {
217        EntityData::OptionalUnsignedInt(v) => {
218            // Encoded as VarInt: 0 = absent, otherwise value + 1
219            VarInt(v.map_or(0, |x| x as i32 + 1)).write(buf)
220        }
221        _ => Err(io::Error::other("Expected OptionalUnsignedInt")),
222    }
223}
224
225fn ser_optional_global_pos(data: &EntityData, buf: &mut Vec<u8>) -> io::Result<()> {
226    match data {
227        EntityData::OptionalGlobalPos(v) => match v {
228            Some(global_pos) => {
229                true.write(buf)?;
230                global_pos.dimension.write(buf)?;
231                PackedBlockPos::from(global_pos.pos).write(buf)
232            }
233            None => false.write(buf),
234        },
235        _ => Err(io::Error::other("Expected OptionalGlobalPos")),
236    }
237}
238
239fn ser_vector3(data: &EntityData, buf: &mut Vec<u8>) -> io::Result<()> {
240    match data {
241        EntityData::Vector3(v) => {
242            v.x.write(buf)?;
243            v.y.write(buf)?;
244            v.z.write(buf)
245        }
246        _ => Err(io::Error::other("Expected Vector3")),
247    }
248}
249
250fn ser_quaternion(data: &EntityData, buf: &mut Vec<u8>) -> io::Result<()> {
251    match data {
252        EntityData::Quaternion(v) => {
253            v.x.write(buf)?;
254            v.y.write(buf)?;
255            v.z.write(buf)?;
256            v.w.write(buf)
257        }
258        _ => Err(io::Error::other("Expected Quaternion")),
259    }
260}
261
262fn ser_resolvable_profile(data: &EntityData, buf: &mut Vec<u8>) -> io::Result<()> {
263    match data {
264        EntityData::ResolvableProfile(value) => value.write(buf),
265        _ => Err(io::Error::other("Expected ResolvableProfile")),
266    }
267}
268
269fn ser_humanoid_arm(data: &EntityData, buf: &mut Vec<u8>) -> io::Result<()> {
270    match data {
271        // Vanilla uses ByteBufCodecs.idMapper which writes as VarInt
272        EntityData::HumanoidArm(v) => VarInt(*v as i32).write(buf),
273        _ => Err(io::Error::other("Expected HumanoidArm")),
274    }
275}
276
277/// Register all vanilla entity data serializers.
278///
279/// **IMPORTANT**: The registration order MUST match vanilla's `EntityDataSerializers.java` exactly,
280/// as the serializer's network ID is determined by its registration order.
281pub fn register_vanilla_entity_data_serializers(registry: &mut EntityDataSerializerRegistry) {
282    // Order matches EntityDataSerializers.java static block.
283    // Registration macro keeps lines concise while preserving comments.
284    macro_rules! reg {
285        ($name:literal, $writer:expr) => {
286            registry.register(Identifier::vanilla_static($name), $writer);
287        };
288    }
289
290    reg!("byte", ser_byte); // 0
291    reg!("int", ser_int); // 1
292    reg!("long", ser_long); // 2
293    reg!("float", ser_float); // 3
294    reg!("string", ser_string); // 4
295    reg!("component", ser_component); // 5
296    reg!("optional_component", ser_optional_component); // 6
297    reg!("item_stack", ser_item_stack); // 7
298    reg!("boolean", ser_boolean); // 8
299    reg!("rotations", ser_rotations); // 9
300    reg!("block_pos", ser_block_pos); // 10
301    reg!("optional_block_pos", ser_optional_block_pos); // 11
302    reg!("direction", ser_direction); // 12
303    reg!(
304        "optional_living_entity_reference",
305        ser_optional_living_entity_reference
306    ); // 13
307    reg!("block_state", ser_block_state); // 14
308    reg!("optional_block_state", ser_optional_block_state); // 15
309    reg!("particle", ser_particle); // 16
310    reg!("particles", ser_particles); // 17
311    reg!("villager_data", ser_villager_data); // 18
312    reg!("optional_unsigned_int", ser_optional_unsigned_int); // 19
313    reg!("pose", ser_pose); // 20
314    reg!("cat_variant", ser_cat_variant); // 21
315    reg!("cat_sound_variant", ser_cat_sound_variant); // 22
316    reg!("cow_variant", ser_cow_variant); // 23
317    reg!("cow_sound_variant", ser_cow_sound_variant); // 24
318    reg!("wolf_variant", ser_wolf_variant); // 25
319    reg!("wolf_sound_variant", ser_wolf_sound_variant); // 26
320    reg!("frog_variant", ser_frog_variant); // 27
321    reg!("pig_variant", ser_pig_variant); // 28
322    reg!("pig_sound_variant", ser_pig_sound_variant); // 29
323    reg!("chicken_variant", ser_chicken_variant); // 30
324    reg!("chicken_sound_variant", ser_chicken_sound_variant); // 31
325    reg!("zombie_nautilus_variant", ser_zombie_nautilus_variant); // 32
326    reg!("optional_global_pos", ser_optional_global_pos); // 33
327    reg!("painting_variant", ser_painting_variant); // 34
328    reg!("sniffer_state", ser_sniffer_state); // 35
329    reg!("armadillo_state", ser_armadillo_state); // 36
330    reg!("copper_golem_state", ser_copper_golem_state); // 37
331    reg!("weathering_copper_state", ser_weathering_copper_state); // 38
332    reg!("vector3", ser_vector3); // 39
333    reg!("quaternion", ser_quaternion); // 40
334    reg!("resolvable_profile", ser_resolvable_profile); // 41
335    reg!("humanoid_arm", ser_humanoid_arm); // 42
336}
337
338#[cfg(test)]
339mod tests {
340    use crate::entity_data::ResolvableProfile;
341    use crate::init_vanilla_registry;
342    use crate::{REGISTRY, RegistryExt, RegistryReference, vanilla_pig_variants};
343
344    use super::*;
345
346    macro_rules! id {
347        ($name:expr) => {
348            Identifier::vanilla_static($name)
349        };
350    }
351
352    #[test]
353    fn test_serializer_registration_order() {
354        let mut registry = EntityDataSerializerRegistry::new();
355        register_vanilla_entity_data_serializers(&mut registry);
356
357        let expected_names = [
358            "byte",
359            "int",
360            "long",
361            "float",
362            "string",
363            "component",
364            "optional_component",
365            "item_stack",
366            "boolean",
367            "rotations",
368            "block_pos",
369            "optional_block_pos",
370            "direction",
371            "optional_living_entity_reference",
372            "block_state",
373            "optional_block_state",
374            "particle",
375            "particles",
376            "villager_data",
377            "optional_unsigned_int",
378            "pose",
379            "cat_variant",
380            "cat_sound_variant",
381            "cow_variant",
382            "cow_sound_variant",
383            "wolf_variant",
384            "wolf_sound_variant",
385            "frog_variant",
386            "pig_variant",
387            "pig_sound_variant",
388            "chicken_variant",
389            "chicken_sound_variant",
390            "zombie_nautilus_variant",
391            "optional_global_pos",
392            "painting_variant",
393            "sniffer_state",
394            "armadillo_state",
395            "copper_golem_state",
396            "weathering_copper_state",
397            "vector3",
398            "quaternion",
399            "resolvable_profile",
400            "humanoid_arm",
401        ];
402        for (expected_id, name) in expected_names.iter().enumerate() {
403            assert_eq!(
404                registry.id_from_key(&id!(name)),
405                Some(expected_id),
406                "serializer {name} must keep vanilla id {expected_id}"
407            );
408        }
409
410        // Total count
411        assert_eq!(registry.len(), 43);
412    }
413
414    #[test]
415    fn test_serializers_write_correctly() {
416        let mut registry = EntityDataSerializerRegistry::new();
417        register_vanilla_entity_data_serializers(&mut registry);
418
419        // Test byte serializer
420        let writer = registry.get_writer(0).unwrap();
421        let mut buf = Vec::new();
422        writer(&EntityData::Byte(42), &mut buf).unwrap();
423        assert_eq!(buf, vec![42]);
424
425        // Test int serializer (VarInt)
426        let writer = registry.get_writer(1).unwrap();
427        let mut buf = Vec::new();
428        writer(&EntityData::Int(300), &mut buf).unwrap();
429        assert_eq!(buf, vec![0xAC, 0x02]); // 300 as VarInt
430
431        // Test float serializer
432        let writer = registry.get_writer(3).unwrap();
433        let mut buf = Vec::new();
434        writer(&EntityData::Float(1.5), &mut buf).unwrap();
435        assert_eq!(buf, 1.5f32.to_be_bytes().to_vec());
436
437        // Test boolean serializer
438        let writer = registry.get_writer(8).unwrap();
439        let mut buf = Vec::new();
440        writer(&EntityData::Boolean(true), &mut buf).unwrap();
441        assert_eq!(buf, vec![1]);
442    }
443
444    #[test]
445    fn holder_serializer_resolves_the_registered_variant_id() {
446        init_vanilla_registry();
447
448        let mut serializers = EntityDataSerializerRegistry::new();
449        register_vanilla_entity_data_serializers(&mut serializers);
450        let serializer_id = serializers
451            .id_from_key(&id!("pig_variant"))
452            .expect("pig variant serializer must be registered");
453        let writer = serializers
454            .get_writer(serializer_id as i32)
455            .expect("pig variant serializer must have a writer");
456
457        let mut encoded = Vec::new();
458        writer(
459            &EntityData::PigVariant(RegistryReference::new(&vanilla_pig_variants::WARM)),
460            &mut encoded,
461        )
462        .expect("pig variant reference should encode");
463
464        let variant_id = REGISTRY
465            .pig_variants
466            .id_from_key(&vanilla_pig_variants::WARM.key)
467            .expect("warm pig variant must be registered");
468        let mut expected = Vec::new();
469        VarInt(variant_id as i32)
470            .write(&mut expected)
471            .expect("pig variant id should encode");
472        assert_eq!(encoded, expected);
473    }
474
475    #[test]
476    fn empty_resolvable_profile_matches_vanilla_static_empty_shape() {
477        let mut buf = Vec::new();
478        ser_resolvable_profile(
479            &EntityData::ResolvableProfile(ResolvableProfile::default()),
480            &mut buf,
481        )
482        .unwrap();
483
484        assert_eq!(buf, vec![0, 0, 0, 0, 0, 0, 0, 0]);
485    }
486}