diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 4cdc3a2473d55..391e3eb0638a6 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -4,7 +4,7 @@ use crate::{ }; pub use bevy_derive::AppLabel; use bevy_ecs::{ - component::RequiredComponentsError, + component::{RelatedComponentsError, Relatedness}, event::{event_update_system, EventCursor}, intern::Interned, prelude::*, @@ -754,25 +754,25 @@ impl App { self } - /// Registers the given component `R` as a [required component] for `T`. + /// Registers the given component `R` as a [related component] for `T`. /// - /// When `T` is added to an entity, `R` and its own required components will also be added + /// When `T` is added to an entity, `R` and its own related 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. + /// If a custom constructor is desired, use [`App::register_related_components_with`] instead. /// - /// For the non-panicking version, see [`App::try_register_required_components`]. + /// For the non-panicking version, see [`App::try_register_related_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 + /// [related component]: Component#related-components /// /// # Panics /// - /// Panics if `R` is already a directly required component for `T`, or if `T` has ever been added + /// Panics if `R` is already a directly related 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 + /// Indirect relationships through other components are allowed. In those cases, any existing requirements /// will only be overwritten if the new requirement is more specific. /// /// # Example @@ -792,8 +792,8 @@ impl App { /// # 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::(); - /// app.register_required_components::(); + /// app.register_related_components::(); + /// app.register_related_components::(); /// /// fn setup(mut commands: Commands) { /// // This will implicitly also insert B and C with their Default constructors. @@ -807,32 +807,34 @@ impl App { /// } /// # app.update(); /// ``` - pub fn register_required_components( + pub fn register_related_components( &mut self, + relatedness: Relatedness, ) -> &mut Self { - self.world_mut().register_required_components::(); + self.world_mut() + .register_related_components::(relatedness); self } - /// Registers the given component `R` as a [required component] for `T`. + /// Registers the given component `R` as a [related component] for `T`. /// - /// When `T` is added to an entity, `R` and its own required components will also be added + /// When `T` is added to an entity, `R` and its own related 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. + /// If a [`Default`] constructor is desired, use [`App::register_related_components`] instead. /// - /// For the non-panicking version, see [`App::try_register_required_components_with`]. + /// For the non-panicking version, see [`App::try_register_related_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 + /// [related component]: Component#related-components /// /// # Panics /// - /// Panics if `R` is already a directly required component for `T`, or if `T` has ever been added + /// Panics if `R` is already a directly related 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 + /// Indirect relationships through other components are allowed. In those cases, any existing relationships /// will only be overwritten if the new requirement is more specific. /// /// # Example @@ -853,9 +855,9 @@ impl App { /// # 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::(); - /// app.register_required_components_with::(|| C(1)); - /// app.register_required_components_with::(|| C(2)); + /// app.register_related_components::(); + /// app.register_related_components_with::(|| C(1)); + /// app.register_related_components_with::(|| C(2)); /// /// fn setup(mut commands: Commands) { /// // This will implicitly also insert B with its Default constructor and C @@ -870,34 +872,35 @@ impl App { /// } /// # app.update(); /// ``` - pub fn register_required_components_with( + pub fn register_related_components_with( &mut self, constructor: fn() -> R, + relatedness: Relatedness, ) -> &mut Self { self.world_mut() - .register_required_components_with::(constructor); + .register_related_components_with::(constructor, relatedness); self } - /// Tries to register the given component `R` as a [required component] for `T`. + /// Tries to register the given component `R` as a [related component] for `T`. /// - /// When `T` is added to an entity, `R` and its own required components will also be added + /// When `T` is added to an entity, `R` and its own related 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. + /// If a custom constructor is desired, use [`App::register_related_components_with`] instead. /// - /// For the panicking version, see [`App::register_required_components`]. + /// For the panicking version, see [`App::register_related_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 + /// [related component]: Component#related-components /// /// # Errors /// - /// Returns a [`RequiredComponentsError`] if `R` is already a directly required component for `T`, or if `T` has ever been added + /// Returns a [`RelatedComponentsError`] if `R` is already a directly related 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 + /// Indirect relationships through other components are allowed. In those cases, any existing relationships /// will only be overwritten if the new requirement is more specific. /// /// # Example @@ -917,11 +920,11 @@ impl App { /// # 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::(); - /// app.register_required_components::(); + /// app.register_related_components::(); + /// app.register_related_components::(); /// /// // Duplicate registration! This will fail. - /// assert!(app.try_register_required_components::().is_err()); + /// assert!(app.try_register_related_components::().is_err()); /// /// fn setup(mut commands: Commands) { /// // This will implicitly also insert B and C with their Default constructors. @@ -935,31 +938,33 @@ impl App { /// } /// # app.update(); /// ``` - pub fn try_register_required_components( + pub fn try_register_related_components( &mut self, - ) -> Result<(), RequiredComponentsError> { - self.world_mut().try_register_required_components::() + relatedness: Relatedness, + ) -> Result<(), RelatedComponentsError> { + self.world_mut() + .try_register_related_components::(relatedness) } - /// Tries to register the given component `R` as a [required component] for `T`. + /// Tries to register the given component `R` as a [related component] for `T`. /// - /// When `T` is added to an entity, `R` and its own required components will also be added + /// When `T` is added to an entity, `R` and its own related 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. + /// If a [`Default`] constructor is desired, use [`App::register_related_components`] instead. /// - /// For the panicking version, see [`App::register_required_components_with`]. + /// For the panicking version, see [`App::register_related_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 + /// [related component]: Component#related-components /// /// # Errors /// - /// Returns a [`RequiredComponentsError`] if `R` is already a directly required component for `T`, or if `T` has ever been added + /// Returns a [`RelatedComponentsError`] if `R` is already a directly related 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 + /// Indirect relationships through other components are allowed. In those cases, any existing relationships /// will only be overwritten if the new requirement is more specific. /// /// # Example @@ -980,12 +985,12 @@ impl App { /// # 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::(); - /// app.register_required_components_with::(|| C(1)); - /// app.register_required_components_with::(|| C(2)); + /// app.register_related_components::(); + /// app.register_related_components_with::(|| C(1)); + /// app.register_related_components_with::(|| C(2)); /// /// // Duplicate registration! Even if the constructors were different, this would fail. - /// assert!(app.try_register_required_components_with::(|| C(1)).is_err()); + /// assert!(app.try_register_related_components_with::(|| C(1)).is_err()); /// /// fn setup(mut commands: Commands) { /// // This will implicitly also insert B with its Default constructor and C @@ -1000,12 +1005,13 @@ impl App { /// } /// # app.update(); /// ``` - pub fn try_register_required_components_with( + pub fn try_register_related_components_with( &mut self, constructor: fn() -> R, - ) -> Result<(), RequiredComponentsError> { + relatedness: Relatedness, + ) -> Result<(), RelatedComponentsError> { self.world_mut() - .try_register_required_components_with::(constructor) + .try_register_related_components_with::(constructor, relatedness) } /// Returns a reference to the [`World`]. diff --git a/crates/bevy_ecs/macros/src/component.rs b/crates/bevy_ecs/macros/src/component.rs index 08b4e73056635..e0ae5aeb7926f 100644 --- a/crates/bevy_ecs/macros/src/component.rs +++ b/crates/bevy_ecs/macros/src/component.rs @@ -75,100 +75,191 @@ pub fn derive_component(input: TokenStream) -> TokenStream { .predicates .push(parse_quote! { Self: Send + Sync + 'static }); - let requires = &attrs.requires; - let mut register_required = Vec::with_capacity(attrs.requires.iter().len()); - let mut register_recursive_requires = Vec::with_capacity(attrs.requires.iter().len()); - if let Some(requires) = requires { - for require in requires { + let RelatedComponentsReturn { + register_related: register_suggested, + register_recursive_related: register_recursive_suggested, + docs: suggested_component_docs, + } = related_components(&attrs.suggests, Relatedness::Suggested); + let RelatedComponentsReturn { + register_related: register_included, + register_recursive_related: register_recursive_included, + docs: included_component_docs, + } = related_components(&attrs.includes, Relatedness::Included); + let RelatedComponentsReturn { + register_related: register_required, + register_recursive_related: register_recursive_required, + docs: required_component_docs, + } = related_components(&attrs.requires, Relatedness::Required); + + let struct_name = &ast.ident; + let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl(); + + // This puts `register_required` before `register_recursive_requires` to ensure that the constructors of _all_ top + // level components are initialized first, giving them precedence over recursively defined constructors for the same component type + TokenStream::from(quote! { + #required_component_docs + #included_component_docs + #suggested_component_docs + 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_related_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::RelatedComponents, + inheritance_depth: u16, + ) { + #(#register_suggested)* + #(#register_recursive_suggested)* + + #(#register_included)* + #(#register_recursive_included)* + + #(#register_required)* + #(#register_recursive_required)* + } + + #[allow(unused_variables)] + fn register_component_hooks(hooks: &mut #bevy_ecs_path::component::ComponentHooks) { + #on_add + #on_insert + #on_replace + #on_remove + } + } + }) +} + +// Because the macro crate cannot access the `bevy_ecs` crate, we need to create our own equivalent. +// This should be kept in sync with the actual `bevy_ecs` crate! +enum Relatedness { + Suggested, + Included, + Required, +} + +impl Relatedness { + /// Returns the token that represents the corresponding `Relatedness` enum variant in `bevy_ecs`. + fn token(&self) -> TokenStream2 { + let bevy_ecs_path: Path = crate::bevy_ecs_path(); + match self { + Relatedness::Suggested => quote! { #bevy_ecs_path::component::Relatedness::Included }, + Relatedness::Included => quote! { #bevy_ecs_path::component::Relatedness::Included }, + Relatedness::Required => quote! { #bevy_ecs_path::component::Relatedness::Required }, + } + } + + /// Returns the stringified name of this `Relatedness` enum variant for use in docs. + fn doc_name(&self) -> &'static str { + match self { + Relatedness::Suggested => "Suggested", + Relatedness::Included => "Included", + Relatedness::Required => "Required", + } + } + + /// Returns the doc string fragment that explains the corresponding `Relatedness` enum variant. + fn doc_explanation(&self) -> &'static str { + match self { + Relatedness::Suggested => "This component might work well with the components listed above, unlocking new functionality.", + Relatedness::Included => "A component's Included Components are inserted whenever it is inserted. Note that this will also insert the included components _of_ the included components, recursively, in depth-first order.", + Relatedness::Required => "A component's Required Components are inserted whenever it is inserted. Note that this will also insert the required components _of_ the required components, recursively, in depth-first order. Unlike included components, this relationship cannot be removed.", + } + } +} + +struct RelatedComponentsReturn { + register_related: Vec, + register_recursive_related: Vec, + docs: Option, +} + +/// Generates the code needed to add related components to the component's related components list. +fn related_components( + attribute: &Option>, + relatedness: Relatedness, +) -> RelatedComponentsReturn { + let mut register_related = Vec::with_capacity(attribute.iter().len()); + let mut register_recursive_related = Vec::with_capacity(attribute.iter().len()); + + let relatedness_token = relatedness.token(); + + if let Some(related) = attribute { + for require in related { let ident = &require.path; - register_recursive_requires.push(quote! { - <#ident as Component>::register_required_components( + register_recursive_related.push(quote! { + <#ident as Component>::register_related_components( requiree, components, storages, required_components, - inheritance_depth + 1 + inheritance_depth + 1, ); }); match &require.func { Some(RequireFunc::Path(func)) => { - register_required.push(quote! { - components.register_required_components_manual::( + register_related.push(quote! { + components.register_related_components_manual::( storages, required_components, || { let x: #ident = #func().into(); x }, - inheritance_depth + inheritance_depth, + #relatedness_token, ); }); } Some(RequireFunc::Closure(func)) => { - register_required.push(quote! { - components.register_required_components_manual::( + register_related.push(quote! { + components.register_related_components_manual::( storages, required_components, || { let x: #ident = (#func)().into(); x }, - inheritance_depth + inheritance_depth, + #relatedness_token, ); }); } None => { - register_required.push(quote! { - components.register_required_components_manual::( + register_related.push(quote! { + components.register_related_components_manual::( storages, required_components, <#ident as Default>::default, - inheritance_depth + inheritance_depth, + #relatedness_token, ); }); } } } } - let struct_name = &ast.ident; - let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl(); - let required_component_docs = attrs.requires.map(|r| { + let docs = attribute.as_ref().map(|r| { let paths = r .iter() .map(|r| format!("[`{}`]", r.path.to_token_stream())) .collect::>() .join(", "); - let doc = format!("Required Components: {paths}. \n\n A component's Required Components are inserted whenever it is inserted. Note that this will also insert the required components _of_ the required components, recursively, in depth-first order."); + let doc_name = relatedness.doc_name(); + let doc_explanation = relatedness.doc_explanation(); + let doc = format!("{doc_name}: {paths}. \n\n {doc_explanation}"); quote! { #[doc = #doc] } }); - // This puts `register_required` before `register_recursive_requires` to ensure that the constructors of _all_ top - // level components are initialized first, giving them precedence over recursively defined constructors for the same component type - TokenStream::from(quote! { - #required_component_docs - 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, - inheritance_depth: u16, - ) { - #(#register_required)* - #(#register_recursive_requires)* - } - - #[allow(unused_variables)] - fn register_component_hooks(hooks: &mut #bevy_ecs_path::component::ComponentHooks) { - #on_add - #on_insert - #on_replace - #on_remove - } - } - }) + RelatedComponentsReturn { + register_related, + register_recursive_related, + docs, + } } pub const COMPONENT: &str = "component"; pub const STORAGE: &str = "storage"; + +pub const SUGGEST: &str = "suggest"; +pub const INCLUDE: &str = "include"; pub const REQUIRE: &str = "require"; pub const ON_ADD: &str = "on_add"; @@ -178,7 +269,9 @@ pub const ON_REMOVE: &str = "on_remove"; struct Attrs { storage: StorageTy, - requires: Option>, + suggests: Option>, + includes: Option>, + requires: Option>, on_add: Option, on_insert: Option, on_replace: Option, @@ -191,7 +284,7 @@ enum StorageTy { SparseSet, } -struct Require { +struct Related { path: Path, func: Option, } @@ -212,6 +305,8 @@ fn parse_component_attr(ast: &DeriveInput) -> Result { on_insert: None, on_replace: None, on_remove: None, + suggests: None, + includes: None, requires: None, }; @@ -246,9 +341,41 @@ fn parse_component_attr(ast: &DeriveInput) -> Result { Err(nested.error("Unsupported attribute")) } })?; + } else if attr.path().is_ident(SUGGEST) { + let punctuated = + attr.parse_args_with(Punctuated::::parse_terminated)?; + for suggest in punctuated.iter() { + if !require_paths.insert(suggest.path.to_token_stream().to_string()) { + return Err(syn::Error::new( + suggest.path.span(), + "Duplicate suggested components are not allowed.", + )); + } + } + if let Some(current) = &mut attrs.suggests { + current.extend(punctuated); + } else { + attrs.suggests = Some(punctuated); + } + } else if attr.path().is_ident(INCLUDE) { + let punctuated = + attr.parse_args_with(Punctuated::::parse_terminated)?; + for include in punctuated.iter() { + if !require_paths.insert(include.path.to_token_stream().to_string()) { + return Err(syn::Error::new( + include.path.span(), + "Duplicate included components are not allowed.", + )); + } + } + if let Some(current) = &mut attrs.includes { + current.extend(punctuated); + } else { + attrs.includes = Some(punctuated); + } } else if attr.path().is_ident(REQUIRE) { let punctuated = - attr.parse_args_with(Punctuated::::parse_terminated)?; + attr.parse_args_with(Punctuated::::parse_terminated)?; for require in punctuated.iter() { if !require_paths.insert(require.path.to_token_stream().to_string()) { return Err(syn::Error::new( @@ -268,7 +395,7 @@ fn parse_component_attr(ast: &DeriveInput) -> Result { Ok(attrs) } -impl Parse for Require { +impl Parse for Related { fn parse(input: syn::parse::ParseStream) -> Result { let path = input.parse::()?; let func = if input.peek(Paren) { @@ -283,7 +410,7 @@ impl Parse for Require { } else { None }; - Ok(Require { path, func }) + Ok(Related { path, func }) } } diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index 10f794075466c..23753297c75d3 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -91,7 +91,7 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream { <#field_type as #ecs_path::bundle::Bundle>::component_ids(components, storages, &mut *ids); }); field_required_components.push(quote! { - <#field_type as #ecs_path::bundle::Bundle>::register_required_components(components, storages, required_components); + <#field_type as #ecs_path::bundle::Bundle>::register_related_components(components, storages, required_components); }); field_get_component_ids.push(quote! { <#field_type as #ecs_path::bundle::Bundle>::get_component_ids(components, &mut *ids); @@ -159,10 +159,10 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream { } } - fn register_required_components( + fn register_related_components( components: &mut #ecs_path::component::Components, storages: &mut #ecs_path::storage::Storages, - required_components: &mut #ecs_path::component::RequiredComponents + required_components: &mut #ecs_path::component::RelatedComponents ){ #(#field_required_components)* } diff --git a/crates/bevy_ecs/src/archetype.rs b/crates/bevy_ecs/src/archetype.rs index 2c2241e29ec6d..9f9e6c46a43d5 100644 --- a/crates/bevy_ecs/src/archetype.rs +++ b/crates/bevy_ecs/src/archetype.rs @@ -21,7 +21,7 @@ use crate::{ bundle::BundleId, - component::{ComponentId, Components, RequiredComponentConstructor, StorageType}, + component::{ComponentId, Components, RelatedComponentConstructor, StorageType}, entity::{Entity, EntityLocation}, observer::Observers, storage::{ImmutableSparseSet, SparseArray, SparseSet, SparseSetIndex, TableId, TableRow}, @@ -126,7 +126,7 @@ pub(crate) struct AddBundle { /// The set of additional required components that must be initialized immediately when adding this Bundle. /// /// The initial values are determined based on the provided constructor, falling back to the `Default` trait if none is given. - pub required_components: Vec, + pub required_components: Vec, /// The components added by this bundle. This includes any Required Components that are inserted when adding this bundle. pub added: Vec, /// The components that were explicitly contributed by this bundle, but already existed in the archetype. This _does not_ include any @@ -229,7 +229,7 @@ impl Edges { bundle_id: BundleId, archetype_id: ArchetypeId, bundle_status: Vec, - required_components: Vec, + required_components: Vec, added: Vec, existing: Vec, ) { diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index 384b7517c77b2..4f2d91d55a4f1 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -10,7 +10,7 @@ use crate::{ SpawnBundleStatus, }, component::{ - Component, ComponentId, Components, RequiredComponentConstructor, RequiredComponents, + Component, ComponentId, Components, RelatedComponentConstructor, RelatedComponents, StorageType, Tick, }, entity::{Entities, Entity, EntityLocation}, @@ -171,10 +171,10 @@ pub unsafe trait Bundle: DynamicBundle + Send + Sync + 'static { Self: Sized; /// Registers components that are required by the components in this [`Bundle`]. - fn register_required_components( + fn register_related_components( _components: &mut Components, _storages: &mut Storages, - _required_components: &mut RequiredComponents, + _required_components: &mut RelatedComponents, ); } @@ -214,13 +214,13 @@ unsafe impl Bundle for C { unsafe { ptr.read() } } - fn register_required_components( + fn register_related_components( components: &mut Components, storages: &mut Storages, - required_components: &mut RequiredComponents, + required_components: &mut RelatedComponents, ) { let component_id = components.register_component::(storages); - ::register_required_components( + ::register_related_components( component_id, components, storages, @@ -273,12 +273,12 @@ macro_rules! tuple_impl { unsafe { ($(<$name as Bundle>::from_components(ctx, func),)*) } } - fn register_required_components( + fn register_related_components( _components: &mut Components, _storages: &mut Storages, - _required_components: &mut RequiredComponents, + _required_components: &mut RelatedComponents, ) { - $(<$name as Bundle>::register_required_components(_components, _storages, _required_components);)* + $(<$name as Bundle>::register_related_components(_components, _storages, _required_components);)* } } @@ -356,7 +356,7 @@ pub struct BundleInfo { /// and the range (0..`explicit_components_len`) must be in the same order as the source bundle /// type writes its components in. component_ids: Vec, - required_components: Vec, + required_components: Vec, explicit_components_len: usize, } @@ -401,15 +401,15 @@ impl BundleInfo { } let explicit_components_len = component_ids.len(); - let mut required_components = RequiredComponents::default(); + let mut related_components = RelatedComponents::default(); for component_id in component_ids.iter().copied() { // SAFETY: caller has verified that all ids are valid let info = unsafe { components.get_info_unchecked(component_id) }; - required_components.merge(info.required_components()); + related_components.merge(info.related_components()); } - required_components.remove_explicit_components(&component_ids); - let required_components = required_components - .0 + related_components.remove_explicit_components(&component_ids); + let required_components = related_components + .required_components .into_iter() .map(|(component_id, v)| { // This adds required components to the component_ids list _after_ using that list to remove explicitly provided @@ -504,7 +504,7 @@ impl BundleInfo { table: &mut Table, sparse_sets: &mut SparseSets, bundle_component_status: &S, - required_components: impl Iterator, + required_components: impl Iterator, entity: Entity, table_row: TableRow, change_tick: Tick, @@ -576,12 +576,12 @@ impl BundleInfo { } } - /// Internal method to initialize a required component from an [`OwningPtr`]. This should ultimately be called - /// in the context of [`BundleInfo::write_components`], via [`RequiredComponentConstructor::initialize`]. + /// Internal method to initialize a related component from an [`OwningPtr`]. This should ultimately be called + /// in the context of [`BundleInfo::write_components`], via [`RelatedComponentConstructor::initialize`]. /// /// # Safety /// - /// `component_ptr` must point to a required component value that matches the given `component_id`. The `storage_type` must match + /// `component_ptr` must point to a related component value that matches the given `component_id`. The `storage_type` must match /// the type associated with `component_id`. The `entity` and `table_row` must correspond to an entity with an uninitialized /// component matching `component_id`. /// @@ -589,7 +589,7 @@ impl BundleInfo { /// For more information, read the [`BundleInfo::write_components`] safety docs. /// This function inherits the safety requirements defined there. #[allow(clippy::too_many_arguments)] - pub(crate) unsafe fn initialize_required_component( + pub(crate) unsafe fn initialize_related_component( table: &mut Table, sparse_sets: &mut SparseSets, change_tick: Tick, diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index 4c0323f2a5844..8ec97c910c91d 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -99,9 +99,19 @@ use derive_more::derive::{Display, Error}; /// [`Table`]: crate::storage::Table /// [`SparseSet`]: crate::storage::SparseSet /// -/// # Required Components +/// # Related Components /// -/// Components can specify Required Components. If some [`Component`] `A` requires [`Component`] `B`, then when `A` is inserted, +/// Components can specify Related Components, which are intended to work together. +/// There are three levels of [`Relatedness`] whose functionality builds on previous levels, ranging from "helpful suggestion" to "manadatory": +/// +/// 1. [`Relatedness::Suggested`]: These components work well together, check it out! +/// 2. [`Relatedness::Included`]: These components are typically used together, so we'll include them for you upon insertion. +/// 3. [`Relatedness::Required`]: These components must be used together. Not only are they included upon insertion, you can't opt out. +/// +/// When using the `suggests`/`includes`/`required` attributes as part of the derive macro, +/// documentation will automatically be generated for the [`Component`] impl of your type. +/// +/// Additionally, if some [`Component`] `A` requires or includes [`Component`] `B`, then when `A` is inserted, /// `B` will _also_ be initialized and inserted (if it was not manually specified). /// /// The [`Default`] constructor will be used to initialize the component, by default: @@ -175,7 +185,7 @@ use derive_more::derive::{Display, Error}; /// assert_eq!(&C(20), world.entity(id).get::().unwrap()); /// ``` /// -/// Required components are _recursive_. This means, if a Required Component has required components, +/// Related components are _recursive_. This means, if a Related Component has related components, /// those components will _also_ be inserted if they are missing: /// /// ``` @@ -234,12 +244,12 @@ use derive_more::derive::{Display, Error}; /// 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 +/// ## Registering related components at runtime /// -/// In most cases, required components should be registered using the `require` attribute as shown above. +/// In most cases, required components should be registered using attributes like `require` 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`] +/// This can be done through [`World::register_related_components`] or [`World::register_related_components_with`] /// for the [`Default`] and custom constructors respectively: /// /// ``` @@ -255,8 +265,8 @@ use derive_more::derive::{Display, Error}; /// /// # let mut world = World::default(); /// // Register B as required by A and C as required by B. -/// world.register_required_components::(); -/// world.register_required_components_with::(|| C(2)); +/// world.register_related_components::(); +/// world.register_related_components_with::(|| C(2)); /// /// // This will implicitly also insert B with its Default constructor /// // and C with the custom constructor defined by B. @@ -381,12 +391,12 @@ pub trait Component: Send + Sync + 'static { /// Called when registering this component, allowing mutable access to its [`ComponentHooks`]. fn register_component_hooks(_hooks: &mut ComponentHooks) {} - /// Registers required components. - fn register_required_components( + /// Registers related components. + fn register_related_components( _component_id: ComponentId, _components: &mut Components, _storages: &mut Storages, - _required_components: &mut RequiredComponents, + _required_components: &mut RelatedComponents, _inheritance_depth: u16, ) { } @@ -600,7 +610,7 @@ pub struct ComponentInfo { id: ComponentId, descriptor: ComponentDescriptor, hooks: ComponentHooks, - required_components: RequiredComponents, + required_components: RelatedComponents, required_by: HashSet, } @@ -688,9 +698,9 @@ impl ComponentInfo { &self.hooks } - /// Retrieves the [`RequiredComponents`] collection, which contains all required components (and their constructors) + /// Retrieves the [`RelatedComponents`] collection, which contains all required components (and their constructors) /// needed by this component. This includes _recursive_ required components. - pub fn required_components(&self) -> &RequiredComponents { + pub fn related_components(&self) -> &RelatedComponents { &self.required_components } } @@ -913,8 +923,8 @@ impl Components { }) }; if registered { - let mut required_components = RequiredComponents::default(); - T::register_required_components(id, self, storages, &mut required_components, 0); + let mut required_components = RelatedComponents::default(); + T::register_related_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; @@ -1001,21 +1011,21 @@ impl Components { } #[inline] - pub(crate) fn get_required_components_mut( + pub(crate) fn get_related_components_mut( &mut self, id: ComponentId, - ) -> Option<&mut RequiredComponents> { + ) -> Option<&mut RelatedComponents> { 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`. + /// Registers the given component `R` and [related 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 + /// [related components]: Component#related-components /// /// # Safety /// @@ -1023,36 +1033,39 @@ impl Components { /// /// # Errors /// - /// Returns a [`RequiredComponentsError`] if the `required` component is already a directly required component for the `requiree`. + /// Returns a [`RelatedComponentsError`] 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( + pub(crate) unsafe fn register_related_components( &mut self, required: ComponentId, requiree: ComponentId, constructor: fn() -> R, - ) -> Result<(), RequiredComponentsError> { + relatedness: Relatedness, + ) -> Result<(), RelatedComponentsError> { // SAFETY: The caller ensures that the `requiree` is valid. - let required_components = unsafe { - self.get_required_components_mut(requiree) + let related_components = unsafe { + self.get_related_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( + let map = match relatedness { + Relatedness::Suggested => &related_components.suggested_components, + Relatedness::Included => &related_components.included_components, + Relatedness::Required => &related_components.required_components, + }; + + // Cannot directly create the same relation between components twice. + if map.get(&required).is_some_and(|c| c.inheritance_depth == 0) { + return Err(RelatedComponentsError::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); + related_components.register_by_id(required, constructor, 0, relatedness); // 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. @@ -1061,20 +1074,20 @@ impl Components { // SAFETY: The caller ensures that the `requiree` and `required` components are valid. let inherited_requirements = - unsafe { self.register_inherited_required_components(requiree, required) }; + unsafe { self.register_inherited_required_components(requiree, required, relatedness) }; // 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) + self.get_related_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); + required_components.register_by_id(required, constructor, 1, relatedness); for (component_id, component) in inherited_requirements.iter() { // Register the required component. @@ -1086,6 +1099,7 @@ impl Components { *component_id, component.constructor.clone(), component.inheritance_depth + 1, + relatedness, ); }; } @@ -1105,18 +1119,19 @@ impl Components { &mut self, requiree: ComponentId, required: ComponentId, - ) -> Vec<(ComponentId, RequiredComponent)> { + relatedness: Relatedness, + ) -> Vec<(ComponentId, RelatedComponent)> { // 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 + let inherited_requirements: Vec<(ComponentId, RelatedComponent)> = required_component_info + .related_components() + .required_components .iter() .map(|(component_id, required_component)| { ( *component_id, - RequiredComponent { + RelatedComponent { constructor: required_component.constructor.clone(), // Add `1` to the inheritance depth since this will be registered // for the component that requires `required`. @@ -1126,21 +1141,22 @@ impl Components { }) .collect(); - // Register the new required components. + // Register the new related 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) + self.get_related_components_mut(requiree) .debug_checked_unwrap() }; - // Register the required component for the requiree. + // Register the related 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, + relatedness, ); }; @@ -1159,44 +1175,46 @@ impl Components { // 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`, + /// Registers the given component `R` as a [related 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`, + /// This method does *not* recursively register related 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`], + /// Only use this method if you know what you are doing. In most cases, you should instead use [`World::register_related_components`], /// or the equivalent method in `bevy_app::App`. /// - /// [required component]: Component#required-components + /// [related component]: Component#related-components #[doc(hidden)] - pub fn register_required_components_manual( + pub fn register_related_components_manual( &mut self, storages: &mut Storages, - required_components: &mut RequiredComponents, + required_components: &mut RelatedComponents, constructor: fn() -> R, inheritance_depth: u16, + relatedness: Relatedness, ) { let requiree = self.register_component::(storages); let required = self.register_component::(storages); // SAFETY: We just created the components. unsafe { - self.register_required_components_manual_unchecked::( + self.register_related_components_manual_unchecked::( requiree, required, required_components, constructor, inheritance_depth, + relatedness, ); } } - /// Registers the given component `R` as a [required component] for `T`, + /// Registers the given component `R` as a [related 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. @@ -1206,18 +1224,19 @@ impl Components { /// 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 + /// [related component]: Component#related-components /// /// # Safety /// /// The given component IDs `required` and `requiree` must be valid. - pub(crate) unsafe fn register_required_components_manual_unchecked( + pub(crate) unsafe fn register_related_components_manual_unchecked( &mut self, requiree: ComponentId, required: ComponentId, - required_components: &mut RequiredComponents, + required_components: &mut RelatedComponents, constructor: fn() -> R, inheritance_depth: u16, + relatedness: Relatedness, ) { // Components cannot require themselves. if required == requiree { @@ -1225,7 +1244,7 @@ impl Components { } // Register the required component `R` for the requiree. - required_components.register_by_id(required, constructor, inheritance_depth); + required_components.register_by_id(required, constructor, inheritance_depth, relatedness); // Add the requiree to the list of components that require `R`. // SAFETY: The caller ensures that the component ID is valid. @@ -1622,11 +1641,13 @@ impl FromWorld for InitComponentId { } } -/// An error returned when the registration of a required component fails. +/// An error returned when the registration of a related component fails. +/// +/// See [`RelatedComponents`] for the place where this information is stored and [`Relatedness`] for the strictness of the relationship. #[derive(Error, Display, Debug)] #[non_exhaustive] -pub enum RequiredComponentsError { - /// The component is already a directly required component for the requiree. +pub enum RelatedComponentsError { + /// The component is already a directly related component for the requiree. #[display("Component {0:?} already directly requires component {_1:?}")] #[error(ignore)] DuplicateRegistration(ComponentId, ComponentId), @@ -1638,27 +1659,27 @@ pub enum RequiredComponentsError { ArchetypeExists(ComponentId), } -/// A Required Component constructor. See [`Component`] for details. +/// A Related Component constructor. See [`Component`] for details. #[cfg(feature = "track_change_detection")] #[derive(Clone)] -pub struct RequiredComponentConstructor( +pub struct RelatedComponentConstructor( pub Arc)>, ); -/// A Required Component constructor. See [`Component`] for details. +/// A Related Component constructor. See [`Component`] for details. #[cfg(not(feature = "track_change_detection"))] #[derive(Clone)] -pub struct RequiredComponentConstructor( +pub struct RelatedComponentConstructor( pub Arc, ); -impl RequiredComponentConstructor { +impl RelatedComponentConstructor { /// # Safety /// This is intended to only be called in the context of [`BundleInfo::write_components`] to initialized required components. /// Calling it _anywhere else_ should be considered unsafe. /// /// `table_row` and `entity` must correspond to a valid entity that currently needs a component initialized via the constructor stored - /// on this [`RequiredComponentConstructor`]. The stored constructor must correspond to a component on `entity` that needs initialization. + /// on this [`RelatedComponentConstructor`]. The stored constructor must correspond to a component on `entity` that needs initialization. /// `table` and `sparse_sets` must correspond to storages on a world where `entity` needs this required component initialized. /// /// Again, don't call this anywhere but [`BundleInfo::write_components`]. @@ -1683,11 +1704,13 @@ impl RequiredComponentConstructor { } } -/// Metadata associated with a required component. See [`Component`] for details. +/// Metadata associated with a required component. +/// +/// See [`Component`] for details on usage, and [`RelatedComponents`] for the storage. #[derive(Clone)] -pub struct RequiredComponent { - /// The constructor used for the required component. - pub constructor: RequiredComponentConstructor, +pub struct RelatedComponent { + /// The constructor used for the related component. + pub constructor: RelatedComponentConstructor, /// 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. @@ -1702,22 +1725,81 @@ pub struct RequiredComponent { pub inheritance_depth: u16, } -/// The collection of metadata for components that are required for a given component. +/// The collection of metadata for components that are related to a given component. /// -/// For more information, see the "Required Components" section of [`Component`]. +/// For usage information, see the "Related Components" section of [`Component`]. +/// For the metadata associated with a relayed component, see [`RelatedComponent`]. #[derive(Default, Clone)] -pub struct RequiredComponents(pub(crate) HashMap); +pub struct RelatedComponents { + // NOTE: these are stored in separate maps to allow for more efficient lookups. + // A double map keyed by [`Relatedness`] then [`ComponentId`] would be more ergonomic, + // but would require more expensive lookups. + /// The components corresponding to [`Relatedness::Suggested`]. + pub(crate) suggested_components: HashMap, + /// The components corresponding to [`Relatedness::Included`]. + pub(crate) included_components: HashMap, + /// The components corresponding to [`Relatedness::Required`]. + pub(crate) required_components: HashMap, +} -impl Debug for RequiredComponents { +impl Debug for RelatedComponents { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_tuple("RequiredComponents") - .field(&self.0.keys()) + f.debug_struct("RelatedComponents") + .field("Suggested", &self.suggested_components.keys()) + .field("Included", &self.included_components.keys()) + .field("Required", &self.required_components.keys()) .finish() } } -impl RequiredComponents { - /// Registers a required component. +/// The strictness of the relationship between components. +/// +/// These enum variants are ordered from least strict to most strict. +/// +/// Related components are unidirectional: if `A` requires `B`, `B` does not necessarily require `A`. +/// These variants describe the strength of the relationship from `A` to `B`, where `A` is the component +/// that you are registering related components for. +/// +/// In many cases, you might create an included/required component relationship in one direction, +/// but merely suggest the relationship in the other direction to help users discover the connection. +/// +/// For usage information, see the "Related Components" section of [`Component`]. +/// For the collection of metadata associated with a specific component, see [`RelatedComponents`]. +/// For a single piece of metadata associated with a related component, see [`RelatedComponent`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum Relatedness { + /// This component has a helpful interaction with the related component. + /// + /// At this level, documentation is automatically generated (when used as part of the derive macro), + /// and runtime metadata linking the components is stored for use with tooling. + /// + /// This level is appropriate to link components that have a helpful, optional effect when used together. + Suggested, + /// This component should typically be used with the related component. + /// + /// In addition to the features of [`Relatedness::Suggested`], + /// whenever this component is added to an entity, the related component will also be added if it was not already provided. + /// + /// This level is appropriate to link components that should almost always be used together, + /// but where power users may wish to opt-out of this behavior. + Included, + /// This component must be used with the related component. + /// + /// In addition to the features of [`Relatedness::Suggested`], + /// whenever this component is added to an entity, the relationship between these components cannot be removed. + /// In the future, we intend to this level to enforce even stricter guarantees, such as removing this component + /// if any of its required component is removed. + /// + /// This level is appropriate for strict correctness requirements, + /// where the related component is a fundamental part of the component's functionality + /// and removing it would break the component's behavior. + /// It cannot currently be relied on for soundness however, + /// as the required component can be removed without removing the requiring component. + Required, +} + +impl RelatedComponents { + /// Registers a related 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. @@ -1731,25 +1813,26 @@ impl RequiredComponents { pub unsafe fn register_dynamic( &mut self, component_id: ComponentId, - constructor: RequiredComponentConstructor, + constructor: RelatedComponentConstructor, inheritance_depth: u16, + relatedness: Relatedness, ) { - self.0 + self.get_map_mut(relatedness) .entry(component_id) .and_modify(|component| { if component.inheritance_depth > inheritance_depth { - // New registration is more specific than existing requirement + // New registration is more specific than existing relation component.constructor = constructor.clone(); component.inheritance_depth = inheritance_depth; } }) - .or_insert(RequiredComponent { + .or_insert(RelatedComponent { constructor, inheritance_depth, }); } - /// Registers a required component. + /// Registers a related 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. @@ -1759,12 +1842,13 @@ impl RequiredComponents { storages: &mut Storages, constructor: fn() -> C, inheritance_depth: u16, + relatedness: Relatedness, ) { let component_id = components.register_component::(storages); - self.register_by_id(component_id, constructor, inheritance_depth); + self.register_by_id(component_id, constructor, inheritance_depth, relatedness); } - /// Registers the [`Component`] with the given ID as required if it exists. + /// Registers the [`Component`] with the given ID as related 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. @@ -1773,8 +1857,9 @@ impl RequiredComponents { component_id: ComponentId, constructor: fn() -> C, inheritance_depth: u16, + relatedness: Relatedness, ) { - let erased: RequiredComponentConstructor = RequiredComponentConstructor(Arc::new( + let erased: RelatedComponentConstructor = RelatedComponentConstructor(Arc::new( move |table, sparse_sets, change_tick, @@ -1787,7 +1872,7 @@ impl RequiredComponents { // C::STORAGE_TYPE is the storage type associated with `component_id` / `C` // `ptr` points to valid `C` data, which matches the type associated with `component_id` unsafe { - BundleInfo::initialize_required_component( + BundleInfo::initialize_related_component( table, sparse_sets, change_tick, @@ -1808,29 +1893,67 @@ 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, inheritance_depth) }; + unsafe { self.register_dynamic(component_id, erased, inheritance_depth, relatedness) }; } - /// Iterates the ids of all required components. This includes recursive required components. - pub fn iter_ids(&self) -> impl Iterator + '_ { - self.0.keys().copied() + /// Iterates the ids of all related components. This includes recursive related components. + pub fn iter_ids(&self, relatedness: Relatedness) -> impl Iterator + '_ { + self.get_map(relatedness).keys().copied() } /// Removes components that are explicitly provided in a given [`Bundle`]. These components should /// be logically treated as normal components, not "required components". /// /// [`Bundle`]: crate::bundle::Bundle - pub(crate) fn remove_explicit_components(&mut self, components: &[ComponentId]) { - for component in components { - self.0.remove(component); + pub(crate) fn remove_explicit_components(&mut self, explicit_components: &[ComponentId]) { + for component in explicit_components { + self.required_components.remove(component); + } + } + + /// Gets read-only access to the map corresponding to the given [`Relatedness`]. + pub(crate) fn get_map( + &self, + relatedness: Relatedness, + ) -> &HashMap { + match relatedness { + Relatedness::Suggested => &self.suggested_components, + Relatedness::Included => &self.included_components, + Relatedness::Required => &self.required_components, } } - // Merges `required_components` into this collection. This only inserts a required component + /// Gets mutable access to the map corresponding to the given [`Relatedness`]. + pub(crate) fn get_map_mut( + &mut self, + relatedness: Relatedness, + ) -> &mut HashMap { + match relatedness { + Relatedness::Suggested => &mut self.suggested_components, + Relatedness::Included => &mut self.included_components, + Relatedness::Required => &mut self.required_components, + } + } + + // Merges `related_components` into this collection. This only inserts a related component // if it _did not already exist_. - pub(crate) fn merge(&mut self, required_components: &RequiredComponents) { - for (id, constructor) in &required_components.0 { - self.0.entry(*id).or_insert_with(|| constructor.clone()); + pub(crate) fn merge(&mut self, related_components: &RelatedComponents) { + for (id, constructor) in &related_components.suggested_components { + self.suggested_components + .entry(*id) + .or_insert_with(|| constructor.clone()); + } + + for (id, constructor) in &related_components.included_components { + self.included_components + .entry(*id) + .or_insert_with(|| constructor.clone()); + } + + for (id, constructor) in &related_components.required_components { + self.required_components + .entry(*id) + .or_insert_with(|| constructor.clone()); } } } diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 67254d6298f11..4e2cbeaa4944f 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -82,7 +82,7 @@ pub mod prelude { #[cfg(test)] mod tests { use crate as bevy_ecs; - use crate::component::{RequiredComponents, RequiredComponentsError}; + use crate::component::{RelatedComponents, RelatedComponentsError, Relatedness}; use crate::{ bundle::Bundle, change_detection::Ref, @@ -2071,8 +2071,8 @@ mod tests { struct V; let mut world = World::new(); - world.register_required_components::(); - world.register_required_components::(); + world.register_related_components::(Relatedness::Required); + world.register_related_components::(Relatedness::Required); let e = world.spawn((X, V)).id(); assert!(world.entity(e).contains::()); @@ -2213,8 +2213,8 @@ mod tests { let mut world = World::new(); - world.register_required_components::(); - world.register_required_components_with::(|| Z(7)); + world.register_related_components::(Relatedness::Required); + world.register_related_components_with::(|| Z(7), Relatedness::Required); let id = world.spawn(X).id(); @@ -2277,9 +2277,9 @@ mod tests { // - 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::(); - world.register_required_components_with::(|| Z(5)); - world.register_required_components_with::(|| Z(7)); + world.register_related_components::(Relatedness::Required); + world.register_related_components_with::(|| Z(5), Relatedness::Required); + world.register_related_components_with::(|| Z(7), Relatedness::Required); let id = world.spawn(X).id(); @@ -2308,9 +2308,9 @@ mod tests { // - 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::(); - world.register_required_components_with::(|| Z(7)); - world.register_required_components_with::(|| Z(5)); + world.register_related_components::(Relatedness::Required); + world.register_related_components_with::(|| Z(7), Relatedness::Required); + world.register_related_components_with::(|| Z(5), Relatedness::Required); let id = world.spawn(X).id(); @@ -2335,8 +2335,8 @@ mod tests { // This may change in the future. world.spawn(X); assert!(matches!( - world.try_register_required_components::(), - Err(RequiredComponentsError::ArchetypeExists(_)) + world.try_register_related_components::(Relatedness::Required), + Err(RelatedComponentsError::ArchetypeExists(_)) )); } @@ -2353,8 +2353,8 @@ mod tests { // This should fail: Tried to register Y as a requirement for X, but the requirement already exists. assert!(matches!( - world.try_register_required_components::(), - Err(RequiredComponentsError::DuplicateRegistration(_, _)) + world.try_register_related_components::(Relatedness::Required), + Err(RelatedComponentsError::DuplicateRegistration(_, _)) )); } @@ -2403,10 +2403,10 @@ mod tests { let y = world.register_component::(); let z = world.register_component::(); - world.register_required_components::(); - world.register_required_components::(); - world.register_required_components::(); - world.register_required_components::(); + world.register_related_components::(Relatedness::Required); + world.register_related_components::(Relatedness::Required); + world.register_related_components::(Relatedness::Required); + world.register_related_components::(Relatedness::Required); world.spawn(X); @@ -2419,9 +2419,9 @@ mod tests { /// 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 + fn to_vec(related: &RelatedComponents) -> Vec<(ComponentId, u16)> { + let mut vec = related + .required_components .iter() .map(|(id, component)| (*id, component.inheritance_depth)) .collect::>(); diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index b6a30b14aeb30..3ce4dccb1c7af 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -477,7 +477,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { #[inline] pub fn iter(&self) -> QueryIter<'_, 's, D::ReadOnly, F> { // SAFETY: - // - `self.world` has permission to access the required components. + // - `self.world` has permission to access the related components. // - The query is read-only, so it can be aliased even if it was originally mutable. unsafe { self.state @@ -548,7 +548,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { &self, ) -> QueryCombinationIter<'_, 's, D::ReadOnly, F, K> { // SAFETY: - // - `self.world` has permission to access the required components. + // - `self.world` has permission to access the related components. // - The query is read-only, so it can be aliased even if it was originally mutable. unsafe { self.state.as_readonly().iter_combinations_unchecked_manual( @@ -634,7 +634,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { entities: EntityList, ) -> QueryManyIter<'_, 's, D::ReadOnly, F, EntityList::IntoIter> { // SAFETY: - // - `self.world` has permission to access the required components. + // - `self.world` has permission to access the related components. // - The query is read-only, so it can be aliased even if it was originally mutable. unsafe { self.state.as_readonly().iter_many_unchecked_manual( @@ -711,7 +711,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { #[inline] pub unsafe fn iter_unsafe(&self) -> QueryIter<'_, 's, D, F> { // SAFETY: - // - `self.world` has permission to access the required components. + // - `self.world` has permission to access the related components. // - The caller ensures that this operation will not result in any aliased mutable accesses. unsafe { self.state @@ -737,7 +737,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { &self, ) -> QueryCombinationIter<'_, 's, D, F, K> { // SAFETY: - // - `self.world` has permission to access the required components. + // - `self.world` has permission to access the related components. // - The caller ensures that this operation will not result in any aliased mutable accesses. unsafe { self.state @@ -764,7 +764,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { entities: EntityList, ) -> QueryManyIter<'_, 's, D, F, EntityList::IntoIter> { // SAFETY: - // - `self.world` has permission to access the required components. + // - `self.world` has permission to access the related components. // - The caller ensures that this operation will not result in any aliased mutable accesses. unsafe { self.state.iter_many_unchecked_manual( diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index d50f8f421cfc2..eed01e42e04d3 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -35,7 +35,7 @@ use crate::{ change_detection::{MutUntyped, TicksMut}, component::{ Component, ComponentDescriptor, ComponentHooks, ComponentId, ComponentInfo, ComponentTicks, - Components, RequiredComponents, RequiredComponentsError, Tick, + Components, RelatedComponents, RelatedComponentsError, Relatedness, Tick, }, entity::{AllocAtWithoutReplacement, Entities, Entity, EntityHashSet, EntityLocation}, event::{Event, EventId, Events, SendBatchIds}, @@ -293,22 +293,22 @@ impl World { self.components.get_hooks_mut(id) } - /// Registers the given component `R` as a [required component] for `T`. + /// Registers the given component `R` as a [related component] for `T`. /// - /// When `T` is added to an entity, `R` and its own required components will also be added + /// When `T` is added to an entity, `R` and its own related 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. + /// If a custom constructor is desired, use [`World::register_related_components_with`] instead. /// - /// For the non-panicking version, see [`World::try_register_required_components`]. + /// For the non-panicking version, see [`World::try_register_related_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 + /// [related component]: Component#related-components /// /// # Panics /// - /// Panics if `R` is already a directly required component for `T`, or if `T` has ever been added + /// Panics if `R` is already a directly related 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 @@ -329,34 +329,38 @@ impl World { /// /// # let mut world = World::default(); /// // Register B as required by A and C as required by B. - /// world.register_required_components::(); - /// world.register_required_components::(); + /// world.register_related_components::(); + /// world.register_related_components::(); /// /// // 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::().unwrap()); /// assert_eq!(&C(0), world.entity(id).get::().unwrap()); /// ``` - pub fn register_required_components(&mut self) { - self.try_register_required_components::().unwrap(); + pub fn register_related_components( + &mut self, + relatedness: Relatedness, + ) { + self.try_register_related_components::(relatedness) + .unwrap(); } - /// Registers the given component `R` as a [required component] for `T`. + /// Registers the given component `R` as a [related component] for `T`. /// - /// When `T` is added to an entity, `R` and its own required components will also be added + /// When `T` is added to an entity, `R` and its own related 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. + /// If a [`Default`] constructor is desired, use [`World::register_related_components`] instead. /// - /// For the non-panicking version, see [`World::try_register_required_components_with`]. + /// For the non-panicking version, see [`World::try_register_related_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 + /// [related component]: Component#related-components /// /// # Panics /// - /// Panics if `R` is already a directly required component for `T`, or if `T` has ever been added + /// Panics if `R` is already a directly related 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 @@ -378,9 +382,9 @@ impl World { /// # 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::(); - /// world.register_required_components_with::(|| C(1)); - /// world.register_required_components_with::(|| C(2)); + /// world.register_related_components::(); + /// world.register_related_components_with::(|| C(1)); + /// world.register_related_components_with::(|| C(2)); /// /// // This will implicitly also insert B with its Default constructor and C /// // with the custom constructor defined by A. @@ -388,30 +392,31 @@ impl World { /// assert_eq!(&B(0), world.entity(id).get::().unwrap()); /// assert_eq!(&C(2), world.entity(id).get::().unwrap()); /// ``` - pub fn register_required_components_with( + pub fn register_related_components_with( &mut self, constructor: fn() -> R, + relatedness: Relatedness, ) { - self.try_register_required_components_with::(constructor) + self.try_register_related_components_with::(constructor, relatedness) .unwrap(); } - /// Tries to register the given component `R` as a [required component] for `T`. + /// Tries to register the given component `R` as a [related component] for `T`. /// - /// When `T` is added to an entity, `R` and its own required components will also be added + /// When `T` is added to an entity, `R` and its own related 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. + /// If a custom constructor is desired, use [`World::register_related_components_with`] instead. /// - /// For the panicking version, see [`World::register_required_components`]. + /// For the panicking version, see [`World::register_related_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 + /// [related component]: Component#related-components /// /// # Errors /// - /// Returns a [`RequiredComponentsError`] if `R` is already a directly required component for `T`, or if `T` has ever been added + /// Returns a [`RelatedComponentsError`] 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 @@ -432,39 +437,40 @@ impl World { /// /// # let mut world = World::default(); /// // Register B as required by A and C as required by B. - /// world.register_required_components::(); - /// world.register_required_components::(); + /// world.register_related_components::(); + /// world.register_related_components::(); /// /// // Duplicate registration! This will fail. - /// assert!(world.try_register_required_components::().is_err()); + /// assert!(world.try_register_related_components::().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::().unwrap()); /// assert_eq!(&C(0), world.entity(id).get::().unwrap()); /// ``` - pub fn try_register_required_components( + pub fn try_register_related_components( &mut self, - ) -> Result<(), RequiredComponentsError> { - self.try_register_required_components_with::(R::default) + relatedness: Relatedness, + ) -> Result<(), RelatedComponentsError> { + self.try_register_related_components_with::(R::default, relatedness) } - /// Tries to register the given component `R` as a [required component] for `T`. + /// Tries to register the given component `R` as a [related component] for `T`. /// - /// When `T` is added to an entity, `R` and its own required components will also be added + /// When `T` is added to an entity, `R` and its own related 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. + /// If a [`Default`] constructor is desired, use [`World::register_related_components`] instead. /// - /// For the panicking version, see [`World::register_required_components_with`]. + /// For the panicking version, see [`World::register_related_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 + /// [related component]: Component#related-components /// /// # Errors /// - /// Returns a [`RequiredComponentsError`] if `R` is already a directly required component for `T`, or if `T` has ever been added + /// Returns a [`RelatedComponentsError`] 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 @@ -486,12 +492,12 @@ impl World { /// # 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::(); - /// world.register_required_components_with::(|| C(1)); - /// world.register_required_components_with::(|| C(2)); + /// world.register_related_components::(); + /// world.register_related_components_with::(|| C(1)); + /// world.register_related_components_with::(|| C(2)); /// /// // Duplicate registration! Even if the constructors were different, this would fail. - /// assert!(world.try_register_required_components_with::(|| C(1)).is_err()); + /// assert!(world.try_register_related_components_with::(|| C(1)).is_err()); /// /// // This will implicitly also insert B with its Default constructor and C /// // with the custom constructor defined by A. @@ -499,37 +505,42 @@ impl World { /// assert_eq!(&B(0), world.entity(id).get::().unwrap()); /// assert_eq!(&C(2), world.entity(id).get::().unwrap()); /// ``` - pub fn try_register_required_components_with( + pub fn try_register_related_components_with( &mut self, constructor: fn() -> R, - ) -> Result<(), RequiredComponentsError> { + relatedness: Relatedness, + ) -> Result<(), RelatedComponentsError> { let requiree = self.register_component::(); // 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)); + return Err(RelatedComponentsError::ArchetypeExists(requiree)); } let required = self.register_component::(); // SAFETY: We just created the `required` and `requiree` components. unsafe { - self.components - .register_required_components::(required, requiree, constructor) + self.components.register_related_components::( + required, + requiree, + constructor, + relatedness, + ) } } - /// Retrieves the [required components](RequiredComponents) for the given component type, if it exists. - pub fn get_required_components(&self) -> Option<&RequiredComponents> { + /// Retrieves the [related components](RelatedComponents) for the given component type, if it exists. + pub fn get_required_components(&self) -> Option<&RelatedComponents> { let id = self.components().component_id::()?; let component_info = self.components().get_info(id)?; - Some(component_info.required_components()) + Some(component_info.related_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> { + /// Retrieves the [related components](RelatedComponents) for the component of the given [`ComponentId`], if it exists. + pub fn get_required_components_by_id(&self, id: ComponentId) -> Option<&RelatedComponents> { let component_info = self.components().get_info(id)?; - Some(component_info.required_components()) + Some(component_info.related_components()) } /// Registers a new [`Component`] type and returns the [`ComponentId`] created for it. diff --git a/crates/bevy_render/src/sync_component.rs b/crates/bevy_render/src/sync_component.rs index 0fac10b409a17..d0f0e579e2a87 100644 --- a/crates/bevy_render/src/sync_component.rs +++ b/crates/bevy_render/src/sync_component.rs @@ -1,7 +1,7 @@ use core::marker::PhantomData; use bevy_app::{App, Plugin}; -use bevy_ecs::component::Component; +use bevy_ecs::component::{Component, Relatedness}; use crate::sync_world::{EntityRecord, PendingSyncEntity, SyncToRenderWorld}; @@ -30,7 +30,7 @@ impl Default for SyncComponentPlugin { impl Plugin for SyncComponentPlugin { fn build(&self, app: &mut App) { - app.register_required_components::(); + app.register_related_components::(Relatedness::Required); app.world_mut().register_component_hooks::().on_remove( |mut world, entity, _component_id| {