diff --git a/Cargo.toml b/Cargo.toml index f4331cc52a6c8..6b863e4cbb1b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -154,6 +154,10 @@ path = "examples/app/headless.rs" name = "plugin" path = "examples/app/plugin.rs" +[[example]] +name = "plugin_group" +path = "examples/app/plugin_group.rs" + [[example]] name = "return_after_run" path = "examples/app/return_after_run.rs" diff --git a/crates/bevy_app/Cargo.toml b/crates/bevy_app/Cargo.toml index 6f0fa28fee2f0..c853699b3ebc7 100644 --- a/crates/bevy_app/Cargo.toml +++ b/crates/bevy_app/Cargo.toml @@ -17,6 +17,7 @@ keywords = ["bevy"] bevy_derive = { path = "../bevy_derive", version = "0.2.1" } bevy_ecs = { path = "../bevy_ecs", version = "0.2.1" } bevy_math = { path = "../bevy_math", version = "0.2.1" } +bevy_utils = { path = "../bevy_utils", version = "0.2.1" } # other log = { version = "0.4", features = ["release_max_level_info"] } diff --git a/crates/bevy_app/src/app_builder.rs b/crates/bevy_app/src/app_builder.rs index be45aaeb56465..bff901098e54f 100644 --- a/crates/bevy_app/src/app_builder.rs +++ b/crates/bevy_app/src/app_builder.rs @@ -2,7 +2,7 @@ use crate::{ app::{App, AppExit}, event::Events, plugin::Plugin, - stage, startup_stage, + stage, startup_stage, PluginGroup, PluginGroupBuilder, }; use bevy_ecs::{FromResources, IntoQuerySystem, Resources, System, World}; @@ -271,4 +271,23 @@ impl AppBuilder { plugin.build(self); self } + + pub fn add_plugin_group(&mut self, mut group: T) -> &mut Self { + let mut plugin_group_builder = PluginGroupBuilder::default(); + group.build(&mut plugin_group_builder); + plugin_group_builder.finish(self); + self + } + + pub fn add_plugin_group_with(&mut self, mut group: T, func: F) -> &mut Self + where + T: PluginGroup, + F: FnOnce(&mut PluginGroupBuilder) -> &mut PluginGroupBuilder, + { + let mut plugin_group_builder = PluginGroupBuilder::default(); + group.build(&mut plugin_group_builder); + func(&mut plugin_group_builder); + plugin_group_builder.finish(self); + self + } } diff --git a/crates/bevy_app/src/lib.rs b/crates/bevy_app/src/lib.rs index d2e241616b55d..3d82bab5b09ba 100644 --- a/crates/bevy_app/src/lib.rs +++ b/crates/bevy_app/src/lib.rs @@ -7,6 +7,7 @@ mod app; mod app_builder; mod event; mod plugin; +mod plugin_group; mod schedule_runner; pub use app::*; @@ -14,6 +15,7 @@ pub use app_builder::*; pub use bevy_derive::DynamicPlugin; pub use event::*; pub use plugin::*; +pub use plugin_group::*; pub use schedule_runner::*; pub mod prelude { @@ -21,7 +23,6 @@ pub mod prelude { app::App, app_builder::AppBuilder, event::{EventReader, Events}, - plugin::Plugin, - stage, DynamicPlugin, + stage, DynamicPlugin, Plugin, PluginGroup, }; } diff --git a/crates/bevy_app/src/plugin_group.rs b/crates/bevy_app/src/plugin_group.rs new file mode 100644 index 0000000000000..81be87a4864b3 --- /dev/null +++ b/crates/bevy_app/src/plugin_group.rs @@ -0,0 +1,103 @@ +use crate::{AppBuilder, Plugin}; +use bevy_utils::HashMap; +use std::any::TypeId; + +pub trait PluginGroup { + fn build(&mut self, group: &mut PluginGroupBuilder); +} + +struct PluginEntry { + plugin: Box, + enabled: bool, +} + +#[derive(Default)] +pub struct PluginGroupBuilder { + plugins: HashMap, + order: Vec, +} + +impl PluginGroupBuilder { + pub fn add(&mut self, plugin: T) -> &mut Self { + self.order.push(TypeId::of::()); + self.plugins.insert( + TypeId::of::(), + PluginEntry { + plugin: Box::new(plugin), + enabled: true, + }, + ); + self + } + + pub fn add_before(&mut self, plugin: T) -> &mut Self { + let target_index = self + .order + .iter() + .enumerate() + .find(|(_i, ty)| **ty == TypeId::of::()) + .map(|(i, _)| i) + .unwrap_or_else(|| { + panic!("Plugin does not exist: {}", std::any::type_name::()) + }); + self.order.insert(target_index, TypeId::of::()); + self.plugins.insert( + TypeId::of::(), + PluginEntry { + plugin: Box::new(plugin), + enabled: true, + }, + ); + self + } + + pub fn add_after(&mut self, plugin: T) -> &mut Self { + let target_index = self + .order + .iter() + .enumerate() + .find(|(_i, ty)| **ty == TypeId::of::()) + .map(|(i, _)| i) + .unwrap_or_else(|| { + panic!("Plugin does not exist: {}", std::any::type_name::()) + }); + self.order.insert(target_index + 1, TypeId::of::()); + self.plugins.insert( + TypeId::of::(), + PluginEntry { + plugin: Box::new(plugin), + enabled: true, + }, + ); + self + } + + pub fn enable(&mut self) -> &mut Self { + let mut plugin_entry = self + .plugins + .get_mut(&TypeId::of::()) + .expect("Cannot enable a plugin that does not exist"); + plugin_entry.enabled = true; + self + } + + pub fn disable(&mut self) -> &mut Self { + let mut plugin_entry = self + .plugins + .get_mut(&TypeId::of::()) + .expect("Cannot disable a plugin that does not exist"); + plugin_entry.enabled = false; + self + } + + pub fn finish(self, app: &mut AppBuilder) { + for ty in self.order.iter() { + if let Some(entry) = self.plugins.get(ty) { + if entry.enabled { + log::debug!("added plugin: {}", entry.plugin.name()); + entry.plugin.build(app); + } + } + } + } +} diff --git a/examples/app/plugin_group.rs b/examples/app/plugin_group.rs new file mode 100644 index 0000000000000..a7e1618edd938 --- /dev/null +++ b/examples/app/plugin_group.rs @@ -0,0 +1,50 @@ +use bevy::{app::PluginGroupBuilder, prelude::*}; + +/// PluginGroups are a way to group sets of plugins that should be registered together. +fn main() { + App::build() + // The app.add_default_plugins() you see in all of the examples is just an alias for this: + .add_plugin_group(DefaultPlugins) + // Adding a plugin group adds all plugins in the group by default + .add_plugin_group(HelloWorldPlugins) + // You can also modify a PluginGroup (such as disabling plugins) like this: + // .add_plugin_group_with(HelloWorldPlugins, |group| { + // group + // .disable::() + // .add_before::(bevy::diagnostic::PrintDiagnosticsPlugin::default()) + // }) + .run(); +} + +/// A group of plugins that produce the "hello world" behavior +pub struct HelloWorldPlugins; + +impl PluginGroup for HelloWorldPlugins { + fn build(&mut self, group: &mut PluginGroupBuilder) { + group.add(PrintHelloPlugin).add(PrintWorldPlugin); + } +} + +pub struct PrintHelloPlugin; + +impl Plugin for PrintHelloPlugin { + fn build(&self, app: &mut AppBuilder) { + app.add_system(print_hello_system.system()); + } +} + +fn print_hello_system() { + println!("hello"); +} + +pub struct PrintWorldPlugin; + +impl Plugin for PrintWorldPlugin { + fn build(&self, app: &mut AppBuilder) { + app.add_system(print_world_system.system()); + } +} + +fn print_world_system() { + println!("world"); +} diff --git a/src/add_default_plugins.rs b/src/add_default_plugins.rs deleted file mode 100644 index ab3ecb789cb8b..0000000000000 --- a/src/add_default_plugins.rs +++ /dev/null @@ -1,50 +0,0 @@ -use crate::app::AppBuilder; - -pub trait AddDefaultPlugins { - fn add_default_plugins(&mut self) -> &mut Self; -} - -impl AddDefaultPlugins for AppBuilder { - fn add_default_plugins(&mut self) -> &mut Self { - self.add_plugin(bevy_type_registry::TypeRegistryPlugin::default()); - self.add_plugin(bevy_core::CorePlugin::default()); - self.add_plugin(bevy_transform::TransformPlugin::default()); - self.add_plugin(bevy_diagnostic::DiagnosticsPlugin::default()); - self.add_plugin(bevy_input::InputPlugin::default()); - self.add_plugin(bevy_window::WindowPlugin::default()); - self.add_plugin(bevy_asset::AssetPlugin::default()); - self.add_plugin(bevy_scene::ScenePlugin::default()); - - #[cfg(feature = "bevy_render")] - self.add_plugin(bevy_render::RenderPlugin::default()); - - #[cfg(feature = "bevy_sprite")] - self.add_plugin(bevy_sprite::SpritePlugin::default()); - - #[cfg(feature = "bevy_pbr")] - self.add_plugin(bevy_pbr::PbrPlugin::default()); - - #[cfg(feature = "bevy_ui")] - self.add_plugin(bevy_ui::UiPlugin::default()); - - #[cfg(feature = "bevy_text")] - self.add_plugin(bevy_text::TextPlugin::default()); - - #[cfg(feature = "bevy_audio")] - self.add_plugin(bevy_audio::AudioPlugin::default()); - - #[cfg(feature = "bevy_gilrs")] - self.add_plugin(bevy_gilrs::GilrsPlugin::default()); - - #[cfg(feature = "bevy_gltf")] - self.add_plugin(bevy_gltf::GltfPlugin::default()); - - #[cfg(feature = "bevy_winit")] - self.add_plugin(bevy_winit::WinitPlugin::default()); - - #[cfg(feature = "bevy_wgpu")] - self.add_plugin(bevy_wgpu::WgpuPlugin::default()); - - self - } -} diff --git a/src/default_plugins.rs b/src/default_plugins.rs new file mode 100644 index 0000000000000..fe7403be7da28 --- /dev/null +++ b/src/default_plugins.rs @@ -0,0 +1,58 @@ +use bevy_app::{PluginGroup, PluginGroupBuilder}; + +use crate::app::AppBuilder; + +pub struct DefaultPlugins; + +impl PluginGroup for DefaultPlugins { + fn build(&mut self, group: &mut PluginGroupBuilder) { + group.add(bevy_type_registry::TypeRegistryPlugin::default()); + group.add(bevy_core::CorePlugin::default()); + group.add(bevy_transform::TransformPlugin::default()); + group.add(bevy_diagnostic::DiagnosticsPlugin::default()); + group.add(bevy_input::InputPlugin::default()); + group.add(bevy_window::WindowPlugin::default()); + group.add(bevy_asset::AssetPlugin::default()); + group.add(bevy_scene::ScenePlugin::default()); + + #[cfg(feature = "bevy_render")] + group.add(bevy_render::RenderPlugin::default()); + + #[cfg(feature = "bevy_sprite")] + group.add(bevy_sprite::SpritePlugin::default()); + + #[cfg(feature = "bevy_pbr")] + group.add(bevy_pbr::PbrPlugin::default()); + + #[cfg(feature = "bevy_ui")] + group.add(bevy_ui::UiPlugin::default()); + + #[cfg(feature = "bevy_text")] + group.add(bevy_text::TextPlugin::default()); + + #[cfg(feature = "bevy_audio")] + group.add(bevy_audio::AudioPlugin::default()); + + #[cfg(feature = "bevy_gilrs")] + group.add(bevy_gilrs::GilrsPlugin::default()); + + #[cfg(feature = "bevy_gltf")] + group.add(bevy_gltf::GltfPlugin::default()); + + #[cfg(feature = "bevy_winit")] + group.add(bevy_winit::WinitPlugin::default()); + + #[cfg(feature = "bevy_wgpu")] + group.add(bevy_wgpu::WgpuPlugin::default()); + } +} + +pub trait AddDefaultPlugins { + fn add_default_plugins(&mut self) -> &mut Self; +} + +impl AddDefaultPlugins for AppBuilder { + fn add_default_plugins(&mut self) -> &mut Self { + self.add_plugin_group(DefaultPlugins) + } +} diff --git a/src/lib.rs b/src/lib.rs index 35bf6358e4cff..d0039fcad2462 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,10 +37,9 @@ html_favicon_url = "https://bevyengine.org/assets/icon.png" )] -mod add_default_plugins; +mod default_plugins; pub mod prelude; -pub use add_default_plugins::*; pub use bevy_app as app; pub use bevy_asset as asset; pub use bevy_core as core; @@ -55,6 +54,7 @@ pub use bevy_transform as transform; pub use bevy_type_registry as type_registry; pub use bevy_utils as utils; pub use bevy_window as window; +pub use default_plugins::*; #[cfg(feature = "bevy_audio")] pub use bevy_audio as audio; diff --git a/src/prelude.rs b/src/prelude.rs index 7c252ffec218e..205b8e822d46d 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,7 +1,7 @@ pub use crate::{ app::prelude::*, asset::prelude::*, core::prelude::*, ecs::prelude::*, input::prelude::*, math::prelude::*, property::prelude::*, scene::prelude::*, transform::prelude::*, - type_registry::RegisterType, window::prelude::*, AddDefaultPlugins, + type_registry::RegisterType, window::prelude::*, AddDefaultPlugins, DefaultPlugins, }; #[cfg(feature = "bevy_audio")]