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