diff --git a/crates/bevy_ecs/macros/src/component.rs b/crates/bevy_ecs/macros/src/component.rs index a7bfb5845cce7..5d96c9a732889 100644 --- a/crates/bevy_ecs/macros/src/component.rs +++ b/crates/bevy_ecs/macros/src/component.rs @@ -228,7 +228,7 @@ pub fn derive_component(input: TokenStream) -> TokenStream { type Mutability = #mutable_type; fn register_required_components( requiree: #bevy_ecs_path::component::ComponentId, - components: &mut #bevy_ecs_path::component::Components, + components: &mut #bevy_ecs_path::component::ComponentsRegistrator, required_components: &mut #bevy_ecs_path::component::RequiredComponents, inheritance_depth: u16, recursion_check_stack: &mut #bevy_ecs_path::__macro_exports::Vec<#bevy_ecs_path::component::ComponentId> diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index f61889651df1e..919711ef496e4 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -134,7 +134,7 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream { #[allow(deprecated)] unsafe impl #impl_generics #ecs_path::bundle::Bundle for #struct_name #ty_generics #where_clause { fn component_ids( - components: &mut #ecs_path::component::Components, + components: &mut #ecs_path::component::ComponentsRegistrator, ids: &mut impl FnMut(#ecs_path::component::ComponentId) ){ #(#field_component_ids)* @@ -148,7 +148,7 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream { } fn register_required_components( - components: &mut #ecs_path::component::Components, + components: &mut #ecs_path::component::ComponentsRegistrator, required_components: &mut #ecs_path::component::RequiredComponents ){ #(#field_required_components)* diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index e91a95a731139..62bf13d3fac00 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -11,8 +11,8 @@ use crate::{ }, change_detection::MaybeLocation, component::{ - Component, ComponentId, Components, RequiredComponentConstructor, RequiredComponents, - StorageType, Tick, + Component, ComponentId, Components, ComponentsRegistrator, RequiredComponentConstructor, + RequiredComponents, StorageType, Tick, }, entity::{Entities, Entity, EntityLocation}, observer::Observers, @@ -31,7 +31,7 @@ use variadics_please::all_tuples; /// The `Bundle` trait enables insertion and removal of [`Component`]s from an entity. /// -/// Implementors of the `Bundle` trait are called 'bundles'. +/// Implementers of the `Bundle` trait are called 'bundles'. /// /// Each bundle represents a static set of [`Component`] types. /// Currently, bundles can only contain one of each [`Component`], and will @@ -72,7 +72,7 @@ use variadics_please::all_tuples; /// That is, if the entity does not have all the components of the bundle, those /// which are present will be removed. /// -/// # Implementors +/// # Implementers /// /// Every type which implements [`Component`] also implements `Bundle`, since /// [`Component`] types can be added to or removed from an entity. @@ -151,14 +151,14 @@ use variadics_please::all_tuples; pub unsafe trait Bundle: DynamicBundle + Send + Sync + 'static { /// Gets this [`Bundle`]'s component ids, in the order of this bundle's [`Component`]s #[doc(hidden)] - fn component_ids(components: &mut Components, ids: &mut impl FnMut(ComponentId)); + fn component_ids(components: &mut ComponentsRegistrator, ids: &mut impl FnMut(ComponentId)); /// Gets this [`Bundle`]'s component ids. This will be [`None`] if the component has not been registered. fn get_component_ids(components: &Components, ids: &mut impl FnMut(Option)); /// Registers components that are required by the components in this [`Bundle`]. fn register_required_components( - _components: &mut Components, + _components: &mut ComponentsRegistrator, _required_components: &mut RequiredComponents, ); } @@ -223,12 +223,12 @@ pub trait BundleEffect { // - `Bundle::component_ids` calls `ids` for C's component id (and nothing else) // - `Bundle::get_components` is called exactly once for C and passes the component's storage type based on its associated constant. unsafe impl Bundle for C { - fn component_ids(components: &mut Components, ids: &mut impl FnMut(ComponentId)) { + fn component_ids(components: &mut ComponentsRegistrator, ids: &mut impl FnMut(ComponentId)) { ids(components.register_component::()); } fn register_required_components( - components: &mut Components, + components: &mut ComponentsRegistrator, required_components: &mut RequiredComponents, ) { let component_id = components.register_component::(); @@ -288,7 +288,7 @@ macro_rules! tuple_impl { // - `Bundle::get_components` is called exactly once for each member. Relies on the above implementation to pass the correct // `StorageType` into the callback. unsafe impl<$($name: Bundle),*> Bundle for ($($name,)*) { - fn component_ids(components: &mut Components, ids: &mut impl FnMut(ComponentId)){ + fn component_ids(components: &mut ComponentsRegistrator, ids: &mut impl FnMut(ComponentId)){ $(<$name as Bundle>::component_ids(components, ids);)* } @@ -297,7 +297,7 @@ macro_rules! tuple_impl { } fn register_required_components( - components: &mut Components, + components: &mut ComponentsRegistrator, required_components: &mut RequiredComponents, ) { $(<$name as Bundle>::register_required_components(components, required_components);)* @@ -999,9 +999,12 @@ impl<'w> BundleInserter<'w> { archetype_id: ArchetypeId, change_tick: Tick, ) -> Self { + // SAFETY: These come from the same world. `world.components_registrator` can't be used since we borrow other fields too. + let mut registrator = + unsafe { ComponentsRegistrator::new(&mut world.components, &mut world.component_ids) }; let bundle_id = world .bundles - .register_info::(&mut world.components, &mut world.storages); + .register_info::(&mut registrator, &mut world.storages); // SAFETY: We just ensured this bundle exists unsafe { Self::new_with_id(world, archetype_id, bundle_id, change_tick) } } @@ -1369,9 +1372,12 @@ pub(crate) struct BundleSpawner<'w> { impl<'w> BundleSpawner<'w> { #[inline] pub fn new(world: &'w mut World, change_tick: Tick) -> Self { + // SAFETY: These come from the same world. `world.components_registrator` can't be used since we borrow other fields too. + let mut registrator = + unsafe { ComponentsRegistrator::new(&mut world.components, &mut world.component_ids) }; let bundle_id = world .bundles - .register_info::(&mut world.components, &mut world.storages); + .register_info::(&mut registrator, &mut world.storages); // SAFETY: we initialized this bundle_id in `init_info` unsafe { Self::new_with_id(world, bundle_id, change_tick) } } @@ -1574,7 +1580,7 @@ impl Bundles { /// Also registers all the components in the bundle. pub(crate) fn register_info( &mut self, - components: &mut Components, + components: &mut ComponentsRegistrator, storages: &mut Storages, ) -> BundleId { let bundle_infos = &mut self.bundle_infos; @@ -1599,7 +1605,7 @@ impl Bundles { /// Also registers all the components in the bundle. pub(crate) fn register_contributed_bundle_info( &mut self, - components: &mut Components, + components: &mut ComponentsRegistrator, storages: &mut Storages, ) -> BundleId { if let Some(id) = self.contributed_bundle_ids.get(&TypeId::of::()).cloned() { diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index cf48c7da03e93..a206f13a2d52b 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -12,12 +12,14 @@ use crate::{ system::{Commands, Local, SystemParam}, world::{DeferredWorld, FromWorld, World}, }; -#[cfg(feature = "bevy_reflect")] use alloc::boxed::Box; use alloc::{borrow::Cow, format, vec::Vec}; pub use bevy_ecs_macros::Component; -use bevy_platform_support::collections::{HashMap, HashSet}; use bevy_platform_support::sync::Arc; +use bevy_platform_support::{ + collections::{HashMap, HashSet}, + sync::PoisonError, +}; use bevy_ptr::{OwningPtr, UnsafeCellDeref}; #[cfg(feature = "bevy_reflect")] use bevy_reflect::Reflect; @@ -29,6 +31,7 @@ use core::{ fmt::Debug, marker::PhantomData, mem::needs_drop, + ops::{Deref, DerefMut}, }; use disqualified::ShortName; use thiserror::Error; @@ -432,7 +435,7 @@ pub trait Component: Send + Sync + 'static { /// Registers required components. fn register_required_components( _component_id: ComponentId, - _components: &mut Components, + _components: &mut ComponentsRegistrator, _required_components: &mut RequiredComponents, _inheritance_depth: u16, _recursion_check_stack: &mut Vec, @@ -1135,206 +1138,884 @@ impl ComponentCloneBehavior { } } -/// Stores metadata associated with each kind of [`Component`] in a given [`World`]. -#[derive(Debug, Default)] -pub struct Components { - components: Vec, - indices: TypeIdMap, - resource_indices: TypeIdMap, +/// A queued component registration. +struct QueuedRegistration { + registrator: Box, + id: ComponentId, } -impl Components { - /// Registers a [`Component`] of type `T` with this instance. - /// If a component of this type has already been registered, this will return - /// the ID of the pre-existing component. +impl QueuedRegistration { + /// Creates the [`QueuedRegistration`]. /// - /// # See also + /// # Safety /// - /// * [`Components::component_id()`] - /// * [`Components::register_component_with_descriptor()`] - #[inline] - pub fn register_component(&mut self) -> ComponentId { - self.register_component_internal::(&mut Vec::new()) + /// [`ComponentId`] must be unique. + unsafe fn new( + id: ComponentId, + func: impl FnOnce(&mut ComponentsRegistrator, ComponentId) + 'static, + ) -> Self { + Self { + registrator: Box::new(func), + id, + } } - #[inline] - fn register_component_internal( - &mut self, - recursion_check_stack: &mut Vec, - ) -> ComponentId { - let mut is_new_registration = false; - let id = { - let Components { - indices, - components, - .. - } = self; - let type_id = TypeId::of::(); - *indices.entry(type_id).or_insert_with(|| { - let id = Components::register_component_inner( - components, - ComponentDescriptor::new::(), - ); - is_new_registration = true; - id - }) - }; - if is_new_registration { - let mut required_components = RequiredComponents::default(); - T::register_required_components( - id, - self, - &mut required_components, - 0, - recursion_check_stack, - ); - let info = &mut self.components[id.index()]; + /// Performs the registration, returning the now valid [`ComponentId`]. + fn register(self, registrator: &mut ComponentsRegistrator) -> ComponentId { + (self.registrator)(registrator, self.id); + self.id + } +} - #[expect( - deprecated, - reason = "need to use this method until it is removed to ensure user defined components register hooks correctly" - )] - // TODO: Replace with `info.hooks.update_from_component::();` once `Component::register_component_hooks` is removed - T::register_component_hooks(&mut info.hooks); +/// Allows queuing components to be registered. +#[derive(Default)] +pub struct QueuedComponents { + components: TypeIdMap, + resources: TypeIdMap, + dynamic_registrations: Vec, +} - info.required_components = required_components; - } - id +impl Debug for QueuedComponents { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let components = self + .components + .iter() + .map(|(type_id, queued)| (type_id, queued.id)) + .collect::>(); + let resources = self + .resources + .iter() + .map(|(type_id, queued)| (type_id, queued.id)) + .collect::>(); + let dynamic_registrations = self + .dynamic_registrations + .iter() + .map(|queued| queued.id) + .collect::>(); + write!(f, "components: {components:?}, resources: {resources:?}, dynamic_registrations: {dynamic_registrations:?}") } +} - /// Registers a component described by `descriptor`. +/// Generates [`ComponentId`]s. +#[derive(Debug, Default)] +pub struct ComponentIds { + next: bevy_platform_support::sync::atomic::AtomicUsize, +} + +impl ComponentIds { + /// Peeks the next [`ComponentId`] to be generated without generating it. + pub fn peek(&self) -> ComponentId { + ComponentId( + self.next + .load(bevy_platform_support::sync::atomic::Ordering::Relaxed), + ) + } + + /// Generates and returns the next [`ComponentId`]. + pub fn next(&self) -> ComponentId { + ComponentId( + self.next + .fetch_add(1, bevy_platform_support::sync::atomic::Ordering::Relaxed), + ) + } + + /// Peeks the next [`ComponentId`] to be generated without generating it. + pub fn peek_mut(&mut self) -> ComponentId { + ComponentId(*self.next.get_mut()) + } + + /// Generates and returns the next [`ComponentId`]. + pub fn next_mut(&mut self) -> ComponentId { + let id = self.next.get_mut(); + let result = ComponentId(*id); + *id += 1; + result + } + + /// Returns the number of [`ComponentId`]s generated. + pub fn len(&self) -> usize { + self.peek().0 + } + + /// Returns true if and only if no ids have been generated. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +/// A type that enables queuing registration in [`Components`]. +/// +/// # Note +/// +/// These queued registrations return [`ComponentId`]s. +/// These ids are not yet valid, but they will become valid +/// when either [`ComponentsRegistrator::apply_queued_registrations`] is called or the same registration is made directly. +/// In either case, the returned [`ComponentId`]s will be correct, but they are not correct yet. +/// +/// Generally, that means these [`ComponentId`]s can be safely used for read-only purposes. +/// Modifying the contents of the world through these [`ComponentId`]s directly without waiting for them to be fully registered +/// and without then confirming that they have been fully registered is not supported. +/// Hence, extra care is needed with these [`ComponentId`]s to ensure all safety rules are followed. +/// +/// As a rule of thumb, if you have mutable access to [`ComponentsRegistrator`], prefer to use that instead. +/// Use this only if you need to know the id of a component but do not need to modify the contents of the world based on that id. +pub struct ComponentsQueuedRegistrator<'w> { + components: &'w Components, + ids: &'w ComponentIds, +} + +impl Deref for ComponentsQueuedRegistrator<'_> { + type Target = Components; + + fn deref(&self) -> &Self::Target { + self.components + } +} + +impl<'w> ComponentsQueuedRegistrator<'w> { + /// Constructs a new [`ComponentsQueuedRegistrator`]. /// - /// # Note + /// # Safety /// - /// If this method is called multiple times with identical descriptors, a distinct [`ComponentId`] - /// will be created for each one. + /// The [`Components`] and [`ComponentIds`] must match. + /// For example, they must be from the same world. + pub unsafe fn new(components: &'w Components, ids: &'w ComponentIds) -> Self { + Self { components, ids } + } + + /// Queues this function to run as a component registrator. /// - /// # See also + /// # Safety /// - /// * [`Components::component_id()`] - /// * [`Components::register_component()`] - pub fn register_component_with_descriptor( - &mut self, - descriptor: ComponentDescriptor, + /// The [`TypeId`] must not already be registered or queued as a component. + unsafe fn force_register_arbitrary_component( + &self, + type_id: TypeId, + func: impl FnOnce(&mut ComponentsRegistrator, ComponentId) + 'static, ) -> ComponentId { - Components::register_component_inner(&mut self.components, descriptor) + let id = self.ids.next(); + self.components + .queued + .write() + .unwrap_or_else(PoisonError::into_inner) + .components + .insert( + type_id, + // SAFETY: The id was just generated. + unsafe { QueuedRegistration::new(id, func) }, + ); + id } - #[inline] - fn register_component_inner( - components: &mut Vec, - descriptor: ComponentDescriptor, + /// Queues this function to run as a resource registrator. + /// + /// # Safety + /// + /// The [`TypeId`] must not already be registered or queued as a resource. + unsafe fn force_register_arbitrary_resource( + &self, + type_id: TypeId, + func: impl FnOnce(&mut ComponentsRegistrator, ComponentId) + 'static, ) -> ComponentId { - let component_id = ComponentId(components.len()); - let info = ComponentInfo::new(component_id, descriptor); - components.push(info); - component_id + let id = self.ids.next(); + self.components + .queued + .write() + .unwrap_or_else(PoisonError::into_inner) + .resources + .insert( + type_id, + // SAFETY: The id was just generated. + unsafe { QueuedRegistration::new(id, func) }, + ); + id } - /// Returns the number of components registered with this instance. - #[inline] - pub fn len(&self) -> usize { - self.components.len() + /// Queues this function to run as a dynamic registrator. + fn force_register_arbitrary_dynamic( + &self, + func: impl FnOnce(&mut ComponentsRegistrator, ComponentId) + 'static, + ) -> ComponentId { + let id = self.ids.next(); + self.components + .queued + .write() + .unwrap_or_else(PoisonError::into_inner) + .dynamic_registrations + .push( + // SAFETY: The id was just generated. + unsafe { QueuedRegistration::new(id, func) }, + ); + id } - /// Returns `true` if there are no components registered with this instance. Otherwise, this returns `false`. + /// This is a queued version of [`ComponentsRegistrator::register_component`]. + /// This will reserve an id and queue the registration. + /// These registrations will be carried out at the next opportunity. + /// + /// # Note + /// + /// Technically speaking, the returned [`ComponentId`] is not valid, but it will become valid later. + /// See type level docs for details. #[inline] - pub fn is_empty(&self) -> bool { - self.components.len() == 0 + pub fn queue_register_component(&self) -> ComponentId { + self.component_id::().unwrap_or_else(|| { + // SAFETY: We just checked that this type was not in the queue. + unsafe { + self.force_register_arbitrary_component(TypeId::of::(), |registrator, id| { + // SAFETY: We just checked that this is not currently registered or queued, and if it was registered since, this would have been dropped from the queue. + #[expect(unused_unsafe, reason = "More precise to specify.")] + unsafe { + registrator.register_component_unchecked::(&mut Vec::new(), id); + } + }) + } + }) } - /// Gets the metadata associated with the given component. + /// This is a queued version of [`ComponentsRegistrator::register_component_with_descriptor`]. + /// This will reserve an id and queue the registration. + /// These registrations will be carried out at the next opportunity. /// - /// This will return an incorrect result if `id` did not come from the same world as `self`. It may return `None` or a garbage value. + /// # Note + /// + /// Technically speaking, the returned [`ComponentId`] is not valid, but it will become valid later. + /// See type level docs for details. #[inline] - pub fn get_info(&self, id: ComponentId) -> Option<&ComponentInfo> { - self.components.get(id.0) + pub fn queue_register_component_with_descriptor( + &self, + descriptor: ComponentDescriptor, + ) -> ComponentId { + self.force_register_arbitrary_dynamic(|registrator, id| { + // SAFETY: Id uniqueness handled by caller. + unsafe { + registrator.register_component_inner(id, descriptor); + } + }) } - /// Returns the name associated with the given component. + /// This is a queued version of [`ComponentsRegistrator::register_resource`]. + /// This will reserve an id and queue the registration. + /// These registrations will be carried out at the next opportunity. /// - /// This will return an incorrect result if `id` did not come from the same world as `self`. It may return `None` or a garbage value. + /// # Note + /// + /// Technically speaking, the returned [`ComponentId`] is not valid, but it will become valid later. + /// See type level docs for details. #[inline] - pub fn get_name(&self, id: ComponentId) -> Option<&str> { - self.get_info(id).map(ComponentInfo::name) + pub fn queue_register_resource(&self) -> ComponentId { + let type_id = TypeId::of::(); + self.get_resource_id(type_id).unwrap_or_else(|| { + // SAFETY: We just checked that this type was not in the queue. + unsafe { + self.force_register_arbitrary_resource(type_id, move |registrator, id| { + // SAFETY: We just checked that this is not currently registered or queued, and if it was registered since, this would have been dropped from the queue. + // SAFETY: Id uniqueness handled by caller, and the type_id matches descriptor. + #[expect(unused_unsafe, reason = "More precise to specify.")] + unsafe { + registrator.register_resource_unchecked_with(type_id, id, || { + ComponentDescriptor::new_resource::() + }); + } + }) + } + }) } - /// Gets the metadata associated with the given component. - /// # Safety + /// This is a queued version of [`ComponentsRegistrator::register_non_send`]. + /// This will reserve an id and queue the registration. + /// These registrations will be carried out at the next opportunity. + /// + /// # Note /// - /// `id` must be a valid [`ComponentId`] + /// Technically speaking, the returned [`ComponentId`] is not valid, but it will become valid later. + /// See type level docs for details. #[inline] - pub unsafe fn get_info_unchecked(&self, id: ComponentId) -> &ComponentInfo { - debug_assert!(id.index() < self.components.len()); - // SAFETY: The caller ensures `id` is valid. - unsafe { self.components.get_unchecked(id.0) } + pub fn queue_register_non_send(&self) -> ComponentId { + let type_id = TypeId::of::(); + self.get_resource_id(type_id).unwrap_or_else(|| { + // SAFETY: We just checked that this type was not in the queue. + unsafe { + self.force_register_arbitrary_resource(type_id, move |registrator, id| { + // SAFETY: We just checked that this is not currently registered or queued, and if it was registered since, this would have been dropped from the queue. + // SAFETY: Id uniqueness handled by caller, and the type_id matches descriptor. + #[expect(unused_unsafe, reason = "More precise to specify.")] + unsafe { + registrator.register_resource_unchecked_with(type_id, id, || { + ComponentDescriptor::new_non_send::(StorageType::default()) + }); + } + }) + } + }) } + /// This is a queued version of [`ComponentsRegistrator::register_resource_with_descriptor`]. + /// This will reserve an id and queue the registration. + /// These registrations will be carried out at the next opportunity. + /// + /// # Note + /// + /// Technically speaking, the returned [`ComponentId`] is not valid, but it will become valid later. + /// See type level docs for details. #[inline] - pub(crate) fn get_hooks_mut(&mut self, id: ComponentId) -> Option<&mut ComponentHooks> { - self.components.get_mut(id.0).map(|info| &mut info.hooks) + pub fn queue_register_resource_with_descriptor( + &self, + descriptor: ComponentDescriptor, + ) -> ComponentId { + self.force_register_arbitrary_dynamic(|registrator, id| { + // SAFETY: Id uniqueness handled by caller. + unsafe { + registrator.register_component_inner(id, descriptor); + } + }) } +} - #[inline] - pub(crate) fn get_required_components_mut( - &mut self, - id: ComponentId, - ) -> Option<&mut RequiredComponents> { +/// A [`Components`] wrapper that enables additional features, like registration. +pub struct ComponentsRegistrator<'w> { + components: &'w mut Components, + ids: &'w mut ComponentIds, +} + +impl Deref for ComponentsRegistrator<'_> { + type Target = Components; + + fn deref(&self) -> &Self::Target { self.components - .get_mut(id.0) - .map(|info| &mut info.required_components) } +} - /// Registers the given component `R` and [required components] inherited from it as required by `T`. - /// - /// When `T` is added to an entity, `R` will also be added if it was not already provided. - /// The given `constructor` will be used for the creation of `R`. - /// - /// [required components]: Component#required-components +impl DerefMut for ComponentsRegistrator<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.components + } +} + +impl<'w> ComponentsRegistrator<'w> { + /// Constructs a new [`ComponentsRegistrator`]. /// /// # Safety /// - /// The given component IDs `required` and `requiree` must be valid. - /// - /// # Errors + /// The [`Components`] and [`ComponentIds`] must match. + /// For example, they must be from the same world. + pub unsafe fn new(components: &'w mut Components, ids: &'w mut ComponentIds) -> Self { + Self { components, ids } + } + + /// Converts this [`ComponentsRegistrator`] into a [`ComponentsQueuedRegistrator`]. + /// This is intended for use to pass this value to a function that requires [`ComponentsQueuedRegistrator`]. + /// It is generally not a good idea to queue a registration when you can instead register directly on this type. + pub fn as_queued(&self) -> ComponentsQueuedRegistrator<'_> { + // SAFETY: ensured by the caller that created self. + unsafe { ComponentsQueuedRegistrator::new(self.components, self.ids) } + } + + /// Applies every queued registration. + /// This ensures that every valid [`ComponentId`] is registered, + /// enabling retrieving [`ComponentInfo`], etc. + pub fn apply_queued_registrations(&mut self) { + if !self.any_queued_mut() { + return; + } + + // Note: + // + // This is not just draining the queue. We need to empty the queue without removing the information from `Components`. + // If we drained directly, we could break invariance. + // + // For example, say `ComponentA` and `ComponentB` are queued, and `ComponentA` requires `ComponentB`. + // If we drain directly, and `ComponentA` was the first to be registered, then, when `ComponentA` + // registers `ComponentB` in `Component::register_required_components`, + // `Components` will not know that `ComponentB` was queued + // (since it will have been drained from the queue.) + // If that happened, `Components` would assign a new `ComponentId` to `ComponentB` + // which would be *different* than the id it was assigned in the queue. + // Then, when the drain iterator gets to `ComponentB`, + // it would be unsafely registering `ComponentB`, which is already registered. + // + // As a result, we need to pop from each queue one by one instead of draining. + + // components + while let Some(registrator) = { + let queued = self + .components + .queued + .get_mut() + .unwrap_or_else(PoisonError::into_inner); + queued.components.keys().next().copied().map(|type_id| { + // SAFETY: the id just came from a valid iterator. + unsafe { queued.components.remove(&type_id).debug_checked_unwrap() } + }) + } { + registrator.register(self); + } + + // resources + while let Some(registrator) = { + let queued = self + .components + .queued + .get_mut() + .unwrap_or_else(PoisonError::into_inner); + queued.resources.keys().next().copied().map(|type_id| { + // SAFETY: the id just came from a valid iterator. + unsafe { queued.resources.remove(&type_id).debug_checked_unwrap() } + }) + } { + registrator.register(self); + } + + // dynamic + let queued = &mut self + .components + .queued + .get_mut() + .unwrap_or_else(PoisonError::into_inner); + if !queued.dynamic_registrations.is_empty() { + for registrator in core::mem::take(&mut queued.dynamic_registrations) { + registrator.register(self); + } + } + } + + /// Registers a [`Component`] of type `T` with this instance. + /// If a component of this type has already been registered, this will return + /// the ID of the pre-existing component. /// - /// Returns a [`RequiredComponentsError`] if the `required` component is already a directly required component for the `requiree`. + /// # See also /// - /// Indirect requirements through other components are allowed. In those cases, the more specific - /// registration will be used. - pub(crate) unsafe fn register_required_components( + /// * [`Components::component_id()`] + /// * [`ComponentsRegistrator::register_component_with_descriptor()`] + #[inline] + pub fn register_component(&mut self) -> ComponentId { + self.register_component_checked::(&mut Vec::new()) + } + + /// Same as [`Self::register_component_unchecked`] but keeps a checks for safety. + #[inline] + fn register_component_checked( &mut self, - requiree: ComponentId, - required: ComponentId, - constructor: fn() -> R, - ) -> Result<(), RequiredComponentsError> { - // SAFETY: The caller ensures that the `requiree` is valid. - let required_components = unsafe { - self.get_required_components_mut(requiree) - .debug_checked_unwrap() - }; + recursion_check_stack: &mut Vec, + ) -> ComponentId { + let type_id = TypeId::of::(); + if let Some(id) = self.indices.get(&type_id) { + return *id; + } - // Cannot directly require the same component twice. - if required_components - .0 - .get(&required) - .is_some_and(|c| c.inheritance_depth == 0) + if let Some(registrator) = self + .components + .queued + .get_mut() + .unwrap_or_else(PoisonError::into_inner) + .components + .remove(&type_id) { - return Err(RequiredComponentsError::DuplicateRegistration( - requiree, required, - )); + // If we are trying to register something that has already been queued, we respect the queue. + // Just like if we are trying to register something that already is, we respect the first registration. + return registrator.register(self); } - // Register the required component for the requiree. - // This is a direct requirement with a depth of `0`. - required_components.register_by_id(required, constructor, 0); + let id = self.ids.next_mut(); + // SAFETY: The component is not currently registered, and the id is fresh. + unsafe { + self.register_component_unchecked::(recursion_check_stack, id); + } + id + } - // Add the requiree to the list of components that require the required component. - // SAFETY: The component is in the list of required components, so it must exist already. - let required_by = unsafe { self.get_required_by_mut(required).debug_checked_unwrap() }; - required_by.insert(requiree); + /// # Safety + /// + /// Neither this component, nor its id may be registered or queued. This must be a new registration. + #[inline] + unsafe fn register_component_unchecked( + &mut self, + recursion_check_stack: &mut Vec, + id: ComponentId, + ) { + // SAFETY: ensured by caller. + unsafe { + self.register_component_inner(id, ComponentDescriptor::new::()); + } + let type_id = TypeId::of::(); + let prev = self.indices.insert(type_id, id); + debug_assert!(prev.is_none()); - let mut required_components_tmp = RequiredComponents::default(); + let mut required_components = RequiredComponents::default(); + T::register_required_components( + id, + self, + &mut required_components, + 0, + recursion_check_stack, + ); + // SAFETY: we just inserted it in `register_component_inner` + let info = unsafe { + &mut self + .components + .components + .get_mut(id.0) + .debug_checked_unwrap() + .as_mut() + .debug_checked_unwrap() + }; + + #[expect( + deprecated, + reason = "need to use this method until it is removed to ensure user defined components register hooks correctly" + )] + // TODO: Replace with `info.hooks.update_from_component::();` once `Component::register_component_hooks` is removed + T::register_component_hooks(&mut info.hooks); + + info.required_components = required_components; + } + + /// Registers a component described by `descriptor`. + /// + /// # Note + /// + /// If this method is called multiple times with identical descriptors, a distinct [`ComponentId`] + /// will be created for each one. + /// + /// # See also + /// + /// * [`Components::component_id()`] + /// * [`ComponentsRegistrator::register_component()`] + #[inline] + pub fn register_component_with_descriptor( + &mut self, + descriptor: ComponentDescriptor, + ) -> ComponentId { + let id = self.ids.next_mut(); + // SAFETY: The id is fresh. + unsafe { + self.register_component_inner(id, descriptor); + } + id + } + + // NOTE: This should maybe be private, but it is currently public so that `bevy_ecs_macros` can use it. + // We can't directly move this there either, because this uses `Components::get_required_by_mut`, + // which is private, and could be equally risky to expose to users. + /// Registers the given component `R` and [required components] inherited from it as required by `T`, + /// and adds `T` to their lists of requirees. + /// + /// The given `inheritance_depth` determines how many levels of inheritance deep the requirement is. + /// A direct requirement has a depth of `0`, and each level of inheritance increases the depth by `1`. + /// Lower depths are more specific requirements, and can override existing less specific registrations. + /// + /// The `recursion_check_stack` allows checking whether this component tried to register itself as its + /// own (indirect) required component. + /// + /// This method does *not* register any components as required by components that require `T`. + /// + /// Only use this method if you know what you are doing. In most cases, you should instead use [`World::register_required_components`], + /// or the equivalent method in `bevy_app::App`. + /// + /// [required component]: Component#required-components + #[doc(hidden)] + pub fn register_required_components_manual( + &mut self, + required_components: &mut RequiredComponents, + constructor: fn() -> R, + inheritance_depth: u16, + recursion_check_stack: &mut Vec, + ) { + let requiree = self.register_component_checked::(recursion_check_stack); + let required = self.register_component_checked::(recursion_check_stack); + + // SAFETY: We just created the components. + unsafe { + self.register_required_components_manual_unchecked::( + requiree, + required, + required_components, + constructor, + inheritance_depth, + ); + } + } + + /// Registers a [`Resource`] of type `T` with this instance. + /// If a resource of this type has already been registered, this will return + /// the ID of the pre-existing resource. + /// + /// # See also + /// + /// * [`Components::resource_id()`] + /// * [`ComponentsRegistrator::register_resource_with_descriptor()`] + #[inline] + pub fn register_resource(&mut self) -> ComponentId { + // SAFETY: The [`ComponentDescriptor`] matches the [`TypeId`] + unsafe { + self.register_resource_with(TypeId::of::(), || { + ComponentDescriptor::new_resource::() + }) + } + } + + /// Registers a [non-send resource](crate::system::NonSend) of type `T` with this instance. + /// If a resource of this type has already been registered, this will return + /// the ID of the pre-existing resource. + #[inline] + pub fn register_non_send(&mut self) -> ComponentId { + // SAFETY: The [`ComponentDescriptor`] matches the [`TypeId`] + unsafe { + self.register_resource_with(TypeId::of::(), || { + ComponentDescriptor::new_non_send::(StorageType::default()) + }) + } + } + + /// Same as [`Components::register_resource_unchecked_with`] but handles safety. + /// + /// # Safety + /// + /// The [`ComponentDescriptor`] must match the [`TypeId`]. + #[inline] + unsafe fn register_resource_with( + &mut self, + type_id: TypeId, + descriptor: impl FnOnce() -> ComponentDescriptor, + ) -> ComponentId { + if let Some(id) = self.resource_indices.get(&type_id) { + return *id; + } + + if let Some(registrator) = self + .components + .queued + .get_mut() + .unwrap_or_else(PoisonError::into_inner) + .resources + .remove(&type_id) + { + // If we are trying to register something that has already been queued, we respect the queue. + // Just like if we are trying to register something that already is, we respect the first registration. + return registrator.register(self); + } + + let id = self.ids.next_mut(); + // SAFETY: The resource is not currently registered, the id is fresh, and the [`ComponentDescriptor`] matches the [`TypeId`] + unsafe { + self.register_resource_unchecked_with(type_id, id, descriptor); + } + id + } + + /// Registers a [`Resource`] described by `descriptor`. + /// + /// # Note + /// + /// If this method is called multiple times with identical descriptors, a distinct [`ComponentId`] + /// will be created for each one. + /// + /// # See also + /// + /// * [`Components::resource_id()`] + /// * [`ComponentsRegistrator::register_resource()`] + #[inline] + pub fn register_resource_with_descriptor( + &mut self, + descriptor: ComponentDescriptor, + ) -> ComponentId { + let id = self.ids.next_mut(); + // SAFETY: The id is fresh. + unsafe { + self.register_component_inner(id, descriptor); + } + id + } +} + +/// Stores metadata associated with each kind of [`Component`] in a given [`World`]. +#[derive(Debug, Default)] +pub struct Components { + components: Vec>, + indices: TypeIdMap, + resource_indices: TypeIdMap, + // This is kept internal and local to verify that no deadlocks can occor. + queued: bevy_platform_support::sync::RwLock, +} + +impl Components { + /// This registers any descriptor, component or resource. + /// + /// # Safety + /// + /// The id must have never been registered before. This must be a fresh registration. + #[inline] + unsafe fn register_component_inner( + &mut self, + id: ComponentId, + descriptor: ComponentDescriptor, + ) { + let info = ComponentInfo::new(id, descriptor); + let least_len = id.0 + 1; + if self.components.len() < least_len { + self.components.resize_with(least_len, || None); + } + // SAFETY: We just extended the vec to make this index valid. + let slot = unsafe { self.components.get_mut(id.0).debug_checked_unwrap() }; + // Caller ensures id is unique + debug_assert!(slot.is_none()); + *slot = Some(info); + } + + /// Returns the number of components registered or queued with this instance. + #[inline] + pub fn len(&self) -> usize { + self.num_queued() + self.num_registered() + } + + /// Returns `true` if there are no components registered or queued with this instance. Otherwise, this returns `false`. + #[inline] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Returns the number of components registered with this instance. + #[inline] + pub fn num_queued(&self) -> usize { + let queued = self.queued.read().unwrap_or_else(PoisonError::into_inner); + queued.components.len() + queued.dynamic_registrations.len() + queued.resources.len() + } + + /// Returns `true` if there are any components registered with this instance. Otherwise, this returns `false`. + #[inline] + pub fn any_queued(&self) -> bool { + self.num_queued() > 0 + } + + /// A faster version of [`Self::num_queued`]. + #[inline] + pub fn num_queued_mut(&mut self) -> usize { + let queued = self + .queued + .get_mut() + .unwrap_or_else(PoisonError::into_inner); + queued.components.len() + queued.dynamic_registrations.len() + queued.resources.len() + } + + /// A faster version of [`Self::any_queued`]. + #[inline] + pub fn any_queued_mut(&mut self) -> bool { + self.num_queued_mut() > 0 + } + + /// Returns the number of components registered with this instance. + #[inline] + pub fn num_registered(&self) -> usize { + self.components.len() + } + + /// Returns `true` if there are any components registered with this instance. Otherwise, this returns `false`. + #[inline] + pub fn any_registered(&self) -> bool { + self.num_registered() > 0 + } + + /// Gets the metadata associated with the given component, if it is registered. + /// This will return `None` if the id is not regiserted or is queued. + /// + /// This will return an incorrect result if `id` did not come from the same world as `self`. It may return `None` or a garbage value. + #[inline] + pub fn get_info(&self, id: ComponentId) -> Option<&ComponentInfo> { + self.components.get(id.0).and_then(|info| info.as_ref()) + } + + /// Returns the name associated with the given component, if it is registered. + /// This will return `None` if the id is not regiserted or is queued. + /// + /// This will return an incorrect result if `id` did not come from the same world as `self`. It may return `None` or a garbage value. + #[inline] + pub fn get_name(&self, id: ComponentId) -> Option<&str> { + self.get_info(id).map(ComponentInfo::name) + } + + /// Gets the metadata associated with the given component. + /// # Safety + /// + /// `id` must be a valid and fully registered [`ComponentId`]. + #[inline] + pub unsafe fn get_info_unchecked(&self, id: ComponentId) -> &ComponentInfo { + // SAFETY: The caller ensures `id` is valid. + unsafe { + self.components + .get(id.0) + .debug_checked_unwrap() + .as_ref() + .debug_checked_unwrap() + } + } + + #[inline] + pub(crate) fn get_hooks_mut(&mut self, id: ComponentId) -> Option<&mut ComponentHooks> { + self.components + .get_mut(id.0) + .and_then(|info| info.as_mut().map(|info| &mut info.hooks)) + } + + #[inline] + pub(crate) fn get_required_components_mut( + &mut self, + id: ComponentId, + ) -> Option<&mut RequiredComponents> { + self.components + .get_mut(id.0) + .and_then(|info| info.as_mut().map(|info| &mut info.required_components)) + } + + /// Registers the given component `R` and [required components] inherited from it as required by `T`. + /// + /// When `T` is added to an entity, `R` will also be added if it was not already provided. + /// The given `constructor` will be used for the creation of `R`. + /// + /// [required components]: Component#required-components + /// + /// # Safety + /// + /// The given component IDs `required` and `requiree` must be valid. + /// + /// # Errors + /// + /// Returns a [`RequiredComponentsError`] if the `required` component is already a directly required component for the `requiree`. + /// + /// Indirect requirements through other components are allowed. In those cases, the more specific + /// registration will be used. + pub(crate) unsafe fn register_required_components( + &mut self, + requiree: ComponentId, + required: ComponentId, + constructor: fn() -> R, + ) -> Result<(), RequiredComponentsError> { + // SAFETY: The caller ensures that the `requiree` is valid. + let required_components = unsafe { + self.get_required_components_mut(requiree) + .debug_checked_unwrap() + }; + + // Cannot directly require the same component twice. + if required_components + .0 + .get(&required) + .is_some_and(|c| c.inheritance_depth == 0) + { + return Err(RequiredComponentsError::DuplicateRegistration( + requiree, required, + )); + } + + // Register the required component for the requiree. + // This is a direct requirement with a depth of `0`. + required_components.register_by_id(required, constructor, 0); + + // Add the requiree to the list of components that require the required component. + // SAFETY: The component is in the list of required components, so it must exist already. + let required_by = unsafe { self.get_required_by_mut(required).debug_checked_unwrap() }; + required_by.insert(requiree); + + let mut required_components_tmp = RequiredComponents::default(); // SAFETY: The caller ensures that the `requiree` and `required` components are valid. let inherited_requirements = unsafe { self.register_inherited_required_components( @@ -1447,48 +2128,6 @@ impl Components { inherited_requirements } - // NOTE: This should maybe be private, but it is currently public so that `bevy_ecs_macros` can use it. - // We can't directly move this there either, because this uses `Components::get_required_by_mut`, - // which is private, and could be equally risky to expose to users. - /// Registers the given component `R` and [required components] inherited from it as required by `T`, - /// and adds `T` to their lists of requirees. - /// - /// The given `inheritance_depth` determines how many levels of inheritance deep the requirement is. - /// A direct requirement has a depth of `0`, and each level of inheritance increases the depth by `1`. - /// Lower depths are more specific requirements, and can override existing less specific registrations. - /// - /// The `recursion_check_stack` allows checking whether this component tried to register itself as its - /// own (indirect) required component. - /// - /// This method does *not* register any components as required by components that require `T`. - /// - /// Only use this method if you know what you are doing. In most cases, you should instead use [`World::register_required_components`], - /// or the equivalent method in `bevy_app::App`. - /// - /// [required component]: Component#required-components - #[doc(hidden)] - pub fn register_required_components_manual( - &mut self, - required_components: &mut RequiredComponents, - constructor: fn() -> R, - inheritance_depth: u16, - recursion_check_stack: &mut Vec, - ) { - let requiree = self.register_component_internal::(recursion_check_stack); - let required = self.register_component_internal::(recursion_check_stack); - - // SAFETY: We just created the components. - unsafe { - self.register_required_components_manual_unchecked::( - requiree, - required, - required_components, - constructor, - inheritance_depth, - ); - } - } - /// Registers the given component `R` and [required components] inherited from it as required by `T`, /// and adds `T` to their lists of requirees. /// @@ -1530,7 +2169,9 @@ impl Components { #[inline] pub(crate) fn get_required_by(&self, id: ComponentId) -> Option<&HashSet> { - self.components.get(id.0).map(|info| &info.required_by) + self.components + .get(id.0) + .and_then(|info| info.as_ref().map(|info| &info.required_by)) } #[inline] @@ -1540,13 +2181,91 @@ impl Components { ) -> Option<&mut HashSet> { self.components .get_mut(id.0) - .map(|info| &mut info.required_by) + .and_then(|info| info.as_mut().map(|info| &mut info.required_by)) + } + + /// Returns true if the [`ComponentId`] is fully registered and valid. + /// Ids may be invalid if they are still queued to be registered. + /// Those ids are still correct, but they are not usable in every context yet. + #[inline] + pub fn is_id_valid(&self, id: ComponentId) -> bool { + self.components.get(id.0).is_some_and(Option::is_some) + } + + /// Type-erased equivalent of [`Components::valid_component_id()`]. + #[inline] + pub fn get_valid_id(&self, type_id: TypeId) -> Option { + self.indices.get(&type_id).copied() + } + + /// Returns the [`ComponentId`] of the given [`Component`] type `T` if it is fully registered. + /// If you want to include queued registration, see [`Components::component_id()`]. + /// + /// ``` + /// use bevy_ecs::prelude::*; + /// + /// let mut world = World::new(); + /// + /// #[derive(Component)] + /// struct ComponentA; + /// + /// let component_a_id = world.register_component::(); + /// + /// assert_eq!(component_a_id, world.components().valid_component_id::().unwrap()) + /// ``` + /// + /// # See also + /// + /// * [`Components::get_valid_id()`] + /// * [`Components::valid_resource_id()`] + /// * [`World::component_id()`] + #[inline] + pub fn valid_component_id(&self) -> Option { + self.get_id(TypeId::of::()) + } + + /// Type-erased equivalent of [`Components::valid_resource_id()`]. + #[inline] + pub fn get_valid_resource_id(&self, type_id: TypeId) -> Option { + self.resource_indices.get(&type_id).copied() + } + + /// Returns the [`ComponentId`] of the given [`Resource`] type `T` if it is fully registered. + /// If you want to include queued registration, see [`Components::resource_id()`]. + /// + /// ``` + /// use bevy_ecs::prelude::*; + /// + /// let mut world = World::new(); + /// + /// #[derive(Resource, Default)] + /// struct ResourceA; + /// + /// let resource_a_id = world.init_resource::(); + /// + /// assert_eq!(resource_a_id, world.components().valid_resource_id::().unwrap()) + /// ``` + /// + /// # See also + /// + /// * [`Components::valid_component_id()`] + /// * [`Components::get_resource_id()`] + #[inline] + pub fn valid_resource_id(&self) -> Option { + self.get_resource_id(TypeId::of::()) } /// Type-erased equivalent of [`Components::component_id()`]. #[inline] pub fn get_id(&self, type_id: TypeId) -> Option { - self.indices.get(&type_id).copied() + self.indices.get(&type_id).copied().or_else(|| { + self.queued + .read() + .unwrap_or_else(PoisonError::into_inner) + .components + .get(&type_id) + .map(|queued| queued.id) + }) } /// Returns the [`ComponentId`] of the given [`Component`] type `T`. @@ -1556,7 +2275,7 @@ impl Components { /// instance. /// /// Returns [`None`] if the `Component` type has not - /// yet been initialized using [`Components::register_component()`]. + /// yet been initialized using [`ComponentsRegistrator::register_component()`] or [`ComponentsQueuedRegistrator::queue_register_component()`]. /// /// ``` /// use bevy_ecs::prelude::*; @@ -1584,7 +2303,14 @@ impl Components { /// Type-erased equivalent of [`Components::resource_id()`]. #[inline] pub fn get_resource_id(&self, type_id: TypeId) -> Option { - self.resource_indices.get(&type_id).copied() + self.resource_indices.get(&type_id).copied().or_else(|| { + self.queued + .read() + .unwrap_or_else(PoisonError::into_inner) + .resources + .get(&type_id) + .map(|queued| queued.id) + }) } /// Returns the [`ComponentId`] of the given [`Resource`] type `T`. @@ -1594,7 +2320,7 @@ impl Components { /// instance. /// /// Returns [`None`] if the `Resource` type has not - /// yet been initialized using [`Components::register_resource()`]. + /// yet been initialized using [`ComponentsRegistrator::register_resource()`] or [`ComponentsQueuedRegistrator::queue_register_resource()`]. /// /// ``` /// use bevy_ecs::prelude::*; @@ -1618,84 +2344,29 @@ impl Components { self.get_resource_id(TypeId::of::()) } - /// Registers a [`Resource`] of type `T` with this instance. - /// If a resource of this type has already been registered, this will return - /// the ID of the pre-existing resource. - /// - /// # See also - /// - /// * [`Components::resource_id()`] - /// * [`Components::register_resource_with_descriptor()`] - #[inline] - pub fn register_resource(&mut self) -> ComponentId { - // SAFETY: The [`ComponentDescriptor`] matches the [`TypeId`] - unsafe { - self.get_or_register_resource_with(TypeId::of::(), || { - ComponentDescriptor::new_resource::() - }) - } - } - - /// Registers a [`Resource`] described by `descriptor`. - /// - /// # Note - /// - /// If this method is called multiple times with identical descriptors, a distinct [`ComponentId`] - /// will be created for each one. - /// - /// # See also - /// - /// * [`Components::resource_id()`] - /// * [`Components::register_resource()`] - pub fn register_resource_with_descriptor( - &mut self, - descriptor: ComponentDescriptor, - ) -> ComponentId { - Components::register_resource_inner(&mut self.components, descriptor) - } - - /// Registers a [non-send resource](crate::system::NonSend) of type `T` with this instance. - /// If a resource of this type has already been registered, this will return - /// the ID of the pre-existing resource. - #[inline] - pub fn register_non_send(&mut self) -> ComponentId { - // SAFETY: The [`ComponentDescriptor`] matches the [`TypeId`] - unsafe { - self.get_or_register_resource_with(TypeId::of::(), || { - ComponentDescriptor::new_non_send::(StorageType::default()) - }) - } - } - /// # Safety /// - /// The [`ComponentDescriptor`] must match the [`TypeId`] + /// The [`ComponentDescriptor`] must match the [`TypeId`]. + /// The [`ComponentId`] must be unique. + /// The [`TypeId`] and [`ComponentId`] must not be registered or queued. #[inline] - unsafe fn get_or_register_resource_with( + unsafe fn register_resource_unchecked_with( &mut self, type_id: TypeId, + component_id: ComponentId, func: impl FnOnce() -> ComponentDescriptor, - ) -> ComponentId { - let components = &mut self.components; - *self.resource_indices.entry(type_id).or_insert_with(|| { - let descriptor = func(); - Components::register_resource_inner(components, descriptor) - }) - } - - #[inline] - fn register_resource_inner( - components: &mut Vec, - descriptor: ComponentDescriptor, - ) -> ComponentId { - let component_id = ComponentId(components.len()); - components.push(ComponentInfo::new(component_id, descriptor)); - component_id + ) { + // SAFETY: ensured by caller + unsafe { + self.register_component_inner(component_id, func()); + } + let prev = self.resource_indices.insert(type_id, component_id); + debug_assert!(prev.is_none()); } - /// Gets an iterator over all components registered with this instance. - pub fn iter(&self) -> impl Iterator + '_ { - self.components.iter() + /// Gets an iterator over all components fully registered with this instance. + pub fn iter_registered(&self) -> impl Iterator + '_ { + self.components.iter().filter_map(Option::as_ref) } } @@ -1838,7 +2509,7 @@ impl ComponentTicks { /// Manually sets the change tick. /// - /// This is normally done automatically via the [`DerefMut`](std::ops::DerefMut) implementation + /// This is normally done automatically via the [`DerefMut`] implementation /// on [`Mut`](crate::change_detection::Mut), [`ResMut`](crate::change_detection::ResMut), etc. /// However, components and resources that make use of interior mutability might require manual updates. /// @@ -1879,7 +2550,7 @@ impl ComponentIdFor<'_, T> { } } -impl core::ops::Deref for ComponentIdFor<'_, T> { +impl Deref for ComponentIdFor<'_, T> { type Target = ComponentId; fn deref(&self) -> &Self::Target { &self.0.component_id @@ -2021,7 +2692,7 @@ impl RequiredComponents { /// is smaller than the depth of the existing registration. Otherwise, the new registration will be ignored. pub fn register( &mut self, - components: &mut Components, + components: &mut ComponentsRegistrator, constructor: fn() -> C, inheritance_depth: u16, ) { diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 8d415d7469183..cbf88e26a1d5e 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -229,7 +229,7 @@ mod tests { y: SparseStored, } let mut ids = Vec::new(); - ::component_ids(&mut world.components, &mut |id| { + ::component_ids(&mut world.components_registrator(), &mut |id| { ids.push(id); }); @@ -279,7 +279,7 @@ mod tests { } let mut ids = Vec::new(); - ::component_ids(&mut world.components, &mut |id| { + ::component_ids(&mut world.components_registrator(), &mut |id| { ids.push(id); }); @@ -331,9 +331,12 @@ mod tests { } let mut ids = Vec::new(); - ::component_ids(&mut world.components, &mut |id| { - ids.push(id); - }); + ::component_ids( + &mut world.components_registrator(), + &mut |id| { + ids.push(id); + }, + ); assert_eq!(ids, &[world.register_component::(),]); diff --git a/crates/bevy_ecs/src/observer/runner.rs b/crates/bevy_ecs/src/observer/runner.rs index fdb3b4fa800ba..a89b046b751e7 100644 --- a/crates/bevy_ecs/src/observer/runner.rs +++ b/crates/bevy_ecs/src/observer/runner.rs @@ -435,7 +435,7 @@ fn hook_on_add>( world.commands().queue(move |world: &mut World| { let event_id = E::register_component_id(world); let mut components = Vec::new(); - B::component_ids(&mut world.components, &mut |id| { + B::component_ids(&mut world.components_registrator(), &mut |id| { components.push(id); }); let mut descriptor = ObserverDescriptor { diff --git a/crates/bevy_ecs/src/query/mod.rs b/crates/bevy_ecs/src/query/mod.rs index 9ed6995876f12..5b0126dd5061e 100644 --- a/crates/bevy_ecs/src/query/mod.rs +++ b/crates/bevy_ecs/src/query/mod.rs @@ -864,7 +864,7 @@ mod tests { } fn init_state(world: &mut World) -> Self::State { - world.components.register_resource::() + world.components_registrator().register_resource::() } fn get_state(components: &Components) -> Option { diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index d67ca188e8172..59764b6cb9d9c 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -146,7 +146,7 @@ impl Schedules { /// Ignore system order ambiguities caused by conflicts on [`Resource`]s of type `T`. pub fn allow_ambiguous_resource(&mut self, world: &mut World) { self.ignored_scheduling_ambiguities - .insert(world.components.register_resource::()); + .insert(world.components_registrator().register_resource::()); } /// Iterate through the [`ComponentId`]'s that will be ignored. diff --git a/crates/bevy_ecs/src/spawn.rs b/crates/bevy_ecs/src/spawn.rs index 2fb04c4c7b293..cf4f8b6fee8f9 100644 --- a/crates/bevy_ecs/src/spawn.rs +++ b/crates/bevy_ecs/src/spawn.rs @@ -175,7 +175,7 @@ unsafe impl + Send + Sync + 'static> Bundle for SpawnRelatedBundle { fn component_ids( - components: &mut crate::component::Components, + components: &mut crate::component::ComponentsRegistrator, ids: &mut impl FnMut(crate::component::ComponentId), ) { ::component_ids(components, ids); @@ -189,7 +189,7 @@ unsafe impl + Send + Sync + 'static> Bundle } fn register_required_components( - components: &mut crate::component::Components, + components: &mut crate::component::ComponentsRegistrator, required_components: &mut crate::component::RequiredComponents, ) { ::register_required_components( @@ -244,7 +244,7 @@ impl DynamicBundle for SpawnOneRelated { // SAFETY: This internally relies on the RelationshipTarget's Bundle implementation, which is sound. unsafe impl Bundle for SpawnOneRelated { fn component_ids( - components: &mut crate::component::Components, + components: &mut crate::component::ComponentsRegistrator, ids: &mut impl FnMut(crate::component::ComponentId), ) { ::component_ids(components, ids); @@ -258,7 +258,7 @@ unsafe impl Bundle for SpawnOneRelated { } fn register_required_components( - components: &mut crate::component::Components, + components: &mut crate::component::ComponentsRegistrator, required_components: &mut crate::component::RequiredComponents, ) { ::register_required_components( diff --git a/crates/bevy_ecs/src/storage/table/mod.rs b/crates/bevy_ecs/src/storage/table/mod.rs index 159a716c4ed05..d211bc10dae68 100644 --- a/crates/bevy_ecs/src/storage/table/mod.rs +++ b/crates/bevy_ecs/src/storage/table/mod.rs @@ -822,7 +822,7 @@ impl Drop for Table { mod tests { use crate::{ change_detection::MaybeLocation, - component::{Component, Components, Tick}, + component::{Component, ComponentIds, Components, ComponentsRegistrator, Tick}, entity::Entity, ptr::OwningPtr, storage::{TableBuilder, TableId, TableRow, Tables}, @@ -847,7 +847,11 @@ mod tests { #[test] fn table() { let mut components = Components::default(); - let component_id = components.register_component::>(); + let mut componentids = ComponentIds::default(); + // SAFETY: They are both new. + let mut registrator = + unsafe { ComponentsRegistrator::new(&mut components, &mut componentids) }; + let component_id = registrator.register_component::>(); let columns = &[component_id]; let mut table = TableBuilder::with_capacity(0, columns.len()) .add_column(components.get_info(component_id).unwrap()) diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index c30cc191dd4b0..ddfcddaa76e47 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -817,7 +817,7 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> { type Item<'w, 's> = Res<'w, T>; fn init_state(world: &mut World, system_meta: &mut SystemMeta) -> Self::State { - let component_id = world.components.register_resource::(); + let component_id = world.components_registrator().register_resource::(); let archetype_component_id = world.initialize_resource_internal(component_id).id(); let combined_access = system_meta.component_access_set.combined_access(); @@ -926,7 +926,7 @@ unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> { type Item<'w, 's> = ResMut<'w, T>; fn init_state(world: &mut World, system_meta: &mut SystemMeta) -> Self::State { - let component_id = world.components.register_resource::(); + let component_id = world.components_registrator().register_resource::(); let archetype_component_id = world.initialize_resource_internal(component_id).id(); let combined_access = system_meta.component_access_set.combined_access(); @@ -1499,7 +1499,7 @@ unsafe impl<'a, T: 'static> SystemParam for NonSend<'a, T> { fn init_state(world: &mut World, system_meta: &mut SystemMeta) -> Self::State { system_meta.set_non_send(); - let component_id = world.components.register_non_send::(); + let component_id = world.components_registrator().register_non_send::(); let archetype_component_id = world.initialize_non_send_internal(component_id).id(); let combined_access = system_meta.component_access_set.combined_access(); @@ -1605,7 +1605,7 @@ unsafe impl<'a, T: 'static> SystemParam for NonSendMut<'a, T> { fn init_state(world: &mut World, system_meta: &mut SystemMeta) -> Self::State { system_meta.set_non_send(); - let component_id = world.components.register_non_send::(); + let component_id = world.components_registrator().register_non_send::(); let archetype_component_id = world.initialize_non_send_internal(component_id).id(); let combined_access = system_meta.component_access_set.combined_access(); diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index 7c6d8ac3603b5..8ce03f2e028bc 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -5,7 +5,10 @@ use crate::{ DynamicBundle, InsertMode, }, change_detection::{MaybeLocation, MutUntyped}, - component::{Component, ComponentId, ComponentTicks, Components, Mutable, StorageType}, + component::{ + Component, ComponentId, ComponentTicks, Components, ComponentsRegistrator, Mutable, + StorageType, + }, entity::{ Entities, Entity, EntityBorrow, EntityCloner, EntityClonerBuilder, EntityLocation, TrustedEntityBorrow, @@ -1769,8 +1772,10 @@ impl<'w> EntityWorldMut<'w> { self.assert_not_despawned(); let world = &mut self.world; let storages = &mut world.storages; - let components = &mut world.components; - let bundle_id = world.bundles.register_info::(components, storages); + // SAFETY: These come from the same world. + let mut registrator = + unsafe { ComponentsRegistrator::new(&mut world.components, &mut world.component_ids) }; + let bundle_id = world.bundles.register_info::(&mut registrator, storages); // SAFETY: We just ensured this bundle exists let bundle_info = unsafe { world.bundles.get_unchecked(bundle_id) }; let old_location = self.location; @@ -1780,7 +1785,7 @@ impl<'w> EntityWorldMut<'w> { bundle_info.remove_bundle_from_archetype( &mut world.archetypes, storages, - components, + ®istrator, &world.observers, old_location.archetype_id, false, @@ -2052,8 +2057,14 @@ impl<'w> EntityWorldMut<'w> { pub(crate) fn remove_with_caller(&mut self, caller: MaybeLocation) -> &mut Self { self.assert_not_despawned(); let storages = &mut self.world.storages; - let components = &mut self.world.components; - let bundle_info = self.world.bundles.register_info::(components, storages); + // SAFETY: These come from the same world. + let mut registrator = unsafe { + ComponentsRegistrator::new(&mut self.world.components, &mut self.world.component_ids) + }; + let bundle_info = self + .world + .bundles + .register_info::(&mut registrator, storages); // SAFETY: the `BundleInfo` is initialized above self.location = unsafe { self.remove_bundle(bundle_info, caller) }; @@ -2078,10 +2089,13 @@ impl<'w> EntityWorldMut<'w> { ) -> &mut Self { self.assert_not_despawned(); let storages = &mut self.world.storages; - let components = &mut self.world.components; + // SAFETY: These come from the same world. + let mut registrator = unsafe { + ComponentsRegistrator::new(&mut self.world.components, &mut self.world.component_ids) + }; let bundles = &mut self.world.bundles; - let bundle_id = bundles.register_contributed_bundle_info::(components, storages); + let bundle_id = bundles.register_contributed_bundle_info::(&mut registrator, storages); // SAFETY: the dynamic `BundleInfo` is initialized above self.location = unsafe { self.remove_bundle(bundle_id, caller) }; @@ -2107,9 +2121,15 @@ impl<'w> EntityWorldMut<'w> { self.assert_not_despawned(); let archetypes = &mut self.world.archetypes; let storages = &mut self.world.storages; - let components = &mut self.world.components; + // SAFETY: These come from the same world. + let mut registrator = unsafe { + ComponentsRegistrator::new(&mut self.world.components, &mut self.world.component_ids) + }; - let retained_bundle = self.world.bundles.register_info::(components, storages); + let retained_bundle = self + .world + .bundles + .register_info::(&mut registrator, storages); // SAFETY: `retained_bundle` exists as we just initialized it. let retained_bundle_info = unsafe { self.world.bundles.get_unchecked(retained_bundle) }; let old_location = self.location; @@ -2123,7 +2143,7 @@ impl<'w> EntityWorldMut<'w> { let remove_bundle = self.world .bundles - .init_dynamic_info(&mut self.world.storages, components, to_remove); + .init_dynamic_info(&mut self.world.storages, ®istrator, to_remove); // SAFETY: the `BundleInfo` for the components to remove is initialized above self.location = unsafe { self.remove_bundle(remove_bundle, caller) }; diff --git a/crates/bevy_ecs/src/world/filtered_resource.rs b/crates/bevy_ecs/src/world/filtered_resource.rs index e5197e05703b6..dead18ff1104a 100644 --- a/crates/bevy_ecs/src/world/filtered_resource.rs +++ b/crates/bevy_ecs/src/world/filtered_resource.rs @@ -542,7 +542,7 @@ impl<'w> FilteredResourcesBuilder<'w> { /// Add accesses required to read the resource of the given type. pub fn add_read(&mut self) -> &mut Self { - let component_id = self.world.components.register_resource::(); + let component_id = self.world.components_registrator().register_resource::(); self.add_read_by_id(component_id) } @@ -588,7 +588,7 @@ impl<'w> FilteredResourcesMutBuilder<'w> { /// Add accesses required to read the resource of the given type. pub fn add_read(&mut self) -> &mut Self { - let component_id = self.world.components.register_resource::(); + let component_id = self.world.components_registrator().register_resource::(); self.add_read_by_id(component_id) } @@ -606,7 +606,7 @@ impl<'w> FilteredResourcesMutBuilder<'w> { /// Add accesses required to get mutable access to the resource of the given type. pub fn add_write(&mut self) -> &mut Self { - let component_id = self.world.components.register_resource::(); + let component_id = self.world.components_registrator().register_resource::(); self.add_write_by_id(component_id) } diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 10db34bac3cde..44962a47080ca 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -38,8 +38,9 @@ use crate::{ }, change_detection::{MaybeLocation, MutUntyped, TicksMut}, component::{ - Component, ComponentDescriptor, ComponentHooks, ComponentId, ComponentInfo, ComponentTicks, - Components, Mutable, RequiredComponents, RequiredComponentsError, Tick, + Component, ComponentDescriptor, ComponentHooks, ComponentId, ComponentIds, ComponentInfo, + ComponentTicks, Components, ComponentsQueuedRegistrator, ComponentsRegistrator, Mutable, + RequiredComponents, RequiredComponentsError, Tick, }, entity::{ AllocAtWithoutReplacement, Entities, Entity, EntityDoesNotExistError, EntityLocation, @@ -90,6 +91,7 @@ pub struct World { id: WorldId, pub(crate) entities: Entities, pub(crate) components: Components, + pub(crate) component_ids: ComponentIds, pub(crate) archetypes: Archetypes, pub(crate) storages: Storages, pub(crate) bundles: Bundles, @@ -120,6 +122,7 @@ impl Default for World { last_check_tick: Tick::new(0), last_trigger_id: 0, command_queue: RawCommandQueue::new(), + component_ids: ComponentIds::default(), }; world.bootstrap(); world @@ -221,6 +224,22 @@ impl World { &self.components } + /// Prepares a [`ComponentsQueuedRegistrator`] for the world. + /// **NOTE:** [`ComponentsQueuedRegistrator`] is easily misused. + /// See its docs for important notes on when and how it should be used. + #[inline] + pub fn components_queue(&self) -> ComponentsQueuedRegistrator { + // SAFETY: These are from the same world. + unsafe { ComponentsQueuedRegistrator::new(&self.components, &self.component_ids) } + } + + /// Prepares a [`ComponentsRegistrator`] for the world. + #[inline] + pub fn components_registrator(&mut self) -> ComponentsRegistrator { + // SAFETY: These are from the same world. + unsafe { ComponentsRegistrator::new(&mut self.components, &mut self.component_ids) } + } + /// Retrieves this world's [`Storages`] collection. #[inline] pub fn storages(&self) -> &Storages { @@ -253,7 +272,7 @@ impl World { /// In most cases, you don't need to call this method directly since component registration /// happens automatically during system initialization. pub fn register_component(&mut self) -> ComponentId { - self.components.register_component::() + self.components_registrator().register_component::() } /// Registers a component type as "disabling", @@ -537,7 +556,7 @@ impl World { &mut self, descriptor: ComponentDescriptor, ) -> ComponentId { - self.components + self.components_registrator() .register_component_with_descriptor(descriptor) } @@ -577,7 +596,7 @@ impl World { /// to insert the [`Resource`] in the [`World`], use [`World::init_resource`] or /// [`World::insert_resource`] instead. pub fn register_resource(&mut self) -> ComponentId { - self.components.register_resource::() + self.components_registrator().register_resource::() } /// Returns the [`ComponentId`] of the given [`Resource`] type `T`. @@ -1574,7 +1593,7 @@ impl World { &mut self, descriptor: ComponentDescriptor, ) -> ComponentId { - self.components + self.components_registrator() .register_resource_with_descriptor(descriptor) } @@ -1589,7 +1608,7 @@ impl World { #[track_caller] pub fn init_resource(&mut self) -> ComponentId { let caller = MaybeLocation::caller(); - let component_id = self.components.register_resource::(); + let component_id = self.components_registrator().register_resource::(); if self .storages .resources @@ -1626,7 +1645,7 @@ impl World { value: R, caller: MaybeLocation, ) { - let component_id = self.components.register_resource::(); + let component_id = self.components_registrator().register_resource::(); OwningPtr::make(value, |ptr| { // SAFETY: component_id was just initialized and corresponds to resource of type R. unsafe { @@ -1650,7 +1669,7 @@ impl World { #[track_caller] pub fn init_non_send_resource(&mut self) -> ComponentId { let caller = MaybeLocation::caller(); - let component_id = self.components.register_non_send::(); + let component_id = self.components_registrator().register_non_send::(); if self .storages .non_send_resources @@ -1681,7 +1700,7 @@ impl World { #[track_caller] pub fn insert_non_send_resource(&mut self, value: R) { let caller = MaybeLocation::caller(); - let component_id = self.components.register_non_send::(); + let component_id = self.components_registrator().register_non_send::(); OwningPtr::make(value, |ptr| { // SAFETY: component_id was just initialized and corresponds to resource of type R. unsafe { @@ -1964,7 +1983,7 @@ impl World { let change_tick = self.change_tick(); let last_change_tick = self.last_change_tick(); - let component_id = self.components.register_resource::(); + let component_id = self.components_registrator().register_resource::(); let data = self.initialize_resource_internal(component_id); if !data.is_present() { OwningPtr::make(func(), |ptr| { @@ -2022,7 +2041,7 @@ impl World { let change_tick = self.change_tick(); let last_change_tick = self.last_change_tick(); - let component_id = self.components.register_resource::(); + let component_id = self.components_registrator().register_resource::(); if self .storages .resources @@ -2177,12 +2196,14 @@ impl World { B: Bundle, { self.flush(); - let change_tick = self.change_tick(); + // SAFETY: These come from the same world. `Self.components_registrator` can't be used since we borrow other fields too. + let mut registrator = + unsafe { ComponentsRegistrator::new(&mut self.components, &mut self.component_ids) }; let bundle_id = self .bundles - .register_info::(&mut self.components, &mut self.storages); + .register_info::(&mut registrator, &mut self.storages); enum SpawnOrInsert<'w> { Spawn(BundleSpawner<'w>), Insert(BundleInserter<'w>, ArchetypeId), @@ -2349,9 +2370,12 @@ impl World { self.flush(); let change_tick = self.change_tick(); + // SAFETY: These come from the same world. `Self.components_registrator` can't be used since we borrow other fields too. + let mut registrator = + unsafe { ComponentsRegistrator::new(&mut self.components, &mut self.component_ids) }; let bundle_id = self .bundles - .register_info::(&mut self.components, &mut self.storages); + .register_info::(&mut registrator, &mut self.storages); let mut batch_iter = batch.into_iter(); @@ -2491,9 +2515,12 @@ impl World { self.flush(); let change_tick = self.change_tick(); + // SAFETY: These come from the same world. `Self.components_registrator` can't be used since we borrow other fields too. + let mut registrator = + unsafe { ComponentsRegistrator::new(&mut self.components, &mut self.component_ids) }; let bundle_id = self .bundles - .register_info::(&mut self.components, &mut self.storages); + .register_info::(&mut registrator, &mut self.storages); let mut invalid_entities = Vec::::new(); let mut batch_iter = batch.into_iter(); @@ -2814,12 +2841,22 @@ impl World { } } + /// Applies any queued component registration. + /// For spawning vanilla rust component types and resources, this is not strictly necessary. + /// However, flushing components can make information available more quickly, and can have performance benefits. + /// Additionally, for components and resources registered dynamically through a raw descriptor or similar, + /// this is the only way to complete their registration. + pub(crate) fn flush_components(&mut self) { + self.components_registrator().apply_queued_registrations(); + } + /// Flushes queued entities and commands. /// /// Queued entities will be spawned, and then commands will be applied. #[inline] pub fn flush(&mut self) { self.flush_entities(); + self.flush_components(); self.flush_commands(); } @@ -3053,9 +3090,12 @@ impl World { /// component in the bundle. #[inline] pub fn register_bundle(&mut self) -> &BundleInfo { + // SAFETY: These come from the same world. `Self.components_registrator` can't be used since we borrow other fields too. + let mut registrator = + unsafe { ComponentsRegistrator::new(&mut self.components, &mut self.component_ids) }; let id = self .bundles - .register_info::(&mut self.components, &mut self.storages); + .register_info::(&mut registrator, &mut self.storages); // SAFETY: We just initialized the bundle so its id should definitely be valid. unsafe { self.bundles.get(id).debug_checked_unwrap() } }