Runtime required components (#15458)

# Objective

Fixes #15367.

Currently, required components can only be defined through the `require`
macro attribute. While this should be used in most cases, there are also
several instances where you may want to define requirements at runtime,
commonly in plugins.

Example use cases:

- Require components only if the relevant optional plugins are enabled.
For example, a `SleepTimer` component (for physics) is only relevant if
the `SleepPlugin` is enabled.
- Third party crates can define their own requirements for first party
types. For example, "each `Handle<Mesh>` should require my custom
rendering data components". This also gets around the orphan rule.
- Generic plugins that add marker components based on the existence of
other components, like a generic `ColliderPlugin<C: AnyCollider>` that
wants to add a `ColliderMarker` component for all types of colliders.
- This is currently relevant for the retained render world in #15320.
The `ExtractComponentPlugin<C>` should add `SyncToRenderWorld` to all
components that should be extracted. This is currently done with
observers, which is more expensive than required components, and causes
archetype moves.
- Replace some built-in components with custom versions. For example, if
`GlobalTransform` required `Transform` through `TransformPlugin`, but we
wanted to use a `CustomTransform` type, we could replace
`TransformPlugin` with our own plugin. (This specific example isn't
good, but there are likely better use cases where this may be useful)

See #15367 for more in-depth reasoning.

## Solution

Add `register_required_components::<T, R>` and
`register_required_components_with::<T, R>` methods for `Default` and
custom constructors respectively. These methods exist on `App` and
`World`.

```rust
struct BirdPlugin;

impl Plugin for BirdPlugin {
    fn plugin(app: &mut App) {
        // Make `Bird` require `Wings` with a `Default` constructor.
        app.register_required_components::<Bird, Wings>();

        // Make `Wings` require `FlapSpeed` with a custom constructor.
        // Fun fact: Some hummingbirds can flutter their wings 80 times per second!
        app.register_required_components_with::<Wings, FlapSpeed>(|| FlapSpeed::from_duration(1.0 / 80.0));
    }
}
```

The custom constructor is a function pointer to match the `require` API,
though it could take a raw value too.

Requirement inheritance works similarly as with the `require` attribute.
If `Bird` required `FlapSpeed` directly, it would take precedence over
indirectly requiring it through `Wings`. The same logic applies to all
levels of the inheritance tree.

Note that registering the same component requirement more than once will
panic, similarly to trying to add multiple component hooks of the same
type to the same component. This avoids constructor conflicts and
confusing ordering issues.

### Implementation

Runtime requirements have two additional challenges in comparison to the
`require` attribute.

1. The `require` attribute uses recursion and macros with clever
ordering to populate hash maps of required components for each component
type. The expected semantics are that "more specific" requirements
override ones deeper in the inheritance tree. However, at runtime, there
is no representation of how "specific" each requirement is.
2. If you first register the requirement `X -> Y`, and later register `Y
-> Z`, then `X` should also indirectly require `Z`. However, `Y` itself
doesn't know that it is required by `X`, so it's not aware that it
should update the list of required components for `X`.

My solutions to these problems are:

1. Store the depth in the inheritance tree for each entry of a given
component's `RequiredComponents`. This is used to determine how
"specific" each requirement is. For `require`-based registration, these
depths are computed as part of the recursion.
2. Store and maintain a `required_by` list in each component's
`ComponentInfo`, next to `required_components`. For `require`-based
registration, these are also added after each registration, as part of
the recursion.

When calling `register_required_components`, it works as follows:

1. Get the required components of `Foo`, and check that `Bar` isn't
already a *direct* requirement.
3. Register `Bar` as a required component for `Foo`, and add `Foo` to
the `required_by` list for `Bar`.
4. Find and register all indirect requirements inherited from `Bar`,
adding `Foo` to the `required_by` list for each component.
5. Iterate through components that require `Foo`, registering the new
inherited requires for them as indirect requirements.

The runtime registration is likely slightly more expensive than the
`require` version, but it is a one-time cost, and quite negligible in
practice, unless projects have hundreds or thousands of runtime
requirements. I have not benchmarked this however.

This does also add a small amount of extra cost to the `require`
attribute for updating `required_by` lists, but I expect it to be very
minor.

## Testing

I added some tests that are copies of the `require` versions, as well as
some tests that are more specific to the runtime implementation. I might
add a few more tests though.

## Discussion

- Is `register_required_components` a good name? Originally I went for
`register_component_requirement` to be consistent with
`register_component_hooks`, but the general feature is often referred to
as "required components", which is why I changed it to
`register_required_components`.
- Should we *not* panic for duplicate requirements? If so, should they
just be ignored, or should the latest registration overwrite earlier
ones?
- If we do want to panic for duplicate, conflicting registrations,
should we at least not panic if the registrations are *exactly* the
same, i.e. same component and same constructor? The current
implementation panics for all duplicate direct registrations regardless
of the constructor.

## Next Steps

- Allow `register_required_components` to take a `Bundle` instead of a
single required component.
    - I could also try to do it in this PR if that would be preferable.
- Not directly related, but archetype invariants?
This commit is contained in:
Joona Aalto 2024-09-30 22:20:16 +03:00 committed by GitHub
parent 66717b04d3
commit f3e8ae03cd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 1151 additions and 21 deletions

View file

@ -4,6 +4,7 @@ use crate::{
};
pub use bevy_derive::AppLabel;
use bevy_ecs::{
component::RequiredComponentsError,
event::{event_update_system, EventCursor},
intern::Interned,
prelude::*,
@ -753,6 +754,260 @@ impl App {
self
}
/// Registers the given component `R` as a [required component] for `T`.
///
/// When `T` is added to an entity, `R` and its own required components will also be added
/// if `R` was not already provided. The [`Default`] `constructor` will be used for the creation of `R`.
/// If a custom constructor is desired, use [`App::register_required_components_with`] instead.
///
/// For the non-panicking version, see [`App::try_register_required_components`].
///
/// Note that requirements must currently be registered before `T` is inserted into the world
/// for the first time. Commonly, this is done in plugins. This limitation may be fixed in the future.
///
/// [required component]: Component#required-components
///
/// # Panics
///
/// Panics if `R` is already a directly required component for `T`, or if `T` has ever been added
/// on an entity before the registration.
///
/// Indirect requirements through other components are allowed. In those cases, any existing requirements
/// will only be overwritten if the new requirement is more specific.
///
/// # Example
///
/// ```
/// # use bevy_app::{App, NoopPluginGroup as MinimalPlugins, Startup};
/// # use bevy_ecs::prelude::*;
/// #[derive(Component)]
/// struct A;
///
/// #[derive(Component, Default, PartialEq, Eq, Debug)]
/// struct B(usize);
///
/// #[derive(Component, Default, PartialEq, Eq, Debug)]
/// struct C(u32);
///
/// # let mut app = App::new();
/// # app.add_plugins(MinimalPlugins).add_systems(Startup, setup);
/// // Register B as required by A and C as required by B.
/// app.register_required_components::<A, B>();
/// app.register_required_components::<B, C>();
///
/// fn setup(mut commands: Commands) {
/// // This will implicitly also insert B and C with their Default constructors.
/// commands.spawn(A);
/// }
///
/// fn validate(query: Query<(&A, &B, &C)>) {
/// let (a, b, c) = query.single();
/// assert_eq!(b, &B(0));
/// assert_eq!(c, &C(0));
/// }
/// # app.update();
/// ```
pub fn register_required_components<T: Component, R: Component + Default>(
&mut self,
) -> &mut Self {
self.world_mut().register_required_components::<T, R>();
self
}
/// Registers the given component `R` as a [required component] for `T`.
///
/// When `T` is added to an entity, `R` and its own required components will also be added
/// if `R` was not already provided. The given `constructor` will be used for the creation of `R`.
/// If a [`Default`] constructor is desired, use [`App::register_required_components`] instead.
///
/// For the non-panicking version, see [`App::try_register_required_components_with`].
///
/// Note that requirements must currently be registered before `T` is inserted into the world
/// for the first time. Commonly, this is done in plugins. This limitation may be fixed in the future.
///
/// [required component]: Component#required-components
///
/// # Panics
///
/// Panics if `R` is already a directly required component for `T`, or if `T` has ever been added
/// on an entity before the registration.
///
/// Indirect requirements through other components are allowed. In those cases, any existing requirements
/// will only be overwritten if the new requirement is more specific.
///
/// # Example
///
/// ```
/// # use bevy_app::{App, NoopPluginGroup as MinimalPlugins, Startup};
/// # use bevy_ecs::prelude::*;
/// #[derive(Component)]
/// struct A;
///
/// #[derive(Component, Default, PartialEq, Eq, Debug)]
/// struct B(usize);
///
/// #[derive(Component, Default, PartialEq, Eq, Debug)]
/// struct C(u32);
///
/// # let mut app = App::new();
/// # app.add_plugins(MinimalPlugins).add_systems(Startup, setup);
/// // Register B and C as required by A and C as required by B.
/// // A requiring C directly will overwrite the indirect requirement through B.
/// app.register_required_components::<A, B>();
/// app.register_required_components_with::<B, C>(|| C(1));
/// app.register_required_components_with::<A, C>(|| C(2));
///
/// fn setup(mut commands: Commands) {
/// // This will implicitly also insert B with its Default constructor and C
/// // with the custom constructor defined by A.
/// commands.spawn(A);
/// }
///
/// fn validate(query: Query<(&A, &B, &C)>) {
/// let (a, b, c) = query.single();
/// assert_eq!(b, &B(0));
/// assert_eq!(c, &C(2));
/// }
/// # app.update();
/// ```
pub fn register_required_components_with<T: Component, R: Component>(
&mut self,
constructor: fn() -> R,
) -> &mut Self {
self.world_mut()
.register_required_components_with::<T, R>(constructor);
self
}
/// Tries to register the given component `R` as a [required component] for `T`.
///
/// When `T` is added to an entity, `R` and its own required components will also be added
/// if `R` was not already provided. The [`Default`] `constructor` will be used for the creation of `R`.
/// If a custom constructor is desired, use [`App::register_required_components_with`] instead.
///
/// For the panicking version, see [`App::register_required_components`].
///
/// Note that requirements must currently be registered before `T` is inserted into the world
/// for the first time. Commonly, this is done in plugins. This limitation may be fixed in the future.
///
/// [required component]: Component#required-components
///
/// # Errors
///
/// Returns a [`RequiredComponentsError`] if `R` is already a directly required component for `T`, or if `T` has ever been added
/// on an entity before the registration.
///
/// Indirect requirements through other components are allowed. In those cases, any existing requirements
/// will only be overwritten if the new requirement is more specific.
///
/// # Example
///
/// ```
/// # use bevy_app::{App, NoopPluginGroup as MinimalPlugins, Startup};
/// # use bevy_ecs::prelude::*;
/// #[derive(Component)]
/// struct A;
///
/// #[derive(Component, Default, PartialEq, Eq, Debug)]
/// struct B(usize);
///
/// #[derive(Component, Default, PartialEq, Eq, Debug)]
/// struct C(u32);
///
/// # let mut app = App::new();
/// # app.add_plugins(MinimalPlugins).add_systems(Startup, setup);
/// // Register B as required by A and C as required by B.
/// app.register_required_components::<A, B>();
/// app.register_required_components::<B, C>();
///
/// // Duplicate registration! This will fail.
/// assert!(app.try_register_required_components::<A, B>().is_err());
///
/// fn setup(mut commands: Commands) {
/// // This will implicitly also insert B and C with their Default constructors.
/// commands.spawn(A);
/// }
///
/// fn validate(query: Query<(&A, &B, &C)>) {
/// let (a, b, c) = query.single();
/// assert_eq!(b, &B(0));
/// assert_eq!(c, &C(0));
/// }
/// # app.update();
/// ```
pub fn try_register_required_components<T: Component, R: Component + Default>(
&mut self,
) -> Result<(), RequiredComponentsError> {
self.world_mut().try_register_required_components::<T, R>()
}
/// Tries to register the given component `R` as a [required component] for `T`.
///
/// When `T` is added to an entity, `R` and its own required components will also be added
/// if `R` was not already provided. The given `constructor` will be used for the creation of `R`.
/// If a [`Default`] constructor is desired, use [`App::register_required_components`] instead.
///
/// For the panicking version, see [`App::register_required_components_with`].
///
/// Note that requirements must currently be registered before `T` is inserted into the world
/// for the first time. Commonly, this is done in plugins. This limitation may be fixed in the future.
///
/// [required component]: Component#required-components
///
/// # Errors
///
/// Returns a [`RequiredComponentsError`] if `R` is already a directly required component for `T`, or if `T` has ever been added
/// on an entity before the registration.
///
/// Indirect requirements through other components are allowed. In those cases, any existing requirements
/// will only be overwritten if the new requirement is more specific.
///
/// # Example
///
/// ```
/// # use bevy_app::{App, NoopPluginGroup as MinimalPlugins, Startup};
/// # use bevy_ecs::prelude::*;
/// #[derive(Component)]
/// struct A;
///
/// #[derive(Component, Default, PartialEq, Eq, Debug)]
/// struct B(usize);
///
/// #[derive(Component, Default, PartialEq, Eq, Debug)]
/// struct C(u32);
///
/// # let mut app = App::new();
/// # app.add_plugins(MinimalPlugins).add_systems(Startup, setup);
/// // Register B and C as required by A and C as required by B.
/// // A requiring C directly will overwrite the indirect requirement through B.
/// app.register_required_components::<A, B>();
/// app.register_required_components_with::<B, C>(|| C(1));
/// app.register_required_components_with::<A, C>(|| C(2));
///
/// // Duplicate registration! Even if the constructors were different, this would fail.
/// assert!(app.try_register_required_components_with::<B, C>(|| C(1)).is_err());
///
/// fn setup(mut commands: Commands) {
/// // This will implicitly also insert B with its Default constructor and C
/// // with the custom constructor defined by A.
/// commands.spawn(A);
/// }
///
/// fn validate(query: Query<(&A, &B, &C)>) {
/// let (a, b, c) = query.single();
/// assert_eq!(b, &B(0));
/// assert_eq!(c, &C(2));
/// }
/// # app.update();
/// ```
pub fn try_register_required_components_with<T: Component, R: Component>(
&mut self,
constructor: fn() -> R,
) -> Result<(), RequiredComponentsError> {
self.world_mut()
.try_register_required_components_with::<T, R>(constructor)
}
/// Returns a reference to the [`World`].
pub fn world(&self) -> &World {
self.main().world()

View file

@ -59,11 +59,9 @@ pub struct SystemInfo {
not(feature = "dynamic_linking")
))]
pub mod internal {
use alloc::sync::Arc;
use bevy_ecs::{prelude::ResMut, system::Local};
use std::{
sync::{Arc, Mutex},
time::Instant,
};
use std::{sync::Mutex, time::Instant};
use bevy_app::{App, First, Startup, Update};
use bevy_ecs::system::Resource;

View file

@ -82,15 +82,31 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
for require in requires {
let ident = &require.path;
register_recursive_requires.push(quote! {
<#ident as Component>::register_required_components(components, storages, required_components);
<#ident as Component>::register_required_components(
requiree,
components,
storages,
required_components,
inheritance_depth + 1
);
});
if let Some(func) = &require.func {
register_required.push(quote! {
required_components.register(components, storages, || { let x: #ident = #func().into(); x });
components.register_required_components_manual::<Self, #ident>(
storages,
required_components,
|| { let x: #ident = #func().into(); x },
inheritance_depth
);
});
} else {
register_required.push(quote! {
required_components.register(components, storages, <#ident as Default>::default);
components.register_required_components_manual::<Self, #ident>(
storages,
required_components,
<#ident as Default>::default,
inheritance_depth
);
});
}
}
@ -117,9 +133,11 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
impl #impl_generics #bevy_ecs_path::component::Component for #struct_name #type_generics #where_clause {
const STORAGE_TYPE: #bevy_ecs_path::component::StorageType = #storage;
fn register_required_components(
requiree: #bevy_ecs_path::component::ComponentId,
components: &mut #bevy_ecs_path::component::Components,
storages: &mut #bevy_ecs_path::storage::Storages,
required_components: &mut #bevy_ecs_path::component::RequiredComponents
required_components: &mut #bevy_ecs_path::component::RequiredComponents,
inheritance_depth: u16,
) {
#(#register_required)*
#(#register_recursive_requires)*

View file

@ -224,7 +224,14 @@ unsafe impl<C: Component> Bundle for C {
storages: &mut Storages,
required_components: &mut RequiredComponents,
) {
<C as Component>::register_required_components(components, storages, required_components);
let component_id = components.register_component::<C>(storages);
<C as Component>::register_required_components(
component_id,
components,
storages,
required_components,
0,
);
}
fn get_component_ids(components: &Components, ids: &mut impl FnMut(Option<ComponentId>)) {
@ -412,7 +419,7 @@ impl BundleInfo {
// This adds required components to the component_ids list _after_ using that list to remove explicitly provided
// components. This ordering is important!
component_ids.push(component_id);
v
v.constructor
})
.collect();

View file

@ -6,6 +6,7 @@ use crate::{
bundle::BundleInfo,
change_detection::MAX_CHANGE_AGE,
entity::Entity,
query::DebugCheckedUnwrap,
storage::{SparseSetIndex, SparseSets, Storages, Table, TableRow},
system::{Local, Resource, SystemParam},
world::{DeferredWorld, FromWorld, World},
@ -15,7 +16,7 @@ pub use bevy_ecs_macros::Component;
use bevy_ptr::{OwningPtr, UnsafeCellDeref};
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::Reflect;
use bevy_utils::{HashMap, TypeIdMap};
use bevy_utils::{HashMap, HashSet, TypeIdMap};
#[cfg(feature = "track_change_detection")]
use core::panic::Location;
use core::{
@ -26,6 +27,7 @@ use core::{
marker::PhantomData,
mem::needs_drop,
};
use thiserror::Error;
/// A data type that can be used to store data for an [entity].
///
@ -232,6 +234,48 @@ use core::{
/// 1. Specifying a required component constructor for Foo directly on a spawned component Bar will result in that constructor being used (and overriding existing constructors lower in the inheritance tree). This is the classic "inheritance override" behavior people expect.
/// 2. For cases where "multiple inheritance" results in constructor clashes, Components should be listed in "importance order". List a component earlier in the requirement list to initialize its inheritance tree earlier.
///
/// ## Registering required components at runtime
///
/// In most cases, required components should be registered using the `require` attribute as shown above.
/// However, in some cases, it may be useful to register required components at runtime.
///
/// This can be done through [`World::register_required_components`] or [`World::register_required_components_with`]
/// for the [`Default`] and custom constructors respectively:
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #[derive(Component)]
/// struct A;
///
/// #[derive(Component, Default, PartialEq, Eq, Debug)]
/// struct B(usize);
///
/// #[derive(Component, PartialEq, Eq, Debug)]
/// struct C(u32);
///
/// # let mut world = World::default();
/// // Register B as required by A and C as required by B.
/// world.register_required_components::<A, B>();
/// world.register_required_components_with::<B, C>(|| C(2));
///
/// // This will implicitly also insert B with its Default constructor
/// // and C with the custom constructor defined by B.
/// let id = world.spawn(A).id();
/// assert_eq!(&B(0), world.entity(id).get::<B>().unwrap());
/// assert_eq!(&C(2), world.entity(id).get::<C>().unwrap());
/// ```
///
/// Similar rules as before apply to duplicate requires fer a given type at different levels
/// of the inheritance tree. `A` requiring `C` directly would take precedence over indirectly
/// requiring it through `A` requiring `B` and `B` requiring `C`.
///
/// Unlike with the `require` attribute, directly requiring the same component multiple times
/// for the same component will result in a panic. This is done to prevent conflicting constructors
/// and confusing ordering dependencies.
///
/// Note that requirements must currently be registered before the requiring component is inserted
/// into the world for the first time. Registering requirements after this will lead to a panic.
///
/// # Adding component's hooks
///
/// See [`ComponentHooks`] for a detailed explanation of component's hooks.
@ -339,9 +383,11 @@ pub trait Component: Send + Sync + 'static {
/// Registers required components.
fn register_required_components(
_component_id: ComponentId,
_components: &mut Components,
_storages: &mut Storages,
_required_components: &mut RequiredComponents,
_inheritance_depth: u16,
) {
}
}
@ -555,6 +601,7 @@ pub struct ComponentInfo {
descriptor: ComponentDescriptor,
hooks: ComponentHooks,
required_components: RequiredComponents,
required_by: HashSet<ComponentId>,
}
impl ComponentInfo {
@ -615,6 +662,7 @@ impl ComponentInfo {
descriptor,
hooks: Default::default(),
required_components: Default::default(),
required_by: Default::default(),
}
}
@ -866,7 +914,7 @@ impl Components {
};
if registered {
let mut required_components = RequiredComponents::default();
T::register_required_components(self, storages, &mut required_components);
T::register_required_components(id, self, storages, &mut required_components, 0);
let info = &mut self.components[id.index()];
T::register_component_hooks(&mut info.hooks);
info.required_components = required_components;
@ -952,6 +1000,255 @@ impl Components {
self.components.get_mut(id.0).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)
.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<R: Component>(
&mut self,
required: ComponentId,
requiree: 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);
// SAFETY: The caller ensures that the `requiree` and `required` components are valid.
let inherited_requirements =
unsafe { self.register_inherited_required_components(requiree, required) };
// Propagate the new required components up the chain to all components that require the requiree.
if let Some(required_by) = self.get_required_by(requiree).cloned() {
for &required_by_id in required_by.iter() {
// SAFETY: The component is in the list of required components, so it must exist already.
let required_components = unsafe {
self.get_required_components_mut(required_by_id)
.debug_checked_unwrap()
};
// Register the original required component for the requiree.
// The inheritance depth is `1` since this is a component required by the original requiree.
required_components.register_by_id(required, constructor, 1);
for (component_id, component) in inherited_requirements.iter() {
// Register the required component.
// The inheritance depth is increased by `1` since this is a component required by the original required component.
// SAFETY: Component ID and constructor match the ones on the original requiree.
// The original requiree is responsible for making sure the registration is safe.
unsafe {
required_components.register_dynamic(
*component_id,
component.constructor.clone(),
component.inheritance_depth + 1,
);
};
}
}
}
Ok(())
}
/// Registers the components inherited from `required` for the given `requiree`,
/// returning the requirements in a list.
///
/// # Safety
///
/// The given component IDs `requiree` and `required` must be valid.
unsafe fn register_inherited_required_components(
&mut self,
requiree: ComponentId,
required: ComponentId,
) -> Vec<(ComponentId, RequiredComponent)> {
// Get required components inherited from the `required` component.
// SAFETY: The caller ensures that the `required` component is valid.
let required_component_info = unsafe { self.get_info(required).debug_checked_unwrap() };
let inherited_requirements: Vec<(ComponentId, RequiredComponent)> = required_component_info
.required_components()
.0
.iter()
.map(|(component_id, required_component)| {
(
*component_id,
RequiredComponent {
constructor: required_component.constructor.clone(),
// Add `1` to the inheritance depth since this will be registered
// for the component that requires `required`.
inheritance_depth: required_component.inheritance_depth + 1,
},
)
})
.collect();
// Register the new required components.
for (component_id, component) in inherited_requirements.iter().cloned() {
// SAFETY: The caller ensures that the `requiree` is valid.
let required_components = unsafe {
self.get_required_components_mut(requiree)
.debug_checked_unwrap()
};
// Register the required component for the requiree.
// SAFETY: Component ID and constructor match the ones on the original requiree.
unsafe {
required_components.register_dynamic(
component_id,
component.constructor,
component.inheritance_depth,
);
};
// Add the requiree to the list of components that require the required component.
// SAFETY: The caller ensures that the required components are valid.
let required_by = unsafe {
self.get_required_by_mut(component_id)
.debug_checked_unwrap()
};
required_by.insert(requiree);
}
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` as a [required component] for `T`,
/// and adds `T` to the list of requirees for `R`.
///
/// 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.
///
/// This method does *not* recursively register required components for components required by `R`,
/// nor does it register them for 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<T: Component, R: Component>(
&mut self,
storages: &mut Storages,
required_components: &mut RequiredComponents,
constructor: fn() -> R,
inheritance_depth: u16,
) {
let requiree = self.register_component::<T>(storages);
let required = self.register_component::<R>(storages);
// SAFETY: We just created the components.
unsafe {
self.register_required_components_manual_unchecked::<R>(
requiree,
required,
required_components,
constructor,
inheritance_depth,
);
}
}
/// Registers the given component `R` as a [required component] for `T`,
/// and adds `T` to the list of requirees for `R`.
///
/// 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.
///
/// This method does *not* recursively register required components for components required by `R`,
/// nor does it register them for components that require `T`.
///
/// [required component]: Component#required-components
///
/// # Safety
///
/// The given component IDs `required` and `requiree` must be valid.
pub(crate) unsafe fn register_required_components_manual_unchecked<R: Component>(
&mut self,
requiree: ComponentId,
required: ComponentId,
required_components: &mut RequiredComponents,
constructor: fn() -> R,
inheritance_depth: u16,
) {
// Components cannot require themselves.
if required == requiree {
return;
}
// Register the required component `R` for the requiree.
required_components.register_by_id(required, constructor, inheritance_depth);
// Add the requiree to the list of components that require `R`.
// SAFETY: The caller ensures that the component ID is valid.
// Assuming it is valid, 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);
}
#[inline]
pub(crate) fn get_required_by(&self, id: ComponentId) -> Option<&HashSet<ComponentId>> {
self.components.get(id.0).map(|info| &info.required_by)
}
#[inline]
pub(crate) fn get_required_by_mut(
&mut self,
id: ComponentId,
) -> Option<&mut HashSet<ComponentId>> {
self.components
.get_mut(id.0)
.map(|info| &mut info.required_by)
}
/// Type-erased equivalent of [`Components::component_id()`].
#[inline]
pub fn get_id(&self, type_id: TypeId) -> Option<ComponentId> {
@ -1325,6 +1622,18 @@ impl<T: Component> FromWorld for InitComponentId<T> {
}
}
/// An error returned when the registration of a required component fails.
#[derive(Error, Debug)]
#[non_exhaustive]
pub enum RequiredComponentsError {
/// The component is already a directly required component for the requiree.
#[error("Component {0:?} already directly requires component {1:?}")]
DuplicateRegistration(ComponentId, ComponentId),
/// An archetype with the component that requires other components already exists
#[error("An archetype with the component {0:?} that requires other components already exists")]
ArchetypeExists(ComponentId),
}
/// A Required Component constructor. See [`Component`] for details.
#[cfg(feature = "track_change_detection")]
#[derive(Clone)]
@ -1370,11 +1679,30 @@ impl RequiredComponentConstructor {
}
}
/// Metadata associated with a required component. See [`Component`] for details.
#[derive(Clone)]
pub struct RequiredComponent {
/// The constructor used for the required component.
pub constructor: RequiredComponentConstructor,
/// The depth of the component requirement in the requirement hierarchy for this component.
/// This is used for determining which constructor is used in cases where there are duplicate requires.
///
/// For example, consider the inheritance tree `X -> Y -> Z`, where `->` indicates a requirement.
/// `X -> Y` and `Y -> Z` are direct requirements with a depth of 0, while `Z` is only indirectly
/// required for `X` with a depth of `1`.
///
/// In cases where there are multiple conflicting requirements with the same depth, a higher priority
/// will be given to components listed earlier in the `require` attribute, or to the latest added requirement
/// if registered at runtime.
pub inheritance_depth: u16,
}
/// The collection of metadata for components that are required for a given component.
///
/// For more information, see the "Required Components" section of [`Component`].
#[derive(Default, Clone)]
pub struct RequiredComponents(pub(crate) HashMap<ComponentId, RequiredComponentConstructor>);
pub struct RequiredComponents(pub(crate) HashMap<ComponentId, RequiredComponent>);
impl Debug for RequiredComponents {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
@ -1385,8 +1713,10 @@ impl Debug for RequiredComponents {
}
impl RequiredComponents {
/// Registers a required component. If the component is already registered, the new registration
/// passed in the arguments will be ignored.
/// Registers a required component.
///
/// If the component is already registered, it will be overwritten if the given inheritance depth
/// is smaller than the depth of the existing registration. Otherwise, the new registration will be ignored.
///
/// # Safety
///
@ -1398,19 +1728,48 @@ impl RequiredComponents {
&mut self,
component_id: ComponentId,
constructor: RequiredComponentConstructor,
inheritance_depth: u16,
) {
self.0.entry(component_id).or_insert(constructor);
self.0
.entry(component_id)
.and_modify(|component| {
if component.inheritance_depth > inheritance_depth {
// New registration is more specific than existing requirement
component.constructor = constructor.clone();
component.inheritance_depth = inheritance_depth;
}
})
.or_insert(RequiredComponent {
constructor,
inheritance_depth,
});
}
/// Registers a required component. If the component is already registered, the new registration
/// passed in the arguments will be ignored.
/// Registers a required component.
///
/// If the component is already registered, it will be overwritten if the given inheritance depth
/// is smaller than the depth of the existing registration. Otherwise, the new registration will be ignored.
pub fn register<C: Component>(
&mut self,
components: &mut Components,
storages: &mut Storages,
constructor: fn() -> C,
inheritance_depth: u16,
) {
let component_id = components.register_component::<C>(storages);
self.register_by_id(component_id, constructor, inheritance_depth);
}
/// Registers the [`Component`] with the given ID as required if it exists.
///
/// If the component is already registered, it will be overwritten if the given inheritance depth
/// is smaller than the depth of the existing registration. Otherwise, the new registration will be ignored.
pub fn register_by_id<C: Component>(
&mut self,
component_id: ComponentId,
constructor: fn() -> C,
inheritance_depth: u16,
) {
let erased: RequiredComponentConstructor = RequiredComponentConstructor(Arc::new(
move |table,
sparse_sets,
@ -1445,7 +1804,7 @@ impl RequiredComponents {
// `erased` initializes a component for `component_id` in such a way that
// matches the storage type of the component. It only uses the given `table_row` or `Entity` to
// initialize the storage corresponding to the given entity.
unsafe { self.register_dynamic(component_id, erased) };
unsafe { self.register_dynamic(component_id, erased, inheritance_depth) };
}
/// Iterates the ids of all required components. This includes recursive required components.

View file

@ -82,6 +82,7 @@ pub mod prelude {
#[cfg(test)]
mod tests {
use crate as bevy_ecs;
use crate::component::{RequiredComponents, RequiredComponentsError};
use crate::{
bundle::Bundle,
change_detection::Ref,
@ -92,7 +93,7 @@ mod tests {
system::Resource,
world::{EntityMut, EntityRef, Mut, World},
};
use alloc::sync::Arc;
use alloc::{sync::Arc, vec};
use bevy_ecs_macros::{VisitEntities, VisitEntitiesMut};
use bevy_tasks::{ComputeTaskPool, TaskPool};
use bevy_utils::HashSet;
@ -2028,6 +2029,259 @@ mod tests {
assert!(e.contains::<Y>());
}
#[test]
fn runtime_required_components() {
// Same as `required_components` test but with runtime registration
#[derive(Component)]
struct X;
#[derive(Component)]
struct Y {
value: String,
}
#[derive(Component)]
struct Z(u32);
impl Default for Y {
fn default() -> Self {
Self {
value: "hello".to_string(),
}
}
}
let mut world = World::new();
world.register_required_components::<X, Y>();
world.register_required_components_with::<Y, Z>(|| Z(7));
let id = world.spawn(X).id();
assert_eq!(
"hello",
world.entity(id).get::<Y>().unwrap().value,
"Y should have the default value"
);
assert_eq!(
7,
world.entity(id).get::<Z>().unwrap().0,
"Z should have the value provided by the constructor defined in Y"
);
let id = world
.spawn((
X,
Y {
value: "foo".to_string(),
},
))
.id();
assert_eq!(
"foo",
world.entity(id).get::<Y>().unwrap().value,
"Y should have the manually provided value"
);
assert_eq!(
7,
world.entity(id).get::<Z>().unwrap().0,
"Z should have the value provided by the constructor defined in Y"
);
let id = world.spawn((X, Z(8))).id();
assert_eq!(
"hello",
world.entity(id).get::<Y>().unwrap().value,
"Y should have the default value"
);
assert_eq!(
8,
world.entity(id).get::<Z>().unwrap().0,
"Z should have the manually provided value"
);
}
#[test]
fn runtime_required_components_override_1() {
#[derive(Component)]
struct X;
#[derive(Component, Default)]
struct Y;
#[derive(Component)]
struct Z(u32);
let mut world = World::new();
// - X requires Y with default constructor
// - Y requires Z with custom constructor
// - X requires Z with custom constructor (more specific than X -> Y -> Z)
world.register_required_components::<X, Y>();
world.register_required_components_with::<Y, Z>(|| Z(5));
world.register_required_components_with::<X, Z>(|| Z(7));
let id = world.spawn(X).id();
assert_eq!(
7,
world.entity(id).get::<Z>().unwrap().0,
"Z should have the value provided by the constructor defined in X"
);
}
#[test]
fn runtime_required_components_override_2() {
// Same as `runtime_required_components_override_1` test but with different registration order
#[derive(Component)]
struct X;
#[derive(Component, Default)]
struct Y;
#[derive(Component)]
struct Z(u32);
let mut world = World::new();
// - X requires Y with default constructor
// - X requires Z with custom constructor (more specific than X -> Y -> Z)
// - Y requires Z with custom constructor
world.register_required_components::<X, Y>();
world.register_required_components_with::<X, Z>(|| Z(7));
world.register_required_components_with::<Y, Z>(|| Z(5));
let id = world.spawn(X).id();
assert_eq!(
7,
world.entity(id).get::<Z>().unwrap().0,
"Z should have the value provided by the constructor defined in X"
);
}
#[test]
fn runtime_required_components_existing_archetype() {
#[derive(Component)]
struct X;
#[derive(Component, Default)]
struct Y;
let mut world = World::new();
// Registering required components after the archetype has already been created should panic.
// This may change in the future.
world.spawn(X);
assert!(matches!(
world.try_register_required_components::<X, Y>(),
Err(RequiredComponentsError::ArchetypeExists(_))
));
}
#[test]
fn runtime_required_components_fail_with_duplicate() {
#[derive(Component)]
#[require(Y)]
struct X;
#[derive(Component, Default)]
struct Y;
let mut world = World::new();
// This should fail: Tried to register Y as a requirement for X, but the requirement already exists.
assert!(matches!(
world.try_register_required_components::<X, Y>(),
Err(RequiredComponentsError::DuplicateRegistration(_, _))
));
}
#[test]
fn required_components_inheritance_depth() {
// Test that inheritance depths are computed correctly for requirements.
//
// Requirements with `require` attribute:
//
// A -> B -> C
// 0 1
//
// Runtime requirements:
//
// X -> A -> B -> C
// 0 1 2
//
// X -> Y -> Z -> B -> C
// 0 1 2 3
#[derive(Component, Default)]
#[require(B)]
struct A;
#[derive(Component, Default)]
#[require(C)]
struct B;
#[derive(Component, Default)]
struct C;
#[derive(Component, Default)]
struct X;
#[derive(Component, Default)]
struct Y;
#[derive(Component, Default)]
struct Z;
let mut world = World::new();
let a = world.register_component::<A>();
let b = world.register_component::<B>();
let c = world.register_component::<C>();
let y = world.register_component::<Y>();
let z = world.register_component::<Z>();
world.register_required_components::<X, A>();
world.register_required_components::<X, Y>();
world.register_required_components::<Y, Z>();
world.register_required_components::<Z, B>();
world.spawn(X);
let required_a = world.get_required_components::<A>().unwrap();
let required_b = world.get_required_components::<B>().unwrap();
let required_c = world.get_required_components::<C>().unwrap();
let required_x = world.get_required_components::<X>().unwrap();
let required_y = world.get_required_components::<Y>().unwrap();
let required_z = world.get_required_components::<Z>().unwrap();
/// Returns the component IDs and inheritance depths of the required components
/// in ascending order based on the component ID.
fn to_vec(required: &RequiredComponents) -> Vec<(ComponentId, u16)> {
let mut vec = required
.0
.iter()
.map(|(id, component)| (*id, component.inheritance_depth))
.collect::<Vec<_>>();
vec.sort_by_key(|(id, _)| *id);
vec
}
// Check that the inheritance depths are correct for each component.
assert_eq!(to_vec(required_a), vec![(b, 0), (c, 1)]);
assert_eq!(to_vec(required_b), vec![(c, 0)]);
assert_eq!(to_vec(required_c), vec![]);
assert_eq!(
to_vec(required_x),
vec![(a, 0), (b, 1), (c, 2), (y, 0), (z, 1)]
);
assert_eq!(to_vec(required_y), vec![(b, 1), (c, 2), (z, 0)]);
assert_eq!(to_vec(required_z), vec![(b, 0), (c, 1)]);
}
// These structs are primarily compilation tests to test the derive macros. Because they are
// never constructed, we have to manually silence the `dead_code` lint.
#[allow(dead_code)]

View file

@ -31,7 +31,7 @@ use crate::{
change_detection::{MutUntyped, TicksMut},
component::{
Component, ComponentDescriptor, ComponentHooks, ComponentId, ComponentInfo, ComponentTicks,
Components, Tick,
Components, RequiredComponents, RequiredComponentsError, Tick,
},
entity::{AllocAtWithoutReplacement, Entities, Entity, EntityHashSet, EntityLocation},
event::{Event, EventId, Events, SendBatchIds},
@ -287,6 +287,245 @@ impl World {
self.components.get_hooks_mut(id)
}
/// Registers the given component `R` as a [required component] for `T`.
///
/// When `T` is added to an entity, `R` and its own required components will also be added
/// if `R` was not already provided. The [`Default`] `constructor` will be used for the creation of `R`.
/// If a custom constructor is desired, use [`World::register_required_components_with`] instead.
///
/// For the non-panicking version, see [`World::try_register_required_components`].
///
/// Note that requirements must currently be registered before `T` is inserted into the world
/// for the first time. This limitation may be fixed in the future.
///
/// [required component]: Component#required-components
///
/// # Panics
///
/// Panics if `R` is already a directly required component for `T`, or if `T` has ever been added
/// on an entity before the registration.
///
/// Indirect requirements through other components are allowed. In those cases, any existing requirements
/// will only be overwritten if the new requirement is more specific.
///
/// # Example
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #[derive(Component)]
/// struct A;
///
/// #[derive(Component, Default, PartialEq, Eq, Debug)]
/// struct B(usize);
///
/// #[derive(Component, Default, PartialEq, Eq, Debug)]
/// struct C(u32);
///
/// # let mut world = World::default();
/// // Register B as required by A and C as required by B.
/// world.register_required_components::<A, B>();
/// world.register_required_components::<B, C>();
///
/// // This will implicitly also insert B and C with their Default constructors.
/// let id = world.spawn(A).id();
/// assert_eq!(&B(0), world.entity(id).get::<B>().unwrap());
/// assert_eq!(&C(0), world.entity(id).get::<C>().unwrap());
/// ```
pub fn register_required_components<T: Component, R: Component + Default>(&mut self) {
self.try_register_required_components::<T, R>().unwrap();
}
/// Registers the given component `R` as a [required component] for `T`.
///
/// When `T` is added to an entity, `R` and its own required components will also be added
/// if `R` was not already provided. The given `constructor` will be used for the creation of `R`.
/// If a [`Default`] constructor is desired, use [`World::register_required_components`] instead.
///
/// For the non-panicking version, see [`World::try_register_required_components_with`].
///
/// Note that requirements must currently be registered before `T` is inserted into the world
/// for the first time. This limitation may be fixed in the future.
///
/// [required component]: Component#required-components
///
/// # Panics
///
/// Panics if `R` is already a directly required component for `T`, or if `T` has ever been added
/// on an entity before the registration.
///
/// Indirect requirements through other components are allowed. In those cases, any existing requirements
/// will only be overwritten if the new requirement is more specific.
///
/// # Example
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #[derive(Component)]
/// struct A;
///
/// #[derive(Component, Default, PartialEq, Eq, Debug)]
/// struct B(usize);
///
/// #[derive(Component, PartialEq, Eq, Debug)]
/// struct C(u32);
///
/// # let mut world = World::default();
/// // Register B and C as required by A and C as required by B.
/// // A requiring C directly will overwrite the indirect requirement through B.
/// world.register_required_components::<A, B>();
/// world.register_required_components_with::<B, C>(|| C(1));
/// world.register_required_components_with::<A, C>(|| C(2));
///
/// // This will implicitly also insert B with its Default constructor and C
/// // with the custom constructor defined by A.
/// let id = world.spawn(A).id();
/// assert_eq!(&B(0), world.entity(id).get::<B>().unwrap());
/// assert_eq!(&C(2), world.entity(id).get::<C>().unwrap());
/// ```
pub fn register_required_components_with<T: Component, R: Component>(
&mut self,
constructor: fn() -> R,
) {
self.try_register_required_components_with::<T, R>(constructor)
.unwrap();
}
/// Tries to register the given component `R` as a [required component] for `T`.
///
/// When `T` is added to an entity, `R` and its own required components will also be added
/// if `R` was not already provided. The [`Default`] `constructor` will be used for the creation of `R`.
/// If a custom constructor is desired, use [`World::register_required_components_with`] instead.
///
/// For the panicking version, see [`World::register_required_components`].
///
/// Note that requirements must currently be registered before `T` is inserted into the world
/// for the first time. This limitation may be fixed in the future.
///
/// [required component]: Component#required-components
///
/// # Errors
///
/// Returns a [`RequiredComponentsError`] if `R` is already a directly required component for `T`, or if `T` has ever been added
/// on an entity before the registration.
///
/// Indirect requirements through other components are allowed. In those cases, any existing requirements
/// will only be overwritten if the new requirement is more specific.
///
/// # Example
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #[derive(Component)]
/// struct A;
///
/// #[derive(Component, Default, PartialEq, Eq, Debug)]
/// struct B(usize);
///
/// #[derive(Component, Default, PartialEq, Eq, Debug)]
/// struct C(u32);
///
/// # let mut world = World::default();
/// // Register B as required by A and C as required by B.
/// world.register_required_components::<A, B>();
/// world.register_required_components::<B, C>();
///
/// // Duplicate registration! This will fail.
/// assert!(world.try_register_required_components::<A, B>().is_err());
///
/// // This will implicitly also insert B and C with their Default constructors.
/// let id = world.spawn(A).id();
/// assert_eq!(&B(0), world.entity(id).get::<B>().unwrap());
/// assert_eq!(&C(0), world.entity(id).get::<C>().unwrap());
/// ```
pub fn try_register_required_components<T: Component, R: Component + Default>(
&mut self,
) -> Result<(), RequiredComponentsError> {
self.try_register_required_components_with::<T, R>(R::default)
}
/// Tries to register the given component `R` as a [required component] for `T`.
///
/// When `T` is added to an entity, `R` and its own required components will also be added
/// if `R` was not already provided. The given `constructor` will be used for the creation of `R`.
/// If a [`Default`] constructor is desired, use [`World::register_required_components`] instead.
///
/// For the panicking version, see [`World::register_required_components_with`].
///
/// Note that requirements must currently be registered before `T` is inserted into the world
/// for the first time. This limitation may be fixed in the future.
///
/// [required component]: Component#required-components
///
/// # Errors
///
/// Returns a [`RequiredComponentsError`] if `R` is already a directly required component for `T`, or if `T` has ever been added
/// on an entity before the registration.
///
/// Indirect requirements through other components are allowed. In those cases, any existing requirements
/// will only be overwritten if the new requirement is more specific.
///
/// # Example
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #[derive(Component)]
/// struct A;
///
/// #[derive(Component, Default, PartialEq, Eq, Debug)]
/// struct B(usize);
///
/// #[derive(Component, PartialEq, Eq, Debug)]
/// struct C(u32);
///
/// # let mut world = World::default();
/// // Register B and C as required by A and C as required by B.
/// // A requiring C directly will overwrite the indirect requirement through B.
/// world.register_required_components::<A, B>();
/// world.register_required_components_with::<B, C>(|| C(1));
/// world.register_required_components_with::<A, C>(|| C(2));
///
/// // Duplicate registration! Even if the constructors were different, this would fail.
/// assert!(world.try_register_required_components_with::<B, C>(|| C(1)).is_err());
///
/// // This will implicitly also insert B with its Default constructor and C
/// // with the custom constructor defined by A.
/// let id = world.spawn(A).id();
/// assert_eq!(&B(0), world.entity(id).get::<B>().unwrap());
/// assert_eq!(&C(2), world.entity(id).get::<C>().unwrap());
/// ```
pub fn try_register_required_components_with<T: Component, R: Component>(
&mut self,
constructor: fn() -> R,
) -> Result<(), RequiredComponentsError> {
let requiree = self.register_component::<T>();
// TODO: Remove this panic and update archetype edges accordingly when required components are added
if self.archetypes().component_index().contains_key(&requiree) {
return Err(RequiredComponentsError::ArchetypeExists(requiree));
}
let required = self.register_component::<R>();
// SAFETY: We just created the `required` and `requiree` components.
unsafe {
self.components
.register_required_components::<R>(required, requiree, constructor)
}
}
/// Retrieves the [required components](RequiredComponents) for the given component type, if it exists.
pub fn get_required_components<C: Component>(&self) -> Option<&RequiredComponents> {
let id = self.components().component_id::<C>()?;
let component_info = self.components().get_info(id)?;
Some(component_info.required_components())
}
/// Retrieves the [required components](RequiredComponents) for the component of the given [`ComponentId`], if it exists.
pub fn get_required_components_by_id(&self, id: ComponentId) -> Option<&RequiredComponents> {
let component_info = self.components().get_info(id)?;
Some(component_info.required_components())
}
/// Registers a new [`Component`] type and returns the [`ComponentId`] created for it.
///
/// This method differs from [`World::register_component`] in that it uses a [`ComponentDescriptor`]