Skip to content

Commit 8564f9d

Browse files
committed
Add binary format support for DynamicScene (de)serializers
1 parent 75e4bd4 commit 8564f9d

File tree

2 files changed

+242
-3
lines changed

2 files changed

+242
-3
lines changed

crates/bevy_scene/Cargo.toml

+4
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

+238-3
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,22 @@ impl<'a, 'de> Visitor<'de> for SceneEntityVisitor<'a> {
273273
formatter.write_str("entities")
274274
}
275275

276+
fn visit_seq<A>(self, mut seq: A) -> std::result::Result<Self::Value, A::Error>
277+
where
278+
A: SeqAccess<'de>,
279+
{
280+
let components = seq
281+
.next_element_seed(ComponentDeserializer {
282+
registry: self.registry,
283+
})?
284+
.ok_or_else(|| Error::missing_field(ENTITY_FIELD_COMPONENTS))?;
285+
286+
Ok(DynamicEntity {
287+
entity: self.id,
288+
components,
289+
})
290+
}
291+
276292
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
277293
where
278294
A: MapAccess<'de>,
@@ -370,12 +386,13 @@ impl<'a, 'de> Visitor<'de> for ComponentVisitor<'a> {
370386

371387
#[cfg(test)]
372388
mod tests {
373-
use crate::serde::SceneDeserializer;
374-
use crate::DynamicSceneBuilder;
389+
use crate::serde::{SceneDeserializer, SceneSerializer};
390+
use crate::{DynamicScene, DynamicSceneBuilder};
375391
use bevy_app::AppTypeRegistry;
376392
use bevy_ecs::entity::EntityMap;
377393
use bevy_ecs::prelude::{Component, ReflectComponent, World};
378-
use bevy_reflect::Reflect;
394+
use bevy_reflect::{FromReflect, Reflect, ReflectSerialize};
395+
use bincode::Options;
379396
use serde::de::DeserializeSeed;
380397

381398
#[derive(Component, Reflect, Default)]
@@ -388,6 +405,24 @@ mod tests {
388405
#[reflect(Component)]
389406
struct Baz(i32);
390407

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

0 commit comments

Comments
 (0)