bevy_app/
plugin.rs

1use crate::App;
2use core::any::Any;
3use downcast_rs::{impl_downcast, Downcast};
4
5/// A collection of Bevy app logic and configuration.
6///
7/// Plugins configure an [`App`]. When an [`App`] registers a plugin,
8/// the plugin's [`Plugin::build`] function is run. By default, a plugin
9/// can only be added once to an [`App`].
10///
11/// If the plugin may need to be added twice or more, the function [`is_unique()`](Self::is_unique)
12/// should be overridden to return `false`. Plugins are considered duplicate if they have the same
13/// [`name()`](Self::name). The default `name()` implementation returns the type name, which means
14/// generic plugins with different type parameters will not be considered duplicates.
15///
16/// ## Lifecycle of a plugin
17///
18/// When adding a plugin to an [`App`]:
19/// * the app calls [`Plugin::build`] immediately, and register the plugin
20/// * once the app started, it will wait for all registered [`Plugin::ready`] to return `true`
21/// * it will then call all registered [`Plugin::finish`]
22/// * and call all registered [`Plugin::cleanup`]
23///
24/// ## Defining a plugin.
25///
26/// Most plugins are simply functions that add configuration to an [`App`].
27///
28/// ```
29/// # use bevy_app::{App, Update};
30/// App::new().add_plugins(my_plugin).run();
31///
32/// // This function implements `Plugin`, along with every other `fn(&mut App)`.
33/// pub fn my_plugin(app: &mut App) {
34///     app.add_systems(Update, hello_world);
35/// }
36/// # fn hello_world() {}
37/// ```
38///
39/// For more advanced use cases, the `Plugin` trait can be implemented manually for a type.
40///
41/// ```
42/// # use bevy_app::*;
43/// pub struct AccessibilityPlugin {
44///     pub flicker_damping: bool,
45///     // ...
46/// }
47///
48/// impl Plugin for AccessibilityPlugin {
49///     fn build(&self, app: &mut App) {
50///         if self.flicker_damping {
51///             app.add_systems(PostUpdate, damp_flickering);
52///         }
53///     }
54/// }
55/// # fn damp_flickering() {}
56/// ```
57pub trait Plugin: Downcast + Any + Send + Sync {
58    /// Configures the [`App`] to which this plugin is added.
59    fn build(&self, app: &mut App);
60
61    /// Has the plugin finished its setup? This can be useful for plugins that need something
62    /// asynchronous to happen before they can finish their setup, like the initialization of a renderer.
63    /// Once the plugin is ready, [`finish`](Plugin::finish) should be called.
64    fn ready(&self, _app: &App) -> bool {
65        true
66    }
67
68    /// Finish adding this plugin to the [`App`], once all plugins registered are ready. This can
69    /// be useful for plugins that depends on another plugin asynchronous setup, like the renderer.
70    fn finish(&self, _app: &mut App) {
71        // do nothing
72    }
73
74    /// Runs after all plugins are built and finished, but before the app schedule is executed.
75    /// This can be useful if you have some resource that other plugins need during their build step,
76    /// but after build you want to remove it and send it to another thread.
77    fn cleanup(&self, _app: &mut App) {
78        // do nothing
79    }
80
81    /// Configures a name for the [`Plugin`] which is primarily used for checking plugin
82    /// uniqueness and debugging.
83    fn name(&self) -> &str {
84        core::any::type_name::<Self>()
85    }
86
87    /// If the plugin can be meaningfully instantiated several times in an [`App`],
88    /// override this method to return `false`.
89    fn is_unique(&self) -> bool {
90        true
91    }
92}
93
94impl_downcast!(Plugin);
95
96impl<T: Fn(&mut App) + Send + Sync + 'static> Plugin for T {
97    fn build(&self, app: &mut App) {
98        self(app);
99    }
100}
101
102/// Plugins state in the application
103#[derive(PartialEq, Eq, Debug, Clone, Copy, PartialOrd, Ord)]
104pub enum PluginsState {
105    /// Plugins are being added.
106    Adding,
107    /// All plugins already added are ready.
108    Ready,
109    /// Finish has been executed for all plugins added.
110    Finished,
111    /// Cleanup has been executed for all plugins added.
112    Cleaned,
113}
114
115/// A dummy plugin that's to temporarily occupy an entry in an app's plugin registry.
116pub(crate) struct PlaceholderPlugin;
117
118impl Plugin for PlaceholderPlugin {
119    fn build(&self, _app: &mut App) {}
120}
121
122/// Types that represent a set of [`Plugin`]s.
123///
124/// This is implemented for all types which implement [`Plugin`],
125/// [`PluginGroup`](super::PluginGroup), and tuples over [`Plugins`].
126pub trait Plugins<Marker>: sealed::Plugins<Marker> {}
127
128impl<Marker, T> Plugins<Marker> for T where T: sealed::Plugins<Marker> {}
129
130mod sealed {
131    use alloc::boxed::Box;
132    use variadics_please::all_tuples;
133
134    use crate::{App, AppError, Plugin, PluginGroup};
135
136    pub trait Plugins<Marker> {
137        fn add_to_app(self, app: &mut App);
138    }
139
140    pub struct PluginMarker;
141    pub struct PluginGroupMarker;
142    pub struct PluginsTupleMarker;
143
144    impl<P: Plugin> Plugins<PluginMarker> for P {
145        #[track_caller]
146        fn add_to_app(self, app: &mut App) {
147            if let Err(AppError::DuplicatePlugin { plugin_name }) =
148                app.add_boxed_plugin(Box::new(self))
149            {
150                panic!(
151                    "Error adding plugin {plugin_name}: : plugin was already added in application"
152                )
153            }
154        }
155    }
156
157    impl<P: PluginGroup> Plugins<PluginGroupMarker> for P {
158        #[track_caller]
159        fn add_to_app(self, app: &mut App) {
160            self.build().finish(app);
161        }
162    }
163
164    macro_rules! impl_plugins_tuples {
165        ($(#[$meta:meta])* $(($param: ident, $plugins: ident)),*) => {
166            $(#[$meta])*
167            impl<$($param, $plugins),*> Plugins<(PluginsTupleMarker, $($param,)*)> for ($($plugins,)*)
168            where
169                $($plugins: Plugins<$param>),*
170            {
171                #[expect(
172                    clippy::allow_attributes,
173                    reason = "This is inside a macro, and as such, may not trigger in all cases."
174                )]
175                #[allow(non_snake_case, reason = "`all_tuples!()` generates non-snake-case variable names.")]
176                #[allow(unused_variables, reason = "`app` is unused when implemented for the unit type `()`.")]
177                #[track_caller]
178                fn add_to_app(self, app: &mut App) {
179                    let ($($plugins,)*) = self;
180                    $($plugins.add_to_app(app);)*
181                }
182            }
183        }
184    }
185
186    all_tuples!(
187        #[doc(fake_variadic)]
188        impl_plugins_tuples,
189        0,
190        15,
191        P,
192        S
193    );
194}