# Objective - Contributes to #15460 ## Solution - Added the following features: - `std` (default) - `bevy_tasks` (default) - `downcast ` (default) - `portable-atomic` - `critical-section` - `downcast` and `bevy_tasks` are now optional dependencies for `bevy_app`. ## Testing - CI - Personal UEFI and Raspberry Pi Pico demo applications compile and run against this branch ## Draft Release Notes Bevy's application framework now supports `no_std` platforms. Following up on `bevy_ecs` gaining `no_std` support, `bevy_app` extends the functionality available on these targets to include the powerful `App` and `Plugin` abstractions. With this, library authors now have the option of making their plugins `no_std` compatible, or even offering plugins specifically to improve Bevy on certain embedded platforms! To start making a `no_std` compatible plugin, simply disable default features when including `bevy_app`: ```toml [dependencies] bevy_app = { version = "0.16", default-features = false } ``` We encourage library authors to do this anyway, as it can also help with compile times and binary size on all platforms. Keep an eye out for future `no_std` updates as we continue to improve the parity between `std` and `no_std`. We look forward to seeing what kinds of applications are now possible with Bevy! ## Notes - `downcast-rs` is optional as it isn't compatible with `portable-atomic`. I will investigate making a PR upstream to add support for this functionality, as it should be very straightforward. - In line with the `bevy_ecs` no-std-ification, I've added documentation to all features, and grouped them as well. - ~~Creating this PR in draft while CI runs and so I can polish before review.~~ --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
207 lines
6.8 KiB
Rust
207 lines
6.8 KiB
Rust
// TODO: Upstream `portable-atomic` support to `downcast_rs` and unconditionally
|
|
// include it as a dependency.
|
|
// See https://github.com/marcianx/downcast-rs/pull/22 for details
|
|
#[cfg(feature = "downcast")]
|
|
use downcast_rs::{impl_downcast, Downcast};
|
|
|
|
use crate::App;
|
|
use core::any::Any;
|
|
|
|
/// Dummy trait with the same name as `downcast_rs::Downcast`. This is to ensure
|
|
/// the `Plugin: Downcast` bound can remain even when `downcast` isn't enabled.
|
|
#[cfg(not(feature = "downcast"))]
|
|
#[doc(hidden)]
|
|
pub trait Downcast {}
|
|
|
|
#[cfg(not(feature = "downcast"))]
|
|
impl<T: ?Sized> Downcast for T {}
|
|
|
|
/// A collection of Bevy app logic and configuration.
|
|
///
|
|
/// Plugins configure an [`App`]. When an [`App`] registers a plugin,
|
|
/// the plugin's [`Plugin::build`] function is run. By default, a plugin
|
|
/// can only be added once to an [`App`].
|
|
///
|
|
/// If the plugin may need to be added twice or more, the function [`is_unique()`](Self::is_unique)
|
|
/// should be overridden to return `false`. Plugins are considered duplicate if they have the same
|
|
/// [`name()`](Self::name). The default `name()` implementation returns the type name, which means
|
|
/// generic plugins with different type parameters will not be considered duplicates.
|
|
///
|
|
/// ## Lifecycle of a plugin
|
|
///
|
|
/// When adding a plugin to an [`App`]:
|
|
/// * the app calls [`Plugin::build`] immediately, and register the plugin
|
|
/// * once the app started, it will wait for all registered [`Plugin::ready`] to return `true`
|
|
/// * it will then call all registered [`Plugin::finish`]
|
|
/// * and call all registered [`Plugin::cleanup`]
|
|
///
|
|
/// ## Defining a plugin.
|
|
///
|
|
/// Most plugins are simply functions that add configuration to an [`App`].
|
|
///
|
|
/// ```
|
|
/// # use bevy_app::{App, Update};
|
|
/// App::new().add_plugins(my_plugin).run();
|
|
///
|
|
/// // This function implements `Plugin`, along with every other `fn(&mut App)`.
|
|
/// pub fn my_plugin(app: &mut App) {
|
|
/// app.add_systems(Update, hello_world);
|
|
/// }
|
|
/// # fn hello_world() {}
|
|
/// ```
|
|
///
|
|
/// For more advanced use cases, the `Plugin` trait can be implemented manually for a type.
|
|
///
|
|
/// ```
|
|
/// # use bevy_app::*;
|
|
/// pub struct AccessibilityPlugin {
|
|
/// pub flicker_damping: bool,
|
|
/// // ...
|
|
/// }
|
|
///
|
|
/// impl Plugin for AccessibilityPlugin {
|
|
/// fn build(&self, app: &mut App) {
|
|
/// if self.flicker_damping {
|
|
/// app.add_systems(PostUpdate, damp_flickering);
|
|
/// }
|
|
/// }
|
|
/// }
|
|
/// # fn damp_flickering() {}
|
|
/// ```
|
|
pub trait Plugin: Downcast + Any + Send + Sync {
|
|
/// Configures the [`App`] to which this plugin is added.
|
|
fn build(&self, app: &mut App);
|
|
|
|
/// Has the plugin finished its setup? This can be useful for plugins that need something
|
|
/// asynchronous to happen before they can finish their setup, like the initialization of a renderer.
|
|
/// Once the plugin is ready, [`finish`](Plugin::finish) should be called.
|
|
fn ready(&self, _app: &App) -> bool {
|
|
true
|
|
}
|
|
|
|
/// Finish adding this plugin to the [`App`], once all plugins registered are ready. This can
|
|
/// be useful for plugins that depends on another plugin asynchronous setup, like the renderer.
|
|
fn finish(&self, _app: &mut App) {
|
|
// do nothing
|
|
}
|
|
|
|
/// Runs after all plugins are built and finished, but before the app schedule is executed.
|
|
/// This can be useful if you have some resource that other plugins need during their build step,
|
|
/// but after build you want to remove it and send it to another thread.
|
|
fn cleanup(&self, _app: &mut App) {
|
|
// do nothing
|
|
}
|
|
|
|
/// Configures a name for the [`Plugin`] which is primarily used for checking plugin
|
|
/// uniqueness and debugging.
|
|
fn name(&self) -> &str {
|
|
core::any::type_name::<Self>()
|
|
}
|
|
|
|
/// If the plugin can be meaningfully instantiated several times in an [`App`],
|
|
/// override this method to return `false`.
|
|
fn is_unique(&self) -> bool {
|
|
true
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "downcast")]
|
|
impl_downcast!(Plugin);
|
|
|
|
impl<T: Fn(&mut App) + Send + Sync + 'static> Plugin for T {
|
|
fn build(&self, app: &mut App) {
|
|
self(app);
|
|
}
|
|
}
|
|
|
|
/// Plugins state in the application
|
|
#[derive(PartialEq, Eq, Debug, Clone, Copy, PartialOrd, Ord)]
|
|
pub enum PluginsState {
|
|
/// Plugins are being added.
|
|
Adding,
|
|
/// All plugins already added are ready.
|
|
Ready,
|
|
/// Finish has been executed for all plugins added.
|
|
Finished,
|
|
/// Cleanup has been executed for all plugins added.
|
|
Cleaned,
|
|
}
|
|
|
|
/// A dummy plugin that's to temporarily occupy an entry in an app's plugin registry.
|
|
pub(crate) struct PlaceholderPlugin;
|
|
|
|
impl Plugin for PlaceholderPlugin {
|
|
fn build(&self, _app: &mut App) {}
|
|
}
|
|
|
|
/// Types that represent a set of [`Plugin`]s.
|
|
///
|
|
/// This is implemented for all types which implement [`Plugin`],
|
|
/// [`PluginGroup`](super::PluginGroup), and tuples over [`Plugins`].
|
|
pub trait Plugins<Marker>: sealed::Plugins<Marker> {}
|
|
|
|
impl<Marker, T> Plugins<Marker> for T where T: sealed::Plugins<Marker> {}
|
|
|
|
mod sealed {
|
|
use alloc::boxed::Box;
|
|
use variadics_please::all_tuples;
|
|
|
|
use crate::{App, AppError, Plugin, PluginGroup};
|
|
|
|
pub trait Plugins<Marker> {
|
|
fn add_to_app(self, app: &mut App);
|
|
}
|
|
|
|
pub struct PluginMarker;
|
|
pub struct PluginGroupMarker;
|
|
pub struct PluginsTupleMarker;
|
|
|
|
impl<P: Plugin> Plugins<PluginMarker> for P {
|
|
#[track_caller]
|
|
fn add_to_app(self, app: &mut App) {
|
|
if let Err(AppError::DuplicatePlugin { plugin_name }) =
|
|
app.add_boxed_plugin(Box::new(self))
|
|
{
|
|
panic!(
|
|
"Error adding plugin {plugin_name}: : plugin was already added in application"
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<P: PluginGroup> Plugins<PluginGroupMarker> for P {
|
|
#[track_caller]
|
|
fn add_to_app(self, app: &mut App) {
|
|
self.build().finish(app);
|
|
}
|
|
}
|
|
|
|
macro_rules! impl_plugins_tuples {
|
|
($(#[$meta:meta])* $(($param: ident, $plugins: ident)),*) => {
|
|
$(#[$meta])*
|
|
impl<$($param, $plugins),*> Plugins<(PluginsTupleMarker, $($param,)*)> for ($($plugins,)*)
|
|
where
|
|
$($plugins: Plugins<$param>),*
|
|
{
|
|
// We use `allow` instead of `expect` here because the lint is not generated for all cases.
|
|
#[allow(non_snake_case, reason = "`all_tuples!()` generates non-snake-case variable names.")]
|
|
#[allow(unused_variables, reason = "`app` is unused when implemented for the unit type `()`.")]
|
|
#[track_caller]
|
|
fn add_to_app(self, app: &mut App) {
|
|
let ($($plugins,)*) = self;
|
|
$($plugins.add_to_app(app);)*
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
all_tuples!(
|
|
#[doc(fake_variadic)]
|
|
impl_plugins_tuples,
|
|
0,
|
|
15,
|
|
P,
|
|
S
|
|
);
|
|
}
|