1use crate::{
2 prelude::Mut,
3 reflect::{AppTypeRegistry, ReflectBundle, ReflectComponent},
4 resource::Resource,
5 system::EntityCommands,
6 world::EntityWorldMut,
7};
8use alloc::{borrow::Cow, boxed::Box};
9use bevy_reflect::{PartialReflect, Reflect, TypeRegistry};
10
11pub trait ReflectCommandExt {
13 fn insert_reflect(&mut self, component: Box<dyn PartialReflect>) -> &mut Self;
88
89 fn insert_reflect_with_registry<T: Resource + AsRef<TypeRegistry>>(
100 &mut self,
101 component: Box<dyn PartialReflect>,
102 ) -> &mut Self;
103
104 fn remove_reflect(&mut self, component_type_path: impl Into<Cow<'static, str>>) -> &mut Self;
162 fn remove_reflect_with_registry<T: Resource + AsRef<TypeRegistry>>(
165 &mut self,
166 component_type_path: impl Into<Cow<'static, str>>,
167 ) -> &mut Self;
168}
169
170impl ReflectCommandExt for EntityCommands<'_> {
171 fn insert_reflect(&mut self, component: Box<dyn PartialReflect>) -> &mut Self {
172 self.queue(move |mut entity: EntityWorldMut| {
173 entity.insert_reflect(component);
174 })
175 }
176
177 fn insert_reflect_with_registry<T: Resource + AsRef<TypeRegistry>>(
178 &mut self,
179 component: Box<dyn PartialReflect>,
180 ) -> &mut Self {
181 self.queue(move |mut entity: EntityWorldMut| {
182 entity.insert_reflect_with_registry::<T>(component);
183 })
184 }
185
186 fn remove_reflect(&mut self, component_type_path: impl Into<Cow<'static, str>>) -> &mut Self {
187 let component_type_path: Cow<'static, str> = component_type_path.into();
188 self.queue(move |mut entity: EntityWorldMut| {
189 entity.remove_reflect(component_type_path);
190 })
191 }
192
193 fn remove_reflect_with_registry<T: Resource + AsRef<TypeRegistry>>(
194 &mut self,
195 component_type_path: impl Into<Cow<'static, str>>,
196 ) -> &mut Self {
197 let component_type_path: Cow<'static, str> = component_type_path.into();
198 self.queue(move |mut entity: EntityWorldMut| {
199 entity.remove_reflect_with_registry::<T>(component_type_path);
200 })
201 }
202}
203
204impl<'w> EntityWorldMut<'w> {
205 pub fn insert_reflect(&mut self, component: Box<dyn PartialReflect>) -> &mut Self {
223 self.assert_not_despawned();
224 self.resource_scope(|entity, registry: Mut<AppTypeRegistry>| {
225 let type_registry = ®istry.as_ref().read();
226 insert_reflect_with_registry_ref(entity, type_registry, component);
227 });
228 self
229 }
230
231 pub fn insert_reflect_with_registry<T: Resource + AsRef<TypeRegistry>>(
244 &mut self,
245 component: Box<dyn PartialReflect>,
246 ) -> &mut Self {
247 self.assert_not_despawned();
248 self.resource_scope(|entity, registry: Mut<T>| {
249 let type_registry = registry.as_ref().as_ref();
250 insert_reflect_with_registry_ref(entity, type_registry, component);
251 });
252 self
253 }
254
255 pub fn remove_reflect(&mut self, component_type_path: Cow<'static, str>) -> &mut Self {
274 self.assert_not_despawned();
275 self.resource_scope(|entity, registry: Mut<AppTypeRegistry>| {
276 let type_registry = ®istry.as_ref().read();
277 remove_reflect_with_registry_ref(entity, type_registry, component_type_path);
278 });
279 self
280 }
281
282 pub fn remove_reflect_with_registry<T: Resource + AsRef<TypeRegistry>>(
297 &mut self,
298 component_type_path: Cow<'static, str>,
299 ) -> &mut Self {
300 self.assert_not_despawned();
301 self.resource_scope(|entity, registry: Mut<T>| {
302 let type_registry = registry.as_ref().as_ref();
303 remove_reflect_with_registry_ref(entity, type_registry, component_type_path);
304 });
305 self
306 }
307
308 pub fn take_reflect(
324 &mut self,
325 component_type_path: Cow<'static, str>,
326 ) -> Option<Box<dyn Reflect>> {
327 self.assert_not_despawned();
328 self.resource_scope(|entity, registry: Mut<AppTypeRegistry>| {
329 let type_registry = ®istry.as_ref().read();
330 take_reflect_with_registry_ref(entity, type_registry, component_type_path)
331 })
332 }
333
334 pub fn take_reflect_with_registry<T: Resource + AsRef<TypeRegistry>>(
346 &mut self,
347 component_type_path: Cow<'static, str>,
348 ) -> Option<Box<dyn Reflect>> {
349 self.assert_not_despawned();
350 self.resource_scope(|entity, registry: Mut<T>| {
351 let type_registry = registry.as_ref().as_ref();
352 take_reflect_with_registry_ref(entity, type_registry, component_type_path)
353 })
354 }
355}
356
357fn insert_reflect_with_registry_ref(
359 entity: &mut EntityWorldMut,
360 type_registry: &TypeRegistry,
361 component: Box<dyn PartialReflect>,
362) {
363 let type_info = component
364 .get_represented_type_info()
365 .expect("component should represent a type.");
366 let type_path = type_info.type_path();
367 let Some(type_registration) = type_registry.get(type_info.type_id()) else {
368 panic!("`{type_path}` should be registered in type registry via `App::register_type<{type_path}>`");
369 };
370
371 if let Some(reflect_component) = type_registration.data::<ReflectComponent>() {
372 reflect_component.insert(entity, component.as_partial_reflect(), type_registry);
373 } else if let Some(reflect_bundle) = type_registration.data::<ReflectBundle>() {
374 reflect_bundle.insert(entity, component.as_partial_reflect(), type_registry);
375 } else {
376 panic!("`{type_path}` should have #[reflect(Component)] or #[reflect(Bundle)]");
377 }
378}
379
380fn remove_reflect_with_registry_ref(
382 entity: &mut EntityWorldMut,
383 type_registry: &TypeRegistry,
384 component_type_path: Cow<'static, str>,
385) {
386 let Some(type_registration) = type_registry.get_with_type_path(&component_type_path) else {
387 return;
388 };
389 if let Some(reflect_component) = type_registration.data::<ReflectComponent>() {
390 reflect_component.remove(entity);
391 } else if let Some(reflect_bundle) = type_registration.data::<ReflectBundle>() {
392 reflect_bundle.remove(entity);
393 }
394}
395
396fn take_reflect_with_registry_ref(
398 entity: &mut EntityWorldMut,
399 type_registry: &TypeRegistry,
400 component_type_path: Cow<'static, str>,
401) -> Option<Box<dyn Reflect>> {
402 let type_registration = type_registry.get_with_type_path(&component_type_path)?;
403 if let Some(reflect_component) = type_registration.data::<ReflectComponent>() {
404 reflect_component.take(entity)
405 } else if let Some(reflect_bundle) = type_registration.data::<ReflectBundle>() {
406 reflect_bundle.take(entity)
407 } else {
408 None
409 }
410}
411
412#[cfg(test)]
413mod tests {
414 use crate::{
415 bundle::Bundle,
416 component::Component,
417 prelude::{AppTypeRegistry, ReflectComponent},
418 reflect::{ReflectBundle, ReflectCommandExt},
419 system::{Commands, SystemState},
420 world::World,
421 };
422 use alloc::{borrow::ToOwned, boxed::Box};
423 use bevy_ecs_macros::Resource;
424 use bevy_reflect::{PartialReflect, Reflect, TypeRegistry};
425
426 #[derive(Resource)]
427 struct TypeRegistryResource {
428 type_registry: TypeRegistry,
429 }
430
431 impl AsRef<TypeRegistry> for TypeRegistryResource {
432 fn as_ref(&self) -> &TypeRegistry {
433 &self.type_registry
434 }
435 }
436
437 #[derive(Component, Reflect, Default, PartialEq, Eq, Debug)]
438 #[reflect(Component)]
439 struct ComponentA(u32);
440
441 #[derive(Component, Reflect, Default, PartialEq, Eq, Debug)]
442 #[reflect(Component)]
443 struct ComponentB(u32);
444
445 #[derive(Bundle, Reflect, Default, Debug, PartialEq)]
446 #[reflect(Bundle)]
447 struct BundleA {
448 a: ComponentA,
449 b: ComponentB,
450 }
451
452 #[test]
453 fn insert_reflected() {
454 let mut world = World::new();
455
456 let type_registry = AppTypeRegistry::default();
457 {
458 let mut registry = type_registry.write();
459 registry.register::<ComponentA>();
460 registry.register_type_data::<ComponentA, ReflectComponent>();
461 }
462 world.insert_resource(type_registry);
463
464 let mut system_state: SystemState<Commands> = SystemState::new(&mut world);
465 let mut commands = system_state.get_mut(&mut world).unwrap();
466
467 let entity = commands.spawn_empty().id();
468 let entity2 = commands.spawn_empty().id();
469 let entity3 = commands.spawn_empty().id();
470
471 let boxed_reflect_component_a = Box::new(ComponentA(916)) as Box<dyn PartialReflect>;
472 let boxed_reflect_component_a_clone = boxed_reflect_component_a.reflect_clone().unwrap();
473 let boxed_reflect_component_a_dynamic = boxed_reflect_component_a.to_dynamic();
474
475 commands
476 .entity(entity)
477 .insert_reflect(boxed_reflect_component_a);
478 commands
479 .entity(entity2)
480 .insert_reflect(boxed_reflect_component_a_clone.into_partial_reflect());
481 commands
482 .entity(entity3)
483 .insert_reflect(boxed_reflect_component_a_dynamic);
484 system_state.apply(&mut world);
485
486 assert_eq!(
487 world.entity(entity).get::<ComponentA>(),
488 world.entity(entity2).get::<ComponentA>(),
489 );
490 assert_eq!(
491 world.entity(entity).get::<ComponentA>(),
492 world.entity(entity3).get::<ComponentA>(),
493 );
494 }
495
496 #[test]
497 fn insert_reflected_with_registry() {
498 let mut world = World::new();
499
500 let mut type_registry = TypeRegistryResource {
501 type_registry: TypeRegistry::new(),
502 };
503
504 type_registry.type_registry.register::<ComponentA>();
505 type_registry
506 .type_registry
507 .register_type_data::<ComponentA, ReflectComponent>();
508 world.insert_resource(type_registry);
509
510 let mut system_state: SystemState<Commands> = SystemState::new(&mut world);
511 let mut commands = system_state.get_mut(&mut world).unwrap();
512
513 let entity = commands.spawn_empty().id();
514
515 let boxed_reflect_component_a = Box::new(ComponentA(916)) as Box<dyn PartialReflect>;
516
517 commands
518 .entity(entity)
519 .insert_reflect_with_registry::<TypeRegistryResource>(boxed_reflect_component_a);
520 system_state.apply(&mut world);
521
522 assert_eq!(
523 world.entity(entity).get::<ComponentA>(),
524 Some(&ComponentA(916))
525 );
526 }
527
528 #[test]
529 fn remove_reflected() {
530 let mut world = World::new();
531
532 let type_registry = AppTypeRegistry::default();
533 {
534 let mut registry = type_registry.write();
535 registry.register::<ComponentA>();
536 registry.register_type_data::<ComponentA, ReflectComponent>();
537 }
538 world.insert_resource(type_registry);
539
540 let mut system_state: SystemState<Commands> = SystemState::new(&mut world);
541 let mut commands = system_state.get_mut(&mut world).unwrap();
542
543 let entity = commands.spawn(ComponentA(0)).id();
544
545 let boxed_reflect_component_a = Box::new(ComponentA(916)) as Box<dyn Reflect>;
546
547 commands
548 .entity(entity)
549 .remove_reflect(boxed_reflect_component_a.reflect_type_path().to_owned());
550 system_state.apply(&mut world);
551
552 assert_eq!(world.entity(entity).get::<ComponentA>(), None);
553 }
554
555 #[test]
556 fn remove_reflected_with_registry() {
557 let mut world = World::new();
558
559 let mut type_registry = TypeRegistryResource {
560 type_registry: TypeRegistry::new(),
561 };
562
563 type_registry.type_registry.register::<ComponentA>();
564 type_registry
565 .type_registry
566 .register_type_data::<ComponentA, ReflectComponent>();
567 world.insert_resource(type_registry);
568
569 let mut system_state: SystemState<Commands> = SystemState::new(&mut world);
570 let mut commands = system_state.get_mut(&mut world).unwrap();
571
572 let entity = commands.spawn(ComponentA(0)).id();
573
574 let boxed_reflect_component_a = Box::new(ComponentA(916)) as Box<dyn Reflect>;
575
576 commands
577 .entity(entity)
578 .remove_reflect_with_registry::<TypeRegistryResource>(
579 boxed_reflect_component_a.reflect_type_path().to_owned(),
580 );
581 system_state.apply(&mut world);
582
583 assert_eq!(world.entity(entity).get::<ComponentA>(), None);
584 }
585
586 #[test]
587 fn insert_reflect_bundle() {
588 let mut world = World::new();
589
590 let type_registry = AppTypeRegistry::default();
591 {
592 let mut registry = type_registry.write();
593 registry.register::<BundleA>();
594 registry.register_type_data::<BundleA, ReflectBundle>();
595 }
596 world.insert_resource(type_registry);
597
598 let mut system_state: SystemState<Commands> = SystemState::new(&mut world);
599 let mut commands = system_state.get_mut(&mut world).unwrap();
600
601 let entity = commands.spawn_empty().id();
602 let bundle = Box::new(BundleA {
603 a: ComponentA(31),
604 b: ComponentB(20),
605 }) as Box<dyn PartialReflect>;
606 commands.entity(entity).insert_reflect(bundle);
607
608 system_state.apply(&mut world);
609
610 assert_eq!(world.get::<ComponentA>(entity), Some(&ComponentA(31)));
611 assert_eq!(world.get::<ComponentB>(entity), Some(&ComponentB(20)));
612 }
613
614 #[test]
615 fn insert_reflect_bundle_with_registry() {
616 let mut world = World::new();
617
618 let mut type_registry = TypeRegistryResource {
619 type_registry: TypeRegistry::new(),
620 };
621
622 type_registry.type_registry.register::<BundleA>();
623 type_registry
624 .type_registry
625 .register_type_data::<BundleA, ReflectBundle>();
626 world.insert_resource(type_registry);
627
628 let mut system_state: SystemState<Commands> = SystemState::new(&mut world);
629 let mut commands = system_state.get_mut(&mut world).unwrap();
630
631 let entity = commands.spawn_empty().id();
632 let bundle = Box::new(BundleA {
633 a: ComponentA(31),
634 b: ComponentB(20),
635 }) as Box<dyn PartialReflect>;
636
637 commands
638 .entity(entity)
639 .insert_reflect_with_registry::<TypeRegistryResource>(bundle);
640 system_state.apply(&mut world);
641
642 assert_eq!(world.get::<ComponentA>(entity), Some(&ComponentA(31)));
643 assert_eq!(world.get::<ComponentB>(entity), Some(&ComponentB(20)));
644 }
645
646 #[test]
647 fn remove_reflected_bundle() {
648 let mut world = World::new();
649
650 let type_registry = AppTypeRegistry::default();
651 {
652 let mut registry = type_registry.write();
653 registry.register::<BundleA>();
654 registry.register_type_data::<BundleA, ReflectBundle>();
655 }
656 world.insert_resource(type_registry);
657
658 let mut system_state: SystemState<Commands> = SystemState::new(&mut world);
659 let mut commands = system_state.get_mut(&mut world).unwrap();
660
661 let entity = commands
662 .spawn(BundleA {
663 a: ComponentA(31),
664 b: ComponentB(20),
665 })
666 .id();
667
668 let boxed_reflect_bundle_a = Box::new(BundleA {
669 a: ComponentA(1),
670 b: ComponentB(23),
671 }) as Box<dyn Reflect>;
672
673 commands
674 .entity(entity)
675 .remove_reflect(boxed_reflect_bundle_a.reflect_type_path().to_owned());
676 system_state.apply(&mut world);
677
678 assert_eq!(world.entity(entity).get::<ComponentA>(), None);
679 assert_eq!(world.entity(entity).get::<ComponentB>(), None);
680 }
681
682 #[test]
683 fn remove_reflected_bundle_with_registry() {
684 let mut world = World::new();
685
686 let mut type_registry = TypeRegistryResource {
687 type_registry: TypeRegistry::new(),
688 };
689
690 type_registry.type_registry.register::<BundleA>();
691 type_registry
692 .type_registry
693 .register_type_data::<BundleA, ReflectBundle>();
694 world.insert_resource(type_registry);
695
696 let mut system_state: SystemState<Commands> = SystemState::new(&mut world);
697 let mut commands = system_state.get_mut(&mut world).unwrap();
698
699 let entity = commands
700 .spawn(BundleA {
701 a: ComponentA(31),
702 b: ComponentB(20),
703 })
704 .id();
705
706 let boxed_reflect_bundle_a = Box::new(BundleA {
707 a: ComponentA(1),
708 b: ComponentB(23),
709 }) as Box<dyn Reflect>;
710
711 commands
712 .entity(entity)
713 .remove_reflect_with_registry::<TypeRegistryResource>(
714 boxed_reflect_bundle_a.reflect_type_path().to_owned(),
715 );
716 system_state.apply(&mut world);
717
718 assert_eq!(world.entity(entity).get::<ComponentA>(), None);
719 assert_eq!(world.entity(entity).get::<ComponentB>(), None);
720 }
721}