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,14component::{15ComponentId, Components, ComponentsRegistrator, RequiredComponentConstructor, StorageType,16Tick,17},18entity::Entity,19query::DebugCheckedUnwrap as _,20storage::{SparseSetIndex, SparseSets, Storages, Table, TableRow},21};2223/// For a specific [`World`], this stores a unique value identifying a type of a registered [`Bundle`].24///25/// [`World`]: crate::world::World26#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]27pub struct BundleId(usize);2829impl 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]34pub fn index(self) -> usize {35self.036}37}3839impl SparseSetIndex for BundleId {40#[inline]41fn sparse_set_index(&self) -> usize {42self.index()43}4445#[inline]46fn get_sparse_set_index(value: usize) -> Self {47Self(value)48}49}5051/// 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.55Replace,56/// Any existing components of a matching type will be left unchanged.57Keep,58}5960/// Stores metadata associated with a specific type of [`Bundle`] for a given [`World`].61///62/// [`World`]: crate::world::World63pub struct BundleInfo {64pub(super) id: BundleId,6566/// The list of all components contributed by the bundle (including Required Components). This is in67/// the order `[EXPLICIT_COMPONENTS][REQUIRED_COMPONENTS]`68///69/// # Safety70/// 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 bundle73/// type writes its components in.74pub(super) contributed_component_ids: Box<[ComponentId]>,7576/// The list of constructors for all required components indirectly contributed by this bundle.77pub(super) required_component_constructors: Box<[RequiredComponentConstructor]>,78}7980impl BundleInfo {81/// Create a new [`BundleInfo`].82///83/// # Safety84///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.87unsafe fn new(88bundle_type_name: &'static str,89storages: &mut Storages,90components: &Components,91mut component_ids: Vec<ComponentId>,92id: BundleId,93) -> BundleInfo {94let explicit_component_ids = component_ids95.iter()96.copied()97.collect::<IndexSet<_, FixedHasher>>();9899// check for duplicates100if explicit_component_ids.len() != component_ids.len() {101// TODO: Replace with `Vec::partition_dedup` once https://github.com/rust-lang/rust/issues/54279 is stabilized102let mut seen = <HashSet<_>>::default();103let mut dups = Vec::new();104for id in component_ids {105if !seen.insert(id) {106dups.push(id);107}108}109110let names = dups111.into_iter()112.map(|id| {113// SAFETY: the caller ensures component_id is valid.114unsafe { components.get_info_unchecked(id).name() }115})116.collect::<Vec<_>>();117118panic!("Bundle {bundle_type_name} has duplicate components: {names:?}");119}120121let mut depth_first_components = IndexMap::<_, _, FixedHasher>::default();122for &component_id in &component_ids {123// SAFETY: caller has verified that all ids are valid124let info = unsafe { components.get_info_unchecked(component_id) };125126for (&required_id, required_component) in &info.required_components().all {127depth_first_components128.entry(required_id)129.or_insert_with(|| required_component.clone());130}131132storages.prepare_component(info);133}134135let required_components = depth_first_components136.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.140storages.prepare_component(unsafe { components.get_info_unchecked(required_id) });141component_ids.push(required_id);142})143.map(|(_, required_component)| required_component.constructor)144.collect::<Box<_>>();145146// SAFETY: The caller ensures that component_ids:147// - is valid for the associated world148// - has had its storage initialized149// - is in the same order as the source bundle type150BundleInfo {151id,152contributed_component_ids: component_ids.into(),153required_component_constructors: required_components,154}155}156157/// Returns a value identifying the associated [`Bundle`] type.158#[inline]159pub const fn id(&self) -> BundleId {160self.id161}162163/// Returns the length of the explicit components part of the [`contributed_components`](Self::contributed_components) list.164#[inline]165pub(super) fn explicit_components_len(&self) -> usize {166self.contributed_component_ids.len() - self.required_component_constructors.len()167}168169/// 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]173pub fn explicit_components(&self) -> &[ComponentId] {174&self.contributed_component_ids[0..self.explicit_components_len()]175}176177/// Returns the [ID](ComponentId) of each Required Component needed by this bundle. This _does not include_ Required Components that are178/// explicitly provided by the bundle.179#[inline]180pub fn required_components(&self) -> &[ComponentId] {181&self.contributed_component_ids[self.explicit_components_len()..]182}183184/// 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]188pub fn contributed_components(&self) -> &[ComponentId] {189&self.contributed_component_ids190}191192/// 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]195pub fn iter_explicit_components(&self) -> impl Iterator<Item = ComponentId> + Clone + '_ {196self.explicit_components().iter().copied()197}198199/// 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]203pub fn iter_contributed_components(&self) -> impl Iterator<Item = ComponentId> + Clone + '_ {204self.contributed_components().iter().copied()205}206207/// Returns an iterator over the [ID](ComponentId) of each Required Component needed by this bundle. This _does not include_ Required Components that are208/// explicitly provided by the bundle.209pub fn iter_required_components(&self) -> impl Iterator<Item = ComponentId> + '_ {210self.required_components().iter().copied()211}212213/// This writes components from a given [`Bundle`] to the given entity.214///215/// # Safety216///217/// `bundle_component_status` must return the "correct" [`ComponentStatus`] for each component218/// 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 status221/// 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 is231/// 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 the235/// `entity`, `bundle` must match this [`BundleInfo`]'s type236///237/// [`apply_effect`]: crate::bundle::DynamicBundle::apply_effect238#[inline]239pub(super) unsafe fn write_components<'a, T: DynamicBundle, S: BundleComponentStatus>(240&self,241table: &mut Table,242sparse_sets: &mut SparseSets,243bundle_component_status: &S,244required_components: impl Iterator<Item = &'a RequiredComponentConstructor>,245entity: Entity,246table_row: TableRow,247change_tick: Tick,248bundle: MovingPtr<'_, T>,249insert_mode: InsertMode,250caller: 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"254let mut bundle_component = 0;255T::get_components(bundle, &mut |storage_type, component_ptr| {256let component_id = *self257.contributed_component_ids258.get_unchecked(bundle_component);259// SAFETY: bundle_component is a valid index for this bundle260let status = unsafe { bundle_component_status.get_status(bundle_component) };261match storage_type {262StorageType::Table => {263let column =264// SAFETY: If component_id is in self.component_ids, BundleInfo::new ensures that265// the target table contains the component.266unsafe { table.get_column_mut(component_id).debug_checked_unwrap() };267match (status, insert_mode) {268(ComponentStatus::Added, _) => {269column.initialize(table_row, component_ptr, change_tick, caller);270}271(ComponentStatus::Existing, InsertMode::Replace) => {272column.replace(table_row, component_ptr, change_tick, caller);273}274(ComponentStatus::Existing, InsertMode::Keep) => {275if let Some(drop_fn) = table.get_drop_for(component_id) {276drop_fn(component_ptr);277}278}279}280}281StorageType::SparseSet => {282let sparse_set =283// SAFETY: If component_id is in self.component_ids, BundleInfo::new ensures that284// a sparse set exists for the component.285unsafe { sparse_sets.get_mut(component_id).debug_checked_unwrap() };286match (status, insert_mode) {287(ComponentStatus::Added, _) | (_, InsertMode::Replace) => {288sparse_set.insert(entity, component_ptr, change_tick, caller);289}290(ComponentStatus::Existing, InsertMode::Keep) => {291if let Some(drop_fn) = sparse_set.get_drop() {292drop_fn(component_ptr);293}294}295}296}297}298bundle_component += 1;299});300301for required_component in required_components {302required_component.initialize(303table,304sparse_sets,305change_tick,306table_row,307entity,308caller,309);310}311}312313/// Internal method to initialize a required component from an [`OwningPtr`]. This should ultimately be called314/// in the context of [`BundleInfo::write_components`], via [`RequiredComponentConstructor::initialize`].315///316/// # Safety317///318/// `component_ptr` must point to a required component value that matches the given `component_id`. The `storage_type` must match319/// the type associated with `component_id`. The `entity` and `table_row` must correspond to an entity with an uninitialized320/// 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.325pub(crate) unsafe fn initialize_required_component(326table: &mut Table,327sparse_sets: &mut SparseSets,328change_tick: Tick,329table_row: TableRow,330entity: Entity,331component_id: ComponentId,332storage_type: StorageType,333component_ptr: OwningPtr,334caller: MaybeLocation,335) {336{337match storage_type {338StorageType::Table => {339let column =340// SAFETY: If component_id is in required_components, BundleInfo::new requires that341// the target table contains the component.342unsafe { table.get_column_mut(component_id).debug_checked_unwrap() };343column.initialize(table_row, component_ptr, change_tick, caller);344}345StorageType::SparseSet => {346let sparse_set =347// SAFETY: If component_id is in required_components, BundleInfo::new requires that348// a sparse set exists for the component.349unsafe { sparse_sets.get_mut(component_id).debug_checked_unwrap() };350sparse_set.insert(entity, component_ptr, change_tick, caller);351}352}353}354}355}356357/// The type of archetype move (or lack thereof) that will result from a bundle358/// 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.362SameArchetype,363/// If only [`sparse set`](StorageType::SparseSet) components are being added,364/// the entity's archetype will change while keeping the same table.365NewArchetypeSameTable { 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.368NewArchetypeNewTable {369new_archetype: NonNull<Archetype>,370new_table: NonNull<Table>,371},372}373374/// Metadata for bundles. Stores a [`BundleInfo`] for each type of [`Bundle`] in a given world.375#[derive(Default)]376pub struct Bundles {377bundle_infos: Vec<BundleInfo>,378/// Cache static [`BundleId`]379bundle_ids: TypeIdMap<BundleId>,380/// Cache bundles, which contains both explicit and required components of [`Bundle`]381contributed_bundle_ids: TypeIdMap<BundleId>,382/// Cache dynamic [`BundleId`] with multiple components383dynamic_bundle_ids: HashMap<Box<[ComponentId]>, BundleId>,384dynamic_bundle_storages: HashMap<BundleId, Vec<StorageType>>,385/// Cache optimized dynamic [`BundleId`] with single component386dynamic_component_bundle_ids: HashMap<ComponentId, BundleId>,387dynamic_component_storages: HashMap<BundleId, StorageType>,388}389390impl Bundles {391/// The total number of [`Bundle`] registered in [`Storages`].392pub fn len(&self) -> usize {393self.bundle_infos.len()394}395396/// Returns true if no [`Bundle`] registered in [`Storages`].397pub fn is_empty(&self) -> bool {398self.len() == 0399}400401/// Iterate over [`BundleInfo`].402pub fn iter(&self) -> impl Iterator<Item = &BundleInfo> {403self.bundle_infos.iter()404}405406/// Gets the metadata associated with a specific type of bundle.407/// Returns `None` if the bundle is not registered with the world.408#[inline]409pub fn get(&self, bundle_id: BundleId) -> Option<&BundleInfo> {410self.bundle_infos.get(bundle_id.index())411}412413/// 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]417pub fn get_id(&self, type_id: TypeId) -> Option<BundleId> {418self.bundle_ids.get(&type_id).cloned()419}420421/// Registers a new [`BundleInfo`] for a statically known type.422///423/// Also registers all the components in the bundle.424///425/// # Safety426///427/// `components` and `storages` must be from the same [`World`] as `self`.428///429/// [`World`]: crate::world::World430#[deny(unsafe_op_in_unsafe_fn)]431pub(crate) unsafe fn register_info<T: Bundle>(432&mut self,433components: &mut ComponentsRegistrator,434storages: &mut Storages,435) -> BundleId {436let bundle_infos = &mut self.bundle_infos;437*self.bundle_ids.entry(TypeId::of::<T>()).or_insert_with(|| {438let mut component_ids= Vec::new();439T::component_ids(components, &mut |id| component_ids.push(id));440let id = BundleId(bundle_infos.len());441let bundle_info =442// SAFETY: T::component_id ensures:443// - its info was created444// - appropriate storage for it has been initialized.445// - it was created in the same order as the components in T446unsafe { BundleInfo::new(core::any::type_name::<T>(), storages, components, component_ids, id) };447bundle_infos.push(bundle_info);448id449})450}451452/// Registers a new [`BundleInfo`], which contains both explicit and required components for a statically known type.453///454/// Also registers all the components in the bundle.455///456/// # Safety457///458/// `components` and `storages` must be from the same [`World`] as `self`.459///460/// [`World`]: crate::world::World461#[deny(unsafe_op_in_unsafe_fn)]462pub(crate) unsafe fn register_contributed_bundle_info<T: Bundle>(463&mut self,464components: &mut ComponentsRegistrator,465storages: &mut Storages,466) -> BundleId {467if let Some(id) = self.contributed_bundle_ids.get(&TypeId::of::<T>()).cloned() {468id469} else {470// SAFETY: as per the guarantees of this function, components and471// storages are from the same world as self472let explicit_bundle_id = unsafe { self.register_info::<T>(components, storages) };473474// SAFETY: reading from `explicit_bundle_id` and creating new bundle in same time. Its valid because bundle hashmap allow this475let id = unsafe {476let (ptr, len) = {477// SAFETY: `explicit_bundle_id` is valid and defined above478let contributed = self479.get_unchecked(explicit_bundle_id)480.contributed_components();481(contributed.as_ptr(), contributed.len())482};483// SAFETY: this is sound because the contributed_components Vec for explicit_bundle_id will not be accessed mutably as484// part of init_dynamic_info. No mutable references will be created and the allocation will remain valid.485self.init_dynamic_info(storages, components, core::slice::from_raw_parts(ptr, len))486};487self.contributed_bundle_ids.insert(TypeId::of::<T>(), id);488id489}490}491492/// # Safety493/// A [`BundleInfo`] with the given [`BundleId`] must have been initialized for this instance of `Bundles`.494pub(crate) unsafe fn get_unchecked(&self, id: BundleId) -> &BundleInfo {495self.bundle_infos.get_unchecked(id.0)496}497498/// # Safety499/// This [`BundleId`] must have been initialized with a single [`Component`](crate::component::Component)500/// (via [`init_component_info`](Self::init_dynamic_info))501pub(crate) unsafe fn get_storage_unchecked(&self, id: BundleId) -> StorageType {502*self503.dynamic_component_storages504.get(&id)505.debug_checked_unwrap()506}507508/// # Safety509/// This [`BundleId`] must have been initialized with multiple [`Component`](crate::component::Component)s510/// (via [`init_dynamic_info`](Self::init_dynamic_info))511pub(crate) unsafe fn get_storages_unchecked(&mut self, id: BundleId) -> &mut Vec<StorageType> {512self.dynamic_bundle_storages513.get_mut(&id)514.debug_checked_unwrap()515}516517/// Initializes a new [`BundleInfo`] for a dynamic [`Bundle`].518///519/// # Panics520///521/// Panics if any of the provided [`ComponentId`]s do not exist in the522/// provided [`Components`].523pub(crate) fn init_dynamic_info(524&mut self,525storages: &mut Storages,526components: &Components,527component_ids: &[ComponentId],528) -> BundleId {529let bundle_infos = &mut self.bundle_infos;530531// Use `raw_entry_mut` to avoid cloning `component_ids` to access `Entry`532let (_, bundle_id) = self533.dynamic_bundle_ids534.raw_entry_mut()535.from_key(component_ids)536.or_insert_with(|| {537let (id, storages) = initialize_dynamic_bundle(538bundle_infos,539storages,540components,541Vec::from(component_ids),542);543// SAFETY: The ID always increases when new bundles are added, and so, the ID is unique.544unsafe {545self.dynamic_bundle_storages546.insert_unique_unchecked(id, storages);547}548(component_ids.into(), id)549});550*bundle_id551}552553/// Initializes a new [`BundleInfo`] for a dynamic [`Bundle`] with single component.554///555/// # Panics556///557/// Panics if the provided [`ComponentId`] does not exist in the provided [`Components`].558pub(crate) fn init_component_info(559&mut self,560storages: &mut Storages,561components: &Components,562component_id: ComponentId,563) -> BundleId {564let bundle_infos = &mut self.bundle_infos;565let bundle_id = self566.dynamic_component_bundle_ids567.entry(component_id)568.or_insert_with(|| {569let (id, storage_type) = initialize_dynamic_bundle(570bundle_infos,571storages,572components,573vec![component_id],574);575self.dynamic_component_storages.insert(id, storage_type[0]);576id577});578*bundle_id579}580}581582/// Asserts that all components are part of [`Components`]583/// and initializes a [`BundleInfo`].584fn initialize_dynamic_bundle(585bundle_infos: &mut Vec<BundleInfo>,586storages: &mut Storages,587components: &Components,588component_ids: Vec<ComponentId>,589) -> (BundleId, Vec<StorageType>) {590// Assert component existence591let storage_types = component_ids.iter().map(|&id| {592components.get_info(id).unwrap_or_else(|| {593panic!(594"init_dynamic_info called with component id {id:?} which doesn't exist in this world"595)596}).storage_type()597}).collect();598599let id = BundleId(bundle_infos.len());600let bundle_info =601// SAFETY: `component_ids` are valid as they were just checked602unsafe { BundleInfo::new("<dynamic bundle>", storages, components, component_ids, id) };603bundle_infos.push(bundle_info);604605(id, storage_types)606}607608609