diff --git a/crates/bevy_ecs/src/reflect/entity_commands.rs b/crates/bevy_ecs/src/reflect/entity_commands.rs index e6cdd6a051df2..34aaf12f38887 100644 --- a/crates/bevy_ecs/src/reflect/entity_commands.rs +++ b/crates/bevy_ecs/src/reflect/entity_commands.rs @@ -1,16 +1,27 @@ -use crate::system::{Command, EntityCommands}; +use crate::reflect::AppTypeRegistry; +use crate::system::{Command, EntityCommands, Resource}; use crate::{entity::Entity, reflect::ReflectComponent, world::World}; use bevy_reflect::{Reflect, TypeRegistry}; +use std::marker::PhantomData; /// An extension trait for [`EntityCommands`] for reflection related functions pub trait EntityCommandsReflectExtension { - /// Inserts the given boxed reflect component to the entity using the reflection data in the supplied - /// [`TypeRegistry`]. This will overwrite any previous component of the same type. - /// Panics if the entity doesn't exist or if the [`TypeRegistry`] doesn't have the reflection data - /// for the given [`Component`]. + /// Inserts the given boxed reflect component to the entity using the reflection data in + /// [`AppTypeRegistry`]. + /// + /// This will overwrite any previous component of the same type. + /// + /// # Panics + /// + /// - If the entity doesn't exist. + /// - If [`AppTypeRegistry`] does not have the reflection data for the given [`Component`]. + /// - If the component data is invalid. See [`Reflect::apply`] for further details. + /// - If [`AppTypeRegistry`] is not present in the [`World`]. /// /// # Note - /// Prefer to use the typed [`EntityCommands::insert`] unless you have good reason to use reflection. + /// + /// Prefer to use the typed [`EntityCommands::insert`] if possible as it is optimized for insertions + /// compared to reflection which requires more overhead. /// /// # Example /// @@ -19,71 +30,172 @@ pub trait EntityCommandsReflectExtension { /// # use bevy_ecs::prelude::*; /// # use bevy_ecs::reflect::EntityCommandsReflectExtension; /// # use bevy_reflect::{FromReflect, FromType, Reflect, TypeRegistry}; - /// - /// # #[derive(Resource)] - /// # struct TypeRegistryResource{ - /// # type_registry: TypeRegistry, - /// # } /// // A resource that can hold any component that implements reflect as a boxed reflect component /// #[derive(Resource)] - /// struct SpecialComponentHolder{ + /// struct Prefab{ /// component: Box, /// } - /// #[derive(Component, Reflect, FromReflect, Default)] + /// #[derive(Component, Reflect, Default)] /// #[reflect(Component)] /// struct ComponentA(u32); - /// #[derive(Component, Reflect, FromReflect, Default)] + /// + /// #[derive(Component, Reflect, Default)] /// #[reflect(Component)] /// struct ComponentB(String); /// /// fn insert_reflected_component( /// mut commands: Commands, - /// mut type_registry: ResMut, - /// mut special_component_holder: ResMut + /// mut prefab: ResMut /// ) { - /// # - /// # type_registry.type_registry.register::(); - /// # let mut registration = type_registry - /// # .type_registry - /// # .get_mut(std::any::TypeId::of::()) - /// # .unwrap(); - /// # registration.insert(>::from_type()); - /// # - /// # type_registry.type_registry.register::(); - /// # let mut registration = type_registry - /// # .type_registry - /// # .get_mut(std::any::TypeId::of::()) - /// # .unwrap(); - /// # registration.insert(>::from_type()); /// // Create a set of new boxed reflect components to use /// let boxed_reflect_component_a = Box::new(ComponentA(916)) as Box; /// let boxed_reflect_component_b = Box::new(ComponentB("NineSixteen".to_string())) as Box; /// /// // You can overwrite the component in the resource with either ComponentA or ComponentB - /// special_component_holder.component = boxed_reflect_component_a; - /// special_component_holder.component = boxed_reflect_component_b; + /// prefab.component = boxed_reflect_component_a; + /// prefab.component = boxed_reflect_component_b; /// /// // No matter which component is in the resource and without knowing the exact type, you can /// // use the insert_reflected entity command to insert that component into an entity. /// commands /// .spawn_empty() - /// .insert_reflected(special_component_holder.component.clone_value(), type_registry.type_registry.clone()); + /// .insert_reflected(prefab.component.clone_value()); + /// } + /// + /// ``` + fn insert_reflected(&mut self, component: Box) -> &mut Self; + + /// Inserts the given boxed reflect component to the entity using the reflection data in the + /// provided [`TypeRegistry`] [Resource](Resource).. + /// + /// This will overwrite any previous component of the same type. + /// + /// # Panics + /// + /// - If the entity doesn't exist. + /// - If the given [`TypeRegistry`] does not have the reflection data for the given [`Component`]. + /// - If the component data is invalid. See [`Reflect::apply`] for further details. + /// - If the given [`Resource`] is not present in the [`World`]. + /// + /// # Note + /// + /// - Prefer to use the typed [`EntityCommands::insert`] if possible as it is optimized for insertions + /// compared to reflection which requires more overhead. + /// - The given [`Resource`] is removed from the [`World`] before the command is applied. + /// + /// # Example + /// + /// ```rust + /// + /// # use bevy_ecs::prelude::*; + /// # use bevy_ecs::reflect::EntityCommandsReflectExtension; + /// # use bevy_reflect::{FromReflect, FromType, Reflect, TypeRegistry}; + /// + /// // A custom TypeRegistry Resource + /// #[derive(Resource)] + /// struct TypeRegistryResource { + /// type_registry: TypeRegistry, + /// } + /// + /// impl AsRef for TypeRegistryResource { + /// fn as_ref(&self) -> &TypeRegistry { + /// &self.type_registry + /// } + /// } + /// // A resource that can hold any component that implements reflect as a boxed reflect component + /// #[derive(Resource)] + /// struct Prefab{ + /// component: Box, + /// } + /// #[derive(Component, Reflect, Default)] + /// #[reflect(Component)] + /// struct ComponentA(u32); + /// #[derive(Component, Reflect, Default)] + /// #[reflect(Component)] + /// struct ComponentB(String); + /// + /// fn insert_reflected_component( + /// mut commands: Commands, + /// mut prefab: ResMut + /// ) { + /// // Create a set of new boxed reflect components to use + /// let boxed_reflect_component_a = Box::new(ComponentA(916)) as Box; + /// let boxed_reflect_component_b = Box::new(ComponentB("NineSixteen".to_string())) as Box; + /// + /// // You can overwrite the component in the resource with either ComponentA or ComponentB + /// prefab.component = boxed_reflect_component_a; + /// prefab.component = boxed_reflect_component_b; + /// + /// // No matter which component is in the resource and without knowing the exact type, you can + /// // use the insert_reflected entity command to insert that component into an entity using + /// // the data in the provided resource. + /// commands + /// .spawn_empty() + /// .insert_reflected_with_registry::(prefab.component.clone_value()); /// } /// /// ``` - fn insert_reflected( + fn insert_reflected_with_registry>( &mut self, component: Box, - type_registry: TypeRegistry, ) -> &mut Self; - /// Removes the component of the same type as the supplied boxed reflect component from the entity - /// using the reflection data in the supplied [`TypeRegistry`]. Does nothing if the entity does not - /// have a component of the same type, if the [`TypeRegistry`] does not contain the reflection data - /// for the given component, or if the entity does not exist. + /// Removes the component of the same type as the component type name from the entity using the + /// reflection data from [`AppTypeRegistry`]. + /// + /// Does nothing if the entity does not have a component of the same type, if [`AppTypeRegistry`] + /// does not contain the reflection data for the given component, or if the entity does not exist. + /// + /// # Note + /// + /// Prefer to use the typed [`EntityCommands::remove`] if possible as it is optimized for removals + /// compared to reflection which requires more overhead. + /// + /// # Example + /// + /// ```rust + /// + /// # use bevy_ecs::prelude::*; + /// # use bevy_ecs::reflect::EntityCommandsReflectExtension; + /// # use bevy_reflect::{FromReflect, FromType, Reflect, TypeRegistry}; + /// + /// // A resource that can hold any component that implements reflect as a boxed reflect component + /// #[derive(Resource)] + /// struct Prefab{ + /// entity: Entity, + /// component: Box, + /// } + /// #[derive(Component, Reflect, Default)] + /// #[reflect(Component)] + /// struct ComponentA(u32); + /// #[derive(Component, Reflect, Default)] + /// #[reflect(Component)] + /// struct ComponentB(String); + /// + /// fn remove_reflected_component( + /// mut commands: Commands, + /// prefab: Res + /// ) { + /// // Prefab can hold any boxed reflect component. In this case either + /// // ComponentA or ComponentB. No matter which component is in the resource though, + /// // we can attempt to remove any component of that same type from an entity. + /// commands.entity(prefab.entity) + /// .remove_reflected(prefab.component.type_name().into()); + /// } + /// + /// ``` + fn remove_reflected(&mut self, component_type_name: String) -> &mut Self; + + /// Removes the component of the same type as the given component type name from the entity + /// using the reflection data in the provided [`TypeRegistry`] [Resource](Resource). + /// + /// Does nothing if the entity does not have a component of the same type, if [`AppTypeRegistry`] + /// does not contain the reflection data for the given component, or if the entity does not exist. /// /// # Note - /// Prefer to use the typed [`EntityCommands::remove`] unless you have good reason to use reflection. + /// + /// Prefer to use the typed [`EntityCommands::remove`] if possible as it is optimized for removals + /// compared to reflection which requires more overhead. /// /// # Example /// @@ -92,110 +204,148 @@ pub trait EntityCommandsReflectExtension { /// # use bevy_ecs::prelude::*; /// # use bevy_ecs::reflect::EntityCommandsReflectExtension; /// # use bevy_reflect::{FromReflect, FromType, Reflect, TypeRegistry}; + /// // A custom TypeRegistry Resource + /// #[derive(Resource)] + /// struct TypeRegistryResource { + /// type_registry: TypeRegistry, + /// } /// - /// # #[derive(Resource)] - /// # struct TypeRegistryResource{ - /// # type_registry: TypeRegistry, - /// # } + /// impl AsRef for TypeRegistryResource { + /// fn as_ref(&self) -> &TypeRegistry { + /// &self.type_registry + /// } + /// } /// // A resource that can hold any component that implements reflect as a boxed reflect component /// #[derive(Resource)] - /// struct SpecialComponentHolder{ + /// struct Prefab{ /// entity: Entity, /// component: Box, /// } - /// #[derive(Component, Reflect, FromReflect, Default)] + /// #[derive(Component, Reflect, Default)] /// #[reflect(Component)] /// struct ComponentA(u32); - /// #[derive(Component, Reflect, FromReflect, Default)] + /// #[derive(Component, Reflect, Default)] /// #[reflect(Component)] /// struct ComponentB(String); /// /// fn remove_reflected_component( /// mut commands: Commands, - /// mut type_registry: ResMut, - /// special_component_holder: Res + /// prefab: Res /// ) { - /// # - /// # type_registry.type_registry.register::(); - /// # let mut registration = type_registry - /// # .type_registry - /// # .get_mut(std::any::TypeId::of::()) - /// # .unwrap(); - /// # registration.insert(>::from_type()); - /// # - /// # type_registry.type_registry.register::(); - /// # let mut registration = type_registry - /// # .type_registry - /// # .get_mut(std::any::TypeId::of::()) - /// # .unwrap(); - /// # registration.insert(>::from_type()); - /// // SpecialComponentHolder can hold any boxed reflect component. In this case either + /// // Prefab can hold any boxed reflect component. In this case either /// // ComponentA or ComponentB. No matter which component is in the resource though, /// // we can attempt to remove any component of that same type from an entity. - /// commands.entity(special_component_holder.entity) - /// .remove_reflected(special_component_holder.component.clone_value(), type_registry.type_registry.clone()); + /// commands.entity(prefab.entity) + /// .remove_reflected_with_registry::(prefab.component.type_name().into()); /// } /// /// ``` - fn remove_reflected( + fn remove_reflected_with_registry>( &mut self, - component: Box, - type_registry: TypeRegistry, + component_type_name: String, ) -> &mut Self; } impl<'w, 's, 'a> EntityCommandsReflectExtension for EntityCommands<'w, 's, 'a> { - fn insert_reflected( - &mut self, - component: Box, - type_registry: TypeRegistry, - ) -> &mut Self { + fn insert_reflected(&mut self, component: Box) -> &mut Self { self.commands.add(InsertReflected { entity: self.entity, - type_registry, component, }); self } - fn remove_reflected( + fn insert_reflected_with_registry>( &mut self, component: Box, - type_registry: TypeRegistry, ) -> &mut Self { - self.commands.add(RemoveReflected { + self.commands.add(InsertReflectedWithRegistry:: { entity: self.entity, - type_registry, + _t: PhantomData, component, }); self } + + fn remove_reflected(&mut self, component_type_name: String) -> &mut Self { + self.commands.add(RemoveReflected { + entity: self.entity, + component_type_name, + }); + self + } + + fn remove_reflected_with_registry>( + &mut self, + component_type_name: String, + ) -> &mut Self { + self.commands.add(RemoveReflectedWithRegistry:: { + entity: self.entity, + _t: PhantomData, + component_type_name, + }); + self + } } -/// A [`Command`] that adds the boxed reflect component to an entity using the data in the provided -/// [`TypeRegistry`]. +/// A [`Command`] that adds the boxed reflect component to an entity using the data in +/// [`AppTypeRegistry`]. +/// +/// See [`EntityCommandsReflectExtension::insert_reflected`] for details. pub struct InsertReflected { /// The entity on which the component will be inserted. pub entity: Entity, - /// The [`TypeRegistry`] that the component is registered in and that will be used to get reflection - /// data in order to insert the component. - pub type_registry: TypeRegistry, /// The reflect [`Component`] that will be added to the entity. pub component: Box, } impl Command for InsertReflected { fn apply(self, world: &mut World) { + let registry = world.get_resource::().unwrap().clone(); + if let Some(mut entity) = world.get_entity_mut(self.entity) { + let type_info = self.component.type_name(); + if let Some(type_registration) = registry.read().get_with_name(type_info) { + if let Some(reflect_component) = type_registration.data::() { + reflect_component.insert(&mut entity, &*self.component); + } else { + panic!("Could not get ReflectComponent data (for component type {}) because it doesn't exist in this TypeRegistration.", self.component.type_name()); + } + } else { + panic!("Could not get type registration (for component type {}) because it doesn't exist in the TypeRegistry.", self.component.type_name()); + } + } else { + panic!("error[B0003]: Could not insert a reflected component (of type {}) for entity {:?} because it doesn't exist in this World.", self.component.type_name(), self.entity); + } + } +} + +/// A [`Command`] that adds the boxed reflect component to an entity using the data in the provided +/// [`Resource`] that implements [`AsRef`]. +/// +/// See [`EntityCommandsReflectExtension::insert_reflected_with_registry`] for details. +pub struct InsertReflectedWithRegistry> { + /// The entity on which the component will be inserted. + pub entity: Entity, + pub _t: PhantomData, + /// The reflect [`Component`] that will be added to the entity. + pub component: Box, +} + +impl> Command for InsertReflectedWithRegistry { + fn apply(self, world: &mut World) { + let registry = world.remove_resource::().unwrap(); + let registry: &TypeRegistry = registry.as_ref(); + if let Some(mut entity) = world.get_entity_mut(self.entity) { let type_info = self.component.type_name(); - if let Some(type_registration) = self.type_registry.get_with_name(type_info) { + if let Some(type_registration) = registry.get_with_name(type_info) { if let Some(reflect_component) = type_registration.data::() { reflect_component.insert(&mut entity, &*self.component); } else { panic!("Could not get ReflectComponent data (for component type {}) because it doesn't exist in this TypeRegistration.", self.component.type_name()); } } else { - panic!("Could not get type registration for component (for component {}) because it doesn't exist in the TypeRegistry.", self.component.type_name()); + panic!("Could not get type registration (for component type {}) because it doesn't exist in the TypeRegistry.", self.component.type_name()); } } else { panic!("error[B0003]: Could not insert a reflected component (of type {}) for entity {:?} because it doesn't exist in this World.", self.component.type_name(), self.entity); @@ -203,26 +353,56 @@ impl Command for InsertReflected { } } -/// A [`Command`] that removes the component of the same type as the given boxed reflect component from -/// the provided entity. Does nothing if the entity does not have a component of the same -/// type, if the [`TypeRegistry`] does not contain the data for the given component, or if the entity -/// does not exist. +/// A [`Command`] that removes the component of the same type as the given component type name from +/// the provided entity. +/// +/// See [`EntityCommandsReflectExtension::remove_reflected`] for details. pub struct RemoveReflected { /// The entity from which the component will be removed. pub entity: Entity, - /// The [`TypeRegistry`] that the component is registered in and that will be used to get reflection - /// data in order to remove the component. - pub type_registry: TypeRegistry, /// The boxed reflect [`Component`] that will be used to remove a component of the same type /// from the entity. - pub component: Box, + pub component_type_name: String, } impl Command for RemoveReflected { fn apply(self, world: &mut World) { + let registry = world.get_resource::().unwrap().clone(); if let Some(mut entity) = world.get_entity_mut(self.entity) { - let type_info = self.component.type_name(); - if let Some(type_registration) = self.type_registry.get_with_name(type_info) { + if let Some(type_registration) = registry + .read() + .get_with_name(self.component_type_name.as_ref()) + { + if let Some(reflect_component) = type_registration.data::() { + reflect_component.remove(&mut entity); + } + } + } + } +} + +/// A [`Command`] that removes the component of the same type as the given component type name from +/// the provided entity using the provided [`Resource`] that implements [`AsRef`]. +/// +/// See [`EntityCommandsReflectExtension::remove_reflected_with_registry`] for details. +pub struct RemoveReflectedWithRegistry> { + /// The entity from which the component will be removed. + pub entity: Entity, + pub _t: PhantomData, + /// The boxed reflect [`Component`] that will be used to remove a component of the same type + /// from the entity. + pub component_type_name: String, +} + +impl> Command for RemoveReflectedWithRegistry { + fn apply(self, world: &mut World) { + let registry = world.remove_resource::().unwrap(); + let registry: &TypeRegistry = registry.as_ref(); + + if let Some(mut entity) = world.get_entity_mut(self.entity) { + if let Some(type_registration) = + registry.get_with_name(self.component_type_name.as_ref()) + { if let Some(reflect_component) = type_registration.data::() { reflect_component.remove(&mut entity); } @@ -233,19 +413,25 @@ impl Command for RemoveReflected { #[cfg(test)] mod tests { - use crate::prelude::ReflectComponent; + use crate::prelude::{AppTypeRegistry, ReflectComponent}; use crate::reflect::EntityCommandsReflectExtension; - use crate::system::{Commands, Res, SystemState}; + use crate::system::{Commands, SystemState}; use crate::{self as bevy_ecs, component::Component, world::World}; use bevy_ecs_macros::Resource; - use bevy_reflect::{FromReflect, FromType, Reflect, TypeRegistry}; + use bevy_reflect::{FromType, Reflect, TypeRegistry}; #[derive(Resource)] struct TypeRegistryResource { type_registry: TypeRegistry, } - #[derive(Component, Reflect, FromReflect, Default)] + impl AsRef for TypeRegistryResource { + fn as_ref(&self) -> &TypeRegistry { + &self.type_registry + } + } + + #[derive(Component, Reflect, Default)] #[reflect(Component)] struct ComponentA(u32); @@ -253,6 +439,36 @@ mod tests { fn insert_reflected() { let mut world = World::new(); + let type_registry = AppTypeRegistry::default(); + let mut registry = type_registry.write(); + registry.register::(); + let registration = registry + .get_mut(std::any::TypeId::of::()) + .unwrap(); + registration.insert(>::from_type()); + drop(registry); + world.insert_resource(type_registry); + + let mut system_state: SystemState = SystemState::new(&mut world); + let mut commands = system_state.get_mut(&mut world); + + let entity = commands.spawn_empty().id(); + + let boxed_reflect_component_a = Box::new(ComponentA(916)) as Box; + + commands + .entity(entity) + .insert_reflected(boxed_reflect_component_a); + system_state.apply(&mut world); + + assert!(world.entity(entity).get::().is_some()); + assert_eq!(world.entity(entity).get::().unwrap().0, 916); + } + + #[test] + fn insert_reflected_with_registry() { + let mut world = World::new(); + let mut type_registry = TypeRegistryResource { type_registry: TypeRegistry::new(), }; @@ -265,18 +481,16 @@ mod tests { registration.insert(>::from_type()); world.insert_resource(type_registry); - let mut system_state: SystemState<(Commands, Res)> = - SystemState::new(&mut world); - let (mut commands, type_registry) = system_state.get_mut(&mut world); + let mut system_state: SystemState = SystemState::new(&mut world); + let mut commands = system_state.get_mut(&mut world); let entity = commands.spawn_empty().id(); let boxed_reflect_component_a = Box::new(ComponentA(916)) as Box; - commands.entity(entity).insert_reflected( - boxed_reflect_component_a, - type_registry.type_registry.clone(), - ); + commands + .entity(entity) + .insert_reflected_with_registry::(boxed_reflect_component_a); system_state.apply(&mut world); assert!(world.entity(entity).get::().is_some()); @@ -287,6 +501,35 @@ mod tests { fn remove_reflected() { let mut world = World::new(); + let type_registry = AppTypeRegistry::default(); + let mut registry = type_registry.write(); + registry.register::(); + let registration = registry + .get_mut(std::any::TypeId::of::()) + .unwrap(); + registration.insert(>::from_type()); + drop(registry); + world.insert_resource(type_registry); + + let mut system_state: SystemState = SystemState::new(&mut world); + let mut commands = system_state.get_mut(&mut world); + + let entity = commands.spawn(ComponentA(0)).id(); + + let boxed_reflect_component_a = Box::new(ComponentA(916)) as Box; + + commands + .entity(entity) + .remove_reflected(boxed_reflect_component_a.type_name().into()); + system_state.apply(&mut world); + + assert!(world.entity(entity).get::().is_none()); + } + + #[test] + fn remove_reflected_with_registry() { + let mut world = World::new(); + let mut type_registry = TypeRegistryResource { type_registry: TypeRegistry::new(), }; @@ -299,18 +542,18 @@ mod tests { registration.insert(>::from_type()); world.insert_resource(type_registry); - let mut system_state: SystemState<(Commands, Res)> = - SystemState::new(&mut world); - let (mut commands, type_registry) = system_state.get_mut(&mut world); + let mut system_state: SystemState = SystemState::new(&mut world); + let mut commands = system_state.get_mut(&mut world); let entity = commands.spawn(ComponentA(0)).id(); let boxed_reflect_component_a = Box::new(ComponentA(916)) as Box; - commands.entity(entity).remove_reflected( - boxed_reflect_component_a, - type_registry.type_registry.clone(), - ); + commands + .entity(entity) + .remove_reflected_with_registry::( + boxed_reflect_component_a.type_name().into(), + ); system_state.apply(&mut world); assert!(world.entity(entity).get::().is_none());