bevy_ecs/reflect/
entity_commands.rs

1use crate::prelude::Mut;
2use crate::reflect::AppTypeRegistry;
3use crate::system::{EntityCommands, Resource};
4use crate::world::Command;
5use crate::{entity::Entity, reflect::ReflectComponent, world::World};
6use bevy_reflect::{Reflect, TypeRegistry};
7use std::borrow::Cow;
8use std::marker::PhantomData;
9
10/// An extension trait for [`EntityCommands`] for reflection related functions
11pub trait ReflectCommandExt {
12    /// Adds the given boxed reflect component to the entity using the reflection data in
13    /// [`AppTypeRegistry`].
14    ///
15    /// This will overwrite any previous component of the same type.
16    ///
17    /// # Panics
18    ///
19    /// - If the entity doesn't exist.
20    /// - If [`AppTypeRegistry`] does not have the reflection data for the given [`Component`](crate::component::Component).
21    /// - If the component data is invalid. See [`Reflect::apply`] for further details.
22    /// - If [`AppTypeRegistry`] is not present in the [`World`].
23    ///
24    /// # Note
25    ///
26    /// Prefer to use the typed [`EntityCommands::insert`] if possible. Adding a reflected component
27    /// is much slower.
28    ///
29    /// # Example
30    ///
31    /// ```
32    /// // Note that you need to register the component type in the AppTypeRegistry prior to using
33    /// // reflection. You can use the helpers on the App with `app.register_type::<ComponentA>()`
34    /// // or write to the TypeRegistry directly to register all your components
35    ///
36    /// # use bevy_ecs::prelude::*;
37    /// # use bevy_ecs::reflect::ReflectCommandExt;
38    /// # use bevy_reflect::{FromReflect, FromType, Reflect, TypeRegistry};
39    /// // A resource that can hold any component that implements reflect as a boxed reflect component
40    /// #[derive(Resource)]
41    /// struct Prefab{
42    ///     component: Box<dyn Reflect>,
43    /// }
44    /// #[derive(Component, Reflect, Default)]
45    /// #[reflect(Component)]
46    /// struct ComponentA(u32);
47    ///
48    /// #[derive(Component, Reflect, Default)]
49    /// #[reflect(Component)]
50    /// struct ComponentB(String);
51    ///
52    /// fn insert_reflect_component(
53    ///     mut commands: Commands,
54    ///     mut prefab: ResMut<Prefab>
55    ///     ) {
56    ///     // Create a set of new boxed reflect components to use
57    ///     let boxed_reflect_component_a: Box<dyn Reflect> = Box::new(ComponentA(916));
58    ///     let boxed_reflect_component_b: Box<dyn Reflect>  = Box::new(ComponentB("NineSixteen".to_string()));
59    ///
60    ///     // You can overwrite the component in the resource with either ComponentA or ComponentB
61    ///     prefab.component = boxed_reflect_component_a;
62    ///     prefab.component = boxed_reflect_component_b;
63    ///     
64    ///     // No matter which component is in the resource and without knowing the exact type, you can
65    ///     // use the insert_reflect entity command to insert that component into an entity.
66    ///     commands
67    ///         .spawn_empty()
68    ///         .insert_reflect(prefab.component.clone_value());
69    /// }
70    ///
71    /// ```
72    fn insert_reflect(&mut self, component: Box<dyn Reflect>) -> &mut Self;
73
74    /// Same as [`insert_reflect`](ReflectCommandExt::insert_reflect), but using the `T` resource as type registry instead of
75    /// `AppTypeRegistry`.
76    ///
77    /// # Panics
78    ///
79    /// - If the given [`Resource`] is not present in the [`World`].
80    ///
81    /// # Note
82    ///
83    /// - The given [`Resource`] is removed from the [`World`] before the command is applied.
84    fn insert_reflect_with_registry<T: Resource + AsRef<TypeRegistry>>(
85        &mut self,
86        component: Box<dyn Reflect>,
87    ) -> &mut Self;
88
89    /// Removes from the entity the component with the given type name registered in [`AppTypeRegistry`].
90    ///
91    /// Does nothing if the entity does not have a component of the same type, if [`AppTypeRegistry`]
92    /// does not contain the reflection data for the given component, or if the entity does not exist.
93    ///
94    /// # Note
95    ///
96    /// Prefer to use the typed [`EntityCommands::remove`] if possible. Removing a reflected component
97    /// is much slower.
98    ///
99    /// # Example
100    ///
101    /// ```
102    /// // Note that you need to register the component type in the AppTypeRegistry prior to using
103    /// // reflection. You can use the helpers on the App with `app.register_type::<ComponentA>()`
104    /// // or write to the TypeRegistry directly to register all your components
105    ///
106    /// # use bevy_ecs::prelude::*;
107    /// # use bevy_ecs::reflect::ReflectCommandExt;
108    /// # use bevy_reflect::{FromReflect, FromType, Reflect, TypeRegistry};
109    ///
110    /// // A resource that can hold any component that implements reflect as a boxed reflect component
111    /// #[derive(Resource)]
112    /// struct Prefab{
113    ///     entity: Entity,
114    ///     component: Box<dyn Reflect>,
115    /// }
116    /// #[derive(Component, Reflect, Default)]
117    /// #[reflect(Component)]
118    /// struct ComponentA(u32);
119    /// #[derive(Component, Reflect, Default)]
120    /// #[reflect(Component)]
121    /// struct ComponentB(String);
122    ///
123    /// fn remove_reflect_component(
124    ///     mut commands: Commands,
125    ///     prefab: Res<Prefab>
126    ///     ) {
127    ///     // Prefab can hold any boxed reflect component. In this case either
128    ///     // ComponentA or ComponentB. No matter which component is in the resource though,
129    ///     // we can attempt to remove any component of that same type from an entity.
130    ///     commands.entity(prefab.entity)
131    ///         .remove_reflect(prefab.component.reflect_type_path().to_owned());
132    /// }
133    ///
134    /// ```
135    fn remove_reflect(&mut self, component_type_name: impl Into<Cow<'static, str>>) -> &mut Self;
136    /// Same as [`remove_reflect`](ReflectCommandExt::remove_reflect), but using the `T` resource as type registry instead of
137    /// `AppTypeRegistry`.
138    fn remove_reflect_with_registry<T: Resource + AsRef<TypeRegistry>>(
139        &mut self,
140        component_type_name: impl Into<Cow<'static, str>>,
141    ) -> &mut Self;
142}
143
144impl ReflectCommandExt for EntityCommands<'_> {
145    fn insert_reflect(&mut self, component: Box<dyn Reflect>) -> &mut Self {
146        self.commands.add(InsertReflect {
147            entity: self.entity,
148            component,
149        });
150        self
151    }
152
153    fn insert_reflect_with_registry<T: Resource + AsRef<TypeRegistry>>(
154        &mut self,
155        component: Box<dyn Reflect>,
156    ) -> &mut Self {
157        self.commands.add(InsertReflectWithRegistry::<T> {
158            entity: self.entity,
159            _t: PhantomData,
160            component,
161        });
162        self
163    }
164
165    fn remove_reflect(&mut self, component_type_path: impl Into<Cow<'static, str>>) -> &mut Self {
166        self.commands.add(RemoveReflect {
167            entity: self.entity,
168            component_type_path: component_type_path.into(),
169        });
170        self
171    }
172
173    fn remove_reflect_with_registry<T: Resource + AsRef<TypeRegistry>>(
174        &mut self,
175        component_type_name: impl Into<Cow<'static, str>>,
176    ) -> &mut Self {
177        self.commands.add(RemoveReflectWithRegistry::<T> {
178            entity: self.entity,
179            _t: PhantomData,
180            component_type_name: component_type_name.into(),
181        });
182        self
183    }
184}
185
186/// Helper function to add a reflect component to a given entity
187fn insert_reflect(
188    world: &mut World,
189    entity: Entity,
190    type_registry: &TypeRegistry,
191    component: Box<dyn Reflect>,
192) {
193    let type_info = component
194        .get_represented_type_info()
195        .expect("component should represent a type.");
196    let type_path = type_info.type_path();
197    let Some(mut entity) = world.get_entity_mut(entity) else {
198        panic!("error[B0003]: Could not insert a reflected component (of type {type_path}) for entity {entity:?} because it doesn't exist in this World. See: https://bevyengine.org/learn/errors/#b0003");
199    };
200    let Some(type_registration) = type_registry.get_with_type_path(type_path) else {
201        panic!("Could not get type registration (for component type {type_path}) because it doesn't exist in the TypeRegistry.");
202    };
203    let Some(reflect_component) = type_registration.data::<ReflectComponent>() else {
204        panic!("Could not get ReflectComponent data (for component type {type_path}) because it doesn't exist in this TypeRegistration.");
205    };
206    reflect_component.insert(&mut entity, &*component, type_registry);
207}
208
209/// A [`Command`] that adds the boxed reflect component to an entity using the data in
210/// [`AppTypeRegistry`].
211///
212/// See [`ReflectCommandExt::insert_reflect`] for details.
213pub struct InsertReflect {
214    /// The entity on which the component will be inserted.
215    pub entity: Entity,
216    /// The reflect [`Component`](crate::component::Component) that will be added to the entity.
217    pub component: Box<dyn Reflect>,
218}
219
220impl Command for InsertReflect {
221    fn apply(self, world: &mut World) {
222        let registry = world.get_resource::<AppTypeRegistry>().unwrap().clone();
223        insert_reflect(world, self.entity, &registry.read(), self.component);
224    }
225}
226
227/// A [`Command`] that adds the boxed reflect component to an entity using the data in the provided
228/// [`Resource`] that implements [`AsRef<TypeRegistry>`].
229///
230/// See [`ReflectCommandExt::insert_reflect_with_registry`] for details.
231pub struct InsertReflectWithRegistry<T: Resource + AsRef<TypeRegistry>> {
232    /// The entity on which the component will be inserted.
233    pub entity: Entity,
234    pub _t: PhantomData<T>,
235    /// The reflect [`Component`](crate::component::Component) that will be added to the entity.
236    pub component: Box<dyn Reflect>,
237}
238
239impl<T: Resource + AsRef<TypeRegistry>> Command for InsertReflectWithRegistry<T> {
240    fn apply(self, world: &mut World) {
241        world.resource_scope(|world, registry: Mut<T>| {
242            let registry: &TypeRegistry = registry.as_ref().as_ref();
243            insert_reflect(world, self.entity, registry, self.component);
244        });
245    }
246}
247
248/// Helper function to remove a reflect component from a given entity
249fn remove_reflect(
250    world: &mut World,
251    entity: Entity,
252    type_registry: &TypeRegistry,
253    component_type_path: Cow<'static, str>,
254) {
255    let Some(mut entity) = world.get_entity_mut(entity) else {
256        return;
257    };
258    let Some(type_registration) = type_registry.get_with_type_path(&component_type_path) else {
259        return;
260    };
261    let Some(reflect_component) = type_registration.data::<ReflectComponent>() else {
262        return;
263    };
264    reflect_component.remove(&mut entity);
265}
266
267/// A [`Command`] that removes the component of the same type as the given component type name from
268/// the provided entity.
269///
270/// See [`ReflectCommandExt::remove_reflect`] for details.
271pub struct RemoveReflect {
272    /// The entity from which the component will be removed.
273    pub entity: Entity,
274    /// The [`Component`](crate::component::Component) type name that will be used to remove a component
275    /// of the same type from the entity.
276    pub component_type_path: Cow<'static, str>,
277}
278
279impl Command for RemoveReflect {
280    fn apply(self, world: &mut World) {
281        let registry = world.get_resource::<AppTypeRegistry>().unwrap().clone();
282        remove_reflect(
283            world,
284            self.entity,
285            &registry.read(),
286            self.component_type_path,
287        );
288    }
289}
290
291/// A [`Command`] that removes the component of the same type as the given component type name from
292/// the provided entity using the provided [`Resource`] that implements [`AsRef<TypeRegistry>`].
293///
294/// See [`ReflectCommandExt::remove_reflect_with_registry`] for details.
295pub struct RemoveReflectWithRegistry<T: Resource + AsRef<TypeRegistry>> {
296    /// The entity from which the component will be removed.
297    pub entity: Entity,
298    pub _t: PhantomData<T>,
299    /// The [`Component`](crate::component::Component) type name that will be used to remove a component
300    /// of the same type from the entity.
301    pub component_type_name: Cow<'static, str>,
302}
303
304impl<T: Resource + AsRef<TypeRegistry>> Command for RemoveReflectWithRegistry<T> {
305    fn apply(self, world: &mut World) {
306        world.resource_scope(|world, registry: Mut<T>| {
307            let registry: &TypeRegistry = registry.as_ref().as_ref();
308            remove_reflect(world, self.entity, registry, self.component_type_name);
309        });
310    }
311}
312
313#[cfg(test)]
314mod tests {
315    use crate::prelude::{AppTypeRegistry, ReflectComponent};
316    use crate::reflect::ReflectCommandExt;
317    use crate::system::{Commands, SystemState};
318    use crate::{self as bevy_ecs, component::Component, world::World};
319    use bevy_ecs_macros::Resource;
320    use bevy_reflect::{Reflect, TypeRegistry};
321
322    #[derive(Resource)]
323    struct TypeRegistryResource {
324        type_registry: TypeRegistry,
325    }
326
327    impl AsRef<TypeRegistry> for TypeRegistryResource {
328        fn as_ref(&self) -> &TypeRegistry {
329            &self.type_registry
330        }
331    }
332
333    #[derive(Component, Reflect, Default, PartialEq, Eq, Debug)]
334    #[reflect(Component)]
335    struct ComponentA(u32);
336
337    #[test]
338    fn insert_reflected() {
339        let mut world = World::new();
340
341        let type_registry = AppTypeRegistry::default();
342        {
343            let mut registry = type_registry.write();
344            registry.register::<ComponentA>();
345            registry.register_type_data::<ComponentA, ReflectComponent>();
346        }
347        world.insert_resource(type_registry);
348
349        let mut system_state: SystemState<Commands> = SystemState::new(&mut world);
350        let mut commands = system_state.get_mut(&mut world);
351
352        let entity = commands.spawn_empty().id();
353        let entity2 = commands.spawn_empty().id();
354
355        let boxed_reflect_component_a = Box::new(ComponentA(916)) as Box<dyn Reflect>;
356        let boxed_reflect_component_a_clone = boxed_reflect_component_a.clone_value();
357
358        commands
359            .entity(entity)
360            .insert_reflect(boxed_reflect_component_a);
361        commands
362            .entity(entity2)
363            .insert_reflect(boxed_reflect_component_a_clone);
364        system_state.apply(&mut world);
365
366        assert_eq!(
367            world.entity(entity).get::<ComponentA>(),
368            world.entity(entity2).get::<ComponentA>()
369        );
370    }
371
372    #[test]
373    fn insert_reflected_with_registry() {
374        let mut world = World::new();
375
376        let mut type_registry = TypeRegistryResource {
377            type_registry: TypeRegistry::new(),
378        };
379
380        type_registry.type_registry.register::<ComponentA>();
381        type_registry
382            .type_registry
383            .register_type_data::<ComponentA, ReflectComponent>();
384        world.insert_resource(type_registry);
385
386        let mut system_state: SystemState<Commands> = SystemState::new(&mut world);
387        let mut commands = system_state.get_mut(&mut world);
388
389        let entity = commands.spawn_empty().id();
390
391        let boxed_reflect_component_a = Box::new(ComponentA(916)) as Box<dyn Reflect>;
392
393        commands
394            .entity(entity)
395            .insert_reflect_with_registry::<TypeRegistryResource>(boxed_reflect_component_a);
396        system_state.apply(&mut world);
397
398        assert_eq!(
399            world.entity(entity).get::<ComponentA>(),
400            Some(&ComponentA(916))
401        );
402    }
403
404    #[test]
405    fn remove_reflected() {
406        let mut world = World::new();
407
408        let type_registry = AppTypeRegistry::default();
409        {
410            let mut registry = type_registry.write();
411            registry.register::<ComponentA>();
412            registry.register_type_data::<ComponentA, ReflectComponent>();
413        }
414        world.insert_resource(type_registry);
415
416        let mut system_state: SystemState<Commands> = SystemState::new(&mut world);
417        let mut commands = system_state.get_mut(&mut world);
418
419        let entity = commands.spawn(ComponentA(0)).id();
420
421        let boxed_reflect_component_a = Box::new(ComponentA(916)) as Box<dyn Reflect>;
422
423        commands
424            .entity(entity)
425            .remove_reflect(boxed_reflect_component_a.reflect_type_path().to_owned());
426        system_state.apply(&mut world);
427
428        assert_eq!(world.entity(entity).get::<ComponentA>(), None);
429    }
430
431    #[test]
432    fn remove_reflected_with_registry() {
433        let mut world = World::new();
434
435        let mut type_registry = TypeRegistryResource {
436            type_registry: TypeRegistry::new(),
437        };
438
439        type_registry.type_registry.register::<ComponentA>();
440        type_registry
441            .type_registry
442            .register_type_data::<ComponentA, ReflectComponent>();
443        world.insert_resource(type_registry);
444
445        let mut system_state: SystemState<Commands> = SystemState::new(&mut world);
446        let mut commands = system_state.get_mut(&mut world);
447
448        let entity = commands.spawn(ComponentA(0)).id();
449
450        let boxed_reflect_component_a = Box::new(ComponentA(916)) as Box<dyn Reflect>;
451
452        commands
453            .entity(entity)
454            .remove_reflect_with_registry::<TypeRegistryResource>(
455                boxed_reflect_component_a.reflect_type_path().to_owned(),
456            );
457        system_state.apply(&mut world);
458
459        assert_eq!(world.entity(entity).get::<ComponentA>(), None);
460    }
461}