bevy_ecs/bundle/info.rs
1use alloc::{boxed::Box, vec, vec::Vec};
2use bevy_platform::{
3 collections::{HashMap, HashSet},
4 hash::FixedHasher,
5};
6use bevy_ptr::{MovingPtr, OwningPtr};
7use bevy_utils::TypeIdMap;
8use core::{any::TypeId, ptr::NonNull};
9use indexmap::{IndexMap, IndexSet};
10
11use crate::{
12 archetype::{Archetype, BundleComponentStatus, ComponentStatus},
13 bundle::{Bundle, DynamicBundle},
14 change_detection::{MaybeLocation, Tick},
15 component::{
16 ComponentId, Components, ComponentsRegistrator, RequiredComponentConstructor, StorageType,
17 },
18 entity::Entity,
19 query::DebugCheckedUnwrap as _,
20 storage::{SparseSetIndex, SparseSets, Storages, Table, TableRow},
21};
22
23/// For a specific [`World`], this stores a unique value identifying a type of a registered [`Bundle`].
24///
25/// [`World`]: crate::world::World
26#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
27pub struct BundleId(usize);
28
29impl BundleId {
30 /// Returns the index of the associated [`Bundle`] type.
31 ///
32 /// Note that this is unique per-world, and should not be reused across them.
33 #[inline]
34 pub fn index(self) -> usize {
35 self.0
36 }
37}
38
39impl SparseSetIndex for BundleId {
40 #[inline]
41 fn sparse_set_index(&self) -> usize {
42 self.index()
43 }
44
45 #[inline]
46 fn get_sparse_set_index(value: usize) -> Self {
47 Self(value)
48 }
49}
50
51/// What to do on insertion if a component already exists.
52#[derive(Clone, Copy, Eq, PartialEq)]
53pub enum InsertMode {
54 /// Any existing components of a matching type will be overwritten.
55 Replace,
56 /// Any existing components of a matching type will be left unchanged.
57 Keep,
58}
59
60/// Stores metadata associated with a specific type of [`Bundle`] for a given [`World`].
61///
62/// [`World`]: crate::world::World
63pub struct BundleInfo {
64 pub(super) id: BundleId,
65
66 /// The list of all components contributed by the bundle (including Required Components). This is in
67 /// the order `[EXPLICIT_COMPONENTS][REQUIRED_COMPONENTS]`
68 ///
69 /// # Safety
70 /// Every ID in this list must be valid within the World that owns the [`BundleInfo`],
71 /// must have its storage initialized (i.e. columns created in tables, sparse set created),
72 /// and the range (0..`explicit_components_len`) must be in the same order as the source bundle
73 /// type writes its components in.
74 pub(super) contributed_component_ids: Box<[ComponentId]>,
75
76 /// The list of constructors for all required components indirectly contributed by this bundle.
77 pub(super) required_component_constructors: Box<[RequiredComponentConstructor]>,
78}
79
80impl BundleInfo {
81 /// Create a new [`BundleInfo`].
82 ///
83 /// # Safety
84 ///
85 /// Every ID in `component_ids` must be valid within the World that owns the `BundleInfo`
86 /// and must be in the same order as the source bundle type writes its components in.
87 unsafe fn new(
88 bundle_type_name: &'static str,
89 storages: &mut Storages,
90 components: &Components,
91 mut component_ids: Vec<ComponentId>,
92 id: BundleId,
93 ) -> BundleInfo {
94 let explicit_component_ids = component_ids
95 .iter()
96 .copied()
97 .collect::<IndexSet<_, FixedHasher>>();
98
99 // check for duplicates
100 if explicit_component_ids.len() != component_ids.len() {
101 // TODO: Replace with `Vec::partition_dedup` once https://github.com/rust-lang/rust/issues/54279 is stabilized
102 let mut seen = <HashSet<_>>::default();
103 let mut dups = Vec::new();
104 for id in component_ids {
105 if !seen.insert(id) {
106 dups.push(id);
107 }
108 }
109
110 let names = dups
111 .into_iter()
112 .map(|id| {
113 // SAFETY: the caller ensures component_id is valid.
114 unsafe { components.get_info_unchecked(id).name() }
115 })
116 .collect::<Vec<_>>();
117
118 panic!("Bundle {bundle_type_name} has duplicate components: {names:?}");
119 }
120
121 let mut depth_first_components = IndexMap::<_, _, FixedHasher>::default();
122 for &component_id in &component_ids {
123 // SAFETY: caller has verified that all ids are valid
124 let info = unsafe { components.get_info_unchecked(component_id) };
125
126 for (&required_id, required_component) in &info.required_components().all {
127 depth_first_components
128 .entry(required_id)
129 .or_insert_with(|| required_component.clone());
130 }
131
132 storages.prepare_component(info);
133 }
134
135 let required_components = depth_first_components
136 .into_iter()
137 .filter(|&(required_id, _)| !explicit_component_ids.contains(&required_id))
138 .inspect(|&(required_id, _)| {
139 // SAFETY: These ids came out of the passed `components`, so they must be valid.
140 storages.prepare_component(unsafe { components.get_info_unchecked(required_id) });
141 component_ids.push(required_id);
142 })
143 .map(|(_, required_component)| required_component.constructor)
144 .collect::<Box<_>>();
145
146 // SAFETY: The caller ensures that component_ids:
147 // - is valid for the associated world
148 // - has had its storage initialized
149 // - is in the same order as the source bundle type
150 BundleInfo {
151 id,
152 contributed_component_ids: component_ids.into(),
153 required_component_constructors: required_components,
154 }
155 }
156
157 /// Returns a value identifying the associated [`Bundle`] type.
158 #[inline]
159 pub const fn id(&self) -> BundleId {
160 self.id
161 }
162
163 /// Returns the length of the explicit components part of the [`contributed_components`](Self::contributed_components) list.
164 #[inline]
165 pub(super) fn explicit_components_len(&self) -> usize {
166 self.contributed_component_ids.len() - self.required_component_constructors.len()
167 }
168
169 /// Returns the [ID](ComponentId) of each component explicitly defined in this bundle (ex: Required Components are excluded).
170 ///
171 /// For all components contributed by this bundle (including Required Components), see [`BundleInfo::contributed_components`]
172 #[inline]
173 pub fn explicit_components(&self) -> &[ComponentId] {
174 &self.contributed_component_ids[0..self.explicit_components_len()]
175 }
176
177 /// Returns the [ID](ComponentId) of each Required Component needed by this bundle. This _does not include_ Required Components that are
178 /// explicitly provided by the bundle.
179 #[inline]
180 pub fn required_components(&self) -> &[ComponentId] {
181 &self.contributed_component_ids[self.explicit_components_len()..]
182 }
183
184 /// Returns the [ID](ComponentId) of each component contributed by this bundle. This includes Required Components.
185 ///
186 /// For only components explicitly defined in this bundle, see [`BundleInfo::explicit_components`]
187 #[inline]
188 pub fn contributed_components(&self) -> &[ComponentId] {
189 &self.contributed_component_ids
190 }
191
192 /// Returns an iterator over the [ID](ComponentId) of each component explicitly defined in this bundle (ex: this excludes Required Components).
193 /// To iterate all components contributed by this bundle (including Required Components), see [`BundleInfo::iter_contributed_components`]
194 #[inline]
195 pub fn iter_explicit_components(&self) -> impl Iterator<Item = ComponentId> + Clone + '_ {
196 self.explicit_components().iter().copied()
197 }
198
199 /// Returns an iterator over the [ID](ComponentId) of each component contributed by this bundle. This includes Required Components.
200 ///
201 /// To iterate only components explicitly defined in this bundle, see [`BundleInfo::iter_explicit_components`]
202 #[inline]
203 pub fn iter_contributed_components(&self) -> impl Iterator<Item = ComponentId> + Clone + '_ {
204 self.contributed_components().iter().copied()
205 }
206
207 /// Returns an iterator over the [ID](ComponentId) of each Required Component needed by this bundle. This _does not include_ Required Components that are
208 /// explicitly provided by the bundle.
209 pub fn iter_required_components(&self) -> impl Iterator<Item = ComponentId> + '_ {
210 self.required_components().iter().copied()
211 }
212
213 /// This writes components from a given [`Bundle`] to the given entity.
214 ///
215 /// # Safety
216 ///
217 /// `bundle_component_status` must return the "correct" [`ComponentStatus`] for each component
218 /// in the [`Bundle`], with respect to the entity's original archetype (prior to the bundle being added).
219 ///
220 /// For example, if the original archetype already has `ComponentA` and `T` also has `ComponentA`, the status
221 /// should be `Existing`. If the original archetype does not have `ComponentA`, the status should be `Added`.
222 ///
223 /// When "inserting" a bundle into an existing entity, [`ArchetypeAfterBundleInsert`](crate::archetype::SpawnBundleStatus)
224 /// should be used, which will report `Added` vs `Existing` status based on the current archetype's structure.
225 ///
226 /// When spawning a bundle, [`SpawnBundleStatus`](crate::archetype::SpawnBundleStatus) can be used instead,
227 /// which removes the need to look up the [`ArchetypeAfterBundleInsert`](crate::archetype::ArchetypeAfterBundleInsert)
228 /// in the archetype graph, which requires ownership of the entity's current archetype.
229 ///
230 /// Regardless of how this is used, [`apply_effect`] must be called at most once on `bundle` after this function is
231 /// called if `T::Effect: !NoBundleEffect` before returning to user-space safe code before returning to user-space safe code.
232 /// This is currently only doable via use of [`MovingPtr::partial_move`].
233 ///
234 /// `table` must be the "new" table for `entity`. `table_row` must have space allocated for the
235 /// `entity`, `bundle` must match this [`BundleInfo`]'s type
236 ///
237 /// [`apply_effect`]: crate::bundle::DynamicBundle::apply_effect
238 #[inline]
239 pub(super) unsafe fn write_components<'a, T: DynamicBundle, S: BundleComponentStatus>(
240 &self,
241 table: &mut Table,
242 sparse_sets: &mut SparseSets,
243 bundle_component_status: &S,
244 required_components: impl Iterator<Item = &'a RequiredComponentConstructor>,
245 entity: Entity,
246 table_row: TableRow,
247 change_tick: Tick,
248 bundle: MovingPtr<'_, T>,
249 insert_mode: InsertMode,
250 caller: MaybeLocation,
251 ) {
252 // NOTE: get_components calls this closure on each component in "bundle order".
253 // bundle_info.component_ids are also in "bundle order"
254 let mut bundle_component = 0;
255 T::get_components(bundle, &mut |storage_type, component_ptr| {
256 let component_id = *self
257 .contributed_component_ids
258 .get_unchecked(bundle_component);
259 // SAFETY: bundle_component is a valid index for this bundle
260 let status = unsafe { bundle_component_status.get_status(bundle_component) };
261 match storage_type {
262 StorageType::Table => {
263 let column =
264 // SAFETY: If component_id is in self.component_ids, BundleInfo::new ensures that
265 // the target table contains the component.
266 unsafe { table.get_column_mut(component_id).debug_checked_unwrap() };
267 match (status, insert_mode) {
268 (ComponentStatus::Added, _) => {
269 column.initialize(table_row, component_ptr, change_tick, caller);
270 }
271 (ComponentStatus::Existing, InsertMode::Replace) => {
272 column.replace(table_row, component_ptr, change_tick, caller);
273 }
274 (ComponentStatus::Existing, InsertMode::Keep) => {
275 if let Some(drop_fn) = table.get_drop_for(component_id) {
276 drop_fn(component_ptr);
277 }
278 }
279 }
280 }
281 StorageType::SparseSet => {
282 let sparse_set =
283 // SAFETY: If component_id is in self.component_ids, BundleInfo::new ensures that
284 // a sparse set exists for the component.
285 unsafe { sparse_sets.get_mut(component_id).debug_checked_unwrap() };
286 match (status, insert_mode) {
287 (ComponentStatus::Added, _) | (_, InsertMode::Replace) => {
288 sparse_set.insert(entity, component_ptr, change_tick, caller);
289 }
290 (ComponentStatus::Existing, InsertMode::Keep) => {
291 if let Some(drop_fn) = sparse_set.get_drop() {
292 drop_fn(component_ptr);
293 }
294 }
295 }
296 }
297 }
298 bundle_component += 1;
299 });
300
301 for required_component in required_components {
302 required_component.initialize(
303 table,
304 sparse_sets,
305 change_tick,
306 table_row,
307 entity,
308 caller,
309 );
310 }
311 }
312
313 /// Internal method to initialize a required component from an [`OwningPtr`]. This should ultimately be called
314 /// in the context of [`BundleInfo::write_components`], via [`RequiredComponentConstructor::initialize`].
315 ///
316 /// # Safety
317 ///
318 /// `component_ptr` must point to a required component value that matches the given `component_id`. The `storage_type` must match
319 /// the type associated with `component_id`. The `entity` and `table_row` must correspond to an entity with an uninitialized
320 /// component matching `component_id`.
321 ///
322 /// This method _should not_ be called outside of [`BundleInfo::write_components`].
323 /// For more information, read the [`BundleInfo::write_components`] safety docs.
324 /// This function inherits the safety requirements defined there.
325 pub(crate) unsafe fn initialize_required_component(
326 table: &mut Table,
327 sparse_sets: &mut SparseSets,
328 change_tick: Tick,
329 table_row: TableRow,
330 entity: Entity,
331 component_id: ComponentId,
332 storage_type: StorageType,
333 component_ptr: OwningPtr,
334 caller: MaybeLocation,
335 ) {
336 {
337 match storage_type {
338 StorageType::Table => {
339 let column =
340 // SAFETY: If component_id is in required_components, BundleInfo::new requires that
341 // the target table contains the component.
342 unsafe { table.get_column_mut(component_id).debug_checked_unwrap() };
343 column.initialize(table_row, component_ptr, change_tick, caller);
344 }
345 StorageType::SparseSet => {
346 let sparse_set =
347 // SAFETY: If component_id is in required_components, BundleInfo::new requires that
348 // a sparse set exists for the component.
349 unsafe { sparse_sets.get_mut(component_id).debug_checked_unwrap() };
350 sparse_set.insert(entity, component_ptr, change_tick, caller);
351 }
352 }
353 }
354 }
355}
356
357/// The type of archetype move (or lack thereof) that will result from a bundle
358/// being inserted into an entity.
359pub(crate) enum ArchetypeMoveType {
360 /// If the entity already has all of the components that are being inserted,
361 /// its archetype won't change.
362 SameArchetype,
363 /// If only [`sparse set`](StorageType::SparseSet) components are being added,
364 /// the entity's archetype will change while keeping the same table.
365 NewArchetypeSameTable { new_archetype: NonNull<Archetype> },
366 /// If any [`table-stored`](StorageType::Table) components are being added,
367 /// both the entity's archetype and table will change.
368 NewArchetypeNewTable {
369 new_archetype: NonNull<Archetype>,
370 new_table: NonNull<Table>,
371 },
372}
373
374/// Metadata for bundles. Stores a [`BundleInfo`] for each type of [`Bundle`] in a given world.
375#[derive(Default)]
376pub struct Bundles {
377 bundle_infos: Vec<BundleInfo>,
378 /// Cache static [`BundleId`]
379 bundle_ids: TypeIdMap<BundleId>,
380 /// Cache bundles, which contains both explicit and required components of [`Bundle`]
381 contributed_bundle_ids: TypeIdMap<BundleId>,
382 /// Cache dynamic [`BundleId`] with multiple components
383 dynamic_bundle_ids: HashMap<Box<[ComponentId]>, BundleId>,
384 dynamic_bundle_storages: HashMap<BundleId, Vec<StorageType>>,
385 /// Cache optimized dynamic [`BundleId`] with single component
386 dynamic_component_bundle_ids: HashMap<ComponentId, BundleId>,
387 dynamic_component_storages: HashMap<BundleId, StorageType>,
388}
389
390impl Bundles {
391 /// The total number of [`Bundle`] registered in [`Storages`].
392 pub fn len(&self) -> usize {
393 self.bundle_infos.len()
394 }
395
396 /// Returns true if no [`Bundle`] registered in [`Storages`].
397 pub fn is_empty(&self) -> bool {
398 self.len() == 0
399 }
400
401 /// Iterate over [`BundleInfo`].
402 pub fn iter(&self) -> impl Iterator<Item = &BundleInfo> {
403 self.bundle_infos.iter()
404 }
405
406 /// Gets the metadata associated with a specific type of bundle.
407 /// Returns `None` if the bundle is not registered with the world.
408 #[inline]
409 pub fn get(&self, bundle_id: BundleId) -> Option<&BundleInfo> {
410 self.bundle_infos.get(bundle_id.index())
411 }
412
413 /// Gets the value identifying a specific type of bundle.
414 /// Returns `None` if the bundle does not exist in the world,
415 /// or if `type_id` does not correspond to a type of bundle.
416 #[inline]
417 pub fn get_id(&self, type_id: TypeId) -> Option<BundleId> {
418 self.bundle_ids.get(&type_id).cloned()
419 }
420
421 /// Registers a new [`BundleInfo`] for a statically known type.
422 ///
423 /// Also registers all the components in the bundle.
424 ///
425 /// # Safety
426 ///
427 /// `components` and `storages` must be from the same [`World`] as `self`.
428 ///
429 /// [`World`]: crate::world::World
430 #[deny(unsafe_op_in_unsafe_fn)]
431 pub(crate) unsafe fn register_info<T: Bundle>(
432 &mut self,
433 components: &mut ComponentsRegistrator,
434 storages: &mut Storages,
435 ) -> BundleId {
436 let bundle_infos = &mut self.bundle_infos;
437 *self.bundle_ids.entry(TypeId::of::<T>()).or_insert_with(|| {
438 let component_ids = T::component_ids(components).collect::<Vec<_>>();
439 let id = BundleId(bundle_infos.len());
440 let bundle_info =
441 // SAFETY: T::component_id ensures:
442 // - its info was created
443 // - appropriate storage for it has been initialized.
444 // - it was created in the same order as the components in T
445 unsafe { BundleInfo::new(core::any::type_name::<T>(), storages, components, component_ids, id) };
446 bundle_infos.push(bundle_info);
447 id
448 })
449 }
450
451 /// Registers a new [`BundleInfo`], which contains both explicit and required components for a statically known type.
452 ///
453 /// Also registers all the components in the bundle.
454 ///
455 /// # Safety
456 ///
457 /// `components` and `storages` must be from the same [`World`] as `self`.
458 ///
459 /// [`World`]: crate::world::World
460 #[deny(unsafe_op_in_unsafe_fn)]
461 pub(crate) unsafe fn register_contributed_bundle_info<T: Bundle>(
462 &mut self,
463 components: &mut ComponentsRegistrator,
464 storages: &mut Storages,
465 ) -> BundleId {
466 if let Some(id) = self.contributed_bundle_ids.get(&TypeId::of::<T>()).cloned() {
467 id
468 } else {
469 // SAFETY: as per the guarantees of this function, components and
470 // storages are from the same world as self
471 let explicit_bundle_id = unsafe { self.register_info::<T>(components, storages) };
472
473 // SAFETY: reading from `explicit_bundle_id` and creating new bundle in same time. Its valid because bundle hashmap allow this
474 let id = unsafe {
475 let (ptr, len) = {
476 // SAFETY: `explicit_bundle_id` is valid and defined above
477 let contributed = self
478 .get_unchecked(explicit_bundle_id)
479 .contributed_components();
480 (contributed.as_ptr(), contributed.len())
481 };
482 // SAFETY: this is sound because the contributed_components Vec for explicit_bundle_id will not be accessed mutably as
483 // part of init_dynamic_info. No mutable references will be created and the allocation will remain valid.
484 self.init_dynamic_info(storages, components, core::slice::from_raw_parts(ptr, len))
485 };
486 self.contributed_bundle_ids.insert(TypeId::of::<T>(), id);
487 id
488 }
489 }
490
491 /// # Safety
492 /// A [`BundleInfo`] with the given [`BundleId`] must have been initialized for this instance of `Bundles`.
493 pub(crate) unsafe fn get_unchecked(&self, id: BundleId) -> &BundleInfo {
494 self.bundle_infos.get_unchecked(id.0)
495 }
496
497 /// # Safety
498 /// This [`BundleId`] must have been initialized with a single [`Component`](crate::component::Component)
499 /// (via [`init_component_info`](Self::init_dynamic_info))
500 pub(crate) unsafe fn get_storage_unchecked(&self, id: BundleId) -> StorageType {
501 *self
502 .dynamic_component_storages
503 .get(&id)
504 .debug_checked_unwrap()
505 }
506
507 /// # Safety
508 /// This [`BundleId`] must have been initialized with multiple [`Component`](crate::component::Component)s
509 /// (via [`init_dynamic_info`](Self::init_dynamic_info))
510 pub(crate) unsafe fn get_storages_unchecked(&mut self, id: BundleId) -> &mut Vec<StorageType> {
511 self.dynamic_bundle_storages
512 .get_mut(&id)
513 .debug_checked_unwrap()
514 }
515
516 /// Initializes a new [`BundleInfo`] for a dynamic [`Bundle`].
517 ///
518 /// # Panics
519 ///
520 /// Panics if any of the provided [`ComponentId`]s do not exist in the
521 /// provided [`Components`].
522 pub(crate) fn init_dynamic_info(
523 &mut self,
524 storages: &mut Storages,
525 components: &Components,
526 component_ids: &[ComponentId],
527 ) -> BundleId {
528 let bundle_infos = &mut self.bundle_infos;
529
530 // Use `raw_entry_mut` to avoid cloning `component_ids` to access `Entry`
531 let (_, bundle_id) = self
532 .dynamic_bundle_ids
533 .raw_entry_mut()
534 .from_key(component_ids)
535 .or_insert_with(|| {
536 let (id, storages) = initialize_dynamic_bundle(
537 bundle_infos,
538 storages,
539 components,
540 Vec::from(component_ids),
541 );
542 // SAFETY: The ID always increases when new bundles are added, and so, the ID is unique.
543 unsafe {
544 self.dynamic_bundle_storages
545 .insert_unique_unchecked(id, storages);
546 }
547 (component_ids.into(), id)
548 });
549 *bundle_id
550 }
551
552 /// Initializes a new [`BundleInfo`] for a dynamic [`Bundle`] with single component.
553 ///
554 /// # Panics
555 ///
556 /// Panics if the provided [`ComponentId`] does not exist in the provided [`Components`].
557 pub(crate) fn init_component_info(
558 &mut self,
559 storages: &mut Storages,
560 components: &Components,
561 component_id: ComponentId,
562 ) -> BundleId {
563 let bundle_infos = &mut self.bundle_infos;
564 let bundle_id = self
565 .dynamic_component_bundle_ids
566 .entry(component_id)
567 .or_insert_with(|| {
568 let (id, storage_type) = initialize_dynamic_bundle(
569 bundle_infos,
570 storages,
571 components,
572 vec![component_id],
573 );
574 self.dynamic_component_storages.insert(id, storage_type[0]);
575 id
576 });
577 *bundle_id
578 }
579}
580
581/// Asserts that all components are part of [`Components`]
582/// and initializes a [`BundleInfo`].
583fn initialize_dynamic_bundle(
584 bundle_infos: &mut Vec<BundleInfo>,
585 storages: &mut Storages,
586 components: &Components,
587 component_ids: Vec<ComponentId>,
588) -> (BundleId, Vec<StorageType>) {
589 // Assert component existence
590 let storage_types = component_ids.iter().map(|&id| {
591 components.get_info(id).unwrap_or_else(|| {
592 panic!(
593 "init_dynamic_info called with component id {id:?} which doesn't exist in this world"
594 )
595 }).storage_type()
596 }).collect();
597
598 let id = BundleId(bundle_infos.len());
599 let bundle_info =
600 // SAFETY: `component_ids` are valid as they were just checked
601 unsafe { BundleInfo::new("<dynamic bundle>", storages, components, component_ids, id) };
602 bundle_infos.push(bundle_info);
603
604 (id, storage_types)
605}