diff --git a/crates/bevy_ecs/Cargo.toml b/crates/bevy_ecs/Cargo.toml index 6e54aaf7359a2..448d2b8214f24 100644 --- a/crates/bevy_ecs/Cargo.toml +++ b/crates/bevy_ecs/Cargo.toml @@ -13,6 +13,7 @@ categories = ["game-engines", "data-structures"] trace = [] multi-threaded = ["bevy_tasks/multi-threaded"] bevy_debug_stepping = [] +bevy_reflect = ["dep:bevy_reflect", "bevy_ecs_macros/bevy_reflect"] default = ["bevy_reflect", "bevy_debug_stepping"] [dependencies] diff --git a/crates/bevy_ecs/macros/Cargo.toml b/crates/bevy_ecs/macros/Cargo.toml index abc6647a8bc52..15aadba827948 100644 --- a/crates/bevy_ecs/macros/Cargo.toml +++ b/crates/bevy_ecs/macros/Cargo.toml @@ -8,6 +8,9 @@ license = "MIT OR Apache-2.0" [lib] proc-macro = true +[features] +bevy_reflect = [] + [dependencies] bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.14.0-dev" } diff --git a/crates/bevy_ecs/macros/src/component.rs b/crates/bevy_ecs/macros/src/component.rs index b7b7e1582c8b3..6516e4fd28e67 100644 --- a/crates/bevy_ecs/macros/src/component.rs +++ b/crates/bevy_ecs/macros/src/component.rs @@ -1,7 +1,7 @@ use proc_macro::TokenStream; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::quote; -use syn::{parse_macro_input, parse_quote, DeriveInput, Ident, LitStr, Path, Result}; +use syn::{parse_macro_input, parse_quote, DeriveInput, Ident, LitStr, Meta, Path, Result}; pub fn derive_event(input: TokenStream) -> TokenStream { let mut ast = parse_macro_input!(input as DeriveInput); @@ -32,9 +32,11 @@ pub fn derive_resource(input: TokenStream) -> TokenStream { let struct_name = &ast.ident; let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl(); + let register_type = get_type_registration(&ast, &bevy_ecs_path, "Resource"); TokenStream::from(quote! { impl #impl_generics #bevy_ecs_path::system::Resource for #struct_name #type_generics #where_clause { + #register_type } }) } @@ -57,16 +59,19 @@ pub fn derive_component(input: TokenStream) -> TokenStream { let struct_name = &ast.ident; let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl(); + let register_type = get_type_registration(&ast, &bevy_ecs_path, "Component"); TokenStream::from(quote! { impl #impl_generics #bevy_ecs_path::component::Component for #struct_name #type_generics #where_clause { const STORAGE_TYPE: #bevy_ecs_path::component::StorageType = #storage; + #register_type } }) } pub const COMPONENT: &str = "component"; pub const STORAGE: &str = "storage"; +const REFLECT: &str = "reflect"; struct Attrs { storage: StorageTy, @@ -109,6 +114,37 @@ fn parse_component_attr(ast: &DeriveInput) -> Result { Ok(attrs) } +fn get_type_registration( + ast: &DeriveInput, + bevy_ecs_path: &Path, + reflect_trait: &'static str, +) -> Option { + // Generics require the generic parameters to implement Reflect + // This is unsupported for now. + if !cfg!(feature = "bevy_reflect") || !ast.generics.params.is_empty() { + return None; + } + + ast.attrs.iter().any(|attr| { + if !attr.path().is_ident(REFLECT) || !matches!(attr.meta, Meta::List(_)) { + return false; + } + + attr.parse_nested_meta(|meta| { + meta.path.is_ident(reflect_trait) + .then_some(()) + .ok_or_else(|| meta.error("missing required reflect attribute")) + }) + .is_ok() + }) + .then(|| quote! { + #[doc(hidden)] + fn __register_type(registry: &#bevy_ecs_path::private::bevy_reflect::TypeRegistryArc) { + #bevy_ecs_path::private::bevy_reflect::register_type_shim::(registry); + } + }) +} + fn storage_path(bevy_ecs_path: &Path, ty: StorageTy) -> TokenStream2 { let storage_type = match ty { StorageTy::Table => Ident::new("Table", Span::call_site()), diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index 808fb148b5f73..922b415b0259b 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -12,7 +12,7 @@ use crate::{ pub use bevy_ecs_macros::Component; use bevy_ptr::{OwningPtr, UnsafeCellDeref}; #[cfg(feature = "bevy_reflect")] -use bevy_reflect::Reflect; +use bevy_reflect::{Reflect, TypeRegistryArc}; use bevy_utils::TypeIdMap; use std::cell::UnsafeCell; use std::{ @@ -156,6 +156,13 @@ pub trait Component: Send + Sync + 'static { /// Called when registering this component, allowing mutable access to it's [`ComponentHooks`]. fn register_component_hooks(_hooks: &mut ComponentHooks) {} + + /// Shim for automatically registering components. Intentionally hidden as it's not part of the + /// public interface. Only available if the `bevy_reflect` feature is enabled. + #[doc(hidden)] + #[allow(unused_variables)] + #[cfg(feature = "bevy_reflect")] + fn __register_type(registry: &TypeRegistryArc) {} } /// The storage used for a specific component type. @@ -525,6 +532,8 @@ pub struct Components { components: Vec, indices: TypeIdMap, resource_indices: TypeIdMap, + #[cfg(feature = "bevy_reflect")] + pub(crate) type_registry: TypeRegistryArc, } impl Components { @@ -552,6 +561,8 @@ impl Components { ComponentDescriptor::new::(), ); T::register_component_hooks(&mut components[index.index()].hooks); + #[cfg(feature = "bevy_reflect")] + T::__register_type(&self.type_registry); index }) } @@ -720,9 +731,16 @@ impl Components { pub fn init_resource(&mut self) -> ComponentId { // SAFETY: The [`ComponentDescriptor`] matches the [`TypeId`] unsafe { - self.get_or_insert_resource_with(TypeId::of::(), || { - ComponentDescriptor::new_resource::() - }) + Self::get_or_insert_resource_with( + &mut self.components, + &mut self.resource_indices, + TypeId::of::(), + || { + #[cfg(feature = "bevy_reflect")] + T::__register_type(&self.type_registry); + ComponentDescriptor::new_resource::() + }, + ) } } @@ -733,9 +751,12 @@ impl Components { pub fn init_non_send(&mut self) -> ComponentId { // SAFETY: The [`ComponentDescriptor`] matches the [`TypeId`] unsafe { - self.get_or_insert_resource_with(TypeId::of::(), || { - ComponentDescriptor::new_non_send::(StorageType::default()) - }) + Self::get_or_insert_resource_with( + &mut self.components, + &mut self.resource_indices, + TypeId::of::(), + || ComponentDescriptor::new_non_send::(StorageType::default()), + ) } } @@ -744,12 +765,12 @@ impl Components { /// The [`ComponentDescriptor`] must match the [`TypeId`] #[inline] unsafe fn get_or_insert_resource_with( - &mut self, + components: &mut Vec, + resource_indices: &mut TypeIdMap, type_id: TypeId, func: impl FnOnce() -> ComponentDescriptor, ) -> ComponentId { - let components = &mut self.components; - *self.resource_indices.entry(type_id).or_insert_with(|| { + *resource_indices.entry(type_id).or_insert_with(|| { let descriptor = func(); let component_id = ComponentId(components.len()); components.push(ComponentInfo::new(component_id, descriptor)); @@ -971,3 +992,40 @@ impl FromWorld for InitComponentId { } } } + +#[cfg(test)] +#[cfg(feature = "bevy_reflect")] +mod test { + use crate as bevy_ecs; + use crate::{ + component::Components, prelude::Component, prelude::Resource, reflect::ReflectComponent, + reflect::ReflectResource, storage::Storages, + }; + use bevy_reflect::Reflect; + use core::any::TypeId; + + #[test] + fn init_component_registers_component() { + #[derive(Component, Reflect)] + #[reflect(Component)] + struct A(usize); + + let mut components = Components::default(); + let mut storages = Storages::default(); + components.init_component::(&mut storages); + + assert!(components.type_registry.read().contains(TypeId::of::())); + } + + #[test] + fn init_resource_registers_resource() { + #[derive(Resource, Reflect)] + #[reflect(Resource)] + struct A(usize); + + let mut components = Components::default(); + components.init_resource::(); + + assert!(components.type_registry.read().contains(TypeId::of::())); + } +} diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 4626fbd6c2d07..e357b5dc669df 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -58,6 +58,15 @@ pub mod prelude { }; } +#[cfg(feature = "bevy_reflect")] +#[doc(hidden)] +pub mod private { + pub mod bevy_reflect { + pub use crate::reflect::register_type_shim; + pub use bevy_reflect::TypeRegistryArc; + } +} + #[cfg(test)] mod tests { use crate as bevy_ecs; diff --git a/crates/bevy_ecs/src/reflect/entity_commands.rs b/crates/bevy_ecs/src/reflect/entity_commands.rs index 8545b346bd15a..9147405b570a0 100644 --- a/crates/bevy_ecs/src/reflect/entity_commands.rs +++ b/crates/bevy_ecs/src/reflect/entity_commands.rs @@ -338,13 +338,12 @@ mod tests { fn insert_reflected() { let mut world = World::new(); - let type_registry = AppTypeRegistry::default(); + world.init_resource::(); { - let mut registry = type_registry.write(); + let mut registry = world.resource::().write(); registry.register::(); registry.register_type_data::(); } - world.insert_resource(type_registry); let mut system_state: SystemState = SystemState::new(&mut world); let mut commands = system_state.get_mut(&mut world); @@ -405,13 +404,7 @@ mod tests { fn remove_reflected() { let mut world = World::new(); - let type_registry = AppTypeRegistry::default(); - { - let mut registry = type_registry.write(); - registry.register::(); - registry.register_type_data::(); - } - world.insert_resource(type_registry); + world.init_resource::(); let mut system_state: SystemState = SystemState::new(&mut world); let mut commands = system_state.get_mut(&mut world); diff --git a/crates/bevy_ecs/src/reflect/mod.rs b/crates/bevy_ecs/src/reflect/mod.rs index f51c6960f5954..d48a97d39188f 100644 --- a/crates/bevy_ecs/src/reflect/mod.rs +++ b/crates/bevy_ecs/src/reflect/mod.rs @@ -1,11 +1,58 @@ //! Types that enable reflection support. +//! +//! # Automatic Reflect Type Registration +//! The [`Component`] and [`Resource`] resource derive macros will automatically register +//! its types that implement [`Reflect`] into the [`AppTypeRegistry`] resource when first +//! seen by the ECS [`World`]. +//! +//! If you find that your component or resource is not registered, they may need to be manually +//! registered. There are a few exceptions: +//! +//! * Automatic registration is only supported via the derive macros. Manual implementations of +//! `Component`, `Resource`, or `Reflect` must be manually registered. +//! * The associated ECS trait must be reflected (via `reflect(Component)` or `reflect(Resource)`). +//! * Generic types are not supported, and must be manually registered. +//! * Types are registered when the World first initializes the type. This may cause registrations +//! to be missing due to mistiming. These initialization points include but are not limited to: +//! - spawning an entity with the component or inserting the resource +//! - inserting the component existing entity +//! - attempting to remove the component or resource, even if it's not present. +//! - a system that references the component or resource is added to a schedule +//! +//! ```rust +//! use bevy_ecs::prelude::*; +//! use bevy_reflect::Reflect; +//! +//! // This will automatically register upon first use! +//! #[derive(Component, Reflect)] +//! #[reflect(Component)] +//! pub struct MyComponent { +//! a: usize, +//! b: (u32, u8) +//! } +//! +//! // This won't! +//! #[derive(Component, Reflect)] +//! #[reflect(Component)] +//! pub struct GenericComponent(T); +//! +//! // This won't! +//! #[derive(Component, Reflect)] +//! pub struct NoReflectComponent; +//! ``` +//! +//! [`Component`]: crate::prelude::Component +//! [`Resource`]: crate::prelude::Resource use std::any::TypeId; use std::ops::{Deref, DerefMut}; use crate as bevy_ecs; -use crate::{system::Resource, world::World}; -use bevy_reflect::{FromReflect, Reflect, TypeRegistry, TypeRegistryArc}; +use crate::{ + system::Resource, + world::{FromWorld, World}, +}; +use bevy_reflect::{FromReflect, GetTypeRegistration, Reflect, TypeRegistry, TypeRegistryArc}; mod bundle; mod component; @@ -21,10 +68,27 @@ pub use from_world::{ReflectFromWorld, ReflectFromWorldFns}; pub use map_entities::ReflectMapEntities; pub use resource::{ReflectResource, ReflectResourceFns}; +#[doc(hidden)] +pub fn register_type_shim(registry: &TypeRegistryArc) { + if let Ok(mut registry) = registry.internal.try_write() { + registry.register::(); + return; + } + if let Ok(registry) = registry.internal.try_read() { + if registry.contains(::core::any::TypeId::of::()) { + return; + } + } + panic!( + "Deadlock while registering <{}>.", + ::std::any::type_name::() + ); +} + /// A [`Resource`] storing [`TypeRegistry`] for /// type registrations relevant to a whole app. -#[derive(Resource, Clone, Default)] -pub struct AppTypeRegistry(pub TypeRegistryArc); +#[derive(Resource, Clone, Debug)] +pub struct AppTypeRegistry(TypeRegistryArc); impl Deref for AppTypeRegistry { type Target = TypeRegistryArc; @@ -42,6 +106,12 @@ impl DerefMut for AppTypeRegistry { } } +impl FromWorld for AppTypeRegistry { + fn from_world(world: &mut World) -> Self { + Self(world.type_registry().clone()) + } +} + /// Creates a `T` from a `&dyn Reflect`. /// /// The first approach uses `T`'s implementation of `FromReflect`. diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index eabe8d5d19983..dce5a80dc3760 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -16,6 +16,8 @@ use bevy_ecs_macros::impl_param_set; pub use bevy_ecs_macros::Resource; pub use bevy_ecs_macros::SystemParam; use bevy_ptr::UnsafeCellDeref; +#[cfg(feature = "bevy_reflect")] +use bevy_reflect::TypeRegistryArc; use bevy_utils::{all_tuples, synccell::SyncCell}; use std::{ fmt::Debug, @@ -424,7 +426,14 @@ impl_param_set!(); /// ``` /// /// [`Exclusive`]: https://doc.rust-lang.org/nightly/std/sync/struct.Exclusive.html -pub trait Resource: Send + Sync + 'static {} +pub trait Resource: Send + Sync + 'static { + /// Shim for automatically registering components. Intentionally hidden as it's not part of the + /// public interface. Only available if the `bevy_reflect` feature is enabled. + #[doc(hidden)] + #[allow(unused_variables)] + #[cfg(feature = "bevy_reflect")] + fn __register_type(registry: &TypeRegistryArc) {} +} // SAFETY: Res only reads a single World resource unsafe impl<'a, T: Resource> ReadOnlySystemParam for Res<'a, T> {} diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index d245df8241930..2009491f3f76a 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -42,6 +42,8 @@ use std::{ sync::atomic::{AtomicU32, Ordering}, }; mod identifier; +#[cfg(feature = "bevy_reflect")] +use bevy_reflect::TypeRegistryArc; use self::unsafe_world_cell::{UnsafeEntityCell, UnsafeWorldCell}; pub use identifier::WorldId; @@ -148,6 +150,11 @@ impl World { World::default() } + #[cfg(feature = "bevy_reflect")] + pub(crate) fn type_registry(&self) -> &TypeRegistryArc { + &self.components().type_registry + } + /// Retrieves this [`World`]'s unique ID #[inline] pub fn id(&self) -> WorldId { diff --git a/crates/bevy_scene/src/dynamic_scene_builder.rs b/crates/bevy_scene/src/dynamic_scene_builder.rs index ea14930466cf1..065847b4224d1 100644 --- a/crates/bevy_scene/src/dynamic_scene_builder.rs +++ b/crates/bevy_scene/src/dynamic_scene_builder.rs @@ -379,10 +379,7 @@ mod tests { #[test] fn extract_one_entity() { let mut world = World::default(); - - let atr = AppTypeRegistry::default(); - atr.write().register::(); - world.insert_resource(atr); + world.init_resource::(); let entity = world.spawn((ComponentA, ComponentB)).id(); @@ -392,7 +389,7 @@ mod tests { assert_eq!(scene.entities.len(), 1); assert_eq!(scene.entities[0].entity, entity); - assert_eq!(scene.entities[0].components.len(), 1); + assert_eq!(scene.entities[0].components.len(), 2); assert!(scene.entities[0].components[0].represents::()); } @@ -400,9 +397,7 @@ mod tests { fn extract_one_entity_twice() { let mut world = World::default(); - let atr = AppTypeRegistry::default(); - atr.write().register::(); - world.insert_resource(atr); + world.init_resource::(); let entity = world.spawn((ComponentA, ComponentB)).id(); @@ -413,7 +408,7 @@ mod tests { assert_eq!(scene.entities.len(), 1); assert_eq!(scene.entities[0].entity, entity); - assert_eq!(scene.entities[0].components.len(), 1); + assert_eq!(scene.entities[0].components.len(), 2); assert!(scene.entities[0].components[0].represents::()); } @@ -421,13 +416,7 @@ mod tests { fn extract_one_entity_two_components() { let mut world = World::default(); - let atr = AppTypeRegistry::default(); - { - let mut register = atr.write(); - register.register::(); - register.register::(); - } - world.insert_resource(atr); + world.init_resource::(); let entity = world.spawn((ComponentA, ComponentB)).id(); @@ -472,13 +461,7 @@ mod tests { fn extract_query() { let mut world = World::default(); - let atr = AppTypeRegistry::default(); - { - let mut register = atr.write(); - register.register::(); - register.register::(); - } - world.insert_resource(atr); + world.init_resource::(); let entity_a_b = world.spawn((ComponentA, ComponentB)).id(); let entity_a = world.spawn(ComponentA).id(); @@ -499,9 +482,7 @@ mod tests { fn remove_componentless_entity() { let mut world = World::default(); - let atr = AppTypeRegistry::default(); - atr.write().register::(); - world.insert_resource(atr); + world.init_resource::(); let entity_a = world.spawn(ComponentA).id(); let entity_b = world.spawn(ComponentB).id(); @@ -511,7 +492,7 @@ mod tests { .remove_empty_entities() .build(); - assert_eq!(scene.entities.len(), 1); + assert_eq!(scene.entities.len(), 2); assert_eq!(scene.entities[0].entity, entity_a); } @@ -519,9 +500,7 @@ mod tests { fn extract_one_resource() { let mut world = World::default(); - let atr = AppTypeRegistry::default(); - atr.write().register::(); - world.insert_resource(atr); + world.init_resource::(); world.insert_resource(ResourceA); @@ -537,9 +516,7 @@ mod tests { fn extract_one_resource_twice() { let mut world = World::default(); - let atr = AppTypeRegistry::default(); - atr.write().register::(); - world.insert_resource(atr); + world.init_resource::(); world.insert_resource(ResourceA); @@ -555,14 +532,7 @@ mod tests { #[test] fn should_extract_allowed_components() { let mut world = World::default(); - - let atr = AppTypeRegistry::default(); - { - let mut register = atr.write(); - register.register::(); - register.register::(); - } - world.insert_resource(atr); + world.init_resource::(); let entity_a_b = world.spawn((ComponentA, ComponentB)).id(); let entity_a = world.spawn(ComponentA).id(); @@ -582,14 +552,7 @@ mod tests { #[test] fn should_not_extract_denied_components() { let mut world = World::default(); - - let atr = AppTypeRegistry::default(); - { - let mut register = atr.write(); - register.register::(); - register.register::(); - } - world.insert_resource(atr); + world.init_resource::(); let entity_a_b = world.spawn((ComponentA, ComponentB)).id(); let entity_a = world.spawn(ComponentA).id(); @@ -609,14 +572,7 @@ mod tests { #[test] fn should_extract_allowed_resources() { let mut world = World::default(); - - let atr = AppTypeRegistry::default(); - { - let mut register = atr.write(); - register.register::(); - register.register::(); - } - world.insert_resource(atr); + world.init_resource::(); world.insert_resource(ResourceA); world.insert_resource(ResourceB); @@ -633,14 +589,7 @@ mod tests { #[test] fn should_not_extract_denied_resources() { let mut world = World::default(); - - let atr = AppTypeRegistry::default(); - { - let mut register = atr.write(); - register.register::(); - register.register::(); - } - world.insert_resource(atr); + world.init_resource::(); world.insert_resource(ResourceA); world.insert_resource(ResourceB); diff --git a/crates/bevy_scene/src/scene_loader.rs b/crates/bevy_scene/src/scene_loader.rs index 46d0484f0e66a..1c10fa3358e99 100644 --- a/crates/bevy_scene/src/scene_loader.rs +++ b/crates/bevy_scene/src/scene_loader.rs @@ -5,7 +5,6 @@ use crate::DynamicScene; use bevy_asset::{io::Reader, AssetLoader, AsyncReadExt, LoadContext}; use bevy_ecs::reflect::AppTypeRegistry; use bevy_ecs::world::{FromWorld, World}; -use bevy_reflect::TypeRegistryArc; #[cfg(feature = "serialize")] use serde::de::DeserializeSeed; use thiserror::Error; @@ -15,14 +14,14 @@ use thiserror::Error; /// The loader handles assets serialized with [`DynamicScene::serialize`]. #[derive(Debug)] pub struct SceneLoader { - type_registry: TypeRegistryArc, + type_registry: AppTypeRegistry, } impl FromWorld for SceneLoader { fn from_world(world: &mut World) -> Self { let type_registry = world.resource::(); SceneLoader { - type_registry: type_registry.0.clone(), + type_registry: type_registry.clone(), } } } diff --git a/crates/bevy_scene/src/scene_spawner.rs b/crates/bevy_scene/src/scene_spawner.rs index e89f7f307a139..ee4140e098ff1 100644 --- a/crates/bevy_scene/src/scene_spawner.rs +++ b/crates/bevy_scene/src/scene_spawner.rs @@ -466,9 +466,7 @@ mod tests { let mut world = World::default(); // setup - let atr = AppTypeRegistry::default(); - atr.write().register::(); - world.insert_resource(atr); + world.init_resource::(); world.insert_resource(Assets::::default()); // start test diff --git a/crates/bevy_scene/src/serde.rs b/crates/bevy_scene/src/serde.rs index b5ac7478c99c4..29da07a469312 100644 --- a/crates/bevy_scene/src/serde.rs +++ b/crates/bevy_scene/src/serde.rs @@ -33,6 +33,38 @@ pub const ENTITY_FIELD_COMPONENTS: &str = "components"; /// 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(); +/// world.init_resource::(); +/// // Components that implement Reflect are automatically registered in the World +/// // upon being first spawned or referenced by a system. +/// // +/// // Note: This only works for non-generic types implemented via the Component +/// // and Reflect derive macros, and Component must be reflected (#[reflect(Component)]). +/// // Manual implementations of Component must be manually registered. +/// world.spawn(MyComponent { +/// foo: [1, 2, 3], +/// bar: (1.3, 3.7), +/// baz: String::from("test"), +/// }); +/// /// # Example /// /// ``` @@ -548,9 +580,9 @@ mod tests { fn create_world() -> World { let mut world = World::new(); - let registry = AppTypeRegistry::default(); + world.init_resource::(); { - let mut registry = registry.write(); + let mut registry = world.resource::().write(); registry.register::(); registry.register::(); registry.register::(); @@ -558,13 +590,9 @@ mod tests { registry.register::(); registry.register::(); registry.register_type_data::(); - registry.register::<[usize; 3]>(); - registry.register::<(f32, f32)>(); registry.register::(); - registry.register::(); registry.register::(); } - world.insert_resource(registry); world } @@ -698,7 +726,7 @@ mod tests { .unwrap(); let mut deserializer = ron::de::Deserializer::from_str(&serialized).unwrap(); let scene_deserializer = SceneDeserializer { - type_registry: ®istry.0.read(), + type_registry: ®istry.read(), }; let deserialized_scene = scene_deserializer.deserialize(&mut deserializer).unwrap();