use alloc::{boxed::Box, vec, vec::Vec};1use bevy_platform::{2collections::{HashMap, HashSet},3hash::FixedHasher,4};5use bevy_ptr::{MovingPtr, OwningPtr};6use bevy_utils::TypeIdMap;7use core::{any::TypeId, ptr::NonNull};8use indexmap::{IndexMap, IndexSet};910use crate::{11archetype::{Archetype, BundleComponentStatus, ComponentStatus},12bundle::{Bundle, DynamicBundle},13change_detection::{MaybeLocation, Tick},14component::{15ComponentId, Components, ComponentsRegistrator, RequiredComponentConstructor, StorageType,16},17entity::Entity,18query::DebugCheckedUnwrap as _,19storage::{SparseSetIndex, SparseSets, Storages, Table, TableRow},20};2122/// For a specific [`World`], this stores a unique value identifying a type of a registered [`Bundle`].23///24/// [`World`]: crate::world::World25#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]26pub struct BundleId(usize);2728impl BundleId {29/// Returns the index of the associated [`Bundle`] type.30///31/// Note that this is unique per-world, and should not be reused across them.32#[inline]33pub fn index(self) -> usize {34self.035}36}3738impl SparseSetIndex for BundleId {39#[inline]40fn sparse_set_index(&self) -> usize {41self.index()42}4344#[inline]45fn get_sparse_set_index(value: usize) -> Self {46Self(value)47}48}4950/// What to do on insertion if a component already exists.51#[derive(Clone, Copy, Eq, PartialEq)]52pub enum InsertMode {53/// Any existing components of a matching type will be overwritten.54Replace,55/// Any existing components of a matching type will be left unchanged.56Keep,57}5859/// Stores metadata associated with a specific type of [`Bundle`] for a given [`World`].60///61/// [`World`]: crate::world::World62pub struct BundleInfo {63pub(super) id: BundleId,6465/// The list of all components contributed by the bundle (including Required Components). This is in66/// the order `[EXPLICIT_COMPONENTS][REQUIRED_COMPONENTS]`67///68/// # Safety69/// Every ID in this list must be valid within the World that owns the [`BundleInfo`],70/// must have its storage initialized (i.e. columns created in tables, sparse set created),71/// and the range (0..`explicit_components_len`) must be in the same order as the source bundle72/// type writes its components in.73pub(super) contributed_component_ids: Box<[ComponentId]>,7475/// The list of constructors for all required components indirectly contributed by this bundle.76pub(super) required_component_constructors: Box<[RequiredComponentConstructor]>,77}7879impl BundleInfo {80/// Create a new [`BundleInfo`].81///82/// # Safety83///84/// Every ID in `component_ids` must be valid within the World that owns the `BundleInfo`85/// and must be in the same order as the source bundle type writes its components in.86unsafe fn new(87bundle_type_name: &'static str,88storages: &mut Storages,89components: &Components,90mut component_ids: Vec<ComponentId>,91id: BundleId,92) -> BundleInfo {93let explicit_component_ids = component_ids94.iter()95.copied()96.collect::<IndexSet<_, FixedHasher>>();9798// check for duplicates99if explicit_component_ids.len() != component_ids.len() {100// TODO: Replace with `Vec::partition_dedup` once https://github.com/rust-lang/rust/issues/54279 is stabilized101let mut seen = <HashSet<_>>::default();102let mut dups = Vec::new();103for id in component_ids {104if !seen.insert(id) {105dups.push(id);106}107}108109let names = dups110.into_iter()111.map(|id| {112// SAFETY: the caller ensures component_id is valid.113unsafe { components.get_info_unchecked(id).name() }114})115.collect::<Vec<_>>();116117panic!("Bundle {bundle_type_name} has duplicate components: {names:?}");118}119120let mut depth_first_components = IndexMap::<_, _, FixedHasher>::default();121for &component_id in &component_ids {122// SAFETY: caller has verified that all ids are valid123let info = unsafe { components.get_info_unchecked(component_id) };124125for (&required_id, required_component) in &info.required_components().all {126depth_first_components127.entry(required_id)128.or_insert_with(|| required_component.clone());129}130131storages.prepare_component(info);132}133134let required_components = depth_first_components135.into_iter()136.filter(|&(required_id, _)| !explicit_component_ids.contains(&required_id))137.inspect(|&(required_id, _)| {138// SAFETY: These ids came out of the passed `components`, so they must be valid.139storages.prepare_component(unsafe { components.get_info_unchecked(required_id) });140component_ids.push(required_id);141})142.map(|(_, required_component)| required_component.constructor)143.collect::<Box<_>>();144145// SAFETY: The caller ensures that component_ids:146// - is valid for the associated world147// - has had its storage initialized148// - is in the same order as the source bundle type149BundleInfo {150id,151contributed_component_ids: component_ids.into(),152required_component_constructors: required_components,153}154}155156/// Returns a value identifying the associated [`Bundle`] type.157#[inline]158pub const fn id(&self) -> BundleId {159self.id160}161162/// Returns the length of the explicit components part of the [`contributed_components`](Self::contributed_components) list.163#[inline]164pub(super) fn explicit_components_len(&self) -> usize {165self.contributed_component_ids.len() - self.required_component_constructors.len()166}167168/// Returns the [ID](ComponentId) of each component explicitly defined in this bundle (ex: Required Components are excluded).169///170/// For all components contributed by this bundle (including Required Components), see [`BundleInfo::contributed_components`]171#[inline]172pub fn explicit_components(&self) -> &[ComponentId] {173&self.contributed_component_ids[0..self.explicit_components_len()]174}175176/// Returns the [ID](ComponentId) of each Required Component needed by this bundle. This _does not include_ Required Components that are177/// explicitly provided by the bundle.178#[inline]179pub fn required_components(&self) -> &[ComponentId] {180&self.contributed_component_ids[self.explicit_components_len()..]181}182183/// Returns the [ID](ComponentId) of each component contributed by this bundle. This includes Required Components.184///185/// For only components explicitly defined in this bundle, see [`BundleInfo::explicit_components`]186#[inline]187pub fn contributed_components(&self) -> &[ComponentId] {188&self.contributed_component_ids189}190191/// Returns an iterator over the [ID](ComponentId) of each component explicitly defined in this bundle (ex: this excludes Required Components).192/// To iterate all components contributed by this bundle (including Required Components), see [`BundleInfo::iter_contributed_components`]193#[inline]194pub fn iter_explicit_components(&self) -> impl Iterator<Item = ComponentId> + Clone + '_ {195self.explicit_components().iter().copied()196}197198/// Returns an iterator over the [ID](ComponentId) of each component contributed by this bundle. This includes Required Components.199///200/// To iterate only components explicitly defined in this bundle, see [`BundleInfo::iter_explicit_components`]201#[inline]202pub fn iter_contributed_components(&self) -> impl Iterator<Item = ComponentId> + Clone + '_ {203self.contributed_components().iter().copied()204}205206/// Returns an iterator over the [ID](ComponentId) of each Required Component needed by this bundle. This _does not include_ Required Components that are207/// explicitly provided by the bundle.208pub fn iter_required_components(&self) -> impl Iterator<Item = ComponentId> + '_ {209self.required_components().iter().copied()210}211212/// This writes components from a given [`Bundle`] to the given entity.213///214/// # Safety215///216/// `bundle_component_status` must return the "correct" [`ComponentStatus`] for each component217/// in the [`Bundle`], with respect to the entity's original archetype (prior to the bundle being added).218///219/// For example, if the original archetype already has `ComponentA` and `T` also has `ComponentA`, the status220/// should be `Existing`. If the original archetype does not have `ComponentA`, the status should be `Added`.221///222/// When "inserting" a bundle into an existing entity, [`ArchetypeAfterBundleInsert`](crate::archetype::SpawnBundleStatus)223/// should be used, which will report `Added` vs `Existing` status based on the current archetype's structure.224///225/// When spawning a bundle, [`SpawnBundleStatus`](crate::archetype::SpawnBundleStatus) can be used instead,226/// which removes the need to look up the [`ArchetypeAfterBundleInsert`](crate::archetype::ArchetypeAfterBundleInsert)227/// in the archetype graph, which requires ownership of the entity's current archetype.228///229/// Regardless of how this is used, [`apply_effect`] must be called at most once on `bundle` after this function is230/// called if `T::Effect: !NoBundleEffect` before returning to user-space safe code before returning to user-space safe code.231/// This is currently only doable via use of [`MovingPtr::partial_move`].232///233/// `table` must be the "new" table for `entity`. `table_row` must have space allocated for the234/// `entity`, `bundle` must match this [`BundleInfo`]'s type235///236/// [`apply_effect`]: crate::bundle::DynamicBundle::apply_effect237#[inline]238pub(super) unsafe fn write_components<'a, T: DynamicBundle, S: BundleComponentStatus>(239&self,240table: &mut Table,241sparse_sets: &mut SparseSets,242bundle_component_status: &S,243required_components: impl Iterator<Item = &'a RequiredComponentConstructor>,244entity: Entity,245table_row: TableRow,246change_tick: Tick,247bundle: MovingPtr<'_, T>,248insert_mode: InsertMode,249caller: MaybeLocation,250) {251// NOTE: get_components calls this closure on each component in "bundle order".252// bundle_info.component_ids are also in "bundle order"253let mut bundle_component = 0;254T::get_components(bundle, &mut |storage_type, component_ptr| {255let component_id = *self256.contributed_component_ids257.get_unchecked(bundle_component);258// SAFETY: bundle_component is a valid index for this bundle259let status = unsafe { bundle_component_status.get_status(bundle_component) };260match storage_type {261StorageType::Table => {262let column =263// SAFETY: If component_id is in self.component_ids, BundleInfo::new ensures that264// the target table contains the component.265unsafe { table.get_column_mut(component_id).debug_checked_unwrap() };266match (status, insert_mode) {267(ComponentStatus::Added, _) => {268column.initialize(table_row, component_ptr, change_tick, caller);269}270(ComponentStatus::Existing, InsertMode::Replace) => {271column.replace(table_row, component_ptr, change_tick, caller);272}273(ComponentStatus::Existing, InsertMode::Keep) => {274if let Some(drop_fn) = table.get_drop_for(component_id) {275drop_fn(component_ptr);276}277}278}279}280StorageType::SparseSet => {281let sparse_set =282// SAFETY: If component_id is in self.component_ids, BundleInfo::new ensures that283// a sparse set exists for the component.284unsafe { sparse_sets.get_mut(component_id).debug_checked_unwrap() };285match (status, insert_mode) {286(ComponentStatus::Added, _) | (_, InsertMode::Replace) => {287sparse_set.insert(entity, component_ptr, change_tick, caller);288}289(ComponentStatus::Existing, InsertMode::Keep) => {290if let Some(drop_fn) = sparse_set.get_drop() {291drop_fn(component_ptr);292}293}294}295}296}297bundle_component += 1;298});299300for required_component in required_components {301required_component.initialize(302table,303sparse_sets,304change_tick,305table_row,306entity,307caller,308);309}310}311312/// Internal method to initialize a required component from an [`OwningPtr`]. This should ultimately be called313/// in the context of [`BundleInfo::write_components`], via [`RequiredComponentConstructor::initialize`].314///315/// # Safety316///317/// `component_ptr` must point to a required component value that matches the given `component_id`. The `storage_type` must match318/// the type associated with `component_id`. The `entity` and `table_row` must correspond to an entity with an uninitialized319/// component matching `component_id`.320///321/// This method _should not_ be called outside of [`BundleInfo::write_components`].322/// For more information, read the [`BundleInfo::write_components`] safety docs.323/// This function inherits the safety requirements defined there.324pub(crate) unsafe fn initialize_required_component(325table: &mut Table,326sparse_sets: &mut SparseSets,327change_tick: Tick,328table_row: TableRow,329entity: Entity,330component_id: ComponentId,331storage_type: StorageType,332component_ptr: OwningPtr,333caller: MaybeLocation,334) {335{336match storage_type {337StorageType::Table => {338let column =339// SAFETY: If component_id is in required_components, BundleInfo::new requires that340// the target table contains the component.341unsafe { table.get_column_mut(component_id).debug_checked_unwrap() };342column.initialize(table_row, component_ptr, change_tick, caller);343}344StorageType::SparseSet => {345let sparse_set =346// SAFETY: If component_id is in required_components, BundleInfo::new requires that347// a sparse set exists for the component.348unsafe { sparse_sets.get_mut(component_id).debug_checked_unwrap() };349sparse_set.insert(entity, component_ptr, change_tick, caller);350}351}352}353}354}355356/// The type of archetype move (or lack thereof) that will result from a bundle357/// being inserted into an entity.358pub(crate) enum ArchetypeMoveType {359/// If the entity already has all of the components that are being inserted,360/// its archetype won't change.361SameArchetype,362/// If only [`sparse set`](StorageType::SparseSet) components are being added,363/// the entity's archetype will change while keeping the same table.364NewArchetypeSameTable { new_archetype: NonNull<Archetype> },365/// If any [`table-stored`](StorageType::Table) components are being added,366/// both the entity's archetype and table will change.367NewArchetypeNewTable {368new_archetype: NonNull<Archetype>,369new_table: NonNull<Table>,370},371}372373/// Metadata for bundles. Stores a [`BundleInfo`] for each type of [`Bundle`] in a given world.374#[derive(Default)]375pub struct Bundles {376bundle_infos: Vec<BundleInfo>,377/// Cache static [`BundleId`]378bundle_ids: TypeIdMap<BundleId>,379/// Cache bundles, which contains both explicit and required components of [`Bundle`]380contributed_bundle_ids: TypeIdMap<BundleId>,381/// Cache dynamic [`BundleId`] with multiple components382dynamic_bundle_ids: HashMap<Box<[ComponentId]>, BundleId>,383dynamic_bundle_storages: HashMap<BundleId, Vec<StorageType>>,384/// Cache optimized dynamic [`BundleId`] with single component385dynamic_component_bundle_ids: HashMap<ComponentId, BundleId>,386dynamic_component_storages: HashMap<BundleId, StorageType>,387}388389impl Bundles {390/// The total number of [`Bundle`] registered in [`Storages`].391pub fn len(&self) -> usize {392self.bundle_infos.len()393}394395/// Returns true if no [`Bundle`] registered in [`Storages`].396pub fn is_empty(&self) -> bool {397self.len() == 0398}399400/// Iterate over [`BundleInfo`].401pub fn iter(&self) -> impl Iterator<Item = &BundleInfo> {402self.bundle_infos.iter()403}404405/// Gets the metadata associated with a specific type of bundle.406/// Returns `None` if the bundle is not registered with the world.407#[inline]408pub fn get(&self, bundle_id: BundleId) -> Option<&BundleInfo> {409self.bundle_infos.get(bundle_id.index())410}411412/// Gets the value identifying a specific type of bundle.413/// Returns `None` if the bundle does not exist in the world,414/// or if `type_id` does not correspond to a type of bundle.415#[inline]416pub fn get_id(&self, type_id: TypeId) -> Option<BundleId> {417self.bundle_ids.get(&type_id).cloned()418}419420/// Registers a new [`BundleInfo`] for a statically known type.421///422/// Also registers all the components in the bundle.423///424/// # Safety425///426/// `components` and `storages` must be from the same [`World`] as `self`.427///428/// [`World`]: crate::world::World429#[deny(unsafe_op_in_unsafe_fn)]430pub(crate) unsafe fn register_info<T: Bundle>(431&mut self,432components: &mut ComponentsRegistrator,433storages: &mut Storages,434) -> BundleId {435let bundle_infos = &mut self.bundle_infos;436*self.bundle_ids.entry(TypeId::of::<T>()).or_insert_with(|| {437let component_ids = T::component_ids(components).collect::<Vec<_>>();438let id = BundleId(bundle_infos.len());439let bundle_info =440// SAFETY: T::component_id ensures:441// - its info was created442// - appropriate storage for it has been initialized.443// - it was created in the same order as the components in T444unsafe { BundleInfo::new(core::any::type_name::<T>(), storages, components, component_ids, id) };445bundle_infos.push(bundle_info);446id447})448}449450/// Registers a new [`BundleInfo`], which contains both explicit and required components for a statically known type.451///452/// Also registers all the components in the bundle.453///454/// # Safety455///456/// `components` and `storages` must be from the same [`World`] as `self`.457///458/// [`World`]: crate::world::World459#[deny(unsafe_op_in_unsafe_fn)]460pub(crate) unsafe fn register_contributed_bundle_info<T: Bundle>(461&mut self,462components: &mut ComponentsRegistrator,463storages: &mut Storages,464) -> BundleId {465if let Some(id) = self.contributed_bundle_ids.get(&TypeId::of::<T>()).cloned() {466id467} else {468// SAFETY: as per the guarantees of this function, components and469// storages are from the same world as self470let explicit_bundle_id = unsafe { self.register_info::<T>(components, storages) };471472// SAFETY: reading from `explicit_bundle_id` and creating new bundle in same time. Its valid because bundle hashmap allow this473let id = unsafe {474let (ptr, len) = {475// SAFETY: `explicit_bundle_id` is valid and defined above476let contributed = self477.get_unchecked(explicit_bundle_id)478.contributed_components();479(contributed.as_ptr(), contributed.len())480};481// SAFETY: this is sound because the contributed_components Vec for explicit_bundle_id will not be accessed mutably as482// part of init_dynamic_info. No mutable references will be created and the allocation will remain valid.483self.init_dynamic_info(storages, components, core::slice::from_raw_parts(ptr, len))484};485self.contributed_bundle_ids.insert(TypeId::of::<T>(), id);486id487}488}489490/// # Safety491/// A [`BundleInfo`] with the given [`BundleId`] must have been initialized for this instance of `Bundles`.492pub(crate) unsafe fn get_unchecked(&self, id: BundleId) -> &BundleInfo {493self.bundle_infos.get_unchecked(id.0)494}495496/// # Safety497/// This [`BundleId`] must have been initialized with a single [`Component`](crate::component::Component)498/// (via [`init_component_info`](Self::init_dynamic_info))499pub(crate) unsafe fn get_storage_unchecked(&self, id: BundleId) -> StorageType {500*self501.dynamic_component_storages502.get(&id)503.debug_checked_unwrap()504}505506/// # Safety507/// This [`BundleId`] must have been initialized with multiple [`Component`](crate::component::Component)s508/// (via [`init_dynamic_info`](Self::init_dynamic_info))509pub(crate) unsafe fn get_storages_unchecked(&mut self, id: BundleId) -> &mut Vec<StorageType> {510self.dynamic_bundle_storages511.get_mut(&id)512.debug_checked_unwrap()513}514515/// Initializes a new [`BundleInfo`] for a dynamic [`Bundle`].516///517/// # Panics518///519/// Panics if any of the provided [`ComponentId`]s do not exist in the520/// provided [`Components`].521pub(crate) fn init_dynamic_info(522&mut self,523storages: &mut Storages,524components: &Components,525component_ids: &[ComponentId],526) -> BundleId {527let bundle_infos = &mut self.bundle_infos;528529// Use `raw_entry_mut` to avoid cloning `component_ids` to access `Entry`530let (_, bundle_id) = self531.dynamic_bundle_ids532.raw_entry_mut()533.from_key(component_ids)534.or_insert_with(|| {535let (id, storages) = initialize_dynamic_bundle(536bundle_infos,537storages,538components,539Vec::from(component_ids),540);541// SAFETY: The ID always increases when new bundles are added, and so, the ID is unique.542unsafe {543self.dynamic_bundle_storages544.insert_unique_unchecked(id, storages);545}546(component_ids.into(), id)547});548*bundle_id549}550551/// Initializes a new [`BundleInfo`] for a dynamic [`Bundle`] with single component.552///553/// # Panics554///555/// Panics if the provided [`ComponentId`] does not exist in the provided [`Components`].556pub(crate) fn init_component_info(557&mut self,558storages: &mut Storages,559components: &Components,560component_id: ComponentId,561) -> BundleId {562let bundle_infos = &mut self.bundle_infos;563let bundle_id = self564.dynamic_component_bundle_ids565.entry(component_id)566.or_insert_with(|| {567let (id, storage_type) = initialize_dynamic_bundle(568bundle_infos,569storages,570components,571vec![component_id],572);573self.dynamic_component_storages.insert(id, storage_type[0]);574id575});576*bundle_id577}578}579580/// Asserts that all components are part of [`Components`]581/// and initializes a [`BundleInfo`].582fn initialize_dynamic_bundle(583bundle_infos: &mut Vec<BundleInfo>,584storages: &mut Storages,585components: &Components,586component_ids: Vec<ComponentId>,587) -> (BundleId, Vec<StorageType>) {588// Assert component existence589let storage_types = component_ids.iter().map(|&id| {590components.get_info(id).unwrap_or_else(|| {591panic!(592"init_dynamic_info called with component id {id:?} which doesn't exist in this world"593)594}).storage_type()595}).collect();596597let id = BundleId(bundle_infos.len());598let bundle_info =599// SAFETY: `component_ids` are valid as they were just checked600unsafe { BundleInfo::new("<dynamic bundle>", storages, components, component_ids, id) };601bundle_infos.push(bundle_info);602603(id, storage_types)604}605606607