Skip to content

Commit

Permalink
Fix TypeRegistry use in dynamic scene (#12715)
Browse files Browse the repository at this point in the history
Adopted from and closes #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](#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>
  • Loading branch information
5 people authored Mar 28, 2024
1 parent 6840f95 commit 760c645
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 59 deletions.
12 changes: 9 additions & 3 deletions crates/bevy_scene/src/dynamic_scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down Expand Up @@ -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<String, ron::Error> {
pub fn serialize(&self, registry: &TypeRegistry) -> Result<String, ron::Error> {
serialize_ron(SceneSerializer::new(self, registry))
}
}
Expand Down
4 changes: 3 additions & 1 deletion crates/bevy_scene/src/scene_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
98 changes: 44 additions & 54 deletions crates/bevy_scene/src/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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::<MyComponent>();
/// }
/// 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::<AppTypeRegistry>();
/// 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, &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> {
/// 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 }
}
}
Expand Down Expand Up @@ -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> {
Expand All @@ -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> {
Expand Down Expand Up @@ -170,7 +157,7 @@ pub struct SceneMapSerializer<'a> {
/// List of boxed values of unique type to serialize.
pub entries: &'a [Box<dyn Reflect>],
/// 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> {
Expand All @@ -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()
Expand Down Expand Up @@ -624,7 +611,7 @@ mod tests {
},
)"#;
let output = scene
.serialize_ron(&world.resource::<AppTypeRegistry>().0)
.serialize(&world.resource::<AppTypeRegistry>().read())
.unwrap();
assert_eq!(expected, output);
}
Expand Down Expand Up @@ -707,7 +694,7 @@ mod tests {
let scene = DynamicScene::from_world(&world);

let serialized = scene
.serialize_ron(&world.resource::<AppTypeRegistry>().0)
.serialize(&world.resource::<AppTypeRegistry>().read())
.unwrap();
let mut deserializer = ron::de::Deserializer::from_str(&serialized).unwrap();
let scene_deserializer = SceneDeserializer {
Expand Down Expand Up @@ -753,10 +740,11 @@ mod tests {
});

let registry = world.resource::<AppTypeRegistry>();
let registry = &registry.read();

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();

assert_eq!(
Expand All @@ -770,7 +758,7 @@ mod tests {
);

let scene_deserializer = SceneDeserializer {
type_registry: &registry.0.read(),
type_registry: registry,
};
let deserialized_scene = scene_deserializer
.deserialize(&mut postcard::Deserializer::from_bytes(&serialized_scene))
Expand All @@ -791,10 +779,11 @@ mod tests {
});

let registry = world.resource::<AppTypeRegistry>();
let registry = &registry.read();

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 ser = rmp_serde::Serializer::new(&mut buf);
scene_serializer.serialize(&mut ser).unwrap();
Expand All @@ -811,7 +800,7 @@ mod tests {
);

let scene_deserializer = SceneDeserializer {
type_registry: &registry.0.read(),
type_registry: registry,
};
let mut reader = BufReader::new(buf.as_slice());

Expand All @@ -834,10 +823,11 @@ mod tests {
});

let registry = world.resource::<AppTypeRegistry>();
let registry = &registry.read();

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();

assert_eq!(
Expand All @@ -853,7 +843,7 @@ mod tests {
);

let scene_deserializer = SceneDeserializer {
type_registry: &registry.0.read(),
type_registry: registry,
};

let deserialized_scene = bincode::DefaultOptions::new()
Expand Down
3 changes: 2 additions & 1 deletion examples/scene/scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ fn save_scene_system(world: &mut World) {

// Scenes can be serialized like this:
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
info!("{}", serialized_scene);
Expand Down

0 comments on commit 760c645

Please sign in to comment.