bevy/crates/bevy_app/src/app.rs
Joona Aalto f3e8ae03cd
Runtime required components (#15458)
# Objective

Fixes #15367.

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

Example use cases:

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

See #15367 for more in-depth reasoning.

## Solution

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

```rust
struct BirdPlugin;

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

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

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

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

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

### Implementation

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

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

My solutions to these problems are:

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

When calling `register_required_components`, it works as follows:

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

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

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

## Testing

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

## Discussion

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

## Next Steps

- Allow `register_required_components` to take a `Bundle` instead of a
single required component.
    - I could also try to do it in this PR if that would be preferable.
- Not directly related, but archetype invariants?
2024-09-30 19:20:16 +00:00

1782 lines
60 KiB
Rust

use crate::{
First, Main, MainSchedulePlugin, PlaceholderPlugin, Plugin, Plugins, PluginsState, SubApp,
SubApps,
};
pub use bevy_derive::AppLabel;
use bevy_ecs::{
component::RequiredComponentsError,
event::{event_update_system, EventCursor},
intern::Interned,
prelude::*,
schedule::{ScheduleBuildSettings, ScheduleLabel},
system::{IntoObserverSystem, SystemId, SystemInput},
};
#[cfg(feature = "trace")]
use bevy_utils::tracing::info_span;
use bevy_utils::{tracing::debug, HashMap};
use core::{fmt::Debug, num::NonZero, panic::AssertUnwindSafe};
use std::{
panic::{catch_unwind, resume_unwind},
process::{ExitCode, Termination},
};
use thiserror::Error;
bevy_ecs::define_label!(
/// A strongly-typed class of labels used to identify an [`App`].
AppLabel,
APP_LABEL_INTERNER
);
pub use bevy_ecs::label::DynEq;
/// A shorthand for `Interned<dyn AppLabel>`.
pub type InternedAppLabel = Interned<dyn AppLabel>;
#[derive(Debug, Error)]
pub(crate) enum AppError {
#[error("duplicate plugin {plugin_name:?}")]
DuplicatePlugin { plugin_name: String },
}
/// [`App`] is the primary API for writing user applications. It automates the setup of a
/// [standard lifecycle](Main) and provides interface glue for [plugins](`Plugin`).
///
/// A single [`App`] can contain multiple [`SubApp`] instances, but [`App`] methods only affect
/// the "main" one. To access a particular [`SubApp`], use [`get_sub_app`](App::get_sub_app)
/// or [`get_sub_app_mut`](App::get_sub_app_mut).
///
///
/// # Examples
///
/// Here is a simple "Hello World" Bevy app:
///
/// ```
/// # use bevy_app::prelude::*;
/// # use bevy_ecs::prelude::*;
/// #
/// fn main() {
/// App::new()
/// .add_systems(Update, hello_world_system)
/// .run();
/// }
///
/// fn hello_world_system() {
/// println!("hello world");
/// }
/// ```
pub struct App {
pub(crate) sub_apps: SubApps,
/// The function that will manage the app's lifecycle.
///
/// Bevy provides the [`WinitPlugin`] and [`ScheduleRunnerPlugin`] for windowed and headless
/// applications, respectively.
///
/// [`WinitPlugin`]: https://docs.rs/bevy/latest/bevy/winit/struct.WinitPlugin.html
/// [`ScheduleRunnerPlugin`]: https://docs.rs/bevy/latest/bevy/app/struct.ScheduleRunnerPlugin.html
pub(crate) runner: RunnerFn,
}
impl Debug for App {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "App {{ sub_apps: ")?;
f.debug_map()
.entries(self.sub_apps.sub_apps.iter())
.finish()?;
write!(f, "}}")
}
}
impl Default for App {
fn default() -> Self {
let mut app = App::empty();
app.sub_apps.main.update_schedule = Some(Main.intern());
#[cfg(feature = "bevy_reflect")]
app.init_resource::<AppTypeRegistry>();
#[cfg(feature = "reflect_functions")]
app.init_resource::<AppFunctionRegistry>();
app.add_plugins(MainSchedulePlugin);
app.add_systems(
First,
event_update_system
.in_set(bevy_ecs::event::EventUpdates)
.run_if(bevy_ecs::event::event_update_condition),
);
app.add_event::<AppExit>();
app
}
}
impl App {
/// Creates a new [`App`] with some default structure to enable core engine features.
/// This is the preferred constructor for most use cases.
pub fn new() -> App {
App::default()
}
/// Creates a new empty [`App`] with minimal default configuration.
///
/// Use this constructor if you want to customize scheduling, exit handling, cleanup, etc.
pub fn empty() -> App {
Self {
sub_apps: SubApps {
main: SubApp::new(),
sub_apps: HashMap::new(),
},
runner: Box::new(run_once),
}
}
/// Runs the default schedules of all sub-apps (starting with the "main" app) once.
pub fn update(&mut self) {
if self.is_building_plugins() {
panic!("App::update() was called while a plugin was building.");
}
self.sub_apps.update();
}
/// Runs the [`App`] by calling its [runner](Self::set_runner).
///
/// This will (re)build the [`App`] first. For general usage, see the example on the item
/// level documentation.
///
/// # Caveats
///
/// Calls to [`App::run()`] will never return on iOS and Web.
///
/// Headless apps can generally expect this method to return control to the caller when
/// it completes, but that is not the case for windowed apps. Windowed apps are typically
/// driven by an event loop and some platforms expect the program to terminate when the
/// event loop ends.
///
/// By default, *Bevy* uses the `winit` crate for window creation.
///
/// # Panics
///
/// Panics if not all plugins have been built.
pub fn run(&mut self) -> AppExit {
#[cfg(feature = "trace")]
let _bevy_app_run_span = info_span!("bevy_app").entered();
if self.is_building_plugins() {
panic!("App::run() was called while a plugin was building.");
}
let runner = core::mem::replace(&mut self.runner, Box::new(run_once));
let app = core::mem::replace(self, App::empty());
(runner)(app)
}
/// Sets the function that will be called when the app is run.
///
/// The runner function `f` is called only once by [`App::run`]. If the
/// presence of a main loop in the app is desired, it is the responsibility of the runner
/// function to provide it.
///
/// The runner function is usually not set manually, but by Bevy integrated plugins
/// (e.g. `WinitPlugin`).
///
/// # Examples
///
/// ```
/// # use bevy_app::prelude::*;
/// #
/// fn my_runner(mut app: App) -> AppExit {
/// loop {
/// println!("In main loop");
/// app.update();
/// if let Some(exit) = app.should_exit() {
/// return exit;
/// }
/// }
/// }
///
/// App::new()
/// .set_runner(my_runner);
/// ```
pub fn set_runner(&mut self, f: impl FnOnce(App) -> AppExit + 'static) -> &mut Self {
self.runner = Box::new(f);
self
}
/// Returns the state of all plugins. This is usually called by the event loop, but can be
/// useful for situations where you want to use [`App::update`].
// TODO: &mut self -> &self
#[inline]
pub fn plugins_state(&mut self) -> PluginsState {
let mut overall_plugins_state = match self.main_mut().plugins_state {
PluginsState::Adding => {
let mut state = PluginsState::Ready;
let plugins = core::mem::take(&mut self.main_mut().plugin_registry);
for plugin in &plugins {
// plugins installed to main need to see all sub-apps
if !plugin.ready(self) {
state = PluginsState::Adding;
break;
}
}
self.main_mut().plugin_registry = plugins;
state
}
state => state,
};
// overall state is the earliest state of any sub-app
self.sub_apps.iter_mut().skip(1).for_each(|s| {
overall_plugins_state = overall_plugins_state.min(s.plugins_state());
});
overall_plugins_state
}
/// Runs [`Plugin::finish`] for each plugin. This is usually called by the event loop once all
/// plugins are ready, but can be useful for situations where you want to use [`App::update`].
pub fn finish(&mut self) {
// plugins installed to main should see all sub-apps
let plugins = core::mem::take(&mut self.main_mut().plugin_registry);
for plugin in &plugins {
plugin.finish(self);
}
let main = self.main_mut();
main.plugin_registry = plugins;
main.plugins_state = PluginsState::Finished;
self.sub_apps.iter_mut().skip(1).for_each(SubApp::finish);
}
/// Runs [`Plugin::cleanup`] for each plugin. This is usually called by the event loop after
/// [`App::finish`], but can be useful for situations where you want to use [`App::update`].
pub fn cleanup(&mut self) {
// plugins installed to main should see all sub-apps
let plugins = core::mem::take(&mut self.main_mut().plugin_registry);
for plugin in &plugins {
plugin.cleanup(self);
}
let main = self.main_mut();
main.plugin_registry = plugins;
main.plugins_state = PluginsState::Cleaned;
self.sub_apps.iter_mut().skip(1).for_each(SubApp::cleanup);
}
/// Returns `true` if any of the sub-apps are building plugins.
pub(crate) fn is_building_plugins(&self) -> bool {
self.sub_apps.iter().any(SubApp::is_building_plugins)
}
/// Adds one or more systems to the given schedule in this app's [`Schedules`].
///
/// # Examples
///
/// ```
/// # use bevy_app::prelude::*;
/// # use bevy_ecs::prelude::*;
/// #
/// # let mut app = App::new();
/// # fn system_a() {}
/// # fn system_b() {}
/// # fn system_c() {}
/// # fn should_run() -> bool { true }
/// #
/// app.add_systems(Update, (system_a, system_b, system_c));
/// app.add_systems(Update, (system_a, system_b).run_if(should_run));
/// ```
pub fn add_systems<M>(
&mut self,
schedule: impl ScheduleLabel,
systems: impl IntoSystemConfigs<M>,
) -> &mut Self {
self.main_mut().add_systems(schedule, systems);
self
}
/// Registers a system and returns a [`SystemId`] so it can later be called by [`World::run_system`].
///
/// It's possible to register the same systems more than once, they'll be stored separately.
///
/// This is different from adding systems to a [`Schedule`] with [`App::add_systems`],
/// because the [`SystemId`] that is returned can be used anywhere in the [`World`] to run the associated system.
/// This allows for running systems in a push-based fashion.
/// Using a [`Schedule`] is still preferred for most cases
/// due to its better performance and ability to run non-conflicting systems simultaneously.
pub fn register_system<I, O, M>(
&mut self,
system: impl IntoSystem<I, O, M> + 'static,
) -> SystemId<I, O>
where
I: SystemInput + 'static,
O: 'static,
{
self.main_mut().register_system(system)
}
/// Configures a collection of system sets in the provided schedule, adding any sets that do not exist.
#[track_caller]
pub fn configure_sets(
&mut self,
schedule: impl ScheduleLabel,
sets: impl IntoSystemSetConfigs,
) -> &mut Self {
self.main_mut().configure_sets(schedule, sets);
self
}
/// Initializes `T` event handling by inserting an event queue resource ([`Events::<T>`])
/// and scheduling an [`event_update_system`] in [`First`].
///
/// See [`Events`] for information on how to define events.
///
/// # Examples
///
/// ```
/// # use bevy_app::prelude::*;
/// # use bevy_ecs::prelude::*;
/// #
/// # #[derive(Event)]
/// # struct MyEvent;
/// # let mut app = App::new();
/// #
/// app.add_event::<MyEvent>();
/// ```
pub fn add_event<T>(&mut self) -> &mut Self
where
T: Event,
{
self.main_mut().add_event::<T>();
self
}
/// Inserts the [`Resource`] into the app, overwriting any existing resource of the same type.
///
/// There is also an [`init_resource`](Self::init_resource) for resources that have
/// [`Default`] or [`FromWorld`] implementations.
///
/// # Examples
///
/// ```
/// # use bevy_app::prelude::*;
/// # use bevy_ecs::prelude::*;
/// #
/// #[derive(Resource)]
/// struct MyCounter {
/// counter: usize,
/// }
///
/// App::new()
/// .insert_resource(MyCounter { counter: 0 });
/// ```
pub fn insert_resource<R: Resource>(&mut self, resource: R) -> &mut Self {
self.main_mut().insert_resource(resource);
self
}
/// Inserts the [`Resource`], initialized with its default value, into the app,
/// if there is no existing instance of `R`.
///
/// `R` must implement [`FromWorld`].
/// If `R` implements [`Default`], [`FromWorld`] will be automatically implemented and
/// initialize the [`Resource`] with [`Default::default`].
///
/// # Examples
///
/// ```
/// # use bevy_app::prelude::*;
/// # use bevy_ecs::prelude::*;
/// #
/// #[derive(Resource)]
/// struct MyCounter {
/// counter: usize,
/// }
///
/// impl Default for MyCounter {
/// fn default() -> MyCounter {
/// MyCounter {
/// counter: 100
/// }
/// }
/// }
///
/// App::new()
/// .init_resource::<MyCounter>();
/// ```
pub fn init_resource<R: Resource + FromWorld>(&mut self) -> &mut Self {
self.main_mut().init_resource::<R>();
self
}
/// Inserts the [`!Send`](Send) resource into the app, overwriting any existing resource
/// of the same type.
///
/// There is also an [`init_non_send_resource`](Self::init_non_send_resource) for
/// resources that implement [`Default`]
///
/// # Examples
///
/// ```
/// # use bevy_app::prelude::*;
/// # use bevy_ecs::prelude::*;
/// #
/// struct MyCounter {
/// counter: usize,
/// }
///
/// App::new()
/// .insert_non_send_resource(MyCounter { counter: 0 });
/// ```
pub fn insert_non_send_resource<R: 'static>(&mut self, resource: R) -> &mut Self {
self.world_mut().insert_non_send_resource(resource);
self
}
/// Inserts the [`!Send`](Send) resource into the app if there is no existing instance of `R`.
///
/// `R` must implement [`FromWorld`].
/// If `R` implements [`Default`], [`FromWorld`] will be automatically implemented and
/// initialize the [`Resource`] with [`Default::default`].
pub fn init_non_send_resource<R: 'static + FromWorld>(&mut self) -> &mut Self {
self.world_mut().init_non_send_resource::<R>();
self
}
pub(crate) fn add_boxed_plugin(
&mut self,
plugin: Box<dyn Plugin>,
) -> Result<&mut Self, AppError> {
debug!("added plugin: {}", plugin.name());
if plugin.is_unique() && self.main_mut().plugin_names.contains(plugin.name()) {
Err(AppError::DuplicatePlugin {
plugin_name: plugin.name().to_string(),
})?;
}
// Reserve position in the plugin registry. If the plugin adds more plugins,
// they'll all end up in insertion order.
let index = self.main().plugin_registry.len();
self.main_mut()
.plugin_registry
.push(Box::new(PlaceholderPlugin));
self.main_mut().plugin_build_depth += 1;
let result = catch_unwind(AssertUnwindSafe(|| plugin.build(self)));
self.main_mut()
.plugin_names
.insert(plugin.name().to_string());
self.main_mut().plugin_build_depth -= 1;
if let Err(payload) = result {
resume_unwind(payload);
}
self.main_mut().plugin_registry[index] = plugin;
Ok(self)
}
/// Returns `true` if the [`Plugin`] has already been added.
pub fn is_plugin_added<T>(&self) -> bool
where
T: Plugin,
{
self.main().is_plugin_added::<T>()
}
/// Returns a vector of references to all plugins of type `T` that have been added.
///
/// This can be used to read the settings of any existing plugins.
/// This vector will be empty if no plugins of that type have been added.
/// If multiple copies of the same plugin are added to the [`App`], they will be listed in insertion order in this vector.
///
/// ```
/// # use bevy_app::prelude::*;
/// # #[derive(Default)]
/// # struct ImagePlugin {
/// # default_sampler: bool,
/// # }
/// # impl Plugin for ImagePlugin {
/// # fn build(&self, app: &mut App) {}
/// # }
/// # let mut app = App::new();
/// # app.add_plugins(ImagePlugin::default());
/// let default_sampler = app.get_added_plugins::<ImagePlugin>()[0].default_sampler;
/// ```
pub fn get_added_plugins<T>(&self) -> Vec<&T>
where
T: Plugin,
{
self.main().get_added_plugins::<T>()
}
/// Installs a [`Plugin`] collection.
///
/// Bevy prioritizes modularity as a core principle. **All** engine features are implemented
/// as plugins, even the complex ones like rendering.
///
/// [`Plugin`]s can be grouped into a set by using a [`PluginGroup`].
///
/// There are built-in [`PluginGroup`]s that provide core engine functionality.
/// The [`PluginGroup`]s available by default are `DefaultPlugins` and `MinimalPlugins`.
///
/// To customize the plugins in the group (reorder, disable a plugin, add a new plugin
/// before / after another plugin), call [`build()`](super::PluginGroup::build) on the group,
/// which will convert it to a [`PluginGroupBuilder`](crate::PluginGroupBuilder).
///
/// You can also specify a group of [`Plugin`]s by using a tuple over [`Plugin`]s and
/// [`PluginGroup`]s. See [`Plugins`] for more details.
///
/// ## Examples
/// ```
/// # use bevy_app::{prelude::*, PluginGroupBuilder, NoopPluginGroup as MinimalPlugins};
/// #
/// # // Dummies created to avoid using `bevy_log`,
/// # // which pulls in too many dependencies and breaks rust-analyzer
/// # pub struct LogPlugin;
/// # impl Plugin for LogPlugin {
/// # fn build(&self, app: &mut App) {}
/// # }
/// App::new()
/// .add_plugins(MinimalPlugins);
/// App::new()
/// .add_plugins((MinimalPlugins, LogPlugin));
/// ```
///
/// # Panics
///
/// Panics if one of the plugins had already been added to the application.
///
/// [`PluginGroup`]:super::PluginGroup
#[track_caller]
pub fn add_plugins<M>(&mut self, plugins: impl Plugins<M>) -> &mut Self {
if matches!(
self.plugins_state(),
PluginsState::Cleaned | PluginsState::Finished
) {
panic!(
"Plugins cannot be added after App::cleanup() or App::finish() has been called."
);
}
plugins.add_to_app(self);
self
}
/// Registers the type `T` in the [`AppTypeRegistry`] resource,
/// adding reflect data as specified in the [`Reflect`](bevy_reflect::Reflect) derive:
/// ```ignore (No serde "derive" feature)
/// #[derive(Component, Serialize, Deserialize, Reflect)]
/// #[reflect(Component, Serialize, Deserialize)] // will register ReflectComponent, ReflectSerialize, ReflectDeserialize
/// ```
///
/// See [`bevy_reflect::TypeRegistry::register`] for more information.
#[cfg(feature = "bevy_reflect")]
pub fn register_type<T: bevy_reflect::GetTypeRegistration>(&mut self) -> &mut Self {
self.main_mut().register_type::<T>();
self
}
/// Associates type data `D` with type `T` in the [`AppTypeRegistry`] resource.
///
/// Most of the time [`register_type`](Self::register_type) can be used instead to register a
/// type you derived [`Reflect`](bevy_reflect::Reflect) for. However, in cases where you want to
/// add a piece of type data that was not included in the list of `#[reflect(...)]` type data in
/// the derive, or where the type is generic and cannot register e.g. `ReflectSerialize`
/// unconditionally without knowing the specific type parameters, this method can be used to
/// insert additional type data.
///
/// # Example
/// ```
/// use bevy_app::App;
/// use bevy_reflect::{ReflectSerialize, ReflectDeserialize};
///
/// App::new()
/// .register_type::<Option<String>>()
/// .register_type_data::<Option<String>, ReflectSerialize>()
/// .register_type_data::<Option<String>, ReflectDeserialize>();
/// ```
///
/// See [`bevy_reflect::TypeRegistry::register_type_data`].
#[cfg(feature = "bevy_reflect")]
pub fn register_type_data<
T: bevy_reflect::Reflect + bevy_reflect::TypePath,
D: bevy_reflect::TypeData + bevy_reflect::FromType<T>,
>(
&mut self,
) -> &mut Self {
self.main_mut().register_type_data::<T, D>();
self
}
/// Registers the given function into the [`AppFunctionRegistry`] resource.
///
/// The given function will internally be stored as a [`DynamicFunction`]
/// and mapped according to its [name].
///
/// Because the function must have a name,
/// anonymous functions (e.g. `|a: i32, b: i32| { a + b }`) and closures must instead
/// be registered using [`register_function_with_name`] or converted to a [`DynamicFunction`]
/// and named using [`DynamicFunction::with_name`].
/// Failure to do so will result in a panic.
///
/// Only types that implement [`IntoFunction`] may be registered via this method.
///
/// See [`FunctionRegistry::register`] for more information.
///
/// # Panics
///
/// Panics if a function has already been registered with the given name
/// or if the function is missing a name (such as when it is an anonymous function).
///
/// # Examples
///
/// ```
/// use bevy_app::App;
///
/// fn add(a: i32, b: i32) -> i32 {
/// a + b
/// }
///
/// App::new().register_function(add);
/// ```
///
/// Functions cannot be registered more than once.
///
/// ```should_panic
/// use bevy_app::App;
///
/// fn add(a: i32, b: i32) -> i32 {
/// a + b
/// }
///
/// App::new()
/// .register_function(add)
/// // Panic! A function has already been registered with the name "my_function"
/// .register_function(add);
/// ```
///
/// Anonymous functions and closures should be registered using [`register_function_with_name`] or given a name using [`DynamicFunction::with_name`].
///
/// ```should_panic
/// use bevy_app::App;
///
/// // Panic! Anonymous functions cannot be registered using `register_function`
/// App::new().register_function(|a: i32, b: i32| a + b);
/// ```
///
/// [`register_function_with_name`]: Self::register_function_with_name
/// [`DynamicFunction`]: bevy_reflect::func::DynamicFunction
/// [name]: bevy_reflect::func::FunctionInfo::name
/// [`DynamicFunction::with_name`]: bevy_reflect::func::DynamicFunction::with_name
/// [`IntoFunction`]: bevy_reflect::func::IntoFunction
/// [`FunctionRegistry::register`]: bevy_reflect::func::FunctionRegistry::register
#[cfg(feature = "reflect_functions")]
pub fn register_function<F, Marker>(&mut self, function: F) -> &mut Self
where
F: bevy_reflect::func::IntoFunction<'static, Marker> + 'static,
{
self.main_mut().register_function(function);
self
}
/// Registers the given function or closure into the [`AppFunctionRegistry`] resource using the given name.
///
/// To avoid conflicts, it's recommended to use a unique name for the function.
/// This can be achieved by "namespacing" the function with a unique identifier,
/// such as the name of your crate.
///
/// For example, to register a function, `add`, from a crate, `my_crate`,
/// you could use the name, `"my_crate::add"`.
///
/// Another approach could be to use the [type name] of the function,
/// however, it should be noted that anonymous functions do _not_ have unique type names.
///
/// For named functions (e.g. `fn add(a: i32, b: i32) -> i32 { a + b }`) where a custom name is not needed,
/// it's recommended to use [`register_function`] instead as the generated name is guaranteed to be unique.
///
/// Only types that implement [`IntoFunction`] may be registered via this method.
///
/// See [`FunctionRegistry::register_with_name`] for more information.
///
/// # Panics
///
/// Panics if a function has already been registered with the given name.
///
/// # Examples
///
/// ```
/// use bevy_app::App;
///
/// fn mul(a: i32, b: i32) -> i32 {
/// a * b
/// }
///
/// let div = |a: i32, b: i32| a / b;
///
/// App::new()
/// // Registering an anonymous function with a unique name
/// .register_function_with_name("my_crate::add", |a: i32, b: i32| {
/// a + b
/// })
/// // Registering an existing function with its type name
/// .register_function_with_name(std::any::type_name_of_val(&mul), mul)
/// // Registering an existing function with a custom name
/// .register_function_with_name("my_crate::mul", mul)
/// // Be careful not to register anonymous functions with their type name.
/// // This code works but registers the function with a non-unique name like `foo::bar::{{closure}}`
/// .register_function_with_name(std::any::type_name_of_val(&div), div);
/// ```
///
/// Names must be unique.
///
/// ```should_panic
/// use bevy_app::App;
///
/// fn one() {}
/// fn two() {}
///
/// App::new()
/// .register_function_with_name("my_function", one)
/// // Panic! A function has already been registered with the name "my_function"
/// .register_function_with_name("my_function", two);
/// ```
///
/// [type name]: std::any::type_name
/// [`register_function`]: Self::register_function
/// [`IntoFunction`]: bevy_reflect::func::IntoFunction
/// [`FunctionRegistry::register_with_name`]: bevy_reflect::func::FunctionRegistry::register_with_name
#[cfg(feature = "reflect_functions")]
pub fn register_function_with_name<F, Marker>(
&mut self,
name: impl Into<alloc::borrow::Cow<'static, str>>,
function: F,
) -> &mut Self
where
F: bevy_reflect::func::IntoFunction<'static, Marker> + 'static,
{
self.main_mut().register_function_with_name(name, function);
self
}
/// Registers the given component `R` as a [required component] for `T`.
///
/// When `T` is added to an entity, `R` and its own required components will also be added
/// if `R` was not already provided. The [`Default`] `constructor` will be used for the creation of `R`.
/// If a custom constructor is desired, use [`App::register_required_components_with`] instead.
///
/// For the non-panicking version, see [`App::try_register_required_components`].
///
/// Note that requirements must currently be registered before `T` is inserted into the world
/// for the first time. Commonly, this is done in plugins. This limitation may be fixed in the future.
///
/// [required component]: Component#required-components
///
/// # Panics
///
/// Panics if `R` is already a directly required component for `T`, or if `T` has ever been added
/// on an entity before the registration.
///
/// Indirect requirements through other components are allowed. In those cases, any existing requirements
/// will only be overwritten if the new requirement is more specific.
///
/// # Example
///
/// ```
/// # use bevy_app::{App, NoopPluginGroup as MinimalPlugins, Startup};
/// # use bevy_ecs::prelude::*;
/// #[derive(Component)]
/// struct A;
///
/// #[derive(Component, Default, PartialEq, Eq, Debug)]
/// struct B(usize);
///
/// #[derive(Component, Default, PartialEq, Eq, Debug)]
/// struct C(u32);
///
/// # let mut app = App::new();
/// # app.add_plugins(MinimalPlugins).add_systems(Startup, setup);
/// // Register B as required by A and C as required by B.
/// app.register_required_components::<A, B>();
/// app.register_required_components::<B, C>();
///
/// fn setup(mut commands: Commands) {
/// // This will implicitly also insert B and C with their Default constructors.
/// commands.spawn(A);
/// }
///
/// fn validate(query: Query<(&A, &B, &C)>) {
/// let (a, b, c) = query.single();
/// assert_eq!(b, &B(0));
/// assert_eq!(c, &C(0));
/// }
/// # app.update();
/// ```
pub fn register_required_components<T: Component, R: Component + Default>(
&mut self,
) -> &mut Self {
self.world_mut().register_required_components::<T, R>();
self
}
/// Registers the given component `R` as a [required component] for `T`.
///
/// When `T` is added to an entity, `R` and its own required components will also be added
/// if `R` was not already provided. The given `constructor` will be used for the creation of `R`.
/// If a [`Default`] constructor is desired, use [`App::register_required_components`] instead.
///
/// For the non-panicking version, see [`App::try_register_required_components_with`].
///
/// Note that requirements must currently be registered before `T` is inserted into the world
/// for the first time. Commonly, this is done in plugins. This limitation may be fixed in the future.
///
/// [required component]: Component#required-components
///
/// # Panics
///
/// Panics if `R` is already a directly required component for `T`, or if `T` has ever been added
/// on an entity before the registration.
///
/// Indirect requirements through other components are allowed. In those cases, any existing requirements
/// will only be overwritten if the new requirement is more specific.
///
/// # Example
///
/// ```
/// # use bevy_app::{App, NoopPluginGroup as MinimalPlugins, Startup};
/// # use bevy_ecs::prelude::*;
/// #[derive(Component)]
/// struct A;
///
/// #[derive(Component, Default, PartialEq, Eq, Debug)]
/// struct B(usize);
///
/// #[derive(Component, Default, PartialEq, Eq, Debug)]
/// struct C(u32);
///
/// # let mut app = App::new();
/// # app.add_plugins(MinimalPlugins).add_systems(Startup, setup);
/// // Register B and C as required by A and C as required by B.
/// // A requiring C directly will overwrite the indirect requirement through B.
/// app.register_required_components::<A, B>();
/// app.register_required_components_with::<B, C>(|| C(1));
/// app.register_required_components_with::<A, C>(|| C(2));
///
/// fn setup(mut commands: Commands) {
/// // This will implicitly also insert B with its Default constructor and C
/// // with the custom constructor defined by A.
/// commands.spawn(A);
/// }
///
/// fn validate(query: Query<(&A, &B, &C)>) {
/// let (a, b, c) = query.single();
/// assert_eq!(b, &B(0));
/// assert_eq!(c, &C(2));
/// }
/// # app.update();
/// ```
pub fn register_required_components_with<T: Component, R: Component>(
&mut self,
constructor: fn() -> R,
) -> &mut Self {
self.world_mut()
.register_required_components_with::<T, R>(constructor);
self
}
/// Tries to register the given component `R` as a [required component] for `T`.
///
/// When `T` is added to an entity, `R` and its own required components will also be added
/// if `R` was not already provided. The [`Default`] `constructor` will be used for the creation of `R`.
/// If a custom constructor is desired, use [`App::register_required_components_with`] instead.
///
/// For the panicking version, see [`App::register_required_components`].
///
/// Note that requirements must currently be registered before `T` is inserted into the world
/// for the first time. Commonly, this is done in plugins. This limitation may be fixed in the future.
///
/// [required component]: Component#required-components
///
/// # Errors
///
/// Returns a [`RequiredComponentsError`] if `R` is already a directly required component for `T`, or if `T` has ever been added
/// on an entity before the registration.
///
/// Indirect requirements through other components are allowed. In those cases, any existing requirements
/// will only be overwritten if the new requirement is more specific.
///
/// # Example
///
/// ```
/// # use bevy_app::{App, NoopPluginGroup as MinimalPlugins, Startup};
/// # use bevy_ecs::prelude::*;
/// #[derive(Component)]
/// struct A;
///
/// #[derive(Component, Default, PartialEq, Eq, Debug)]
/// struct B(usize);
///
/// #[derive(Component, Default, PartialEq, Eq, Debug)]
/// struct C(u32);
///
/// # let mut app = App::new();
/// # app.add_plugins(MinimalPlugins).add_systems(Startup, setup);
/// // Register B as required by A and C as required by B.
/// app.register_required_components::<A, B>();
/// app.register_required_components::<B, C>();
///
/// // Duplicate registration! This will fail.
/// assert!(app.try_register_required_components::<A, B>().is_err());
///
/// fn setup(mut commands: Commands) {
/// // This will implicitly also insert B and C with their Default constructors.
/// commands.spawn(A);
/// }
///
/// fn validate(query: Query<(&A, &B, &C)>) {
/// let (a, b, c) = query.single();
/// assert_eq!(b, &B(0));
/// assert_eq!(c, &C(0));
/// }
/// # app.update();
/// ```
pub fn try_register_required_components<T: Component, R: Component + Default>(
&mut self,
) -> Result<(), RequiredComponentsError> {
self.world_mut().try_register_required_components::<T, R>()
}
/// Tries to register the given component `R` as a [required component] for `T`.
///
/// When `T` is added to an entity, `R` and its own required components will also be added
/// if `R` was not already provided. The given `constructor` will be used for the creation of `R`.
/// If a [`Default`] constructor is desired, use [`App::register_required_components`] instead.
///
/// For the panicking version, see [`App::register_required_components_with`].
///
/// Note that requirements must currently be registered before `T` is inserted into the world
/// for the first time. Commonly, this is done in plugins. This limitation may be fixed in the future.
///
/// [required component]: Component#required-components
///
/// # Errors
///
/// Returns a [`RequiredComponentsError`] if `R` is already a directly required component for `T`, or if `T` has ever been added
/// on an entity before the registration.
///
/// Indirect requirements through other components are allowed. In those cases, any existing requirements
/// will only be overwritten if the new requirement is more specific.
///
/// # Example
///
/// ```
/// # use bevy_app::{App, NoopPluginGroup as MinimalPlugins, Startup};
/// # use bevy_ecs::prelude::*;
/// #[derive(Component)]
/// struct A;
///
/// #[derive(Component, Default, PartialEq, Eq, Debug)]
/// struct B(usize);
///
/// #[derive(Component, Default, PartialEq, Eq, Debug)]
/// struct C(u32);
///
/// # let mut app = App::new();
/// # app.add_plugins(MinimalPlugins).add_systems(Startup, setup);
/// // Register B and C as required by A and C as required by B.
/// // A requiring C directly will overwrite the indirect requirement through B.
/// app.register_required_components::<A, B>();
/// app.register_required_components_with::<B, C>(|| C(1));
/// app.register_required_components_with::<A, C>(|| C(2));
///
/// // Duplicate registration! Even if the constructors were different, this would fail.
/// assert!(app.try_register_required_components_with::<B, C>(|| C(1)).is_err());
///
/// fn setup(mut commands: Commands) {
/// // This will implicitly also insert B with its Default constructor and C
/// // with the custom constructor defined by A.
/// commands.spawn(A);
/// }
///
/// fn validate(query: Query<(&A, &B, &C)>) {
/// let (a, b, c) = query.single();
/// assert_eq!(b, &B(0));
/// assert_eq!(c, &C(2));
/// }
/// # app.update();
/// ```
pub fn try_register_required_components_with<T: Component, R: Component>(
&mut self,
constructor: fn() -> R,
) -> Result<(), RequiredComponentsError> {
self.world_mut()
.try_register_required_components_with::<T, R>(constructor)
}
/// Returns a reference to the [`World`].
pub fn world(&self) -> &World {
self.main().world()
}
/// Returns a mutable reference to the [`World`].
pub fn world_mut(&mut self) -> &mut World {
self.main_mut().world_mut()
}
/// Returns a reference to the main [`SubApp`].
pub fn main(&self) -> &SubApp {
&self.sub_apps.main
}
/// Returns a mutable reference to the main [`SubApp`].
pub fn main_mut(&mut self) -> &mut SubApp {
&mut self.sub_apps.main
}
/// Returns a reference to the [`SubApp`] with the given label.
///
/// # Panics
///
/// Panics if the [`SubApp`] doesn't exist.
pub fn sub_app(&self, label: impl AppLabel) -> &SubApp {
let str = label.intern();
self.get_sub_app(label).unwrap_or_else(|| {
panic!("No sub-app with label '{:?}' exists.", str);
})
}
/// Returns a reference to the [`SubApp`] with the given label.
///
/// # Panics
///
/// Panics if the [`SubApp`] doesn't exist.
pub fn sub_app_mut(&mut self, label: impl AppLabel) -> &mut SubApp {
let str = label.intern();
self.get_sub_app_mut(label).unwrap_or_else(|| {
panic!("No sub-app with label '{:?}' exists.", str);
})
}
/// Returns a reference to the [`SubApp`] with the given label, if it exists.
pub fn get_sub_app(&self, label: impl AppLabel) -> Option<&SubApp> {
self.sub_apps.sub_apps.get(&label.intern())
}
/// Returns a mutable reference to the [`SubApp`] with the given label, if it exists.
pub fn get_sub_app_mut(&mut self, label: impl AppLabel) -> Option<&mut SubApp> {
self.sub_apps.sub_apps.get_mut(&label.intern())
}
/// Inserts a [`SubApp`] with the given label.
pub fn insert_sub_app(&mut self, label: impl AppLabel, sub_app: SubApp) {
self.sub_apps.sub_apps.insert(label.intern(), sub_app);
}
/// Removes the [`SubApp`] with the given label, if it exists.
pub fn remove_sub_app(&mut self, label: impl AppLabel) -> Option<SubApp> {
self.sub_apps.sub_apps.remove(&label.intern())
}
/// Extract data from the main world into the [`SubApp`] with the given label and perform an update if it exists.
pub fn update_sub_app_by_label(&mut self, label: impl AppLabel) {
self.sub_apps.update_subapp_by_label(label);
}
/// Inserts a new `schedule` under the provided `label`, overwriting any existing
/// schedule with the same label.
pub fn add_schedule(&mut self, schedule: Schedule) -> &mut Self {
self.main_mut().add_schedule(schedule);
self
}
/// Initializes an empty `schedule` under the provided `label`, if it does not exist.
///
/// See [`add_schedule`](Self::add_schedule) to insert an existing schedule.
pub fn init_schedule(&mut self, label: impl ScheduleLabel) -> &mut Self {
self.main_mut().init_schedule(label);
self
}
/// Returns a reference to the [`Schedule`] with the provided `label` if it exists.
pub fn get_schedule(&self, label: impl ScheduleLabel) -> Option<&Schedule> {
self.main().get_schedule(label)
}
/// Returns a mutable reference to the [`Schedule`] with the provided `label` if it exists.
pub fn get_schedule_mut(&mut self, label: impl ScheduleLabel) -> Option<&mut Schedule> {
self.main_mut().get_schedule_mut(label)
}
/// Runs function `f` with the [`Schedule`] associated with `label`.
///
/// **Note:** This will create the schedule if it does not already exist.
pub fn edit_schedule(
&mut self,
label: impl ScheduleLabel,
f: impl FnMut(&mut Schedule),
) -> &mut Self {
self.main_mut().edit_schedule(label, f);
self
}
/// Applies the provided [`ScheduleBuildSettings`] to all schedules.
pub fn configure_schedules(
&mut self,
schedule_build_settings: ScheduleBuildSettings,
) -> &mut Self {
self.main_mut().configure_schedules(schedule_build_settings);
self
}
/// When doing [ambiguity checking](ScheduleBuildSettings) this
/// ignores systems that are ambiguous on [`Component`] T.
///
/// This settings only applies to the main world. To apply this to other worlds call the
/// [corresponding method](World::allow_ambiguous_component) on World
///
/// ## Example
///
/// ```
/// # use bevy_app::prelude::*;
/// # use bevy_ecs::prelude::*;
/// # use bevy_ecs::schedule::{LogLevel, ScheduleBuildSettings};
/// # use bevy_utils::default;
///
/// #[derive(Component)]
/// struct A;
///
/// // these systems are ambiguous on A
/// fn system_1(_: Query<&mut A>) {}
/// fn system_2(_: Query<&A>) {}
///
/// let mut app = App::new();
/// app.configure_schedules(ScheduleBuildSettings {
/// ambiguity_detection: LogLevel::Error,
/// ..default()
/// });
///
/// app.add_systems(Update, ( system_1, system_2 ));
/// app.allow_ambiguous_component::<A>();
///
/// // running the app does not error.
/// app.update();
/// ```
pub fn allow_ambiguous_component<T: Component>(&mut self) -> &mut Self {
self.main_mut().allow_ambiguous_component::<T>();
self
}
/// When doing [ambiguity checking](ScheduleBuildSettings) this
/// ignores systems that are ambiguous on [`Resource`] T.
///
/// This settings only applies to the main world. To apply this to other worlds call the
/// [corresponding method](World::allow_ambiguous_resource) on World
///
/// ## Example
///
/// ```
/// # use bevy_app::prelude::*;
/// # use bevy_ecs::prelude::*;
/// # use bevy_ecs::schedule::{LogLevel, ScheduleBuildSettings};
/// # use bevy_utils::default;
///
/// #[derive(Resource)]
/// struct R;
///
/// // these systems are ambiguous on R
/// fn system_1(_: ResMut<R>) {}
/// fn system_2(_: Res<R>) {}
///
/// let mut app = App::new();
/// app.configure_schedules(ScheduleBuildSettings {
/// ambiguity_detection: LogLevel::Error,
/// ..default()
/// });
/// app.insert_resource(R);
///
/// app.add_systems(Update, ( system_1, system_2 ));
/// app.allow_ambiguous_resource::<R>();
///
/// // running the app does not error.
/// app.update();
/// ```
pub fn allow_ambiguous_resource<T: Resource>(&mut self) -> &mut Self {
self.main_mut().allow_ambiguous_resource::<T>();
self
}
/// Suppress warnings and errors that would result from systems in these sets having ambiguities
/// (conflicting access but indeterminate order) with systems in `set`.
///
/// When possible, do this directly in the `.add_systems(Update, a.ambiguous_with(b))` call.
/// However, sometimes two independent plugins `A` and `B` are reported as ambiguous, which you
/// can only suppress as the consumer of both.
#[track_caller]
pub fn ignore_ambiguity<M1, M2, S1, S2>(
&mut self,
schedule: impl ScheduleLabel,
a: S1,
b: S2,
) -> &mut Self
where
S1: IntoSystemSet<M1>,
S2: IntoSystemSet<M2>,
{
self.main_mut().ignore_ambiguity(schedule, a, b);
self
}
/// Attempts to determine if an [`AppExit`] was raised since the last update.
///
/// Will attempt to return the first [`Error`](AppExit::Error) it encounters.
/// This should be called after every [`update()`](App::update) otherwise you risk
/// dropping possible [`AppExit`] events.
pub fn should_exit(&self) -> Option<AppExit> {
let mut reader = EventCursor::default();
let events = self.world().get_resource::<Events<AppExit>>()?;
let mut events = reader.read(events);
if events.len() != 0 {
return Some(
events
.find(|exit| exit.is_error())
.cloned()
.unwrap_or(AppExit::Success),
);
}
None
}
/// Spawns an [`Observer`] entity, which will watch for and respond to the given event.
///
/// # Examples
///
/// ```rust
/// # use bevy_app::prelude::*;
/// # use bevy_ecs::prelude::*;
/// # use bevy_utils::default;
/// #
/// # let mut app = App::new();
/// #
/// # #[derive(Event)]
/// # struct Party {
/// # friends_allowed: bool,
/// # };
/// #
/// # #[derive(Event)]
/// # struct Invite;
/// #
/// # #[derive(Component)]
/// # struct Friend;
/// #
/// // An observer system can be any system where the first parameter is a trigger
/// app.observe(|trigger: Trigger<Party>, friends: Query<Entity, With<Friend>>, mut commands: Commands| {
/// if trigger.event().friends_allowed {
/// for friend in friends.iter() {
/// commands.trigger_targets(Invite, friend);
/// }
/// }
/// });
/// ```
pub fn observe<E: Event, B: Bundle, M>(
&mut self,
observer: impl IntoObserverSystem<E, B, M>,
) -> &mut Self {
self.world_mut().observe(observer);
self
}
}
type RunnerFn = Box<dyn FnOnce(App) -> AppExit>;
fn run_once(mut app: App) -> AppExit {
while app.plugins_state() == PluginsState::Adding {
#[cfg(not(target_arch = "wasm32"))]
bevy_tasks::tick_global_task_pools_on_main_thread();
}
app.finish();
app.cleanup();
app.update();
app.should_exit().unwrap_or(AppExit::Success)
}
/// An event that indicates the [`App`] should exit. If one or more of these are present at the end of an update,
/// the [runner](App::set_runner) will end and ([maybe](App::run)) return control to the caller.
///
/// This event can be used to detect when an exit is requested. Make sure that systems listening
/// for this event run before the current update ends.
///
/// # Portability
/// This type is roughly meant to map to a standard definition of a process exit code (0 means success, not 0 means error). Due to portability concerns
/// (see [`ExitCode`](https://doc.rust-lang.org/std/process/struct.ExitCode.html) and [`process::exit`](https://doc.rust-lang.org/std/process/fn.exit.html#))
/// we only allow error codes between 1 and [255](u8::MAX).
#[derive(Event, Debug, Clone, Default, PartialEq, Eq)]
pub enum AppExit {
/// [`App`] exited without any problems.
#[default]
Success,
/// The [`App`] experienced an unhandleable error.
/// Holds the exit code we expect our app to return.
Error(NonZero<u8>),
}
impl AppExit {
/// Creates a [`AppExit::Error`] with a error code of 1.
#[must_use]
pub const fn error() -> Self {
Self::Error(NonZero::<u8>::MIN)
}
/// Returns `true` if `self` is a [`AppExit::Success`].
#[must_use]
pub const fn is_success(&self) -> bool {
matches!(self, AppExit::Success)
}
/// Returns `true` if `self` is a [`AppExit::Error`].
#[must_use]
pub const fn is_error(&self) -> bool {
matches!(self, AppExit::Error(_))
}
/// Creates a [`AppExit`] from a code.
///
/// When `code` is 0 a [`AppExit::Success`] is constructed otherwise a
/// [`AppExit::Error`] is constructed.
#[must_use]
pub const fn from_code(code: u8) -> Self {
match NonZero::<u8>::new(code) {
Some(code) => Self::Error(code),
None => Self::Success,
}
}
}
impl From<u8> for AppExit {
#[must_use]
fn from(value: u8) -> Self {
Self::from_code(value)
}
}
impl Termination for AppExit {
fn report(self) -> ExitCode {
match self {
AppExit::Success => ExitCode::SUCCESS,
// We leave logging an error to our users
AppExit::Error(value) => ExitCode::from(value.get()),
}
}
}
#[cfg(test)]
mod tests {
use core::{iter, marker::PhantomData};
use std::sync::Mutex;
use bevy_ecs::{
change_detection::{DetectChanges, ResMut},
component::Component,
entity::Entity,
event::{Event, EventWriter, Events},
query::With,
removal_detection::RemovedComponents,
schedule::{IntoSystemConfigs, ScheduleLabel},
system::{Commands, Query, Resource},
world::{FromWorld, World},
};
use crate::{App, AppExit, Plugin, SubApp, Update};
struct PluginA;
impl Plugin for PluginA {
fn build(&self, _app: &mut App) {}
}
struct PluginB;
impl Plugin for PluginB {
fn build(&self, _app: &mut App) {}
}
struct PluginC<T>(T);
impl<T: Send + Sync + 'static> Plugin for PluginC<T> {
fn build(&self, _app: &mut App) {}
}
struct PluginD;
impl Plugin for PluginD {
fn build(&self, _app: &mut App) {}
fn is_unique(&self) -> bool {
false
}
}
struct PluginE;
impl Plugin for PluginE {
fn build(&self, _app: &mut App) {}
fn finish(&self, app: &mut App) {
if app.is_plugin_added::<PluginA>() {
panic!("cannot run if PluginA is already registered");
}
}
}
#[test]
fn can_add_two_plugins() {
App::new().add_plugins((PluginA, PluginB));
}
#[test]
#[should_panic]
fn cant_add_twice_the_same_plugin() {
App::new().add_plugins((PluginA, PluginA));
}
#[test]
fn can_add_twice_the_same_plugin_with_different_type_param() {
App::new().add_plugins((PluginC(0), PluginC(true)));
}
#[test]
fn can_add_twice_the_same_plugin_not_unique() {
App::new().add_plugins((PluginD, PluginD));
}
#[test]
#[should_panic]
fn cant_call_app_run_from_plugin_build() {
struct PluginRun;
struct InnerPlugin;
impl Plugin for InnerPlugin {
fn build(&self, _: &mut App) {}
}
impl Plugin for PluginRun {
fn build(&self, app: &mut App) {
app.add_plugins(InnerPlugin).run();
}
}
App::new().add_plugins(PluginRun);
}
#[derive(ScheduleLabel, Hash, Clone, PartialEq, Eq, Debug)]
struct EnterMainMenu;
fn bar(mut commands: Commands) {
commands.spawn_empty();
}
fn foo(mut commands: Commands) {
commands.spawn_empty();
}
#[test]
fn add_systems_should_create_schedule_if_it_does_not_exist() {
let mut app = App::new();
app.add_systems(EnterMainMenu, (foo, bar));
app.world_mut().run_schedule(EnterMainMenu);
assert_eq!(app.world().entities().len(), 2);
}
#[test]
#[should_panic]
fn test_is_plugin_added_works_during_finish() {
let mut app = App::new();
app.add_plugins(PluginA);
app.add_plugins(PluginE);
app.finish();
}
#[test]
fn test_derive_app_label() {
use super::AppLabel;
use crate::{self as bevy_app};
#[derive(AppLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
struct UnitLabel;
#[derive(AppLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
struct TupleLabel(u32, u32);
#[derive(AppLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
struct StructLabel {
a: u32,
b: u32,
}
#[derive(AppLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
struct EmptyTupleLabel();
#[derive(AppLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
struct EmptyStructLabel {}
#[derive(AppLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
enum EnumLabel {
#[default]
Unit,
Tuple(u32, u32),
Struct {
a: u32,
b: u32,
},
}
#[derive(AppLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
struct GenericLabel<T>(PhantomData<T>);
assert_eq!(UnitLabel.intern(), UnitLabel.intern());
assert_eq!(EnumLabel::Unit.intern(), EnumLabel::Unit.intern());
assert_ne!(UnitLabel.intern(), EnumLabel::Unit.intern());
assert_ne!(UnitLabel.intern(), TupleLabel(0, 0).intern());
assert_ne!(EnumLabel::Unit.intern(), EnumLabel::Tuple(0, 0).intern());
assert_eq!(TupleLabel(0, 0).intern(), TupleLabel(0, 0).intern());
assert_eq!(
EnumLabel::Tuple(0, 0).intern(),
EnumLabel::Tuple(0, 0).intern()
);
assert_ne!(TupleLabel(0, 0).intern(), TupleLabel(0, 1).intern());
assert_ne!(
EnumLabel::Tuple(0, 0).intern(),
EnumLabel::Tuple(0, 1).intern()
);
assert_ne!(TupleLabel(0, 0).intern(), EnumLabel::Tuple(0, 0).intern());
assert_ne!(
TupleLabel(0, 0).intern(),
StructLabel { a: 0, b: 0 }.intern()
);
assert_ne!(
EnumLabel::Tuple(0, 0).intern(),
EnumLabel::Struct { a: 0, b: 0 }.intern()
);
assert_eq!(
StructLabel { a: 0, b: 0 }.intern(),
StructLabel { a: 0, b: 0 }.intern()
);
assert_eq!(
EnumLabel::Struct { a: 0, b: 0 }.intern(),
EnumLabel::Struct { a: 0, b: 0 }.intern()
);
assert_ne!(
StructLabel { a: 0, b: 0 }.intern(),
StructLabel { a: 0, b: 1 }.intern()
);
assert_ne!(
EnumLabel::Struct { a: 0, b: 0 }.intern(),
EnumLabel::Struct { a: 0, b: 1 }.intern()
);
assert_ne!(
StructLabel { a: 0, b: 0 }.intern(),
EnumLabel::Struct { a: 0, b: 0 }.intern()
);
assert_ne!(
StructLabel { a: 0, b: 0 }.intern(),
EnumLabel::Struct { a: 0, b: 0 }.intern()
);
assert_ne!(StructLabel { a: 0, b: 0 }.intern(), UnitLabel.intern(),);
assert_ne!(
EnumLabel::Struct { a: 0, b: 0 }.intern(),
EnumLabel::Unit.intern()
);
assert_eq!(
GenericLabel::<u32>(PhantomData).intern(),
GenericLabel::<u32>(PhantomData).intern()
);
assert_ne!(
GenericLabel::<u32>(PhantomData).intern(),
GenericLabel::<u64>(PhantomData).intern()
);
}
#[test]
fn test_update_clears_trackers_once() {
#[derive(Component, Copy, Clone)]
struct Foo;
let mut app = App::new();
app.world_mut().spawn_batch(iter::repeat(Foo).take(5));
fn despawn_one_foo(mut commands: Commands, foos: Query<Entity, With<Foo>>) {
if let Some(e) = foos.iter().next() {
commands.entity(e).despawn();
};
}
fn check_despawns(mut removed_foos: RemovedComponents<Foo>) {
let mut despawn_count = 0;
for _ in removed_foos.read() {
despawn_count += 1;
}
assert_eq!(despawn_count, 2);
}
app.add_systems(Update, despawn_one_foo);
app.update(); // Frame 0
app.update(); // Frame 1
app.add_systems(Update, check_despawns.after(despawn_one_foo));
app.update(); // Should see despawns from frames 1 & 2, but not frame 0
}
#[test]
fn test_extract_sees_changes() {
use super::AppLabel;
use crate::{self as bevy_app};
#[derive(AppLabel, Clone, Copy, Hash, PartialEq, Eq, Debug)]
struct MySubApp;
#[derive(Resource)]
struct Foo(usize);
let mut app = App::new();
app.world_mut().insert_resource(Foo(0));
app.add_systems(Update, |mut foo: ResMut<Foo>| {
foo.0 += 1;
});
let mut sub_app = SubApp::new();
sub_app.set_extract(|main_world, _sub_world| {
assert!(main_world.get_resource_ref::<Foo>().unwrap().is_changed());
});
app.insert_sub_app(MySubApp, sub_app);
app.update();
}
#[test]
fn runner_returns_correct_exit_code() {
fn raise_exits(mut exits: EventWriter<AppExit>) {
// Exit codes chosen by a fair dice roll.
// Unlikely to overlap with default values.
exits.send(AppExit::Success);
exits.send(AppExit::from_code(4));
exits.send(AppExit::from_code(73));
}
let exit = App::new().add_systems(Update, raise_exits).run();
assert_eq!(exit, AppExit::from_code(4));
}
/// Custom runners should be in charge of when `app::update` gets called as they may need to
/// coordinate some state.
/// bug: <https://github.com/bevyengine/bevy/issues/10385>
/// fix: <https://github.com/bevyengine/bevy/pull/10389>
#[test]
fn regression_test_10385() {
use super::{Res, Resource};
use crate::PreUpdate;
#[derive(Resource)]
struct MyState {}
fn my_runner(mut app: App) -> AppExit {
let my_state = MyState {};
app.world_mut().insert_resource(my_state);
for _ in 0..5 {
app.update();
}
AppExit::Success
}
fn my_system(_: Res<MyState>) {
// access state during app update
}
// Should not panic due to missing resource
App::new()
.set_runner(my_runner)
.add_systems(PreUpdate, my_system)
.run();
}
#[test]
fn app_exit_size() {
// There wont be many of them so the size isn't a issue but
// it's nice they're so small let's keep it that way.
assert_eq!(size_of::<AppExit>(), size_of::<u8>());
}
#[test]
fn initializing_resources_from_world() {
#[derive(Resource)]
struct TestResource;
impl FromWorld for TestResource {
fn from_world(_world: &mut World) -> Self {
TestResource
}
}
#[derive(Resource)]
struct NonSendTestResource {
_marker: PhantomData<Mutex<()>>,
}
impl FromWorld for NonSendTestResource {
fn from_world(_world: &mut World) -> Self {
NonSendTestResource {
_marker: PhantomData,
}
}
}
App::new()
.init_non_send_resource::<NonSendTestResource>()
.init_resource::<TestResource>();
}
#[test]
/// Plugin should not be considered inserted while it's being built
///
/// bug: <https://github.com/bevyengine/bevy/issues/13815>
fn plugin_should_not_be_added_during_build_time() {
pub struct Foo;
impl Plugin for Foo {
fn build(&self, app: &mut App) {
assert!(!app.is_plugin_added::<Self>());
}
}
App::new().add_plugins(Foo);
}
#[test]
fn events_should_be_updated_once_per_update() {
#[derive(Event, Clone)]
struct TestEvent;
let mut app = App::new();
app.add_event::<TestEvent>();
// Starts empty
let test_events = app.world().resource::<Events<TestEvent>>();
assert_eq!(test_events.len(), 0);
assert_eq!(test_events.iter_current_update_events().count(), 0);
app.update();
// Sending one event
app.world_mut().send_event(TestEvent);
let test_events = app.world().resource::<Events<TestEvent>>();
assert_eq!(test_events.len(), 1);
assert_eq!(test_events.iter_current_update_events().count(), 1);
app.update();
// Sending two events on the next frame
app.world_mut().send_event(TestEvent);
app.world_mut().send_event(TestEvent);
let test_events = app.world().resource::<Events<TestEvent>>();
assert_eq!(test_events.len(), 3); // Events are double-buffered, so we see 1 + 2 = 3
assert_eq!(test_events.iter_current_update_events().count(), 2);
app.update();
// Sending zero events
let test_events = app.world().resource::<Events<TestEvent>>();
assert_eq!(test_events.len(), 2); // Events are double-buffered, so we see 2 + 0 = 2
assert_eq!(test_events.iter_current_update_events().count(), 0);
}
}