Fix TypeRegistry use in dynamic scene (#12715)

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, &registry_arc);
  + let registry = registry_arc.read();
  + let serializer = SceneSerializer(&scene, &registry);
  ```
* Rename `DynamicScene::serialize_ron()` to `serialize()`.

---------

Co-authored-by: Jerome Humbert <djeedai@gmail.com>
Co-authored-by: Alice Cecile <alice.i.cecil@gmail.com>
Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
Co-authored-by: James Liu <contact@jamessliu.com>
This commit is contained in:
Charles Bournhonesque 2024-03-27 23:09:31 -04:00 committed by GitHub
parent 6840f95d62
commit 760c645de1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 58 additions and 59 deletions

View File

@ -5,7 +5,7 @@ use bevy_ecs::{
reflect::{AppTypeRegistry, ReflectComponent, ReflectMapEntities}, reflect::{AppTypeRegistry, ReflectComponent, ReflectMapEntities},
world::World, world::World,
}; };
use bevy_reflect::{Reflect, TypePath, TypeRegistryArc}; use bevy_reflect::{Reflect, TypePath, TypeRegistry};
use bevy_utils::TypeIdMap; use bevy_utils::TypeIdMap;
#[cfg(feature = "serialize")] #[cfg(feature = "serialize")]
@ -171,9 +171,15 @@ impl DynamicScene {
} }
// TODO: move to AssetSaver when it is implemented // 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")] #[cfg(feature = "serialize")]
pub fn serialize_ron(&self, registry: &TypeRegistryArc) -> Result<String, ron::Error> { pub fn serialize(&self, registry: &TypeRegistry) -> Result<String, ron::Error> {
serialize_ron(SceneSerializer::new(self, registry)) serialize_ron(SceneSerializer::new(self, registry))
} }
} }

View File

@ -10,7 +10,9 @@ use bevy_reflect::TypeRegistryArc;
use serde::de::DeserializeSeed; use serde::de::DeserializeSeed;
use thiserror::Error; 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)] #[derive(Debug)]
pub struct SceneLoader { pub struct SceneLoader {
type_registry: TypeRegistryArc, type_registry: TypeRegistryArc,

View File

@ -5,7 +5,7 @@ use bevy_ecs::entity::Entity;
use bevy_reflect::serde::{TypedReflectDeserializer, TypedReflectSerializer}; use bevy_reflect::serde::{TypedReflectDeserializer, TypedReflectSerializer};
use bevy_reflect::{ use bevy_reflect::{
serde::{ReflectDeserializer, TypeRegistrationDeserializer}, serde::{ReflectDeserializer, TypeRegistrationDeserializer},
Reflect, TypeRegistry, TypeRegistryArc, Reflect, TypeRegistry,
}; };
use bevy_utils::HashSet; use bevy_utils::HashSet;
use serde::ser::SerializeMap; use serde::ser::SerializeMap;
@ -28,59 +28,46 @@ pub const ENTITY_STRUCT: &str = "Entity";
/// Name of the serialized component field in an entity struct. /// Name of the serialized component field in an entity struct.
pub const ENTITY_FIELD_COMPONENTS: &str = "components"; 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.
///
/// # Example
/// ///
/// ``` /// ```
/// # use bevy_scene::{serde::SceneSerializer, DynamicScene}; /// # use bevy_ecs::prelude::*;
/// # use bevy_ecs::{ /// # use bevy_scene::{DynamicScene, serde::SceneSerializer};
/// # prelude::{Component, World}, /// # let mut world = World::default();
/// # reflect::{AppTypeRegistry, ReflectComponent}, /// # world.insert_resource(AppTypeRegistry::default());
/// # }; /// // Get the type registry
/// # 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::<MyComponent>();
/// }
/// world.insert_resource(registry);
/// world.spawn(MyComponent {
/// foo: [1, 2, 3],
/// bar: (1.3, 3.7),
/// baz: String::from("test"),
/// });
///
/// // Print out our serialized scene in the RON format.
/// let registry = world.resource::<AppTypeRegistry>(); /// let registry = world.resource::<AppTypeRegistry>();
/// let registry = registry.read();
///
/// // Get a DynamicScene to serialize, for example from the World itself
/// let scene = DynamicScene::from_world(&world); /// let scene = DynamicScene::from_world(&world);
/// let scene_serializer = SceneSerializer::new(&scene, &registry.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, &registry);
///
/// // Serialize through any serde-compatible Serializer
/// let ron_string = bevy_scene::ron::ser::to_string(&scene_serializer);
/// ``` /// ```
pub struct SceneSerializer<'a> { pub struct SceneSerializer<'a> {
/// The scene to serialize. /// The scene to serialize.
pub scene: &'a DynamicScene, pub scene: &'a DynamicScene,
/// Type registry in which the components and resources types used in the scene are registered. /// The type registry containing the types present in the scene.
pub registry: &'a TypeRegistryArc, pub registry: &'a TypeRegistry,
} }
impl<'a> SceneSerializer<'a> { impl<'a> SceneSerializer<'a> {
/// Creates a scene serializer. /// Create a new serializer from a [`DynamicScene`] and an associated [`TypeRegistry`].
pub fn new(scene: &'a DynamicScene, registry: &'a TypeRegistryArc) -> Self { ///
/// 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 } SceneSerializer { scene, registry }
} }
} }
@ -114,7 +101,7 @@ pub struct EntitiesSerializer<'a> {
/// The entities to serialize. /// The entities to serialize.
pub entities: &'a [DynamicEntity], pub entities: &'a [DynamicEntity],
/// Type registry in which the component types used by the entities are registered. /// 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> { impl<'a> Serialize for EntitiesSerializer<'a> {
@ -141,7 +128,7 @@ pub struct EntitySerializer<'a> {
/// The entity to serialize. /// The entity to serialize.
pub entity: &'a DynamicEntity, pub entity: &'a DynamicEntity,
/// Type registry in which the component types used by the entity are registered. /// 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> { impl<'a> Serialize for EntitySerializer<'a> {
@ -170,7 +157,7 @@ pub struct SceneMapSerializer<'a> {
/// List of boxed values of unique type to serialize. /// List of boxed values of unique type to serialize.
pub entries: &'a [Box<dyn Reflect>], pub entries: &'a [Box<dyn Reflect>],
/// Type registry in which the types used in `entries` are registered. /// 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> { impl<'a> Serialize for SceneMapSerializer<'a> {
@ -182,7 +169,7 @@ impl<'a> Serialize for SceneMapSerializer<'a> {
for reflect in self.entries { for reflect in self.entries {
state.serialize_entry( state.serialize_entry(
reflect.get_represented_type_info().unwrap().type_path(), reflect.get_represented_type_info().unwrap().type_path(),
&TypedReflectSerializer::new(&**reflect, &self.registry.read()), &TypedReflectSerializer::new(&**reflect, self.registry),
)?; )?;
} }
state.end() state.end()
@ -624,7 +611,7 @@ mod tests {
}, },
)"#; )"#;
let output = scene let output = scene
.serialize_ron(&world.resource::<AppTypeRegistry>().0) .serialize(&world.resource::<AppTypeRegistry>().read())
.unwrap(); .unwrap();
assert_eq!(expected, output); assert_eq!(expected, output);
} }
@ -707,7 +694,7 @@ mod tests {
let scene = DynamicScene::from_world(&world); let scene = DynamicScene::from_world(&world);
let serialized = scene let serialized = scene
.serialize_ron(&world.resource::<AppTypeRegistry>().0) .serialize(&world.resource::<AppTypeRegistry>().read())
.unwrap(); .unwrap();
let mut deserializer = ron::de::Deserializer::from_str(&serialized).unwrap(); let mut deserializer = ron::de::Deserializer::from_str(&serialized).unwrap();
let scene_deserializer = SceneDeserializer { let scene_deserializer = SceneDeserializer {
@ -753,10 +740,11 @@ mod tests {
}); });
let registry = world.resource::<AppTypeRegistry>(); let registry = world.resource::<AppTypeRegistry>();
let registry = &registry.read();
let scene = DynamicScene::from_world(&world); let scene = DynamicScene::from_world(&world);
let scene_serializer = SceneSerializer::new(&scene, &registry.0); let scene_serializer = SceneSerializer::new(&scene, registry);
let serialized_scene = postcard::to_allocvec(&scene_serializer).unwrap(); let serialized_scene = postcard::to_allocvec(&scene_serializer).unwrap();
assert_eq!( assert_eq!(
@ -770,7 +758,7 @@ mod tests {
); );
let scene_deserializer = SceneDeserializer { let scene_deserializer = SceneDeserializer {
type_registry: &registry.0.read(), type_registry: registry,
}; };
let deserialized_scene = scene_deserializer let deserialized_scene = scene_deserializer
.deserialize(&mut postcard::Deserializer::from_bytes(&serialized_scene)) .deserialize(&mut postcard::Deserializer::from_bytes(&serialized_scene))
@ -791,10 +779,11 @@ mod tests {
}); });
let registry = world.resource::<AppTypeRegistry>(); let registry = world.resource::<AppTypeRegistry>();
let registry = &registry.read();
let scene = DynamicScene::from_world(&world); let scene = DynamicScene::from_world(&world);
let scene_serializer = SceneSerializer::new(&scene, &registry.0); let scene_serializer = SceneSerializer::new(&scene, registry);
let mut buf = Vec::new(); let mut buf = Vec::new();
let mut ser = rmp_serde::Serializer::new(&mut buf); let mut ser = rmp_serde::Serializer::new(&mut buf);
scene_serializer.serialize(&mut ser).unwrap(); scene_serializer.serialize(&mut ser).unwrap();
@ -811,7 +800,7 @@ mod tests {
); );
let scene_deserializer = SceneDeserializer { let scene_deserializer = SceneDeserializer {
type_registry: &registry.0.read(), type_registry: registry,
}; };
let mut reader = BufReader::new(buf.as_slice()); let mut reader = BufReader::new(buf.as_slice());
@ -834,10 +823,11 @@ mod tests {
}); });
let registry = world.resource::<AppTypeRegistry>(); let registry = world.resource::<AppTypeRegistry>();
let registry = &registry.read();
let scene = DynamicScene::from_world(&world); let scene = DynamicScene::from_world(&world);
let scene_serializer = SceneSerializer::new(&scene, &registry.0); let scene_serializer = SceneSerializer::new(&scene, registry);
let serialized_scene = bincode::serialize(&scene_serializer).unwrap(); let serialized_scene = bincode::serialize(&scene_serializer).unwrap();
assert_eq!( assert_eq!(
@ -853,7 +843,7 @@ mod tests {
); );
let scene_deserializer = SceneDeserializer { let scene_deserializer = SceneDeserializer {
type_registry: &registry.0.read(), type_registry: registry,
}; };
let deserialized_scene = bincode::DefaultOptions::new() let deserialized_scene = bincode::DefaultOptions::new()

View File

@ -125,7 +125,8 @@ fn save_scene_system(world: &mut World) {
// Scenes can be serialized like this: // Scenes can be serialized like this:
let type_registry = world.resource::<AppTypeRegistry>(); let type_registry = world.resource::<AppTypeRegistry>();
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 // Showing the scene in the console
info!("{}", serialized_scene); info!("{}", serialized_scene);