From 760c645de1de38c8b4332289bc482f30bb09f6bf Mon Sep 17 00:00:00 2001 From: Charles Bournhonesque Date: Wed, 27 Mar 2024 23:09:31 -0400 Subject: [PATCH] Fix TypeRegistry use in dynamic scene (#12715) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adopted from and closes https://github.com/bevyengine/bevy/pull/9914 by @djeedai # Objective Fix the use of `TypeRegistry` instead of `TypeRegistryArc` in dynamic scene and its serializer. Rename `DynamicScene::serialize_ron()` into `serialize()` to highlight the fact this is not about serializing to RON specifically, but rather about serializing to the official Bevy scene format (`.scn` / `.scn.ron`) which the `SceneLoader` can deserialize (and which happens to be based in RON, but that not the object here). Also make the link with the documentation of `SceneLoader` so users understand the full serializing cycle of a Bevy dynamic scene. Document `SceneSerializer` with an example showing how to serialize to a custom format (here: RON), which is easily transposed to serializing into any other format. Fixes #9520 ## Changelog ### Changed * `SceneSerializer` and all related serializing helper types now take a `&TypeRegistry` instead of a `&TypeRegistryArc`. ([SceneSerializer needlessly uses specifically &TypeRegistryArc #9520](https://github.com/bevyengine/bevy/issues/9520)) * `DynamicScene::serialize_ron()` was renamed to `serialize()`. ## Migration Guide * `SceneSerializer` and all related serializing helper types now take a `&TypeRegistry` instead of a `&TypeRegistryArc`. You can upgrade by getting the former from the latter with `TypeRegistryArc::read()`, _e.g._ ```diff let registry_arc: TypeRegistryArc = [...]; - let serializer = SceneSerializer(&scene, ®istry_arc); + let registry = registry_arc.read(); + let serializer = SceneSerializer(&scene, ®istry); ``` * Rename `DynamicScene::serialize_ron()` to `serialize()`. --------- Co-authored-by: Jerome Humbert Co-authored-by: Alice Cecile Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com> Co-authored-by: James Liu --- crates/bevy_scene/src/dynamic_scene.rs | 12 +++- crates/bevy_scene/src/scene_loader.rs | 4 +- crates/bevy_scene/src/serde.rs | 98 ++++++++++++-------------- examples/scene/scene.rs | 3 +- 4 files changed, 58 insertions(+), 59 deletions(-) diff --git a/crates/bevy_scene/src/dynamic_scene.rs b/crates/bevy_scene/src/dynamic_scene.rs index ac2f98ffae8bb..99aa528cda471 100644 --- a/crates/bevy_scene/src/dynamic_scene.rs +++ b/crates/bevy_scene/src/dynamic_scene.rs @@ -5,7 +5,7 @@ use bevy_ecs::{ reflect::{AppTypeRegistry, ReflectComponent, ReflectMapEntities}, world::World, }; -use bevy_reflect::{Reflect, TypePath, TypeRegistryArc}; +use bevy_reflect::{Reflect, TypePath, TypeRegistry}; use bevy_utils::TypeIdMap; #[cfg(feature = "serialize")] @@ -171,9 +171,15 @@ impl DynamicScene { } // TODO: move to AssetSaver when it is implemented - /// Serialize this dynamic scene into rust object notation (ron). + /// Serialize this dynamic scene into the official Bevy scene format (`.scn` / `.scn.ron`). + /// + /// The Bevy scene format is based on [Rusty Object Notation (RON)]. It describes the scene + /// in a human-friendly format. To deserialize the scene, use the [`SceneLoader`]. + /// + /// [`SceneLoader`]: crate::SceneLoader + /// [Rusty Object Notation (RON)]: https://crates.io/crates/ron #[cfg(feature = "serialize")] - pub fn serialize_ron(&self, registry: &TypeRegistryArc) -> Result { + pub fn serialize(&self, registry: &TypeRegistry) -> Result { serialize_ron(SceneSerializer::new(self, registry)) } } diff --git a/crates/bevy_scene/src/scene_loader.rs b/crates/bevy_scene/src/scene_loader.rs index 107d014b2bb09..46d0484f0e66a 100644 --- a/crates/bevy_scene/src/scene_loader.rs +++ b/crates/bevy_scene/src/scene_loader.rs @@ -10,7 +10,9 @@ use bevy_reflect::TypeRegistryArc; use serde::de::DeserializeSeed; use thiserror::Error; -/// [`AssetLoader`] for loading serialized Bevy scene files as [`DynamicScene`]. +/// Asset loader for a Bevy dynamic scene (`.scn` / `.scn.ron`). +/// +/// The loader handles assets serialized with [`DynamicScene::serialize`]. #[derive(Debug)] pub struct SceneLoader { type_registry: TypeRegistryArc, diff --git a/crates/bevy_scene/src/serde.rs b/crates/bevy_scene/src/serde.rs index 54b5f98101635..b5ac7478c99c4 100644 --- a/crates/bevy_scene/src/serde.rs +++ b/crates/bevy_scene/src/serde.rs @@ -5,7 +5,7 @@ use bevy_ecs::entity::Entity; use bevy_reflect::serde::{TypedReflectDeserializer, TypedReflectSerializer}; use bevy_reflect::{ serde::{ReflectDeserializer, TypeRegistrationDeserializer}, - Reflect, TypeRegistry, TypeRegistryArc, + Reflect, TypeRegistry, }; use bevy_utils::HashSet; use serde::ser::SerializeMap; @@ -28,59 +28,46 @@ pub const ENTITY_STRUCT: &str = "Entity"; /// Name of the serialized component field in an entity struct. pub const ENTITY_FIELD_COMPONENTS: &str = "components"; -/// Handles serialization of a scene as a struct containing its entities and resources. +/// Serializer for a [`DynamicScene`]. /// -/// # Examples +/// Helper object defining Bevy's serialize format for a [`DynamicScene`] and implementing +/// the [`Serialize`] trait for use with Serde. /// -/// ``` -/// # use bevy_scene::{serde::SceneSerializer, DynamicScene}; -/// # use bevy_ecs::{ -/// # prelude::{Component, World}, -/// # reflect::{AppTypeRegistry, ReflectComponent}, -/// # }; -/// # use bevy_reflect::Reflect; -/// // Define an example component type. -/// #[derive(Component, Reflect, Default)] -/// #[reflect(Component)] -/// struct MyComponent { -/// foo: [usize; 3], -/// bar: (f32, f32), -/// baz: String, -/// } -/// -/// // Create our world, provide it with a type registry. -/// // Normally, [`App`] handles providing the type registry. -/// let mut world = World::new(); -/// let registry = AppTypeRegistry::default(); -/// { -/// let mut registry = registry.write(); -/// // Register our component. Primitives and String are registered by default. -/// // Sequence types are automatically handled. -/// registry.register::(); -/// } -/// world.insert_resource(registry); -/// world.spawn(MyComponent { -/// foo: [1, 2, 3], -/// bar: (1.3, 3.7), -/// baz: String::from("test"), -/// }); +/// # Example /// -/// // Print out our serialized scene in the RON format. +/// ``` +/// # use bevy_ecs::prelude::*; +/// # use bevy_scene::{DynamicScene, serde::SceneSerializer}; +/// # let mut world = World::default(); +/// # world.insert_resource(AppTypeRegistry::default()); +/// // Get the type registry /// let registry = world.resource::(); +/// let registry = registry.read(); +/// +/// // Get a DynamicScene to serialize, for example from the World itself /// let scene = DynamicScene::from_world(&world); -/// let scene_serializer = SceneSerializer::new(&scene, ®istry.0); -/// println!("{}", bevy_scene::serialize_ron(scene_serializer).unwrap()); +/// +/// // Create a serializer for that DynamicScene, using the associated TypeRegistry +/// let scene_serializer = SceneSerializer::new(&scene, ®istry); +/// +/// // Serialize through any serde-compatible Serializer +/// let ron_string = bevy_scene::ron::ser::to_string(&scene_serializer); /// ``` pub struct SceneSerializer<'a> { /// The scene to serialize. pub scene: &'a DynamicScene, - /// Type registry in which the components and resources types used in the scene are registered. - pub registry: &'a TypeRegistryArc, + /// The type registry containing the types present in the scene. + pub registry: &'a TypeRegistry, } impl<'a> SceneSerializer<'a> { - /// Creates a scene serializer. - pub fn new(scene: &'a DynamicScene, registry: &'a TypeRegistryArc) -> Self { + /// Create a new serializer from a [`DynamicScene`] and an associated [`TypeRegistry`]. + /// + /// The type registry must contain all types present in the scene. This is generally the case + /// if you obtain both the scene and the registry from the same [`World`]. + /// + /// [`World`]: bevy_ecs::world::World + pub fn new(scene: &'a DynamicScene, registry: &'a TypeRegistry) -> Self { SceneSerializer { scene, registry } } } @@ -114,7 +101,7 @@ pub struct EntitiesSerializer<'a> { /// The entities to serialize. pub entities: &'a [DynamicEntity], /// Type registry in which the component types used by the entities are registered. - pub registry: &'a TypeRegistryArc, + pub registry: &'a TypeRegistry, } impl<'a> Serialize for EntitiesSerializer<'a> { @@ -141,7 +128,7 @@ pub struct EntitySerializer<'a> { /// The entity to serialize. pub entity: &'a DynamicEntity, /// Type registry in which the component types used by the entity are registered. - pub registry: &'a TypeRegistryArc, + pub registry: &'a TypeRegistry, } impl<'a> Serialize for EntitySerializer<'a> { @@ -170,7 +157,7 @@ pub struct SceneMapSerializer<'a> { /// List of boxed values of unique type to serialize. pub entries: &'a [Box], /// Type registry in which the types used in `entries` are registered. - pub registry: &'a TypeRegistryArc, + pub registry: &'a TypeRegistry, } impl<'a> Serialize for SceneMapSerializer<'a> { @@ -182,7 +169,7 @@ impl<'a> Serialize for SceneMapSerializer<'a> { for reflect in self.entries { state.serialize_entry( reflect.get_represented_type_info().unwrap().type_path(), - &TypedReflectSerializer::new(&**reflect, &self.registry.read()), + &TypedReflectSerializer::new(&**reflect, self.registry), )?; } state.end() @@ -624,7 +611,7 @@ mod tests { }, )"#; let output = scene - .serialize_ron(&world.resource::().0) + .serialize(&world.resource::().read()) .unwrap(); assert_eq!(expected, output); } @@ -707,7 +694,7 @@ mod tests { let scene = DynamicScene::from_world(&world); let serialized = scene - .serialize_ron(&world.resource::().0) + .serialize(&world.resource::().read()) .unwrap(); let mut deserializer = ron::de::Deserializer::from_str(&serialized).unwrap(); let scene_deserializer = SceneDeserializer { @@ -753,10 +740,11 @@ mod tests { }); let registry = world.resource::(); + let registry = ®istry.read(); let scene = DynamicScene::from_world(&world); - let scene_serializer = SceneSerializer::new(&scene, ®istry.0); + let scene_serializer = SceneSerializer::new(&scene, registry); let serialized_scene = postcard::to_allocvec(&scene_serializer).unwrap(); assert_eq!( @@ -770,7 +758,7 @@ mod tests { ); let scene_deserializer = SceneDeserializer { - type_registry: ®istry.0.read(), + type_registry: registry, }; let deserialized_scene = scene_deserializer .deserialize(&mut postcard::Deserializer::from_bytes(&serialized_scene)) @@ -791,10 +779,11 @@ mod tests { }); let registry = world.resource::(); + let registry = ®istry.read(); let scene = DynamicScene::from_world(&world); - let scene_serializer = SceneSerializer::new(&scene, ®istry.0); + let scene_serializer = SceneSerializer::new(&scene, registry); let mut buf = Vec::new(); let mut ser = rmp_serde::Serializer::new(&mut buf); scene_serializer.serialize(&mut ser).unwrap(); @@ -811,7 +800,7 @@ mod tests { ); let scene_deserializer = SceneDeserializer { - type_registry: ®istry.0.read(), + type_registry: registry, }; let mut reader = BufReader::new(buf.as_slice()); @@ -834,10 +823,11 @@ mod tests { }); let registry = world.resource::(); + let registry = ®istry.read(); let scene = DynamicScene::from_world(&world); - let scene_serializer = SceneSerializer::new(&scene, ®istry.0); + let scene_serializer = SceneSerializer::new(&scene, registry); let serialized_scene = bincode::serialize(&scene_serializer).unwrap(); assert_eq!( @@ -853,7 +843,7 @@ mod tests { ); let scene_deserializer = SceneDeserializer { - type_registry: ®istry.0.read(), + type_registry: registry, }; let deserialized_scene = bincode::DefaultOptions::new() diff --git a/examples/scene/scene.rs b/examples/scene/scene.rs index 6b42451d885ef..a75eba99ff43d 100644 --- a/examples/scene/scene.rs +++ b/examples/scene/scene.rs @@ -125,7 +125,8 @@ fn save_scene_system(world: &mut World) { // Scenes can be serialized like this: let type_registry = world.resource::(); - let serialized_scene = scene.serialize_ron(type_registry).unwrap(); + let type_registry = type_registry.read(); + let serialized_scene = scene.serialize(&type_registry).unwrap(); // Showing the scene in the console info!("{}", serialized_scene);