Skip to content

More forms of related components: suggested and included components #16267

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 57 additions & 51 deletions crates/bevy_app/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*,
Expand Down Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Note that requirements must currently be registered before `T` is inserted into the world
/// Note that related components must currently be registered before `T` is inserted into the world

?

Suggested change
/// Note that requirements must currently be registered before `T` is inserted into the world
/// Note that relations 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.
Comment on lines +775 to 776
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Indirect relationships through other components are allowed. In those cases, any existing requirements
/// will only be overwritten if the new requirement is more specific.
/// Indirect relationships through other components are allowed. In those cases, any existing relationships
/// will only be overwritten if the new relationship is more specific.

PS. Trying to help rephrase this has made me realise the difficulty of referring to "relatedness" whilst trying to avoid the term "relationship" due to its future usage in Bevy 🤔

///
/// # Example
Expand All @@ -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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// // Register B as required by A and C as required by B.
/// // Register B as related to A and C as related to B.

?

/// app.register_required_components::<A, B>();
/// app.register_required_components::<B, C>();
/// app.register_related_components::<A, B>();
/// app.register_related_components::<B, C>();
///
/// fn setup(mut commands: Commands) {
/// // This will implicitly also insert B and C with their Default constructors.
Expand All @@ -807,32 +807,34 @@ impl App {
/// }
/// # app.update();
/// ```
pub fn register_required_components<T: Component, R: Component + Default>(
pub fn register_related_components<T: Component, R: Component + Default>(
&mut self,
relatedness: Relatedness,
) -> &mut Self {
self.world_mut().register_required_components::<T, R>();
self.world_mut()
.register_related_components::<T, R>(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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Note that requirements must currently be registered before `T` is inserted into the world
/// Note that related components must currently be registered before `T` is inserted into the world

?

Suggested change
/// Note that requirements must currently be registered before `T` is inserted into the world
/// Note that relations 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
Expand All @@ -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.
Comment on lines 856 to 857
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// // 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.
/// // Register B and C as related to A and C as related to B.
/// // A relating to C directly will overwrite the indirect relation 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));
/// app.register_related_components::<A, B>();
/// app.register_related_components_with::<B, C>(|| C(1));
/// app.register_related_components_with::<A, C>(|| C(2));
///
/// fn setup(mut commands: Commands) {
/// // This will implicitly also insert B with its Default constructor and C
Expand All @@ -870,34 +872,35 @@ impl App {
/// }
/// # app.update();
/// ```
pub fn register_required_components_with<T: Component, R: Component>(
pub fn register_related_components_with<T: Component, R: Component>(
&mut self,
constructor: fn() -> R,
relatedness: Relatedness,
) -> &mut Self {
self.world_mut()
.register_required_components_with::<T, R>(constructor);
.register_related_components_with::<T, R>(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.
Comment on lines 893 to 894
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// 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.
/// Note that relatations 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.

?

Suggested change
/// 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.
/// Note that related components 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
Expand All @@ -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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// // Register B as required by A and C as required by B.
/// // Register B as related to A and C as related to B.

/// app.register_required_components::<A, B>();
/// app.register_required_components::<B, C>();
/// app.register_related_components::<A, B>();
/// app.register_related_components::<B, C>();
///
/// // Duplicate registration! This will fail.
/// assert!(app.try_register_required_components::<A, B>().is_err());
/// assert!(app.try_register_related_components::<A, B>().is_err());
///
/// fn setup(mut commands: Commands) {
/// // This will implicitly also insert B and C with their Default constructors.
Expand All @@ -935,31 +938,33 @@ impl App {
/// }
/// # app.update();
/// ```
pub fn try_register_required_components<T: Component, R: Component + Default>(
pub fn try_register_related_components<T: Component, R: Component + Default>(
&mut self,
) -> Result<(), RequiredComponentsError> {
self.world_mut().try_register_required_components::<T, R>()
relatedness: Relatedness,
) -> Result<(), RelatedComponentsError> {
self.world_mut()
.try_register_related_components::<T, R>(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.
Comment on lines 957 to 958
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// 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.
/// Note that relationships 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
Expand All @@ -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::<A, B>();
/// app.register_required_components_with::<B, C>(|| C(1));
/// app.register_required_components_with::<A, C>(|| C(2));
/// app.register_related_components::<A, B>();
/// app.register_related_components_with::<B, C>(|| C(1));
/// app.register_related_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());
/// assert!(app.try_register_related_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
Expand All @@ -1000,12 +1005,13 @@ impl App {
/// }
/// # app.update();
/// ```
pub fn try_register_required_components_with<T: Component, R: Component>(
pub fn try_register_related_components_with<T: Component, R: Component>(
&mut self,
constructor: fn() -> R,
) -> Result<(), RequiredComponentsError> {
relatedness: Relatedness,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Working very hard not to say relationship I see :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah :p "Related" needs a rename, as I brought up on Discord.

) -> Result<(), RelatedComponentsError> {
self.world_mut()
.try_register_required_components_with::<T, R>(constructor)
.try_register_related_components_with::<T, R>(constructor, relatedness)
}

/// Returns a reference to the [`World`].
Expand Down
Loading
Loading