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
277fn ser_dye_color(data: &EntityData, buf: &mut Vec<u8>) -> io::Result<()> {
278    match data {
279        EntityData::DyeColor(v) => v.write(buf),
280        _ => Err(io::Error::other("Expected DyeColor")),
281    }
282}
283
284/// Register all vanilla entity data serializers.
285///
286/// **IMPORTANT**: The registration order MUST match vanilla's `EntityDataSerializers.java` exactly,
287/// as the serializer's network ID is determined by its registration order.
288pub fn register_vanilla_entity_data_serializers(registry: &mut EntityDataSerializerRegistry) {
289    // Order matches EntityDataSerializers.java static block.
290    // Registration macro keeps lines concise while preserving comments.
291    macro_rules! reg {
292        ($name:literal, $writer:expr) => {
293            registry.register(Identifier::vanilla_static($name), $writer);
294        };
295    }
296
297    reg!("byte", ser_byte); // 0
298    reg!("int", ser_int); // 1
299    reg!("long", ser_long); // 2
300    reg!("float", ser_float); // 3
301    reg!("string", ser_string); // 4
302    reg!("component", ser_component); // 5
303    reg!("optional_component", ser_optional_component); // 6
304    reg!("item_stack", ser_item_stack); // 7
305    reg!("boolean", ser_boolean); // 8
306    reg!("rotations", ser_rotations); // 9
307    reg!("block_pos", ser_block_pos); // 10
308    reg!("optional_block_pos", ser_optional_block_pos); // 11
309    reg!("direction", ser_direction); // 12
310    reg!(
311        "optional_living_entity_reference",
312        ser_optional_living_entity_reference
313    ); // 13
314    reg!("block_state", ser_block_state); // 14
315    reg!("optional_block_state", ser_optional_block_state); // 15
316    reg!("particle", ser_particle); // 16
317    reg!("particles", ser_particles); // 17
318    reg!("villager_data", ser_villager_data); // 18
319    reg!("optional_unsigned_int", ser_optional_unsigned_int); // 19
320    reg!("pose", ser_pose); // 20
321    reg!("cat_variant", ser_cat_variant); // 21
322    reg!("cat_sound_variant", ser_cat_sound_variant); // 22
323    reg!("cow_variant", ser_cow_variant); // 23
324    reg!("cow_sound_variant", ser_cow_sound_variant); // 24
325    reg!("wolf_variant", ser_wolf_variant); // 25
326    reg!("wolf_sound_variant", ser_wolf_sound_variant); // 26
327    reg!("frog_variant", ser_frog_variant); // 27
328    reg!("pig_variant", ser_pig_variant); // 28
329    reg!("pig_sound_variant", ser_pig_sound_variant); // 29
330    reg!("chicken_variant", ser_chicken_variant); // 30
331    reg!("chicken_sound_variant", ser_chicken_sound_variant); // 31
332    reg!("zombie_nautilus_variant", ser_zombie_nautilus_variant); // 32
333    reg!("optional_global_pos", ser_optional_global_pos); // 33
334    reg!("painting_variant", ser_painting_variant); // 34
335    reg!("sniffer_state", ser_sniffer_state); // 35
336    reg!("armadillo_state", ser_armadillo_state); // 36
337    reg!("copper_golem_state", ser_copper_golem_state); // 37
338    reg!("weathering_copper_state", ser_weathering_copper_state); // 38
339    reg!("vector3", ser_vector3); // 39
340    reg!("quaternion", ser_quaternion); // 40
341    reg!("resolvable_profile", ser_resolvable_profile); // 41
342    reg!("humanoid_arm", ser_humanoid_arm); // 42
343    reg!("dye_color", ser_dye_color); // 43
344}
345
346#[cfg(test)]
347mod tests {
348    use crate::entity_data::ResolvableProfile;
349    use crate::init_vanilla_registry;
350    use crate::{REGISTRY, RegistryExt, RegistryReference, vanilla_pig_variants};
351
352    use super::*;
353
354    macro_rules! id {
355        ($name:expr) => {
356            Identifier::vanilla_static($name)
357        };
358    }
359
360    #[test]
361    fn test_serializer_registration_order() {
362        let mut registry = EntityDataSerializerRegistry::new();
363        register_vanilla_entity_data_serializers(&mut registry);
364
365        let expected_names = [
366            "byte",
367            "int",
368            "long",
369            "float",
370            "string",
371            "component",
372            "optional_component",
373            "item_stack",
374            "boolean",
375            "rotations",
376            "block_pos",
377            "optional_block_pos",
378            "direction",
379            "optional_living_entity_reference",
380            "block_state",
381            "optional_block_state",
382            "particle",
383            "particles",
384            "villager_data",
385            "optional_unsigned_int",
386            "pose",
387            "cat_variant",
388            "cat_sound_variant",
389            "cow_variant",
390            "cow_sound_variant",
391            "wolf_variant",
392            "wolf_sound_variant",
393            "frog_variant",
394            "pig_variant",
395            "pig_sound_variant",
396            "chicken_variant",
397            "chicken_sound_variant",
398            "zombie_nautilus_variant",
399            "optional_global_pos",
400            "painting_variant",
401            "sniffer_state",
402            "armadillo_state",
403            "copper_golem_state",
404            "weathering_copper_state",
405            "vector3",
406            "quaternion",
407            "resolvable_profile",
408            "humanoid_arm",
409            "dye_color",
410        ];
411        for (expected_id, name) in expected_names.iter().enumerate() {
412            assert_eq!(
413                registry.id_from_key(&id!(name)),
414                Some(expected_id),
415                "serializer {name} must keep vanilla id {expected_id}"
416            );
417        }
418
419        // Total count
420        assert_eq!(registry.len(), 44);
421    }
422
423    #[test]
424    fn test_serializers_write_correctly() {
425        let mut registry = EntityDataSerializerRegistry::new();
426        register_vanilla_entity_data_serializers(&mut registry);
427
428        // Test byte serializer
429        let writer = registry.get_writer(0).unwrap();
430        let mut buf = Vec::new();
431        writer(&EntityData::Byte(42), &mut buf).unwrap();
432        assert_eq!(buf, vec![42]);
433
434        // Test int serializer (VarInt)
435        let writer = registry.get_writer(1).unwrap();
436        let mut buf = Vec::new();
437        writer(&EntityData::Int(300), &mut buf).unwrap();
438        assert_eq!(buf, vec![0xAC, 0x02]); // 300 as VarInt
439
440        // Test float serializer
441        let writer = registry.get_writer(3).unwrap();
442        let mut buf = Vec::new();
443        writer(&EntityData::Float(1.5), &mut buf).unwrap();
444        assert_eq!(buf, 1.5f32.to_be_bytes().to_vec());
445
446        // Test boolean serializer
447        let writer = registry.get_writer(8).unwrap();
448        let mut buf = Vec::new();
449        writer(&EntityData::Boolean(true), &mut buf).unwrap();
450        assert_eq!(buf, vec![1]);
451    }
452
453    #[test]
454    fn holder_serializer_resolves_the_registered_variant_id() {
455        init_vanilla_registry();
456
457        let mut serializers = EntityDataSerializerRegistry::new();
458        register_vanilla_entity_data_serializers(&mut serializers);
459        let serializer_id = serializers
460            .id_from_key(&id!("pig_variant"))
461            .expect("pig variant serializer must be registered");
462        let writer = serializers
463            .get_writer(serializer_id as i32)
464            .expect("pig variant serializer must have a writer");
465
466        let mut encoded = Vec::new();
467        writer(
468            &EntityData::PigVariant(RegistryReference::new(&vanilla_pig_variants::WARM)),
469            &mut encoded,
470        )
471        .expect("pig variant reference should encode");
472
473        let variant_id = REGISTRY
474            .pig_variants
475            .id_from_key(&vanilla_pig_variants::WARM.key)
476            .expect("warm pig variant must be registered");
477        let mut expected = Vec::new();
478        VarInt(variant_id as i32)
479            .write(&mut expected)
480            .expect("pig variant id should encode");
481        assert_eq!(encoded, expected);
482    }
483
484    #[test]
485    fn empty_resolvable_profile_matches_vanilla_static_empty_shape() {
486        let mut buf = Vec::new();
487        ser_resolvable_profile(
488            &EntityData::ResolvableProfile(ResolvableProfile::default()),
489            &mut buf,
490        )
491        .unwrap();
492
493        assert_eq!(buf, vec![0, 0, 0, 0, 0, 0, 0, 0]);
494    }
495}