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