bevy_ecs/reflect/
entity_commands.rs

1use crate::{
2    entity::Entity,
3    prelude::Mut,
4    reflect::{AppTypeRegistry, ReflectBundle, ReflectComponent},
5    system::{EntityCommands, Resource},
6    world::{Command, World},
7};
8use alloc::borrow::Cow;
9use bevy_reflect::{PartialReflect, TypeRegistry};
10use core::marker::PhantomData;
11
12/// An extension trait for [`EntityCommands`] for reflection related functions
13pub trait ReflectCommandExt {
14    /// Adds the given boxed reflect component or bundle to the entity using the reflection data in
15    /// [`AppTypeRegistry`].
16    ///
17    /// This will overwrite any previous component(s) of the same type.
18    ///
19    /// # Panics
20    ///
21    /// - If the entity doesn't exist.
22    /// - If [`AppTypeRegistry`] does not have the reflection data for the given
23    ///     [`Component`](crate::component::Component) or [`Bundle`](crate::bundle::Bundle).
24    /// - If the component or bundle data is invalid. See [`PartialReflect::apply`] for further details.
25    /// - If [`AppTypeRegistry`] is not present in the [`World`].
26    ///
27    /// # Note
28    ///
29    /// Prefer to use the typed [`EntityCommands::insert`] if possible. Adding a reflected component
30    /// is much slower.
31    ///
32    /// # Example
33    ///
34    /// ```
35    /// // Note that you need to register the component type in the AppTypeRegistry prior to using
36    /// // reflection. You can use the helpers on the App with `app.register_type::<ComponentA>()`
37    /// // or write to the TypeRegistry directly to register all your components
38    ///
39    /// # use bevy_ecs::prelude::*;
40    /// # use bevy_ecs::reflect::{ReflectCommandExt, ReflectBundle};
41    /// # use bevy_reflect::{FromReflect, FromType, Reflect, TypeRegistry};
42    /// // A resource that can hold any component that implements reflect as a boxed reflect component
43    /// #[derive(Resource)]
44    /// struct Prefab {
45    ///     data: Box<dyn Reflect>,
46    /// }
47    /// #[derive(Component, Reflect, Default)]
48    /// #[reflect(Component)]
49    /// struct ComponentA(u32);
50    ///
51    /// #[derive(Component, Reflect, Default)]
52    /// #[reflect(Component)]
53    /// struct ComponentB(String);
54    ///
55    /// #[derive(Bundle, Reflect, Default)]
56    /// #[reflect(Bundle)]
57    /// struct BundleA {
58    ///     a: ComponentA,
59    ///     b: ComponentB,
60    /// }
61    ///
62    /// fn insert_reflect_component(
63    ///     mut commands: Commands,
64    ///     mut prefab: ResMut<Prefab>
65    ///     ) {
66    ///     // Create a set of new boxed reflect components to use
67    ///     let boxed_reflect_component_a: Box<dyn Reflect> = Box::new(ComponentA(916));
68    ///     let boxed_reflect_component_b: Box<dyn Reflect>  = Box::new(ComponentB("NineSixteen".to_string()));
69    ///     let boxed_reflect_bundle_a: Box<dyn Reflect> = Box::new(BundleA {
70    ///         a: ComponentA(24),
71    ///         b: ComponentB("Twenty-Four".to_string()),
72    ///     });
73    ///
74    ///     // You can overwrite the component in the resource with either ComponentA or ComponentB
75    ///     prefab.data = boxed_reflect_component_a;
76    ///     prefab.data = boxed_reflect_component_b;
77    ///
78    ///     // Or even with BundleA
79    ///     prefab.data = boxed_reflect_bundle_a;
80    ///
81    ///     // No matter which component or bundle is in the resource and without knowing the exact type, you can
82    ///     // use the insert_reflect entity command to insert that component/bundle into an entity.
83    ///     commands
84    ///         .spawn_empty()
85    ///         .insert_reflect(prefab.data.clone_value());
86    /// }
87    /// ```
88    fn insert_reflect(&mut self, component: Box<dyn PartialReflect>) -> &mut Self;
89
90    /// Same as [`insert_reflect`](ReflectCommandExt::insert_reflect), but using the `T` resource as type registry instead of
91    /// `AppTypeRegistry`.
92    ///
93    /// # Panics
94    ///
95    /// - If the given [`Resource`] is not present in the [`World`].
96    ///
97    /// # Note
98    ///
99    /// - The given [`Resource`] is removed from the [`World`] before the command is applied.
100    fn insert_reflect_with_registry<T: Resource + AsRef<TypeRegistry>>(
101        &mut self,
102        component: Box<dyn PartialReflect>,
103    ) -> &mut Self;
104
105    /// Removes from the entity the component or bundle with the given type name registered in [`AppTypeRegistry`].
106    ///
107    /// If the type is a bundle, it will remove any components in that bundle regardless if the entity
108    /// contains all the components.
109    ///
110    /// Does nothing if the type is a component and the entity does not have a component of the same type,
111    /// if the type is a bundle and the entity does not contain any of the components in the bundle,
112    /// if [`AppTypeRegistry`] does not contain the reflection data for the given component,
113    /// or if the entity does not exist.
114    ///
115    /// # Note
116    ///
117    /// Prefer to use the typed [`EntityCommands::remove`] if possible. Removing a reflected component
118    /// is much slower.
119    ///
120    /// # Example
121    ///
122    /// ```
123    /// // Note that you need to register the component/bundle type in the AppTypeRegistry prior to using
124    /// // reflection. You can use the helpers on the App with `app.register_type::<ComponentA>()`
125    /// // or write to the TypeRegistry directly to register all your components and bundles
126    ///
127    /// # use bevy_ecs::prelude::*;
128    /// # use bevy_ecs::reflect::{ReflectCommandExt, ReflectBundle};
129    /// # use bevy_reflect::{FromReflect, FromType, Reflect, TypeRegistry};
130    ///
131    /// // A resource that can hold any component or bundle that implements reflect as a boxed reflect
132    /// #[derive(Resource)]
133    /// struct Prefab{
134    ///     entity: Entity,
135    ///     data: Box<dyn Reflect>,
136    /// }
137    /// #[derive(Component, Reflect, Default)]
138    /// #[reflect(Component)]
139    /// struct ComponentA(u32);
140    /// #[derive(Component, Reflect, Default)]
141    /// #[reflect(Component)]
142    /// struct ComponentB(String);
143    /// #[derive(Bundle, Reflect, Default)]
144    /// #[reflect(Bundle)]
145    /// struct BundleA {
146    ///     a: ComponentA,
147    ///     b: ComponentB,
148    /// }
149    ///
150    /// fn remove_reflect_component(
151    ///     mut commands: Commands,
152    ///     prefab: Res<Prefab>
153    ///     ) {
154    ///     // Prefab can hold any boxed reflect component or bundle. In this case either
155    ///     // ComponentA, ComponentB, or BundleA. No matter which component or bundle is in the resource though,
156    ///     // we can attempt to remove any component (or set of components in the case of a bundle)
157    ///     // of that same type from an entity.
158    ///     commands.entity(prefab.entity)
159    ///         .remove_reflect(prefab.data.reflect_type_path().to_owned());
160    /// }
161    /// ```
162    fn remove_reflect(&mut self, component_type_name: impl Into<Cow<'static, str>>) -> &mut Self;
163    /// Same as [`remove_reflect`](ReflectCommandExt::remove_reflect), but using the `T` resource as type registry instead of
164    /// `AppTypeRegistry`.
165    fn remove_reflect_with_registry<T: Resource + AsRef<TypeRegistry>>(
166        &mut self,
167        component_type_name: impl Into<Cow<'static, str>>,
168    ) -> &mut Self;
169}
170
171impl ReflectCommandExt for EntityCommands<'_> {
172    fn insert_reflect(&mut self, component: Box<dyn PartialReflect>) -> &mut Self {
173        self.commands.queue(InsertReflect {
174            entity: self.entity,
175            component,
176        });
177        self
178    }
179
180    fn insert_reflect_with_registry<T: Resource + AsRef<TypeRegistry>>(
181        &mut self,
182        component: Box<dyn PartialReflect>,
183    ) -> &mut Self {
184        self.commands.queue(InsertReflectWithRegistry::<T> {
185            entity: self.entity,
186            _t: PhantomData,
187            component,
188        });
189        self
190    }
191
192    fn remove_reflect(&mut self, component_type_path: impl Into<Cow<'static, str>>) -> &mut Self {
193        self.commands.queue(RemoveReflect {
194            entity: self.entity,
195            component_type_path: component_type_path.into(),
196        });
197        self
198    }
199
200    fn remove_reflect_with_registry<T: Resource + AsRef<TypeRegistry>>(
201        &mut self,
202        component_type_name: impl Into<Cow<'static, str>>,
203    ) -> &mut Self {
204        self.commands.queue(RemoveReflectWithRegistry::<T> {
205            entity: self.entity,
206            _t: PhantomData,
207            component_type_name: component_type_name.into(),
208        });
209        self
210    }
211}
212
213/// Helper function to add a reflect component or bundle to a given entity
214fn insert_reflect(
215    world: &mut World,
216    entity: Entity,
217    type_registry: &TypeRegistry,
218    component: Box<dyn PartialReflect>,
219) {
220    let type_info = component
221        .get_represented_type_info()
222        .expect("component should represent a type.");
223    let type_path = type_info.type_path();
224    let Ok(mut entity) = world.get_entity_mut(entity) else {
225        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");
226    };
227    let Some(type_registration) = type_registry.get(type_info.type_id()) else {
228        panic!("`{type_path}` should be registered in type registry via `App::register_type<{type_path}>`");
229    };
230
231    if let Some(reflect_component) = type_registration.data::<ReflectComponent>() {
232        reflect_component.insert(&mut entity, component.as_partial_reflect(), type_registry);
233    } else if let Some(reflect_bundle) = type_registration.data::<ReflectBundle>() {
234        reflect_bundle.insert(&mut entity, component.as_partial_reflect(), type_registry);
235    } else {
236        panic!("`{type_path}` should have #[reflect(Component)] or #[reflect(Bundle)]");
237    }
238}
239
240/// A [`Command`] that adds the boxed reflect component or bundle to an entity using the data in
241/// [`AppTypeRegistry`].
242///
243/// See [`ReflectCommandExt::insert_reflect`] for details.
244pub struct InsertReflect {
245    /// The entity on which the component will be inserted.
246    pub entity: Entity,
247    /// The reflect [`Component`](crate::component::Component) or [`Bundle`](crate::bundle::Bundle)
248    /// that will be added to the entity.
249    pub component: Box<dyn PartialReflect>,
250}
251
252impl Command for InsertReflect {
253    fn apply(self, world: &mut World) {
254        let registry = world.get_resource::<AppTypeRegistry>().unwrap().clone();
255        insert_reflect(world, self.entity, &registry.read(), self.component);
256    }
257}
258
259/// A [`Command`] that adds the boxed reflect component or bundle to an entity using the data in the provided
260/// [`Resource`] that implements [`AsRef<TypeRegistry>`].
261///
262/// See [`ReflectCommandExt::insert_reflect_with_registry`] for details.
263pub struct InsertReflectWithRegistry<T: Resource + AsRef<TypeRegistry>> {
264    /// The entity on which the component will be inserted.
265    pub entity: Entity,
266    pub _t: PhantomData<T>,
267    /// The reflect [`Component`](crate::component::Component) that will be added to the entity.
268    pub component: Box<dyn PartialReflect>,
269}
270
271impl<T: Resource + AsRef<TypeRegistry>> Command for InsertReflectWithRegistry<T> {
272    fn apply(self, world: &mut World) {
273        world.resource_scope(|world, registry: Mut<T>| {
274            let registry: &TypeRegistry = registry.as_ref().as_ref();
275            insert_reflect(world, self.entity, registry, self.component);
276        });
277    }
278}
279
280/// Helper function to remove a reflect component or bundle from a given entity
281fn remove_reflect(
282    world: &mut World,
283    entity: Entity,
284    type_registry: &TypeRegistry,
285    component_type_path: Cow<'static, str>,
286) {
287    let Ok(mut entity) = world.get_entity_mut(entity) else {
288        return;
289    };
290    let Some(type_registration) = type_registry.get_with_type_path(&component_type_path) else {
291        return;
292    };
293    if let Some(reflect_component) = type_registration.data::<ReflectComponent>() {
294        reflect_component.remove(&mut entity);
295    } else if let Some(reflect_bundle) = type_registration.data::<ReflectBundle>() {
296        reflect_bundle.remove(&mut entity);
297    }
298}
299
300/// A [`Command`] that removes the component or bundle of the same type as the given type name from
301/// the provided entity.
302///
303/// See [`ReflectCommandExt::remove_reflect`] for details.
304pub struct RemoveReflect {
305    /// The entity from which the component will be removed.
306    pub entity: Entity,
307    /// The [`Component`](crate::component::Component) or [`Bundle`](crate::bundle::Bundle)
308    /// type name that will be used to remove a component
309    /// of the same type from the entity.
310    pub component_type_path: Cow<'static, str>,
311}
312
313impl Command for RemoveReflect {
314    fn apply(self, world: &mut World) {
315        let registry = world.get_resource::<AppTypeRegistry>().unwrap().clone();
316        remove_reflect(
317            world,
318            self.entity,
319            &registry.read(),
320            self.component_type_path,
321        );
322    }
323}
324
325/// A [`Command`] that removes the component or bundle of the same type as the given type name from
326/// the provided entity using the provided [`Resource`] that implements [`AsRef<TypeRegistry>`].
327///
328/// See [`ReflectCommandExt::remove_reflect_with_registry`] for details.
329pub struct RemoveReflectWithRegistry<T: Resource + AsRef<TypeRegistry>> {
330    /// The entity from which the component will be removed.
331    pub entity: Entity,
332    pub _t: PhantomData<T>,
333    /// The [`Component`](crate::component::Component) or [`Bundle`](crate::bundle::Bundle)
334    /// type name that will be used to remove a component
335    /// of the same type from the entity.
336    pub component_type_name: Cow<'static, str>,
337}
338
339impl<T: Resource + AsRef<TypeRegistry>> Command for RemoveReflectWithRegistry<T> {
340    fn apply(self, world: &mut World) {
341        world.resource_scope(|world, registry: Mut<T>| {
342            let registry: &TypeRegistry = registry.as_ref().as_ref();
343            remove_reflect(world, self.entity, registry, self.component_type_name);
344        });
345    }
346}
347
348#[cfg(test)]
349mod tests {
350    use crate::{
351        self as bevy_ecs,
352        bundle::Bundle,
353        component::Component,
354        prelude::{AppTypeRegistry, ReflectComponent},
355        reflect::{ReflectBundle, ReflectCommandExt},
356        system::{Commands, SystemState},
357        world::World,
358    };
359    use bevy_ecs_macros::Resource;
360    use bevy_reflect::{PartialReflect, Reflect, TypeRegistry};
361
362    #[derive(Resource)]
363    struct TypeRegistryResource {
364        type_registry: TypeRegistry,
365    }
366
367    impl AsRef<TypeRegistry> for TypeRegistryResource {
368        fn as_ref(&self) -> &TypeRegistry {
369            &self.type_registry
370        }
371    }
372
373    #[derive(Component, Reflect, Default, PartialEq, Eq, Debug)]
374    #[reflect(Component)]
375    struct ComponentA(u32);
376
377    #[derive(Component, Reflect, Default, PartialEq, Eq, Debug)]
378    #[reflect(Component)]
379    struct ComponentB(u32);
380
381    #[derive(Bundle, Reflect, Default, Debug, PartialEq)]
382    #[reflect(Bundle)]
383    struct BundleA {
384        a: ComponentA,
385        b: ComponentB,
386    }
387
388    #[test]
389    fn insert_reflected() {
390        let mut world = World::new();
391
392        let type_registry = AppTypeRegistry::default();
393        {
394            let mut registry = type_registry.write();
395            registry.register::<ComponentA>();
396            registry.register_type_data::<ComponentA, ReflectComponent>();
397        }
398        world.insert_resource(type_registry);
399
400        let mut system_state: SystemState<Commands> = SystemState::new(&mut world);
401        let mut commands = system_state.get_mut(&mut world);
402
403        let entity = commands.spawn_empty().id();
404        let entity2 = commands.spawn_empty().id();
405
406        let boxed_reflect_component_a = Box::new(ComponentA(916)) as Box<dyn PartialReflect>;
407        let boxed_reflect_component_a_clone = boxed_reflect_component_a.clone_value();
408
409        commands
410            .entity(entity)
411            .insert_reflect(boxed_reflect_component_a);
412        commands
413            .entity(entity2)
414            .insert_reflect(boxed_reflect_component_a_clone);
415        system_state.apply(&mut world);
416
417        assert_eq!(
418            world.entity(entity).get::<ComponentA>(),
419            world.entity(entity2).get::<ComponentA>()
420        );
421    }
422
423    #[test]
424    fn insert_reflected_with_registry() {
425        let mut world = World::new();
426
427        let mut type_registry = TypeRegistryResource {
428            type_registry: TypeRegistry::new(),
429        };
430
431        type_registry.type_registry.register::<ComponentA>();
432        type_registry
433            .type_registry
434            .register_type_data::<ComponentA, ReflectComponent>();
435        world.insert_resource(type_registry);
436
437        let mut system_state: SystemState<Commands> = SystemState::new(&mut world);
438        let mut commands = system_state.get_mut(&mut world);
439
440        let entity = commands.spawn_empty().id();
441
442        let boxed_reflect_component_a = Box::new(ComponentA(916)) as Box<dyn PartialReflect>;
443
444        commands
445            .entity(entity)
446            .insert_reflect_with_registry::<TypeRegistryResource>(boxed_reflect_component_a);
447        system_state.apply(&mut world);
448
449        assert_eq!(
450            world.entity(entity).get::<ComponentA>(),
451            Some(&ComponentA(916))
452        );
453    }
454
455    #[test]
456    fn remove_reflected() {
457        let mut world = World::new();
458
459        let type_registry = AppTypeRegistry::default();
460        {
461            let mut registry = type_registry.write();
462            registry.register::<ComponentA>();
463            registry.register_type_data::<ComponentA, ReflectComponent>();
464        }
465        world.insert_resource(type_registry);
466
467        let mut system_state: SystemState<Commands> = SystemState::new(&mut world);
468        let mut commands = system_state.get_mut(&mut world);
469
470        let entity = commands.spawn(ComponentA(0)).id();
471
472        let boxed_reflect_component_a = Box::new(ComponentA(916)) as Box<dyn Reflect>;
473
474        commands
475            .entity(entity)
476            .remove_reflect(boxed_reflect_component_a.reflect_type_path().to_owned());
477        system_state.apply(&mut world);
478
479        assert_eq!(world.entity(entity).get::<ComponentA>(), None);
480    }
481
482    #[test]
483    fn remove_reflected_with_registry() {
484        let mut world = World::new();
485
486        let mut type_registry = TypeRegistryResource {
487            type_registry: TypeRegistry::new(),
488        };
489
490        type_registry.type_registry.register::<ComponentA>();
491        type_registry
492            .type_registry
493            .register_type_data::<ComponentA, ReflectComponent>();
494        world.insert_resource(type_registry);
495
496        let mut system_state: SystemState<Commands> = SystemState::new(&mut world);
497        let mut commands = system_state.get_mut(&mut world);
498
499        let entity = commands.spawn(ComponentA(0)).id();
500
501        let boxed_reflect_component_a = Box::new(ComponentA(916)) as Box<dyn Reflect>;
502
503        commands
504            .entity(entity)
505            .remove_reflect_with_registry::<TypeRegistryResource>(
506                boxed_reflect_component_a.reflect_type_path().to_owned(),
507            );
508        system_state.apply(&mut world);
509
510        assert_eq!(world.entity(entity).get::<ComponentA>(), None);
511    }
512
513    #[test]
514    fn insert_reflect_bundle() {
515        let mut world = World::new();
516
517        let type_registry = AppTypeRegistry::default();
518        {
519            let mut registry = type_registry.write();
520            registry.register::<BundleA>();
521            registry.register_type_data::<BundleA, ReflectBundle>();
522        }
523        world.insert_resource(type_registry);
524
525        let mut system_state: SystemState<Commands> = SystemState::new(&mut world);
526        let mut commands = system_state.get_mut(&mut world);
527
528        let entity = commands.spawn_empty().id();
529        let bundle = Box::new(BundleA {
530            a: ComponentA(31),
531            b: ComponentB(20),
532        }) as Box<dyn PartialReflect>;
533        commands.entity(entity).insert_reflect(bundle);
534
535        system_state.apply(&mut world);
536
537        assert_eq!(world.get::<ComponentA>(entity), Some(&ComponentA(31)));
538        assert_eq!(world.get::<ComponentB>(entity), Some(&ComponentB(20)));
539    }
540
541    #[test]
542    fn insert_reflect_bundle_with_registry() {
543        let mut world = World::new();
544
545        let mut type_registry = TypeRegistryResource {
546            type_registry: TypeRegistry::new(),
547        };
548
549        type_registry.type_registry.register::<BundleA>();
550        type_registry
551            .type_registry
552            .register_type_data::<BundleA, ReflectBundle>();
553        world.insert_resource(type_registry);
554
555        let mut system_state: SystemState<Commands> = SystemState::new(&mut world);
556        let mut commands = system_state.get_mut(&mut world);
557
558        let entity = commands.spawn_empty().id();
559        let bundle = Box::new(BundleA {
560            a: ComponentA(31),
561            b: ComponentB(20),
562        }) as Box<dyn PartialReflect>;
563
564        commands
565            .entity(entity)
566            .insert_reflect_with_registry::<TypeRegistryResource>(bundle);
567        system_state.apply(&mut world);
568
569        assert_eq!(world.get::<ComponentA>(entity), Some(&ComponentA(31)));
570        assert_eq!(world.get::<ComponentB>(entity), Some(&ComponentB(20)));
571    }
572
573    #[test]
574    fn remove_reflected_bundle() {
575        let mut world = World::new();
576
577        let type_registry = AppTypeRegistry::default();
578        {
579            let mut registry = type_registry.write();
580            registry.register::<BundleA>();
581            registry.register_type_data::<BundleA, ReflectBundle>();
582        }
583        world.insert_resource(type_registry);
584
585        let mut system_state: SystemState<Commands> = SystemState::new(&mut world);
586        let mut commands = system_state.get_mut(&mut world);
587
588        let entity = commands
589            .spawn(BundleA {
590                a: ComponentA(31),
591                b: ComponentB(20),
592            })
593            .id();
594
595        let boxed_reflect_bundle_a = Box::new(BundleA {
596            a: ComponentA(1),
597            b: ComponentB(23),
598        }) as Box<dyn Reflect>;
599
600        commands
601            .entity(entity)
602            .remove_reflect(boxed_reflect_bundle_a.reflect_type_path().to_owned());
603        system_state.apply(&mut world);
604
605        assert_eq!(world.entity(entity).get::<ComponentA>(), None);
606        assert_eq!(world.entity(entity).get::<ComponentB>(), None);
607    }
608
609    #[test]
610    fn remove_reflected_bundle_with_registry() {
611        let mut world = World::new();
612
613        let mut type_registry = TypeRegistryResource {
614            type_registry: TypeRegistry::new(),
615        };
616
617        type_registry.type_registry.register::<BundleA>();
618        type_registry
619            .type_registry
620            .register_type_data::<BundleA, ReflectBundle>();
621        world.insert_resource(type_registry);
622
623        let mut system_state: SystemState<Commands> = SystemState::new(&mut world);
624        let mut commands = system_state.get_mut(&mut world);
625
626        let entity = commands
627            .spawn(BundleA {
628                a: ComponentA(31),
629                b: ComponentB(20),
630            })
631            .id();
632
633        let boxed_reflect_bundle_a = Box::new(BundleA {
634            a: ComponentA(1),
635            b: ComponentB(23),
636        }) as Box<dyn Reflect>;
637
638        commands
639            .entity(entity)
640            .remove_reflect_with_registry::<TypeRegistryResource>(
641                boxed_reflect_bundle_a.reflect_type_path().to_owned(),
642            );
643        system_state.apply(&mut world);
644
645        assert_eq!(world.entity(entity).get::<ComponentA>(), None);
646        assert_eq!(world.entity(entity).get::<ComponentB>(), None);
647    }
648}