1use std::any::TypeId;
6
7pub use bevy_ecs_macros::Bundle;
8
9use crate::{
10 archetype::{
11 AddBundle, Archetype, ArchetypeId, Archetypes, BundleComponentStatus, ComponentStatus,
12 SpawnBundleStatus,
13 },
14 component::{Component, ComponentId, Components, StorageType, Tick},
15 entity::{Entities, Entity, EntityLocation},
16 observer::Observers,
17 prelude::World,
18 query::DebugCheckedUnwrap,
19 storage::{SparseSetIndex, SparseSets, Storages, Table, TableRow},
20 world::{unsafe_world_cell::UnsafeWorldCell, ON_ADD, ON_INSERT},
21};
22
23use bevy_ptr::{ConstNonNull, OwningPtr};
24use bevy_utils::{all_tuples, HashMap, HashSet, TypeIdMap};
25use std::ptr::NonNull;
26
27#[diagnostic::on_unimplemented(
147 message = "`{Self}` is not a `Bundle`",
148 label = "invalid `Bundle`",
149 note = "consider annotating `{Self}` with `#[derive(Component)]` or `#[derive(Bundle)]`"
150)]
151pub unsafe trait Bundle: DynamicBundle + Send + Sync + 'static {
152 #[doc(hidden)]
154 fn component_ids(
155 components: &mut Components,
156 storages: &mut Storages,
157 ids: &mut impl FnMut(ComponentId),
158 );
159
160 fn get_component_ids(components: &Components, ids: &mut impl FnMut(Option<ComponentId>));
162
163 #[doc(hidden)]
170 unsafe fn from_components<T, F>(ctx: &mut T, func: &mut F) -> Self
171 where
172 F: for<'a> FnMut(&'a mut T) -> OwningPtr<'a>,
174 Self: Sized;
175}
176
177pub trait DynamicBundle {
179 #[doc(hidden)]
186 fn get_components(self, func: &mut impl FnMut(StorageType, OwningPtr<'_>));
187}
188
189unsafe impl<C: Component> Bundle for C {
194 fn component_ids(
195 components: &mut Components,
196 storages: &mut Storages,
197 ids: &mut impl FnMut(ComponentId),
198 ) {
199 ids(components.init_component::<C>(storages));
200 }
201
202 unsafe fn from_components<T, F>(ctx: &mut T, func: &mut F) -> Self
203 where
204 F: for<'a> FnMut(&'a mut T) -> OwningPtr<'a>,
206 Self: Sized,
207 {
208 let ptr = func(ctx);
209 unsafe { ptr.read() }
211 }
212
213 fn get_component_ids(components: &Components, ids: &mut impl FnMut(Option<ComponentId>)) {
214 ids(components.get_id(TypeId::of::<C>()));
215 }
216}
217
218impl<C: Component> DynamicBundle for C {
219 #[inline]
220 fn get_components(self, func: &mut impl FnMut(StorageType, OwningPtr<'_>)) {
221 OwningPtr::make(self, |ptr| func(C::STORAGE_TYPE, ptr));
222 }
223}
224
225macro_rules! tuple_impl {
226 ($($name: ident),*) => {
227 unsafe impl<$($name: Bundle),*> Bundle for ($($name,)*) {
234 #[allow(unused_variables)]
235 fn component_ids(components: &mut Components, storages: &mut Storages, ids: &mut impl FnMut(ComponentId)){
236 $(<$name as Bundle>::component_ids(components, storages, ids);)*
237 }
238
239 #[allow(unused_variables)]
240 fn get_component_ids(components: &Components, ids: &mut impl FnMut(Option<ComponentId>)){
241 $(<$name as Bundle>::get_component_ids(components, ids);)*
242 }
243
244 #[allow(unused_variables, unused_mut)]
245 #[allow(clippy::unused_unit)]
246 unsafe fn from_components<T, F>(ctx: &mut T, func: &mut F) -> Self
247 where
248 F: FnMut(&mut T) -> OwningPtr<'_>
249 {
250 #[allow(unused_unsafe)]
251 unsafe { ($(<$name as Bundle>::from_components(ctx, func),)*) }
254 }
255 }
256
257 impl<$($name: Bundle),*> DynamicBundle for ($($name,)*) {
258 #[allow(unused_variables, unused_mut)]
259 #[inline(always)]
260 fn get_components(self, func: &mut impl FnMut(StorageType, OwningPtr<'_>)) {
261 #[allow(non_snake_case)]
262 let ($(mut $name,)*) = self;
263 $(
264 $name.get_components(&mut *func);
265 )*
266 }
267 }
268 }
269}
270
271all_tuples!(tuple_impl, 0, 15, B);
272
273#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
277pub struct BundleId(usize);
278
279impl BundleId {
280 #[inline]
284 pub fn index(self) -> usize {
285 self.0
286 }
287}
288
289impl SparseSetIndex for BundleId {
290 #[inline]
291 fn sparse_set_index(&self) -> usize {
292 self.index()
293 }
294
295 #[inline]
296 fn get_sparse_set_index(value: usize) -> Self {
297 Self(value)
298 }
299}
300
301pub struct BundleInfo {
305 id: BundleId,
306 component_ids: Vec<ComponentId>,
310}
311
312impl BundleInfo {
313 unsafe fn new(
321 bundle_type_name: &'static str,
322 components: &Components,
323 component_ids: Vec<ComponentId>,
324 id: BundleId,
325 ) -> BundleInfo {
326 let mut deduped = component_ids.clone();
327 deduped.sort();
328 deduped.dedup();
329
330 if deduped.len() != component_ids.len() {
331 let mut seen = HashSet::new();
333 let mut dups = Vec::new();
334 for id in component_ids {
335 if !seen.insert(id) {
336 dups.push(id);
337 }
338 }
339
340 let names = dups
341 .into_iter()
342 .map(|id| {
343 unsafe { components.get_info_unchecked(id).name() }
345 })
346 .collect::<Vec<_>>()
347 .join(", ");
348
349 panic!("Bundle {bundle_type_name} has duplicate components: {names}");
350 }
351
352 BundleInfo { id, component_ids }
357 }
358
359 #[inline]
361 pub const fn id(&self) -> BundleId {
362 self.id
363 }
364
365 #[inline]
367 pub fn components(&self) -> &[ComponentId] {
368 &self.component_ids
369 }
370
371 #[inline]
373 pub fn iter_components(&self) -> impl Iterator<Item = ComponentId> + '_ {
374 self.component_ids.iter().cloned()
375 }
376
377 #[inline]
394 #[allow(clippy::too_many_arguments)]
395 unsafe fn write_components<T: DynamicBundle, S: BundleComponentStatus>(
396 &self,
397 table: &mut Table,
398 sparse_sets: &mut SparseSets,
399 bundle_component_status: &S,
400 entity: Entity,
401 table_row: TableRow,
402 change_tick: Tick,
403 bundle: T,
404 ) {
405 let mut bundle_component = 0;
408 bundle.get_components(&mut |storage_type, component_ptr| {
409 let component_id = *self.component_ids.get_unchecked(bundle_component);
410 match storage_type {
411 StorageType::Table => {
412 let column =
413 unsafe { table.get_column_mut(component_id).debug_checked_unwrap() };
416 let status = unsafe { bundle_component_status.get_status(bundle_component) };
418 match status {
419 ComponentStatus::Added => {
420 column.initialize(table_row, component_ptr, change_tick);
421 }
422 ComponentStatus::Mutated => {
423 column.replace(table_row, component_ptr, change_tick);
424 }
425 }
426 }
427 StorageType::SparseSet => {
428 let sparse_set =
429 unsafe { sparse_sets.get_mut(component_id).debug_checked_unwrap() };
432 sparse_set.insert(entity, component_ptr, change_tick);
433 }
434 }
435 bundle_component += 1;
436 });
437 }
438
439 pub(crate) unsafe fn add_bundle_to_archetype(
445 &self,
446 archetypes: &mut Archetypes,
447 storages: &mut Storages,
448 components: &Components,
449 observers: &Observers,
450 archetype_id: ArchetypeId,
451 ) -> ArchetypeId {
452 if let Some(add_bundle_id) = archetypes[archetype_id].edges().get_add_bundle(self.id) {
453 return add_bundle_id;
454 }
455 let mut new_table_components = Vec::new();
456 let mut new_sparse_set_components = Vec::new();
457 let mut bundle_status = Vec::with_capacity(self.component_ids.len());
458 let mut added = Vec::new();
459
460 let current_archetype = &mut archetypes[archetype_id];
461 for component_id in self.component_ids.iter().cloned() {
462 if current_archetype.contains(component_id) {
463 bundle_status.push(ComponentStatus::Mutated);
464 } else {
465 bundle_status.push(ComponentStatus::Added);
466 added.push(component_id);
467 let component_info = unsafe { components.get_info_unchecked(component_id) };
469 match component_info.storage_type() {
470 StorageType::Table => new_table_components.push(component_id),
471 StorageType::SparseSet => new_sparse_set_components.push(component_id),
472 }
473 }
474 }
475
476 if new_table_components.is_empty() && new_sparse_set_components.is_empty() {
477 let edges = current_archetype.edges_mut();
478 edges.insert_add_bundle(self.id, archetype_id, bundle_status, added);
480 archetype_id
481 } else {
482 let table_id;
483 let table_components;
484 let sparse_set_components;
485 {
487 let current_archetype = &archetypes[archetype_id];
488 table_components = if new_table_components.is_empty() {
489 table_id = current_archetype.table_id();
491 current_archetype.table_components().collect()
492 } else {
493 new_table_components.extend(current_archetype.table_components());
494 new_table_components.sort();
496 table_id = unsafe {
498 storages
499 .tables
500 .get_id_or_insert(&new_table_components, components)
501 };
502
503 new_table_components
504 };
505
506 sparse_set_components = if new_sparse_set_components.is_empty() {
507 current_archetype.sparse_set_components().collect()
508 } else {
509 new_sparse_set_components.extend(current_archetype.sparse_set_components());
510 new_sparse_set_components.sort();
512 new_sparse_set_components
513 };
514 };
515 let new_archetype_id = archetypes.get_id_or_insert(
517 components,
518 observers,
519 table_id,
520 table_components,
521 sparse_set_components,
522 );
523 archetypes[archetype_id].edges_mut().insert_add_bundle(
525 self.id,
526 new_archetype_id,
527 bundle_status,
528 added,
529 );
530 new_archetype_id
531 }
532 }
533}
534
535pub(crate) struct BundleInserter<'w> {
537 world: UnsafeWorldCell<'w>,
538 bundle_info: ConstNonNull<BundleInfo>,
539 add_bundle: ConstNonNull<AddBundle>,
540 table: NonNull<Table>,
541 archetype: NonNull<Archetype>,
542 result: InsertBundleResult,
543 change_tick: Tick,
544}
545
546pub(crate) enum InsertBundleResult {
547 SameArchetype,
548 NewArchetypeSameTable {
549 new_archetype: NonNull<Archetype>,
550 },
551 NewArchetypeNewTable {
552 new_archetype: NonNull<Archetype>,
553 new_table: NonNull<Table>,
554 },
555}
556
557impl<'w> BundleInserter<'w> {
558 #[inline]
559 pub(crate) fn new<T: Bundle>(
560 world: &'w mut World,
561 archetype_id: ArchetypeId,
562 change_tick: Tick,
563 ) -> Self {
564 let bundle_id = world
565 .bundles
566 .init_info::<T>(&mut world.components, &mut world.storages);
567 unsafe { Self::new_with_id(world, archetype_id, bundle_id, change_tick) }
569 }
570
571 #[inline]
576 pub(crate) unsafe fn new_with_id(
577 world: &'w mut World,
578 archetype_id: ArchetypeId,
579 bundle_id: BundleId,
580 change_tick: Tick,
581 ) -> Self {
582 let bundle_info = world.bundles.get_unchecked(bundle_id);
584 let bundle_id = bundle_info.id();
585 let new_archetype_id = bundle_info.add_bundle_to_archetype(
586 &mut world.archetypes,
587 &mut world.storages,
588 &world.components,
589 &world.observers,
590 archetype_id,
591 );
592 if new_archetype_id == archetype_id {
593 let archetype = &mut world.archetypes[archetype_id];
594 let add_bundle = unsafe {
596 archetype
597 .edges()
598 .get_add_bundle_internal(bundle_id)
599 .debug_checked_unwrap()
600 };
601 let table_id = archetype.table_id();
602 let table = &mut world.storages.tables[table_id];
603 Self {
604 add_bundle: add_bundle.into(),
605 archetype: archetype.into(),
606 bundle_info: bundle_info.into(),
607 table: table.into(),
608 result: InsertBundleResult::SameArchetype,
609 change_tick,
610 world: world.as_unsafe_world_cell(),
611 }
612 } else {
613 let (archetype, new_archetype) =
614 world.archetypes.get_2_mut(archetype_id, new_archetype_id);
615 let add_bundle = unsafe {
617 archetype
618 .edges()
619 .get_add_bundle_internal(bundle_id)
620 .debug_checked_unwrap()
621 };
622 let table_id = archetype.table_id();
623 let new_table_id = new_archetype.table_id();
624 if table_id == new_table_id {
625 let table = &mut world.storages.tables[table_id];
626 Self {
627 add_bundle: add_bundle.into(),
628 archetype: archetype.into(),
629 bundle_info: bundle_info.into(),
630 table: table.into(),
631 result: InsertBundleResult::NewArchetypeSameTable {
632 new_archetype: new_archetype.into(),
633 },
634 change_tick,
635 world: world.as_unsafe_world_cell(),
636 }
637 } else {
638 let (table, new_table) = world.storages.tables.get_2_mut(table_id, new_table_id);
639 Self {
640 add_bundle: add_bundle.into(),
641 archetype: archetype.into(),
642 bundle_info: bundle_info.into(),
643 table: table.into(),
644 result: InsertBundleResult::NewArchetypeNewTable {
645 new_archetype: new_archetype.into(),
646 new_table: new_table.into(),
647 },
648 change_tick,
649 world: world.as_unsafe_world_cell(),
650 }
651 }
652 }
653 }
654
655 #[inline]
659 pub(crate) unsafe fn insert<T: DynamicBundle>(
660 &mut self,
661 entity: Entity,
662 location: EntityLocation,
663 bundle: T,
664 ) -> EntityLocation {
665 let bundle_info = self.bundle_info.as_ref();
666 let add_bundle = self.add_bundle.as_ref();
667 let table = self.table.as_mut();
668 let archetype = self.archetype.as_mut();
669
670 let (new_archetype, new_location) = match &mut self.result {
671 InsertBundleResult::SameArchetype => {
672 let sparse_sets = {
674 let world = self.world.world_mut();
675 &mut world.storages.sparse_sets
676 };
677
678 bundle_info.write_components(
679 table,
680 sparse_sets,
681 add_bundle,
682 entity,
683 location.table_row,
684 self.change_tick,
685 bundle,
686 );
687
688 (archetype, location)
689 }
690 InsertBundleResult::NewArchetypeSameTable { new_archetype } => {
691 let new_archetype = new_archetype.as_mut();
692
693 let (sparse_sets, entities) = {
695 let world = self.world.world_mut();
696 (&mut world.storages.sparse_sets, &mut world.entities)
697 };
698
699 let result = archetype.swap_remove(location.archetype_row);
700 if let Some(swapped_entity) = result.swapped_entity {
701 let swapped_location =
702 unsafe { entities.get(swapped_entity).debug_checked_unwrap() };
704 entities.set(
705 swapped_entity.index(),
706 EntityLocation {
707 archetype_id: swapped_location.archetype_id,
708 archetype_row: location.archetype_row,
709 table_id: swapped_location.table_id,
710 table_row: swapped_location.table_row,
711 },
712 );
713 }
714 let new_location = new_archetype.allocate(entity, result.table_row);
715 entities.set(entity.index(), new_location);
716 bundle_info.write_components(
717 table,
718 sparse_sets,
719 add_bundle,
720 entity,
721 result.table_row,
722 self.change_tick,
723 bundle,
724 );
725
726 (new_archetype, new_location)
727 }
728 InsertBundleResult::NewArchetypeNewTable {
729 new_archetype,
730 new_table,
731 } => {
732 let new_table = new_table.as_mut();
733 let new_archetype = new_archetype.as_mut();
734
735 let (archetypes_ptr, sparse_sets, entities) = {
737 let world = self.world.world_mut();
738 let archetype_ptr: *mut Archetype = world.archetypes.archetypes.as_mut_ptr();
739 (
740 archetype_ptr,
741 &mut world.storages.sparse_sets,
742 &mut world.entities,
743 )
744 };
745 let result = archetype.swap_remove(location.archetype_row);
746 if let Some(swapped_entity) = result.swapped_entity {
747 let swapped_location =
748 unsafe { entities.get(swapped_entity).debug_checked_unwrap() };
750 entities.set(
751 swapped_entity.index(),
752 EntityLocation {
753 archetype_id: swapped_location.archetype_id,
754 archetype_row: location.archetype_row,
755 table_id: swapped_location.table_id,
756 table_row: swapped_location.table_row,
757 },
758 );
759 }
760 let move_result = table.move_to_superset_unchecked(result.table_row, new_table);
763 let new_location = new_archetype.allocate(entity, move_result.new_row);
764 entities.set(entity.index(), new_location);
765
766 if let Some(swapped_entity) = move_result.swapped_entity {
768 let swapped_location =
769 unsafe { entities.get(swapped_entity).debug_checked_unwrap() };
771
772 entities.set(
773 swapped_entity.index(),
774 EntityLocation {
775 archetype_id: swapped_location.archetype_id,
776 archetype_row: swapped_location.archetype_row,
777 table_id: swapped_location.table_id,
778 table_row: result.table_row,
779 },
780 );
781
782 if archetype.id() == swapped_location.archetype_id {
783 archetype
784 .set_entity_table_row(swapped_location.archetype_row, result.table_row);
785 } else if new_archetype.id() == swapped_location.archetype_id {
786 new_archetype
787 .set_entity_table_row(swapped_location.archetype_row, result.table_row);
788 } else {
789 (*archetypes_ptr.add(swapped_location.archetype_id.index()))
791 .set_entity_table_row(swapped_location.archetype_row, result.table_row);
792 }
793 }
794
795 bundle_info.write_components(
796 new_table,
797 sparse_sets,
798 add_bundle,
799 entity,
800 move_result.new_row,
801 self.change_tick,
802 bundle,
803 );
804
805 (new_archetype, new_location)
806 }
807 };
808
809 let new_archetype = &*new_archetype;
810 let mut deferred_world = unsafe { self.world.into_deferred() };
812
813 unsafe {
816 deferred_world.trigger_on_add(new_archetype, entity, add_bundle.added.iter().cloned());
817 if new_archetype.has_add_observer() {
818 deferred_world.trigger_observers(ON_ADD, entity, add_bundle.added.iter().cloned());
819 }
820 deferred_world.trigger_on_insert(new_archetype, entity, bundle_info.iter_components());
821 if new_archetype.has_insert_observer() {
822 deferred_world.trigger_observers(ON_INSERT, entity, bundle_info.iter_components());
823 }
824 }
825
826 new_location
827 }
828
829 #[inline]
830 pub(crate) fn entities(&mut self) -> &mut Entities {
831 unsafe { &mut self.world.world_mut().entities }
833 }
834}
835
836pub(crate) struct BundleSpawner<'w> {
838 world: UnsafeWorldCell<'w>,
839 bundle_info: ConstNonNull<BundleInfo>,
840 table: NonNull<Table>,
841 archetype: NonNull<Archetype>,
842 change_tick: Tick,
843}
844
845impl<'w> BundleSpawner<'w> {
846 #[inline]
847 pub fn new<T: Bundle>(world: &'w mut World, change_tick: Tick) -> Self {
848 let bundle_id = world
849 .bundles
850 .init_info::<T>(&mut world.components, &mut world.storages);
851 unsafe { Self::new_with_id(world, bundle_id, change_tick) }
853 }
854
855 #[inline]
860 pub(crate) unsafe fn new_with_id(
861 world: &'w mut World,
862 bundle_id: BundleId,
863 change_tick: Tick,
864 ) -> Self {
865 let bundle_info = world.bundles.get_unchecked(bundle_id);
866 let new_archetype_id = bundle_info.add_bundle_to_archetype(
867 &mut world.archetypes,
868 &mut world.storages,
869 &world.components,
870 &world.observers,
871 ArchetypeId::EMPTY,
872 );
873 let archetype = &mut world.archetypes[new_archetype_id];
874 let table = &mut world.storages.tables[archetype.table_id()];
875 Self {
876 bundle_info: bundle_info.into(),
877 table: table.into(),
878 archetype: archetype.into(),
879 change_tick,
880 world: world.as_unsafe_world_cell(),
881 }
882 }
883
884 #[inline]
885 pub fn reserve_storage(&mut self, additional: usize) {
886 let (archetype, table) = unsafe { (self.archetype.as_mut(), self.table.as_mut()) };
888 archetype.reserve(additional);
889 table.reserve(additional);
890 }
891
892 #[inline]
895 pub unsafe fn spawn_non_existent<T: DynamicBundle>(
896 &mut self,
897 entity: Entity,
898 bundle: T,
899 ) -> EntityLocation {
900 let bundle_info = self.bundle_info.as_ref();
902 let location = {
903 let table = self.table.as_mut();
904 let archetype = self.archetype.as_mut();
905
906 let (sparse_sets, entities) = {
908 let world = self.world.world_mut();
909 (&mut world.storages.sparse_sets, &mut world.entities)
910 };
911 let table_row = table.allocate(entity);
912 let location = archetype.allocate(entity, table_row);
913 bundle_info.write_components(
914 table,
915 sparse_sets,
916 &SpawnBundleStatus,
917 entity,
918 table_row,
919 self.change_tick,
920 bundle,
921 );
922 entities.set(entity.index(), location);
923 location
924 };
925
926 let mut deferred_world = unsafe { self.world.into_deferred() };
928 let archetype = self.archetype.as_ref();
930 unsafe {
933 deferred_world.trigger_on_add(archetype, entity, bundle_info.iter_components());
934 if archetype.has_add_observer() {
935 deferred_world.trigger_observers(ON_ADD, entity, bundle_info.iter_components());
936 }
937 deferred_world.trigger_on_insert(archetype, entity, bundle_info.iter_components());
938 if archetype.has_insert_observer() {
939 deferred_world.trigger_observers(ON_INSERT, entity, bundle_info.iter_components());
940 }
941 };
942
943 location
944 }
945
946 #[inline]
949 pub unsafe fn spawn<T: Bundle>(&mut self, bundle: T) -> Entity {
950 let entity = self.entities().alloc();
951 unsafe {
953 self.spawn_non_existent(entity, bundle);
954 }
955 entity
956 }
957
958 #[inline]
959 pub(crate) fn entities(&mut self) -> &mut Entities {
960 unsafe { &mut self.world.world_mut().entities }
962 }
963
964 #[inline]
967 pub(crate) unsafe fn flush_commands(&mut self) {
968 self.world.world_mut().flush();
970 }
971}
972
973#[derive(Default)]
975pub struct Bundles {
976 bundle_infos: Vec<BundleInfo>,
977 bundle_ids: TypeIdMap<BundleId>,
979 dynamic_bundle_ids: HashMap<Box<[ComponentId]>, BundleId>,
981 dynamic_bundle_storages: HashMap<BundleId, Vec<StorageType>>,
982 dynamic_component_bundle_ids: HashMap<ComponentId, BundleId>,
984 dynamic_component_storages: HashMap<BundleId, StorageType>,
985}
986
987impl Bundles {
988 #[inline]
991 pub fn get(&self, bundle_id: BundleId) -> Option<&BundleInfo> {
992 self.bundle_infos.get(bundle_id.index())
993 }
994
995 #[inline]
999 pub fn get_id(&self, type_id: TypeId) -> Option<BundleId> {
1000 self.bundle_ids.get(&type_id).cloned()
1001 }
1002
1003 pub(crate) fn init_info<T: Bundle>(
1007 &mut self,
1008 components: &mut Components,
1009 storages: &mut Storages,
1010 ) -> BundleId {
1011 let bundle_infos = &mut self.bundle_infos;
1012 let id = *self.bundle_ids.entry(TypeId::of::<T>()).or_insert_with(|| {
1013 let mut component_ids = Vec::new();
1014 T::component_ids(components, storages, &mut |id| component_ids.push(id));
1015 let id = BundleId(bundle_infos.len());
1016 let bundle_info =
1017 unsafe { BundleInfo::new(std::any::type_name::<T>(), components, component_ids, id) };
1022 bundle_infos.push(bundle_info);
1023 id
1024 });
1025 id
1026 }
1027
1028 pub(crate) unsafe fn get_unchecked(&self, id: BundleId) -> &BundleInfo {
1031 self.bundle_infos.get_unchecked(id.0)
1032 }
1033
1034 pub(crate) unsafe fn get_storage_unchecked(&self, id: BundleId) -> StorageType {
1035 *self
1036 .dynamic_component_storages
1037 .get(&id)
1038 .debug_checked_unwrap()
1039 }
1040
1041 pub(crate) unsafe fn get_storages_unchecked(&mut self, id: BundleId) -> &mut Vec<StorageType> {
1042 self.dynamic_bundle_storages
1043 .get_mut(&id)
1044 .debug_checked_unwrap()
1045 }
1046
1047 pub(crate) fn init_dynamic_info(
1054 &mut self,
1055 components: &Components,
1056 component_ids: &[ComponentId],
1057 ) -> BundleId {
1058 let bundle_infos = &mut self.bundle_infos;
1059
1060 let (_, bundle_id) = self
1062 .dynamic_bundle_ids
1063 .raw_entry_mut()
1064 .from_key(component_ids)
1065 .or_insert_with(|| {
1066 let (id, storages) =
1067 initialize_dynamic_bundle(bundle_infos, components, Vec::from(component_ids));
1068 self.dynamic_bundle_storages
1069 .insert_unique_unchecked(id, storages);
1070 (component_ids.into(), id)
1071 });
1072 *bundle_id
1073 }
1074
1075 pub(crate) fn init_component_info(
1081 &mut self,
1082 components: &Components,
1083 component_id: ComponentId,
1084 ) -> BundleId {
1085 let bundle_infos = &mut self.bundle_infos;
1086 let bundle_id = self
1087 .dynamic_component_bundle_ids
1088 .entry(component_id)
1089 .or_insert_with(|| {
1090 let (id, storage_type) =
1091 initialize_dynamic_bundle(bundle_infos, components, vec![component_id]);
1092 self.dynamic_component_storages.insert(id, storage_type[0]);
1093 id
1094 });
1095 *bundle_id
1096 }
1097}
1098
1099fn initialize_dynamic_bundle(
1102 bundle_infos: &mut Vec<BundleInfo>,
1103 components: &Components,
1104 component_ids: Vec<ComponentId>,
1105) -> (BundleId, Vec<StorageType>) {
1106 let storage_types = component_ids.iter().map(|&id| {
1108 components.get_info(id).unwrap_or_else(|| {
1109 panic!(
1110 "init_dynamic_info called with component id {id:?} which doesn't exist in this world"
1111 )
1112 }).storage_type()
1113 }).collect();
1114
1115 let id = BundleId(bundle_infos.len());
1116 let bundle_info =
1117 unsafe { BundleInfo::new("<dynamic bundle>", components, component_ids, id) };
1119 bundle_infos.push(bundle_info);
1120
1121 (id, storage_types)
1122}
1123
1124#[cfg(test)]
1125mod tests {
1126 use crate as bevy_ecs;
1127 use crate::prelude::*;
1128
1129 #[derive(Component)]
1130 struct A;
1131
1132 #[derive(Component)]
1133 struct B;
1134
1135 #[derive(Component)]
1136 struct C;
1137
1138 #[derive(Component)]
1139 struct D;
1140
1141 #[derive(Resource, Default)]
1142 struct R(usize);
1143
1144 impl R {
1145 #[track_caller]
1146 fn assert_order(&mut self, count: usize) {
1147 assert_eq!(count, self.0);
1148 self.0 += 1;
1149 }
1150 }
1151
1152 #[test]
1153 fn component_hook_order_spawn_despawn() {
1154 let mut world = World::new();
1155 world.init_resource::<R>();
1156 world
1157 .register_component_hooks::<A>()
1158 .on_add(|mut world, _, _| {
1159 world.resource_mut::<R>().assert_order(0);
1160 })
1161 .on_insert(|mut world, _, _| world.resource_mut::<R>().assert_order(1))
1162 .on_remove(|mut world, _, _| world.resource_mut::<R>().assert_order(2));
1163
1164 let entity = world.spawn(A).id();
1165 world.despawn(entity);
1166 assert_eq!(3, world.resource::<R>().0);
1167 }
1168
1169 #[test]
1170 fn component_hook_order_insert_remove() {
1171 let mut world = World::new();
1172 world.init_resource::<R>();
1173 world
1174 .register_component_hooks::<A>()
1175 .on_add(|mut world, _, _| {
1176 world.resource_mut::<R>().assert_order(0);
1177 })
1178 .on_insert(|mut world, _, _| {
1179 world.resource_mut::<R>().assert_order(1);
1180 })
1181 .on_remove(|mut world, _, _| {
1182 world.resource_mut::<R>().assert_order(2);
1183 });
1184
1185 let mut entity = world.spawn_empty();
1186 entity.insert(A);
1187 entity.remove::<A>();
1188 entity.flush();
1189 assert_eq!(3, world.resource::<R>().0);
1190 }
1191
1192 #[test]
1193 fn component_hook_order_recursive() {
1194 let mut world = World::new();
1195 world.init_resource::<R>();
1196 world
1197 .register_component_hooks::<A>()
1198 .on_add(|mut world, entity, _| {
1199 world.resource_mut::<R>().assert_order(0);
1200 world.commands().entity(entity).insert(B);
1201 })
1202 .on_remove(|mut world, entity, _| {
1203 world.resource_mut::<R>().assert_order(2);
1204 world.commands().entity(entity).remove::<B>();
1205 });
1206
1207 world
1208 .register_component_hooks::<B>()
1209 .on_add(|mut world, entity, _| {
1210 world.resource_mut::<R>().assert_order(1);
1211 world.commands().entity(entity).remove::<A>();
1212 })
1213 .on_remove(|mut world, _, _| {
1214 world.resource_mut::<R>().assert_order(3);
1215 });
1216
1217 let entity = world.spawn(A).flush();
1218 let entity = world.get_entity(entity).unwrap();
1219 assert!(!entity.contains::<A>());
1220 assert!(!entity.contains::<B>());
1221 assert_eq!(4, world.resource::<R>().0);
1222 }
1223
1224 #[test]
1225 fn component_hook_order_recursive_multiple() {
1226 let mut world = World::new();
1227 world.init_resource::<R>();
1228 world
1229 .register_component_hooks::<A>()
1230 .on_add(|mut world, entity, _| {
1231 world.resource_mut::<R>().assert_order(0);
1232 world.commands().entity(entity).insert(B).insert(C);
1233 });
1234
1235 world
1236 .register_component_hooks::<B>()
1237 .on_add(|mut world, entity, _| {
1238 world.resource_mut::<R>().assert_order(1);
1239 world.commands().entity(entity).insert(D);
1240 });
1241
1242 world
1243 .register_component_hooks::<C>()
1244 .on_add(|mut world, _, _| {
1245 world.resource_mut::<R>().assert_order(3);
1246 });
1247
1248 world
1249 .register_component_hooks::<D>()
1250 .on_add(|mut world, _, _| {
1251 world.resource_mut::<R>().assert_order(2);
1252 });
1253
1254 world.spawn(A).flush();
1255 assert_eq!(4, world.resource::<R>().0);
1256 }
1257}