Skip to content

Commit 940d207

Browse files
committed
Add binary format support for DynamicScene (de)serializers
1 parent 29542b1 commit 940d207

File tree

2 files changed

+242
-3
lines changed

2 files changed

+242
-3
lines changed

crates/bevy_scene/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,7 @@ ron = "0.8.0"
2626
uuid = { version = "1.1", features = ["v4", "serde"] }
2727
anyhow = "1.0.4"
2828
thiserror = "1.0"
29+
30+
[dev-dependencies]
31+
postcard = { version = "1.0", features = ["alloc"] }
32+
bincode = "1.3"

crates/bevy_scene/src/serde.rs

Lines changed: 238 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,22 @@ impl<'a, 'de> Visitor<'de> for SceneEntityVisitor<'a> {
268268
formatter.write_str("entities")
269269
}
270270

271+
fn visit_seq<A>(self, mut seq: A) -> std::result::Result<Self::Value, A::Error>
272+
where
273+
A: SeqAccess<'de>,
274+
{
275+
let entity = seq
276+
.next_element::<u32>()?
277+
.ok_or_else(|| Error::missing_field(ENTITY_FIELD_ENTITY))?;
278+
let components = seq
279+
.next_element_seed(ComponentDeserializer {
280+
registry: self.registry,
281+
})?
282+
.ok_or_else(|| Error::missing_field(ENTITY_FIELD_COMPONENTS))?;
283+
284+
Ok(DynamicEntity { entity, components })
285+
}
286+
271287
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
272288
where
273289
A: MapAccess<'de>,
@@ -376,12 +392,13 @@ impl<'a, 'de> Visitor<'de> for ComponentVisitor<'a> {
376392

377393
#[cfg(test)]
378394
mod tests {
379-
use crate::serde::SceneDeserializer;
380-
use crate::DynamicSceneBuilder;
395+
use crate::serde::{SceneDeserializer, SceneSerializer};
396+
use crate::{DynamicScene, DynamicSceneBuilder};
381397
use bevy_app::AppTypeRegistry;
382398
use bevy_ecs::entity::EntityMap;
383399
use bevy_ecs::prelude::{Component, ReflectComponent, World};
384-
use bevy_reflect::Reflect;
400+
use bevy_reflect::{FromReflect, Reflect, ReflectSerialize};
401+
use bincode::Options;
385402
use serde::de::DeserializeSeed;
386403

387404
#[derive(Component, Reflect, Default)]
@@ -394,6 +411,24 @@ mod tests {
394411
#[reflect(Component)]
395412
struct Baz(i32);
396413

414+
#[derive(Component, Reflect, Default)]
415+
#[reflect(Component)]
416+
struct MyComponent {
417+
foo: [usize; 3],
418+
bar: (f32, f32),
419+
baz: MyEnum,
420+
}
421+
422+
#[derive(Reflect, FromReflect, Default)]
423+
enum MyEnum {
424+
#[default]
425+
Unit,
426+
Tuple(String),
427+
Struct {
428+
value: u32,
429+
},
430+
}
431+
397432
fn create_world() -> World {
398433
let mut world = World::new();
399434
let registry = AppTypeRegistry::default();
@@ -402,6 +437,12 @@ mod tests {
402437
registry.register::<Foo>();
403438
registry.register::<Bar>();
404439
registry.register::<Baz>();
440+
registry.register::<MyComponent>();
441+
registry.register::<MyEnum>();
442+
registry.register::<String>();
443+
registry.register_type_data::<String, ReflectSerialize>();
444+
registry.register::<[usize; 3]>();
445+
registry.register::<(f32, f32)>();
405446
}
406447
world.insert_resource(registry);
407448
world
@@ -499,4 +540,198 @@ mod tests {
499540
assert_eq!(2, dst_world.query::<&Bar>().iter(&dst_world).count());
500541
assert_eq!(1, dst_world.query::<&Baz>().iter(&dst_world).count());
501542
}
543+
544+
#[test]
545+
fn should_roundtrip_postcard() {
546+
let mut world = create_world();
547+
548+
world.spawn(MyComponent {
549+
foo: [1, 2, 3],
550+
bar: (1.3, 3.7),
551+
baz: MyEnum::Tuple("Hello World!".to_string()),
552+
});
553+
554+
let registry = world.resource::<AppTypeRegistry>();
555+
556+
let scene = DynamicScene::from_world(&world, registry);
557+
558+
let scene_serializer = SceneSerializer::new(&scene, &registry.0);
559+
let serialized_scene = postcard::to_allocvec(&scene_serializer).unwrap();
560+
561+
assert_eq!(
562+
vec![
563+
1, 0, 1, 37, 98, 101, 118, 121, 95, 115, 99, 101, 110, 101, 58, 58, 115, 101, 114,
564+
100, 101, 58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121, 67, 111, 109, 112, 111,
565+
110, 101, 110, 116, 1, 2, 3, 102, 102, 166, 63, 205, 204, 108, 64, 1, 12, 72, 101,
566+
108, 108, 111, 32, 87, 111, 114, 108, 100, 33
567+
],
568+
serialized_scene
569+
);
570+
571+
let scene_deserializer = SceneDeserializer {
572+
type_registry: &registry.0.read(),
573+
};
574+
let deserialized_scene = scene_deserializer
575+
.deserialize(&mut postcard::Deserializer::from_bytes(&serialized_scene))
576+
.unwrap();
577+
578+
assert_eq!(1, deserialized_scene.entities.len());
579+
assert_scene_eq(&scene, &deserialized_scene);
580+
}
581+
582+
#[test]
583+
fn should_roundtrip_bincode() {
584+
let mut world = create_world();
585+
586+
world.spawn(MyComponent {
587+
foo: [1, 2, 3],
588+
bar: (1.3, 3.7),
589+
baz: MyEnum::Tuple("Hello World!".to_string()),
590+
});
591+
592+
let registry = world.resource::<AppTypeRegistry>();
593+
594+
let scene = DynamicScene::from_world(&world, registry);
595+
596+
let scene_serializer = SceneSerializer::new(&scene, &registry.0);
597+
let serialized_scene = bincode::serialize(&scene_serializer).unwrap();
598+
599+
assert_eq!(
600+
vec![
601+
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0,
602+
0, 98, 101, 118, 121, 95, 115, 99, 101, 110, 101, 58, 58, 115, 101, 114, 100, 101,
603+
58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121, 67, 111, 109, 112, 111, 110, 101,
604+
110, 116, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0,
605+
102, 102, 166, 63, 205, 204, 108, 64, 1, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 72, 101,
606+
108, 108, 111, 32, 87, 111, 114, 108, 100, 33
607+
],
608+
serialized_scene
609+
);
610+
611+
let scene_deserializer = SceneDeserializer {
612+
type_registry: &registry.0.read(),
613+
};
614+
615+
let deserialized_scene = bincode::DefaultOptions::new()
616+
.with_fixint_encoding()
617+
.deserialize_seed(scene_deserializer, &serialized_scene)
618+
.unwrap();
619+
620+
assert_eq!(1, deserialized_scene.entities.len());
621+
assert_scene_eq(&scene, &deserialized_scene);
622+
}
623+
624+
/// A crude equality checker for [`DynamicScene`], used solely for testing purposes.
625+
fn assert_scene_eq(expected: &DynamicScene, received: &DynamicScene) {
626+
assert_eq!(
627+
expected.entities.len(),
628+
received.entities.len(),
629+
"entity count did not match",
630+
);
631+
632+
for expected in &expected.entities {
633+
let received = received
634+
.entities
635+
.iter()
636+
.find(|dynamic_entity| dynamic_entity.entity == expected.entity)
637+
.unwrap_or_else(|| panic!("missing entity (expected: `{}`)", expected.entity));
638+
639+
assert_eq!(expected.entity, received.entity, "entities did not match",);
640+
641+
for expected in &expected.components {
642+
let received = received
643+
.components
644+
.iter()
645+
.find(|component| component.type_name() == expected.type_name())
646+
.unwrap_or_else(|| {
647+
panic!("missing component (expected: `{}`)", expected.type_name())
648+
});
649+
650+
assert!(
651+
expected
652+
.reflect_partial_eq(received.as_ref())
653+
.unwrap_or_default(),
654+
"components did not match: (expected: `{:?}`, received: `{:?}`)",
655+
expected,
656+
received
657+
);
658+
}
659+
}
660+
}
661+
662+
/// These tests just verify that that the [`assert_scene_eq`] function is working properly for our tests.
663+
mod assert_scene_eq_tests {
664+
use super::*;
665+
666+
#[test]
667+
#[should_panic(expected = "entity count did not match")]
668+
fn should_panic_when_entity_count_not_eq() {
669+
let mut world = create_world();
670+
let registry = world.resource::<AppTypeRegistry>();
671+
let scene_a = DynamicScene::from_world(&world, registry);
672+
673+
world.spawn(MyComponent {
674+
foo: [1, 2, 3],
675+
bar: (1.3, 3.7),
676+
baz: MyEnum::Unit,
677+
});
678+
679+
let registry = world.resource::<AppTypeRegistry>();
680+
let scene_b = DynamicScene::from_world(&world, registry);
681+
682+
assert_scene_eq(&scene_a, &scene_b);
683+
}
684+
685+
#[test]
686+
#[should_panic(expected = "components did not match")]
687+
fn should_panic_when_components_not_eq() {
688+
let mut world = create_world();
689+
690+
let entity = world
691+
.spawn(MyComponent {
692+
foo: [1, 2, 3],
693+
bar: (1.3, 3.7),
694+
baz: MyEnum::Unit,
695+
})
696+
.id();
697+
698+
let registry = world.resource::<AppTypeRegistry>();
699+
let scene_a = DynamicScene::from_world(&world, registry);
700+
701+
world.entity_mut(entity).insert(MyComponent {
702+
foo: [3, 2, 1],
703+
bar: (1.3, 3.7),
704+
baz: MyEnum::Unit,
705+
});
706+
707+
let registry = world.resource::<AppTypeRegistry>();
708+
let scene_b = DynamicScene::from_world(&world, registry);
709+
710+
assert_scene_eq(&scene_a, &scene_b);
711+
}
712+
713+
#[test]
714+
#[should_panic(expected = "missing component")]
715+
fn should_panic_when_missing_component() {
716+
let mut world = create_world();
717+
718+
let entity = world
719+
.spawn(MyComponent {
720+
foo: [1, 2, 3],
721+
bar: (1.3, 3.7),
722+
baz: MyEnum::Unit,
723+
})
724+
.id();
725+
726+
let registry = world.resource::<AppTypeRegistry>();
727+
let scene_a = DynamicScene::from_world(&world, registry);
728+
729+
world.entity_mut(entity).remove::<MyComponent>();
730+
731+
let registry = world.resource::<AppTypeRegistry>();
732+
let scene_b = DynamicScene::from_world(&world, registry);
733+
734+
assert_scene_eq(&scene_a, &scene_b);
735+
}
736+
}
502737
}

0 commit comments

Comments
 (0)