diff --git a/benches/benches/bevy_ecs/scheduling/schedule.rs b/benches/benches/bevy_ecs/scheduling/schedule.rs index dc7068325e6ca..0930d8e122b0f 100644 --- a/benches/benches/bevy_ecs/scheduling/schedule.rs +++ b/benches/benches/bevy_ecs/scheduling/schedule.rs @@ -127,19 +127,19 @@ pub fn empty_schedule_run(criterion: &mut Criterion) { let mut schedule = Schedule::default(); schedule.set_executor_kind(bevy_ecs::schedule::ExecutorKind::SingleThreaded); group.bench_function("SingleThreaded", |bencher| { - bencher.iter(|| schedule.run(&mut app.world)); + bencher.iter(|| schedule.run(app.world_mut())); }); let mut schedule = Schedule::default(); schedule.set_executor_kind(bevy_ecs::schedule::ExecutorKind::MultiThreaded); group.bench_function("MultiThreaded", |bencher| { - bencher.iter(|| schedule.run(&mut app.world)); + bencher.iter(|| schedule.run(app.world_mut())); }); let mut schedule = Schedule::default(); schedule.set_executor_kind(bevy_ecs::schedule::ExecutorKind::Simple); group.bench_function("Simple", |bencher| { - bencher.iter(|| schedule.run(&mut app.world)); + bencher.iter(|| schedule.run(app.world_mut())); }); group.finish(); } diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 7b57f321e07e1..20adf4c368d64 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -1,22 +1,18 @@ -use crate::{First, Main, MainSchedulePlugin, Plugin, Plugins, StateTransition}; +use crate::{ + Main, MainSchedulePlugin, PlaceholderPlugin, Plugin, Plugins, PluginsState, SubApp, SubApps, +}; pub use bevy_derive::AppLabel; use bevy_ecs::{ prelude::*, - schedule::{ - common_conditions::run_once as run_once_condition, run_enter_schedule, - InternedScheduleLabel, ScheduleBuildSettings, ScheduleLabel, - }, + schedule::{ScheduleBuildSettings, ScheduleLabel}, system::SystemId, }; -use bevy_utils::{intern::Interned, tracing::debug, HashMap, HashSet}; -use std::{ - fmt::Debug, - panic::{catch_unwind, resume_unwind, AssertUnwindSafe}, -}; -use thiserror::Error; - #[cfg(feature = "trace")] use bevy_utils::tracing::info_span; +use bevy_utils::{intern::Interned, tracing::debug, HashMap}; +use std::fmt::Debug; +use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe}; +use thiserror::Error; bevy_utils::define_label!( /// A strongly-typed class of labels used to identify an [`App`]. @@ -36,12 +32,13 @@ pub(crate) enum AppError { } #[allow(clippy::needless_doctest_main)] -/// A container of app logic and data. +/// [`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). /// -/// Bundles together the necessary elements like [`World`] and [`Schedule`] to create -/// an ECS-based application. It also stores a pointer to a [runner function](Self::set_runner). -/// The runner is responsible for managing the application's event loop and applying the -/// [`Schedule`] to the [`World`] to drive application logic. /// /// # Examples /// @@ -62,120 +59,23 @@ pub(crate) enum AppError { /// } /// ``` pub struct App { - /// The main ECS [`World`] of the [`App`]. - /// This stores and provides access to all the main data of the application. - /// The systems of the [`App`] will run using this [`World`]. - /// If additional separate [`World`]-[`Schedule`] pairs are needed, you can use [`sub_app`](App::insert_sub_app)s. - pub world: World, - /// The [runner function](Self::set_runner) is primarily responsible for managing - /// the application's event loop and advancing the [`Schedule`]. - /// Typically, it is not configured manually, but set by one of Bevy's built-in plugins. - /// See `bevy::winit::WinitPlugin` and [`ScheduleRunnerPlugin`](crate::schedule_runner::ScheduleRunnerPlugin). - pub runner: Box, // Send bound is required to make App Send - /// The schedule that systems are added to by default. - /// - /// The schedule that runs the main loop of schedule execution. - /// - /// This is initially set to [`Main`]. - pub main_schedule_label: InternedScheduleLabel, - sub_apps: HashMap, - plugin_registry: Vec>, - plugin_name_added: HashSet>, - /// A private counter to prevent incorrect calls to `App::run()` from `Plugin::build()` - building_plugin_depth: usize, - plugins_state: PluginsState, + 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 std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "App {{ sub_apps: ")?; - f.debug_map().entries(self.sub_apps.iter()).finish()?; - write!(f, "}}") - } -} - -/// A [`SubApp`] contains its own [`Schedule`] and [`World`] separate from the main [`App`]. -/// This is useful for situations where data and data processing should be kept completely separate -/// from the main application. The primary use of this feature in bevy is to enable pipelined rendering. -/// -/// # Example -/// -/// ``` -/// # use bevy_app::{App, AppLabel, SubApp, Main}; -/// # use bevy_ecs::prelude::*; -/// # use bevy_ecs::schedule::ScheduleLabel; -/// -/// #[derive(Resource, Default)] -/// struct Val(pub i32); -/// -/// #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, AppLabel)] -/// struct ExampleApp; -/// -/// let mut app = App::new(); -/// -/// // initialize the main app with a value of 0; -/// app.insert_resource(Val(10)); -/// -/// // create a app with a resource and a single schedule -/// let mut sub_app = App::empty(); -/// // add an outer schedule that runs the main schedule -/// sub_app.insert_resource(Val(100)); -/// -/// // initialize main schedule -/// sub_app.add_systems(Main, |counter: Res| { -/// // since we assigned the value from the main world in extract -/// // we see that value instead of 100 -/// assert_eq!(counter.0, 10); -/// }); -/// -/// // add the sub_app to the app -/// app.insert_sub_app(ExampleApp, SubApp::new(sub_app, |main_world, sub_app| { -/// // extract the value from the main app to the sub app -/// sub_app.world.resource_mut::().0 = main_world.resource::().0; -/// })); -/// -/// // This will run the schedules once, since we're using the default runner -/// app.run(); -/// ``` -pub struct SubApp { - /// The [`SubApp`]'s instance of [`App`] - pub app: App, - - /// A function that allows access to both the main [`App`] [`World`] and the [`SubApp`]. This is - /// useful for moving data between the sub app and the main app. - extract: Box, -} - -impl SubApp { - /// Creates a new [`SubApp`]. - /// - /// The provided function `extract` is normally called by the [`update`](App::update) method. - /// After extract is called, the [`Schedule`] of the sub app is run. The [`World`] - /// parameter represents the main app world, while the [`App`] parameter is just a mutable - /// reference to the `SubApp` itself. - pub fn new(app: App, extract: impl Fn(&mut World, &mut App) + Send + 'static) -> Self { - Self { - app, - extract: Box::new(extract), - } - } - - /// Runs the [`SubApp`]'s default schedule. - pub fn run(&mut self) { - self.app.world.run_schedule(self.app.main_schedule_label); - self.app.world.clear_trackers(); - } - - /// Extracts data from main world to this sub-app. - pub fn extract(&mut self, main_world: &mut World) { - (self.extract)(main_world, &mut self.app); - } -} - -impl Debug for SubApp { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "SubApp {{ app: ")?; - f.debug_map().entries(self.app.sub_apps.iter()).finish()?; + f.debug_map() + .entries(self.sub_apps.sub_apps.iter()) + .finish()?; write!(f, "}}") } } @@ -183,9 +83,10 @@ impl Debug for SubApp { 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::(); - app.add_plugins(MainSchedulePlugin); app.add_event::(); @@ -194,26 +95,6 @@ impl Default for App { } } -/// Plugins state in the application -#[derive(PartialEq, Eq, Debug, Clone, Copy)] -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, -} - -// Dummy plugin used to temporary hold the place in the plugin registry -struct PlaceholderPlugin; - -impl Plugin for PlaceholderPlugin { - fn build(&self, _app: &mut 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. @@ -223,202 +104,201 @@ impl App { /// Creates a new empty [`App`] with minimal default configuration. /// - /// This constructor should be used if you wish to provide custom scheduling, exit handling, cleanup, etc. + /// Use this constructor if you want to customize scheduling, exit handling, cleanup, etc. pub fn empty() -> App { - let mut world = World::new(); - world.init_resource::(); Self { - world, + sub_apps: SubApps { + main: SubApp::new(), + sub_apps: HashMap::new(), + }, runner: Box::new(run_once), - sub_apps: HashMap::default(), - plugin_registry: Vec::default(), - plugin_name_added: Default::default(), - main_schedule_label: Main.intern(), - building_plugin_depth: 0, - plugins_state: PluginsState::Adding, } } - /// Advances the execution of the [`Schedule`] by one cycle. - /// - /// This method also updates sub apps. - /// See [`insert_sub_app`](Self::insert_sub_app) for more details. - /// - /// The schedule run by this method is determined by the [`main_schedule_label`](App) field. - /// By default this is [`Main`]. - /// - /// # Panics - /// - /// The active schedule of the app must be set before this method is called. + /// Runs the default schedules of all sub-apps (starting with the "main" app) once. pub fn update(&mut self) { - #[cfg(feature = "trace")] - let _bevy_update_span = info_span!("update").entered(); - { - #[cfg(feature = "trace")] - let _bevy_main_update_span = info_span!("main app").entered(); - self.world.run_schedule(self.main_schedule_label); - } - for (_label, sub_app) in &mut self.sub_apps { - #[cfg(feature = "trace")] - let _sub_app_span = info_span!("sub app", name = ?_label).entered(); - sub_app.extract(&mut self.world); - sub_app.run(); + if self.is_building_plugins() { + panic!("App::update() was called while a plugin was building."); } - self.world.clear_trackers(); + self.sub_apps.update(); } - /// Starts the application by calling the app's [runner function](Self::set_runner). + /// Runs the [`App`] by calling its [runner](Self::set_runner). /// - /// Finalizes the [`App`] configuration. For general usage, see the example on the item + /// This will (re)build the [`App`] first. For general usage, see the example on the item /// level documentation. /// - /// # `run()` might not return + /// # Caveats /// /// Calls to [`App::run()`] will never return on iOS and Web. /// - /// In simple and *headless* applications, one can expect that execution will - /// proceed, normally, after calling [`run()`](App::run()) but this is not the case for - /// windowed applications. - /// - /// Windowed apps are typically driven by an *event loop* or *message loop* and - /// some window-manager APIs expect programs to terminate when their primary - /// window is closed and that event loop terminates – behavior of processes that - /// do not is often platform dependent or undocumented. + /// 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 called from `Plugin::build()`, because it would prevent other plugins to properly build. + /// Panics if not all plugins have been built. pub fn run(&mut self) { #[cfg(feature = "trace")] let _bevy_app_run_span = info_span!("bevy_app").entered(); - - let mut app = std::mem::replace(self, App::empty()); - if app.building_plugin_depth > 0 { - panic!("App::run() was called from within Plugin::build(), which is not allowed."); + if self.is_building_plugins() { + panic!("App::run() was called while a plugin was building."); } - let runner = std::mem::replace(&mut app.runner, Box::new(run_once)); - runner(app); + let runner = std::mem::replace(&mut self.runner, Box::new(run_once)); + let app = std::mem::replace(self, App::empty()); + (runner)(app); } - /// Check the state of all plugins already added to this app. This is usually called by the - /// event loop, but can be useful for situations where you want to use [`App::update`] + /// 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) { + /// loop { + /// println!("In main loop"); + /// app.update(); + /// } + /// } + /// + /// App::new() + /// .set_runner(my_runner); + /// ``` + pub fn set_runner(&mut self, f: impl FnOnce(App) + '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(&self) -> PluginsState { - match self.plugins_state { + pub fn plugins_state(&mut self) -> PluginsState { + let mut overall_plugins_state = match self.main_mut().plugins_state { PluginsState::Adding => { - for plugin in &self.plugin_registry { + let mut state = PluginsState::Ready; + let plugins = std::mem::take(&mut self.main_mut().plugins); + for plugin in &plugins.registry { + // plugins installed to main need to see all sub-apps if !plugin.ready(self) { - return PluginsState::Adding; + state = PluginsState::Adding; + break; } } - PluginsState::Ready + self.main_mut().plugins = 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 } - /// Run [`Plugin::finish`] for each plugin. This is usually called by the event loop once all + /// 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) { - // temporarily remove the plugin registry to run each plugin's setup function on app. - let plugin_registry = std::mem::take(&mut self.plugin_registry); - for plugin in &plugin_registry { + // plugins installed to main should see all sub-apps + let plugins = std::mem::take(&mut self.main_mut().plugins); + for plugin in &plugins.registry { plugin.finish(self); } - self.plugin_registry = plugin_registry; - self.plugins_state = PluginsState::Finished; + let main = self.main_mut(); + main.plugins = plugins; + main.plugins_state = PluginsState::Finished; + self.sub_apps.iter_mut().skip(1).for_each(|s| s.finish()); } - /// Run [`Plugin::cleanup`] for each plugin. This is usually called by the event loop after + /// 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) { - // temporarily remove the plugin registry to run each plugin's setup function on app. - let plugin_registry = std::mem::take(&mut self.plugin_registry); - for plugin in &plugin_registry { + // plugins installed to main should see all sub-apps + let plugins = std::mem::take(&mut self.main_mut().plugins); + for plugin in &plugins.registry { plugin.cleanup(self); } - self.plugin_registry = plugin_registry; - self.plugins_state = PluginsState::Cleaned; + let main = self.main_mut(); + main.plugins = plugins; + main.plugins_state = PluginsState::Cleaned; + self.sub_apps.iter_mut().skip(1).for_each(|s| s.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(|s| s.is_building_plugins()) } /// Initializes a [`State`] with standard starting values. /// /// If the [`State`] already exists, nothing happens. /// - /// Adds [`State`] and [`NextState`] resources, [`OnEnter`] and [`OnExit`] schedules - /// for each state variant (if they don't already exist), an instance of [`apply_state_transition::`] in - /// [`StateTransition`] so that transitions happen before [`Update`](crate::Update) and - /// a instance of [`run_enter_schedule::`] in [`StateTransition`] with a - /// [`run_once`](`run_once_condition`) condition to run the on enter schedule of the - /// initial state. + /// Adds [`State`] and [`NextState`] resources, [`OnEnter`] and [`OnExit`] schedules for + /// each state variant (if they don't already exist), an instance of [`apply_state_transition::`] + /// in [`StateTransition`] so that transitions happen before [`Update`] and an instance of + /// [`run_enter_schedule::`] in [`StateTransition`] with a [`run_once`] condition to run the + /// on enter schedule of the initial state. /// - /// If you would like to control how other systems run based on the current state, - /// you can emulate this behavior using the [`in_state`] [`Condition`]. + /// If you would like to control how other systems run based on the current state, you can + /// emulate this behavior using the [`in_state`] [`Condition`]. /// - /// Note that you can also apply state transitions at other points in the schedule - /// by adding the [`apply_state_transition`] system manually. + /// Note that you can also apply state transitions at other points in the schedule by adding + /// the [`apply_state_transition::`] system manually. + /// + /// [`StateTransition`]: crate::StateTransition + /// [`Update`]: crate::Update + /// [`run_once`]: bevy_ecs::schedule::common_conditions::run_once + /// [`run_enter_schedule::`]: bevy_ecs::schedule::run_enter_schedule + /// [`apply_state_transition::`]: bevy_ecs::schedule::apply_state_transition pub fn init_state(&mut self) -> &mut Self { - if !self.world.contains_resource::>() { - self.init_resource::>() - .init_resource::>() - .add_event::>() - .add_systems( - StateTransition, - ( - run_enter_schedule::.run_if(run_once_condition()), - apply_state_transition::, - ) - .chain(), - ); - } - - // The OnEnter, OnExit, and OnTransition schedules are lazily initialized - // (i.e. when the first system is added to them), and World::try_run_schedule is used to fail - // gracefully if they aren't present. - + self.main_mut().init_state::(); self } - /// Inserts a specific [`State`] to the current [`App`] and - /// overrides any [`State`] previously added of the same type. + /// Inserts a specific [`State`] to the current [`App`] and overrides any [`State`] previously + /// added of the same type. + /// + /// Adds [`State`] and [`NextState`] resources, [`OnEnter`] and [`OnExit`] schedules for + /// each state variant (if they don't already exist), an instance of [`apply_state_transition::`] + /// in [`StateTransition`] so that transitions happen before [`Update`](crate::Update) and an + /// instance of [`run_enter_schedule::`] in [`StateTransition`] with a [`run_once`] + /// condition to run the on enter schedule of the initial state. /// - /// Adds [`State`] and [`NextState`] resources, [`OnEnter`] and [`OnExit`] schedules - /// for each state variant (if they don't already exist), an instance of [`apply_state_transition::`] in - /// [`StateTransition`] so that transitions happen before [`Update`](crate::Update) and - /// a instance of [`run_enter_schedule::`] in [`StateTransition`] with a - /// [`run_once`](`run_once_condition`) condition to run the on enter schedule of the - /// initial state. + /// If you would like to control how other systems run based on the current state, you can + /// emulate this behavior using the [`in_state`] [`Condition`]. /// - /// If you would like to control how other systems run based on the current state, - /// you can emulate this behavior using the [`in_state`] [`Condition`]. + /// Note that you can also apply state transitions at other points in the schedule by adding + /// the [`apply_state_transition::`] system manually. /// - /// Note that you can also apply state transitions at other points in the schedule - /// by adding the [`apply_state_transition`] system manually. + /// [`StateTransition`]: crate::StateTransition + /// [`Update`]: crate::Update + /// [`run_once`]: bevy_ecs::schedule::common_conditions::run_once + /// [`run_enter_schedule::`]: bevy_ecs::schedule::run_enter_schedule + /// [`apply_state_transition::`]: bevy_ecs::schedule::apply_state_transition pub fn insert_state(&mut self, state: S) -> &mut Self { - self.insert_resource(State::new(state)) - .init_resource::>() - .add_event::>() - .add_systems( - StateTransition, - ( - run_enter_schedule::.run_if(run_once_condition()), - apply_state_transition::, - ) - .chain(), - ); - - // The OnEnter, OnExit, and OnTransition schedules are lazily initialized - // (i.e. when the first system is added to them), and World::try_run_schedule is used to fail - // gracefully if they aren't present. - + self.main_mut().insert_state(state); self } - /// Adds a system to the given schedule in this app's [`Schedules`]. + /// Adds a collection of systems to `schedule` (stored in the main world's [`Schedules`]). /// /// # Examples /// @@ -440,17 +320,7 @@ impl App { schedule: impl ScheduleLabel, systems: impl IntoSystemConfigs, ) -> &mut Self { - let schedule = schedule.intern(); - let mut schedules = self.world.resource_mut::(); - - if let Some(schedule) = schedules.get_mut(schedule) { - schedule.add_systems(systems); - } else { - let mut new_schedule = Schedule::new(schedule); - new_schedule.add_systems(systems); - schedules.insert(new_schedule); - } - + self.main_mut().add_systems(schedule, systems); self } @@ -467,7 +337,7 @@ impl App { &mut self, system: S, ) -> SystemId { - self.world.register_system(system) + self.main_mut().register_system(system) } /// Configures a collection of system sets in the provided schedule, adding any sets that do not exist. @@ -477,24 +347,14 @@ impl App { schedule: impl ScheduleLabel, sets: impl IntoSystemSetConfigs, ) -> &mut Self { - let schedule = schedule.intern(); - let mut schedules = self.world.resource_mut::(); - if let Some(schedule) = schedules.get_mut(schedule) { - schedule.configure_sets(sets); - } else { - let mut new_schedule = Schedule::new(schedule); - new_schedule.configure_sets(sets); - schedules.insert(new_schedule); - } + self.main_mut().configure_sets(schedule, sets); self } - /// Setup the application to manage events of type `T`. + /// Initializes `T` event handling by inserting an event queue resource ([`Events::`]) + /// and scheduling an [`event_update_system`] in [`First`](crate::First). /// - /// This is done by adding a [`Resource`] of type [`Events::`], - /// and inserting an [`event_update_system`] into [`First`]. - /// - /// See [`Events`] for defining events. + /// See [`Events`] for information on how to define events. /// /// # Examples /// @@ -514,23 +374,14 @@ impl App { where T: Event, { - if !self.world.contains_resource::>() { - self.init_resource::>().add_systems( - First, - bevy_ecs::event::event_update_system:: - .in_set(bevy_ecs::event::EventUpdates) - .run_if(bevy_ecs::event::event_update_condition::), - ); - } + self.main_mut().add_event::(); self } - /// Inserts a [`Resource`] to the current [`App`] and overwrites any [`Resource`] previously added of the same type. + /// Inserts the [`Resource`] into the app, overwriting any existing resource of the same type. /// - /// A [`Resource`] in Bevy represents globally unique data. [`Resource`]s must be added to Bevy apps - /// before using them. This happens with [`insert_resource`](Self::insert_resource). - /// - /// See [`init_resource`](Self::init_resource) for [`Resource`]s that implement [`Default`] or [`FromWorld`]. + /// There is also an [`init_resource`](Self::init_resource) for resources that have + /// [`Default`] or [`FromWorld`] implementations. /// /// # Examples /// @@ -547,40 +398,16 @@ impl App { /// .insert_resource(MyCounter { counter: 0 }); /// ``` pub fn insert_resource(&mut self, resource: R) -> &mut Self { - self.world.insert_resource(resource); + self.main_mut().insert_resource(resource); self } - /// Inserts a non-send resource to the app. + /// Inserts the [`Resource`], initialized with its default value, into the app, + /// if there is no existing instance of `R`. /// - /// You usually want to use [`insert_resource`](Self::insert_resource), - /// but there are some special cases when a resource cannot be sent across threads. - /// - /// # 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(&mut self, resource: R) -> &mut Self { - self.world.insert_non_send_resource(resource); - self - } - - /// Initialize a [`Resource`] with standard starting values by adding it to the [`World`]. - /// - /// If the [`Resource`] already exists, nothing happens. - /// - /// The [`Resource`] must implement the [`FromWorld`] trait. - /// If the [`Default`] trait is implemented, the [`FromWorld`] trait will use - /// the [`Default::default`] method to initialize the [`Resource`]. + /// `R` must implement [`FromWorld`]. + /// If `R` implements [`Default`], [`FromWorld`] will be automatically implemented and + /// initialize the [`Resource`] with [`Default::default`]. /// /// # Examples /// @@ -605,91 +432,90 @@ impl App { /// .init_resource::(); /// ``` pub fn init_resource(&mut self) -> &mut Self { - self.world.init_resource::(); - self - } - - /// Initialize a non-send [`Resource`] with standard starting values by adding it to the [`World`]. - /// - /// The [`Resource`] must implement the [`FromWorld`] trait. - /// If the [`Default`] trait is implemented, the [`FromWorld`] trait will use - /// the [`Default::default`] method to initialize the [`Resource`]. - pub fn init_non_send_resource(&mut self) -> &mut Self { - self.world.init_non_send_resource::(); + self.main_mut().init_resource::(); self } - /// Sets the function that will be called when the app is run. + /// Inserts the [`!Send`](Send) resource into the app, overwriting any existing resource + /// of the same type. /// - /// The runner function `run_fn` 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`). + /// 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::*; /// # - /// fn my_runner(mut app: App) { - /// loop { - /// println!("In main loop"); - /// app.update(); - /// } + /// struct MyCounter { + /// counter: usize, /// } /// /// App::new() - /// .set_runner(my_runner); + /// .insert_non_send_resource(MyCounter { counter: 0 }); /// ``` - pub fn set_runner(&mut self, run_fn: impl FnOnce(App) + 'static + Send) -> &mut Self { - self.runner = Box::new(run_fn); + pub fn insert_non_send_resource(&mut self, resource: R) -> &mut Self { + self.world_mut().insert_non_send_resource(resource); + self + } + + /// Inserts the [`!Send`](Send) resource into the app, initialized with its default value, + /// if there is no existing instance of `R`. + pub fn init_non_send_resource(&mut self) -> &mut Self { + self.world_mut().init_non_send_resource::(); self } - /// Boxed variant of [`add_plugins`](App::add_plugins) that can be used from a - /// [`PluginGroup`](super::PluginGroup) pub(crate) fn add_boxed_plugin( &mut self, plugin: Box, ) -> Result<&mut Self, AppError> { debug!("added plugin: {}", plugin.name()); - if plugin.is_unique() && !self.plugin_name_added.insert(plugin.name().into()) { + if plugin.is_unique() + && !self + .main_mut() + .plugins + .names + .insert(plugin.name().to_string()) + { Err(AppError::DuplicatePlugin { plugin_name: plugin.name().to_string(), })?; } - // Reserve that position in the plugin registry. if a plugin adds plugins, they will be correctly ordered - let plugin_position_in_registry = self.plugin_registry.len(); - self.plugin_registry.push(Box::new(PlaceholderPlugin)); + // Reserve position in the plugin registry. If the plugin adds more plugins, + // they'll all end up in insertion order. + let index = self.main().plugins.registry.len(); + self.main_mut() + .plugins + .registry + .push(Box::new(PlaceholderPlugin)); - self.building_plugin_depth += 1; + self.main_mut().plugin_build_depth += 1; let result = catch_unwind(AssertUnwindSafe(|| plugin.build(self))); - self.building_plugin_depth -= 1; + self.main_mut().plugin_build_depth -= 1; + if let Err(payload) = result { resume_unwind(payload); } - self.plugin_registry[plugin_position_in_registry] = plugin; + + self.main_mut().plugins.registry[index] = plugin; Ok(self) } - /// Checks if a [`Plugin`] has already been added. - /// - /// This can be used by plugins to check if a plugin they depend upon has already been - /// added. + /// Returns `true` if the [`Plugin`] has already been added. pub fn is_plugin_added(&self) -> bool where T: Plugin, { - self.plugin_registry.iter().any(|p| p.is::()) + self.main().is_plugin_added::() } - /// Returns a vector of references to any plugins of type `T` that have been added. + /// 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 already added plugins. - /// This vector will be length zero if no plugins of that type 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. /// /// ``` @@ -709,16 +535,13 @@ impl App { where T: Plugin, { - self.plugin_registry - .iter() - .filter_map(|p| p.downcast_ref()) - .collect() + self.main().get_added_plugins::() } - /// Adds one or more [`Plugin`]s. + /// Installs a [`Plugin`] collection. /// - /// One of Bevy's core principles is modularity. All Bevy engine features are implemented - /// as [`Plugin`]s. This includes internal features like the renderer. + /// 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`]. /// @@ -750,7 +573,7 @@ impl App { /// /// # Panics /// - /// Panics if one of the plugins was already added to the application. + /// Panics if one of the plugins had already been added to the application. /// /// [`PluginGroup`]:super::PluginGroup #[track_caller] @@ -774,20 +597,21 @@ impl App { /// #[reflect(Component, Serialize, Deserialize)] // will register ReflectComponent, ReflectSerialize, ReflectDeserialize /// ``` /// - /// See [`bevy_reflect::TypeRegistry::register`]. + /// See [`bevy_reflect::TypeRegistry::register`] for more information. #[cfg(feature = "bevy_reflect")] pub fn register_type(&mut self) -> &mut Self { - let registry = self.world.resource_mut::(); - registry.write().register::(); + self.main_mut().register_type::(); self } - /// Adds the type data `D` to type `T` in the [`TypeRegistry`](bevy_reflect::TypeRegistry) resource. + /// Associates type data `D` with type `T` in the [`TypeRegistry`](bevy_reflect::TypeRegistry) resource. /// - /// Most of the time [`App::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. + /// 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 /// ``` @@ -808,120 +632,108 @@ impl App { >( &mut self, ) -> &mut Self { - let registry = self.world.resource_mut::(); - registry.write().register_type_data::(); + self.main_mut().register_type_data::(); self } - /// Retrieves a `SubApp` stored inside this [`App`]. + /// 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_mut(&mut self, label: impl AppLabel) -> &mut App { - match self.get_sub_app_mut(label) { - Ok(app) => app, - Err(label) => panic!("Sub-App with label '{:?}' does not exist", label), - } - } - - /// Retrieves a `SubApp` inside this [`App`] with the given label, if it exists. Otherwise returns - /// an [`Err`] containing the given label. - pub fn get_sub_app_mut(&mut self, label: impl AppLabel) -> Result<&mut App, impl AppLabel> { - self.sub_apps - .get_mut(&label.intern()) - .map(|sub_app| &mut sub_app.app) - .ok_or(label) + /// 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); + }) } - /// Retrieves a `SubApp` stored inside this [`App`]. + /// 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) -> &App { - match self.get_sub_app(label) { - Ok(app) => app, - Err(label) => panic!("Sub-App with label '{:?}' does not exist", label), - } + /// 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); + }) } - /// Inserts an existing sub app into the app - pub fn insert_sub_app(&mut self, label: impl AppLabel, sub_app: SubApp) { - self.sub_apps.insert(label.intern(), sub_app); + /// 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()) } - /// Removes a sub app from the app. Returns [`None`] if the label doesn't exist. - pub fn remove_sub_app(&mut self, label: impl AppLabel) -> Option { - self.sub_apps.remove(&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()) } - /// Retrieves a `SubApp` inside this [`App`] with the given label, if it exists. Otherwise returns - /// an [`Err`] containing the given label. - pub fn get_sub_app(&self, label: impl AppLabel) -> Result<&App, impl AppLabel> { - self.sub_apps - .get(&label.intern()) - .map(|sub_app| &sub_app.app) - .ok_or(label) + /// 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); } - /// Adds a new `schedule` to the [`App`]. - /// - /// # Warning - /// This method will overwrite any existing schedule with the same label. - /// To avoid this behavior, use the `init_schedule` method instead. - pub fn add_schedule(&mut self, schedule: Schedule) -> &mut Self { - let mut schedules = self.world.resource_mut::(); - schedules.insert(schedule); + /// Removes the [`SubApp`] with the given label, if it exists. + pub fn remove_sub_app(&mut self, label: impl AppLabel) -> Option { + self.sub_apps.sub_apps.remove(&label.intern()) + } + /// 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 a new empty `schedule` to the [`App`] under the provided `label` if it does not exists. + /// Initializes an empty `schedule` under the provided `label`, if it does not exist. /// - /// See [`App::add_schedule`] to pass in a pre-constructed schedule. + /// See [`add_schedule`](Self::add_schedule) to insert an existing schedule. pub fn init_schedule(&mut self, label: impl ScheduleLabel) -> &mut Self { - let label = label.intern(); - let mut schedules = self.world.resource_mut::(); - if !schedules.contains(label) { - schedules.insert(Schedule::new(label)); - } + self.main_mut().init_schedule(label); self } - /// Gets read-only access to the [`Schedule`] with the provided `label` if it exists. + /// Returns a reference to the [`Schedule`] with the provided `label` if it exists. pub fn get_schedule(&self, label: impl ScheduleLabel) -> Option<&Schedule> { - let schedules = self.world.get_resource::()?; - schedules.get(label) + self.main().get_schedule(label) } - /// Gets read-write access to a [`Schedule`] with the provided `label` if it exists. + /// 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> { - let schedules = self.world.get_resource_mut::()?; - // We need to call .into_inner here to satisfy the borrow checker: - // it can reason about reborrows using ordinary references but not the `Mut` smart pointer. - schedules.into_inner().get_mut(label) + self.main_mut().get_schedule_mut(label) } - /// Applies the function to the [`Schedule`] associated with `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 FnOnce(&mut Schedule), + f: impl FnMut(&mut Schedule), ) -> &mut Self { - let label = label.intern(); - let mut schedules = self.world.resource_mut::(); - - if schedules.get(label).is_none() { - schedules.insert(Schedule::new(label)); - } - - let schedule = schedules.get_mut(label).unwrap(); - // Call the function f, passing in the schedule retrieved - f(schedule); - + self.main_mut().edit_schedule(label, f); self } @@ -930,9 +742,7 @@ impl App { &mut self, schedule_build_settings: ScheduleBuildSettings, ) -> &mut Self { - self.world - .resource_mut::() - .configure_schedules(schedule_build_settings); + self.main_mut().configure_schedules(schedule_build_settings); self } @@ -970,7 +780,7 @@ impl App { /// app.update(); /// ``` pub fn allow_ambiguous_component(&mut self) -> &mut Self { - self.world.allow_ambiguous_component::(); + self.main_mut().allow_ambiguous_component::(); self } @@ -1009,7 +819,7 @@ impl App { /// app.update(); /// ``` pub fn allow_ambiguous_resource(&mut self) -> &mut Self { - self.world.allow_ambiguous_resource::(); + self.main_mut().allow_ambiguous_resource::(); self } @@ -1030,22 +840,13 @@ impl App { S1: IntoSystemSet, S2: IntoSystemSet, { - let schedule = schedule.intern(); - let mut schedules = self.world.resource_mut::(); - - if let Some(schedule) = schedules.get_mut(schedule) { - let schedule: &mut Schedule = schedule; - schedule.ignore_ambiguity(a, b); - } else { - let mut new_schedule = Schedule::new(schedule); - new_schedule.ignore_ambiguity(a, b); - schedules.insert(new_schedule); - } - + self.main_mut().ignore_ambiguity(schedule, a, b); self } } +type RunnerFn = Box; + fn run_once(mut app: App) { while app.plugins_state() == PluginsState::Adding { #[cfg(not(target_arch = "wasm32"))] @@ -1057,16 +858,12 @@ fn run_once(mut app: App) { app.update(); } -/// An event that indicates the [`App`] should exit. This will fully exit the app process at the -/// start of the next tick of the schedule. -/// -/// You can also use this event to detect that an exit was requested. In order to receive it, systems -/// subscribing to this event should run after it was emitted and before the schedule of the same -/// frame is over. This is important since [`App::run()`] might never return. +/// 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. /// -/// If you don't require access to other components or resources, consider implementing the [`Drop`] -/// trait on components/resources for code that runs on exit. That saves you from worrying about -/// system schedule ordering, and is idiomatic Rust. +/// 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. #[derive(Event, Debug, Clone, Default)] pub struct AppExit; @@ -1157,8 +954,8 @@ mod tests { app.init_state::() .add_systems(OnEnter(AppState::MainMenu), (foo, bar)); - app.world.run_schedule(OnEnter(AppState::MainMenu)); - assert_eq!(app.world.entities().len(), 2); + app.world_mut().run_schedule(OnEnter(AppState::MainMenu)); + assert_eq!(app.world().entities().len(), 2); } #[test] @@ -1167,8 +964,8 @@ mod tests { app.add_systems(OnEnter(AppState::MainMenu), (foo, bar)) .init_state::(); - app.world.run_schedule(OnEnter(AppState::MainMenu)); - assert_eq!(app.world.entities().len(), 2); + app.world_mut().run_schedule(OnEnter(AppState::MainMenu)); + assert_eq!(app.world().entities().len(), 2); } #[test] @@ -1288,7 +1085,7 @@ mod tests { fn my_runner(mut app: App) { let my_state = MyState {}; - app.world.insert_resource(my_state); + app.world_mut().insert_resource(my_state); for _ in 0..5 { app.update(); diff --git a/crates/bevy_app/src/lib.rs b/crates/bevy_app/src/lib.rs index 232c3e801e73a..8d4fbcfc63923 100644 --- a/crates/bevy_app/src/lib.rs +++ b/crates/bevy_app/src/lib.rs @@ -13,6 +13,7 @@ mod panic_handler; mod plugin; mod plugin_group; mod schedule_runner; +mod sub_app; pub use app::*; pub use bevy_derive::DynamicPlugin; @@ -21,6 +22,7 @@ pub use panic_handler::*; pub use plugin::*; pub use plugin_group::*; pub use schedule_runner::*; +pub use sub_app::*; #[allow(missing_docs)] pub mod prelude { @@ -32,6 +34,7 @@ pub mod prelude { PostStartup, PostUpdate, PreStartup, PreUpdate, SpawnScene, Startup, StateTransition, Update, }, + sub_app::SubApp, DynamicPlugin, Plugin, PluginGroup, }; } diff --git a/crates/bevy_app/src/main_schedule.rs b/crates/bevy_app/src/main_schedule.rs index 62dd158944c40..0d3a804df750b 100644 --- a/crates/bevy_app/src/main_schedule.rs +++ b/crates/bevy_app/src/main_schedule.rs @@ -27,13 +27,14 @@ use bevy_ecs::{ /// # Rendering /// /// Note rendering is not executed in the main schedule by default. -/// Instead, rendering is performed in a separate [`SubApp`](crate::app::SubApp) +/// Instead, rendering is performed in a separate [`SubApp`] /// which exchanges data with the main app in between the main schedule runs. /// /// See [`RenderPlugin`] and [`PipelinedRenderingPlugin`] for more details. /// /// [`RenderPlugin`]: https://docs.rs/bevy/latest/bevy/render/struct.RenderPlugin.html /// [`PipelinedRenderingPlugin`]: https://docs.rs/bevy/latest/bevy/render/pipelined_rendering/struct.PipelinedRenderingPlugin.html +/// [`SubApp`]: crate::SubApp #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)] pub struct Main; diff --git a/crates/bevy_app/src/plugin.rs b/crates/bevy_app/src/plugin.rs index 3dfb28b428ab7..d3307485fecbb 100644 --- a/crates/bevy_app/src/plugin.rs +++ b/crates/bevy_app/src/plugin.rs @@ -100,6 +100,26 @@ impl Plugin for T { } } +/// 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) {} +} + /// A type representing an unsafe function that returns a mutable pointer to a [`Plugin`]. /// It is used for dynamically loading plugins. /// diff --git a/crates/bevy_app/src/schedule_runner.rs b/crates/bevy_app/src/schedule_runner.rs index 1e17f8322a5ee..d7dd902548438 100644 --- a/crates/bevy_app/src/schedule_runner.rs +++ b/crates/bevy_app/src/schedule_runner.rs @@ -94,7 +94,7 @@ impl Plugin for ScheduleRunnerPlugin { app.update(); if let Some(app_exit_events) = - app.world.get_resource_mut::>() + app.world_mut().get_resource_mut::>() { if let Some(exit) = app_exit_event_reader.read(&app_exit_events).last() { diff --git a/crates/bevy_app/src/sub_app.rs b/crates/bevy_app/src/sub_app.rs new file mode 100644 index 0000000000000..b4baa6968925d --- /dev/null +++ b/crates/bevy_app/src/sub_app.rs @@ -0,0 +1,517 @@ +use crate::{App, First, InternedAppLabel, Plugin, Plugins, PluginsState, StateTransition}; +use bevy_ecs::{ + prelude::*, + schedule::{ + common_conditions::run_once as run_once_condition, run_enter_schedule, + InternedScheduleLabel, ScheduleBuildSettings, ScheduleLabel, + }, + system::SystemId, +}; +#[cfg(feature = "trace")] +use bevy_utils::tracing::info_span; +use bevy_utils::{default, HashMap, HashSet}; +use std::fmt::Debug; + +type ExtractFn = Box; + +#[derive(Default)] +pub(crate) struct PluginStore { + pub(crate) registry: Vec>, + pub(crate) names: HashSet, +} + +/// A secondary application with its own [`World`]. These can run independently of each other. +/// +/// These are useful for situations where certain processes (e.g. a render thread) need to be kept +/// separate from the main application. +/// +/// # Example +/// +/// ``` +/// # use bevy_app::{App, AppLabel, SubApp, Main}; +/// # use bevy_ecs::prelude::*; +/// # use bevy_ecs::schedule::ScheduleLabel; +/// +/// #[derive(Resource, Default)] +/// struct Val(pub i32); +/// +/// #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, AppLabel)] +/// struct ExampleApp; +/// +/// // Create an app with a certain resource. +/// let mut app = App::new(); +/// app.insert_resource(Val(10)); +/// +/// // Create a sub-app with the same resource and a single schedule. +/// let mut sub_app = SubApp::new(); +/// sub_app.insert_resource(Val(100)); +/// +/// // Setup an extract function to copy the resource's value in the main world. +/// sub_app.set_extract(|main_world, sub_world| { +/// sub_world.resource_mut::().0 = main_world.resource::().0; +/// }); +/// +/// // Schedule a system that will verify extraction is working. +/// sub_app.add_systems(Main, |counter: Res| { +/// // The value will be copied during extraction, so we should see 10 instead of 100. +/// assert_eq!(counter.0, 10); +/// }); +/// +/// // Add the sub-app to the main app. +/// app.insert_sub_app(ExampleApp, sub_app); +/// +/// // Update the application once (using the default runner). +/// app.run(); +/// ``` +pub struct SubApp { + /// The data of this application. + world: World, + /// Metadata for installed plugins. + pub(crate) plugins: PluginStore, + /// Panics if an update is attempted while plugins are building. + pub(crate) plugin_build_depth: usize, + pub(crate) plugins_state: PluginsState, + /// The schedule that will be run by [`update`](Self::update). + pub update_schedule: Option, + /// A function that gives mutable access to two app worlds. This is primarily + /// intended for copying data from the main world to secondary worlds. + extract: Option, +} + +impl Debug for SubApp { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "SubApp") + } +} + +impl Default for SubApp { + fn default() -> Self { + let mut world = World::new(); + world.init_resource::(); + Self { + world, + plugins: default(), + plugin_build_depth: 0, + plugins_state: PluginsState::Adding, + update_schedule: None, + extract: None, + } + } +} + +impl SubApp { + /// Returns a default, empty [`SubApp`]. + pub fn new() -> Self { + Self::default() + } + + /// This method is a workaround. Each [`SubApp`] can have its own plugins, but [`Plugin`] + /// works on an [`App`] as a whole. + fn run_as_app(&mut self, f: F) + where + F: FnOnce(&mut App), + { + let mut app = App::empty(); + std::mem::swap(self, &mut app.sub_apps.main); + f(&mut app); + std::mem::swap(self, &mut app.sub_apps.main); + } + + /// Returns a reference to the [`World`]. + pub fn world(&self) -> &World { + &self.world + } + + /// Returns a mutable reference to the [`World`]. + pub fn world_mut(&mut self) -> &mut World { + &mut self.world + } + + /// Runs the default schedule. + pub fn update(&mut self) { + if self.is_building_plugins() { + panic!("SubApp::update() was called while a plugin was building."); + } + + if let Some(label) = self.update_schedule { + self.world.run_schedule(label); + } + self.world.clear_trackers(); + } + + /// Extracts data from `world` into the app's world using the registered extract method. + /// + /// **Note:** There is no default extract method. Calling `extract` does nothing if + /// [`set_extract`](Self::set_extract) has not been called. + pub fn extract(&mut self, world: &mut World) { + if let Some(f) = self.extract.as_mut() { + f(world, &mut self.world); + } + } + + /// Sets the method that will be called by [`extract`](Self::extract). + /// + /// The first argument is the `World` to extract data from, the second argument is the app `World`. + pub fn set_extract(&mut self, extract: F) -> &mut Self + where + F: Fn(&mut World, &mut World) + Send + 'static, + { + self.extract = Some(Box::new(extract)); + self + } + + /// See [`App::insert_resource`]. + pub fn insert_resource(&mut self, resource: R) -> &mut Self { + self.world.insert_resource(resource); + self + } + + /// See [`App::init_resource`]. + pub fn init_resource(&mut self) -> &mut Self { + self.world.init_resource::(); + self + } + + /// See [`App::add_systems`]. + pub fn add_systems( + &mut self, + schedule: impl ScheduleLabel, + systems: impl IntoSystemConfigs, + ) -> &mut Self { + let label = schedule.intern(); + let mut schedules = self.world.resource_mut::(); + if let Some(schedule) = schedules.get_mut(label) { + schedule.add_systems(systems); + } else { + let mut new_schedule = Schedule::new(label); + new_schedule.add_systems(systems); + schedules.insert(new_schedule); + } + + self + } + + /// See [`App::register_system`]. + pub fn register_system + 'static>( + &mut self, + system: S, + ) -> SystemId { + self.world.register_system(system) + } + + /// See [`App::configure_sets`]. + #[track_caller] + pub fn configure_sets( + &mut self, + schedule: impl ScheduleLabel, + sets: impl IntoSystemSetConfigs, + ) -> &mut Self { + let label = schedule.intern(); + let mut schedules = self.world.resource_mut::(); + if let Some(schedule) = schedules.get_mut(label) { + schedule.configure_sets(sets); + } else { + let mut new_schedule = Schedule::new(label); + new_schedule.configure_sets(sets); + schedules.insert(new_schedule); + } + self + } + + /// See [`App::add_schedule`]. + pub fn add_schedule(&mut self, schedule: Schedule) -> &mut Self { + let mut schedules = self.world.resource_mut::(); + schedules.insert(schedule); + self + } + + /// See [`App::init_schedule`]. + pub fn init_schedule(&mut self, label: impl ScheduleLabel) -> &mut Self { + let label = label.intern(); + let mut schedules = self.world.resource_mut::(); + if !schedules.contains(label) { + schedules.insert(Schedule::new(label)); + } + self + } + + /// See [`App::get_schedule`]. + pub fn get_schedule(&self, label: impl ScheduleLabel) -> Option<&Schedule> { + let schedules = self.world.get_resource::()?; + schedules.get(label) + } + + /// See [`App::get_schedule_mut`]. + pub fn get_schedule_mut(&mut self, label: impl ScheduleLabel) -> Option<&mut Schedule> { + let schedules = self.world.get_resource_mut::()?; + // We must call `.into_inner` here because the borrow checker only understands reborrows + // using ordinary references, not our `Mut` smart pointers. + schedules.into_inner().get_mut(label) + } + + /// See [`App::edit_schedule`]. + pub fn edit_schedule( + &mut self, + label: impl ScheduleLabel, + mut f: impl FnMut(&mut Schedule), + ) -> &mut Self { + let label = label.intern(); + let mut schedules = self.world.resource_mut::(); + if !schedules.contains(label) { + schedules.insert(Schedule::new(label)); + } + + let schedule = schedules.get_mut(label).unwrap(); + f(schedule); + + self + } + + /// See [`App::configure_schedules`]. + pub fn configure_schedules( + &mut self, + schedule_build_settings: ScheduleBuildSettings, + ) -> &mut Self { + self.world_mut() + .resource_mut::() + .configure_schedules(schedule_build_settings); + self + } + + /// See [`App::allow_ambiguous_component`]. + pub fn allow_ambiguous_component(&mut self) -> &mut Self { + self.world_mut().allow_ambiguous_component::(); + self + } + + /// See [`App::allow_ambiguous_resource`]. + pub fn allow_ambiguous_resource(&mut self) -> &mut Self { + self.world_mut().allow_ambiguous_resource::(); + self + } + + /// See [`App::ignore_ambiguity`]. + #[track_caller] + pub fn ignore_ambiguity( + &mut self, + schedule: impl ScheduleLabel, + a: S1, + b: S2, + ) -> &mut Self + where + S1: IntoSystemSet, + S2: IntoSystemSet, + { + let schedule = schedule.intern(); + let mut schedules = self.world.resource_mut::(); + + if let Some(schedule) = schedules.get_mut(schedule) { + let schedule: &mut Schedule = schedule; + schedule.ignore_ambiguity(a, b); + } else { + let mut new_schedule = Schedule::new(schedule); + new_schedule.ignore_ambiguity(a, b); + schedules.insert(new_schedule); + } + + self + } + + /// See [`App::init_state`]. + pub fn init_state(&mut self) -> &mut Self { + if !self.world.contains_resource::>() { + self.init_resource::>() + .init_resource::>() + .add_event::>() + .add_systems( + StateTransition, + ( + run_enter_schedule::.run_if(run_once_condition()), + apply_state_transition::, + ) + .chain(), + ); + } + + // The OnEnter, OnExit, and OnTransition schedules are lazily initialized + // (i.e. when the first system is added to them), so World::try_run_schedule + // is used to fail gracefully if they aren't present. + self + } + + /// See [`App::insert_state`]. + pub fn insert_state(&mut self, state: S) -> &mut Self { + self.insert_resource(State::new(state)) + .init_resource::>() + .add_event::>() + .add_systems( + StateTransition, + ( + run_enter_schedule::.run_if(run_once_condition()), + apply_state_transition::, + ) + .chain(), + ); + + self + } + + /// See [`App::add_event`]. + pub fn add_event(&mut self) -> &mut Self + where + T: Event, + { + if !self.world.contains_resource::>() { + self.init_resource::>().add_systems( + First, + bevy_ecs::event::event_update_system:: + .in_set(bevy_ecs::event::EventUpdates) + .run_if(bevy_ecs::event::event_update_condition::), + ); + } + + self + } + + /// See [`App::add_plugins`]. + pub fn add_plugins(&mut self, plugins: impl Plugins) -> &mut Self { + self.run_as_app(|app| plugins.add_to_app(app)); + self + } + + /// See [`App::is_plugin_added`]. + pub fn is_plugin_added(&self) -> bool + where + T: Plugin, + { + self.plugins + .registry + .iter() + .any(|p| p.downcast_ref::().is_some()) + } + + /// See [`App::get_added_plugins`]. + pub fn get_added_plugins(&self) -> Vec<&T> + where + T: Plugin, + { + self.plugins + .registry + .iter() + .filter_map(|p| p.downcast_ref()) + .collect() + } + + /// Returns `true` if there is no plugin in the middle of being built. + pub(crate) fn is_building_plugins(&self) -> bool { + self.plugin_build_depth > 0 + } + + /// Return the state of plugins. + #[inline] + pub fn plugins_state(&mut self) -> PluginsState { + match self.plugins_state { + PluginsState::Adding => { + let mut state = PluginsState::Ready; + let plugins = std::mem::take(&mut self.plugins); + self.run_as_app(|app| { + for plugin in &plugins.registry { + if !plugin.ready(app) { + state = PluginsState::Adding; + return; + } + } + }); + self.plugins = plugins; + state + } + state => state, + } + } + + /// Runs [`Plugin::finish`] for each plugin. + pub fn finish(&mut self) { + let plugins = std::mem::take(&mut self.plugins); + self.run_as_app(|app| { + for plugin in &plugins.registry { + plugin.finish(app); + } + }); + self.plugins = plugins; + self.plugins_state = PluginsState::Finished; + } + + /// Runs [`Plugin::cleanup`] for each plugin. + pub fn cleanup(&mut self) { + let plugins = std::mem::take(&mut self.plugins); + self.run_as_app(|app| { + for plugin in &plugins.registry { + plugin.cleanup(app); + } + }); + self.plugins = plugins; + self.plugins_state = PluginsState::Cleaned; + } + + /// See [`App::register_type`]. + #[cfg(feature = "bevy_reflect")] + pub fn register_type(&mut self) -> &mut Self { + let registry = self.world.resource_mut::(); + registry.write().register::(); + self + } + + /// See [`App::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, + >( + &mut self, + ) -> &mut Self { + let registry = self.world.resource_mut::(); + registry.write().register_type_data::(); + self + } +} + +/// The collection of sub-apps that belong to an [`App`]. +#[derive(Default)] +pub struct SubApps { + /// The primary sub-app that contains the "main" world. + pub main: SubApp, + /// Other, labeled sub-apps. + pub sub_apps: HashMap, +} + +impl SubApps { + /// Calls [`update`](SubApp::update) for the main sub-app, and then calls + /// [`extract`](SubApp::extract) and [`update`](SubApp::update) for the rest. + pub fn update(&mut self) { + #[cfg(feature = "trace")] + let _bevy_update_span = info_span!("update").entered(); + { + #[cfg(feature = "trace")] + let _bevy_frame_update_span = info_span!("main app").entered(); + self.main.update(); + } + for (_label, sub_app) in self.sub_apps.iter_mut() { + #[cfg(feature = "trace")] + let _sub_app_span = info_span!("sub app", name = ?_label).entered(); + sub_app.extract(&mut self.main.world); + sub_app.update(); + } + + self.main.world.clear_trackers(); + } + + /// Returns an iterator over the sub-apps (starting with the main one). + pub fn iter(&self) -> impl Iterator + '_ { + std::iter::once(&self.main).chain(self.sub_apps.values()) + } + + /// Returns a mutable iterator over the sub-apps (starting with the main one). + pub fn iter_mut(&mut self) -> impl Iterator + '_ { + std::iter::once(&mut self.main).chain(self.sub_apps.values_mut()) + } +} diff --git a/crates/bevy_asset/src/io/embedded/mod.rs b/crates/bevy_asset/src/io/embedded/mod.rs index d8f85c315cbe6..018fb9ed9a6ff 100644 --- a/crates/bevy_asset/src/io/embedded/mod.rs +++ b/crates/bevy_asset/src/io/embedded/mod.rs @@ -224,7 +224,7 @@ macro_rules! embedded_asset { ($app: ident, $source_path: expr, $path: expr) => {{ let mut embedded = $app - .world + .world_mut() .resource_mut::<$crate::io::embedded::EmbeddedAssetRegistry>(); let path = $crate::embedded_path!($source_path, $path); let watched_path = $crate::io::embedded::watched_path(file!(), $path); @@ -253,7 +253,7 @@ pub fn watched_path(_source_file_path: &'static str, _asset_path: &'static str) #[macro_export] macro_rules! load_internal_asset { ($app: ident, $handle: expr, $path_str: expr, $loader: expr) => {{ - let mut assets = $app.world.resource_mut::<$crate::Assets<_>>(); + let mut assets = $app.world_mut().resource_mut::<$crate::Assets<_>>(); assets.insert($handle.id(), ($loader)( include_str!($path_str), std::path::Path::new(file!()) @@ -265,7 +265,7 @@ macro_rules! load_internal_asset { }}; // we can't support params without variadic arguments, so internal assets with additional params can't be hot-reloaded ($app: ident, $handle: ident, $path_str: expr, $loader: expr $(, $param:expr)+) => {{ - let mut assets = $app.world.resource_mut::<$crate::Assets<_>>(); + let mut assets = $app.world_mut().resource_mut::<$crate::Assets<_>>(); assets.insert($handle.id(), ($loader)( include_str!($path_str), std::path::Path::new(file!()) @@ -282,7 +282,7 @@ macro_rules! load_internal_asset { #[macro_export] macro_rules! load_internal_binary_asset { ($app: ident, $handle: expr, $path_str: expr, $loader: expr) => {{ - let mut assets = $app.world.resource_mut::<$crate::Assets<_>>(); + let mut assets = $app.world_mut().resource_mut::<$crate::Assets<_>>(); assets.insert( $handle.id(), ($loader)( diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index 4c525524b01b4..239ea361f8ac0 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -153,7 +153,7 @@ impl Plugin for AssetPlugin { let embedded = EmbeddedAssetRegistry::default(); { let mut sources = app - .world + .world_mut() .get_resource_or_insert_with::(Default::default); sources.init_default_source( &self.file_path, @@ -169,10 +169,10 @@ impl Plugin for AssetPlugin { } match self.mode { AssetMode::Unprocessed => { - let mut builders = app.world.resource_mut::(); + let mut builders = app.world_mut().resource_mut::(); let sources = builders.build_sources(watch, false); let meta_check = app - .world + .world() .get_resource::() .cloned() .unwrap_or_else(AssetMetaCheck::default); @@ -187,7 +187,7 @@ impl Plugin for AssetPlugin { AssetMode::Processed => { #[cfg(feature = "asset_processor")] { - let mut builders = app.world.resource_mut::(); + let mut builders = app.world_mut().resource_mut::(); let processor = AssetProcessor::new(&mut builders); let mut sources = builders.build_sources(false, watch); sources.gate_on_processor(processor.data.clone()); @@ -204,7 +204,7 @@ impl Plugin for AssetPlugin { } #[cfg(not(feature = "asset_processor"))] { - let mut builders = app.world.resource_mut::(); + let mut builders = app.world_mut().resource_mut::(); let sources = builders.build_sources(false, watch); app.insert_resource(AssetServer::new_with_meta_check( sources, @@ -318,12 +318,14 @@ pub trait AssetApp { impl AssetApp for App { fn register_asset_loader(&mut self, loader: L) -> &mut Self { - self.world.resource::().register_loader(loader); + self.world() + .resource::() + .register_loader(loader); self } fn register_asset_processor(&mut self, processor: P) -> &mut Self { - if let Some(asset_processor) = self.world.get_resource::() { + if let Some(asset_processor) = self.world().get_resource::() { asset_processor.register_processor(processor); } self @@ -335,13 +337,13 @@ impl AssetApp for App { source: AssetSourceBuilder, ) -> &mut Self { let id = id.into(); - if self.world.get_resource::().is_some() { + if self.world().get_resource::().is_some() { error!("{} must be registered before `AssetPlugin` (typically added as part of `DefaultPlugins`)", id); } { let mut sources = self - .world + .world_mut() .get_resource_or_insert_with(AssetSourceBuilders::default); sources.insert(id, source); } @@ -350,22 +352,24 @@ impl AssetApp for App { } fn set_default_asset_processor(&mut self, extension: &str) -> &mut Self { - if let Some(asset_processor) = self.world.get_resource::() { + if let Some(asset_processor) = self.world().get_resource::() { asset_processor.set_default_processor::

(extension); } self } fn init_asset_loader(&mut self) -> &mut Self { - let loader = L::from_world(&mut self.world); + let loader = L::from_world(self.world_mut()); self.register_asset_loader(loader) } fn init_asset(&mut self) -> &mut Self { let assets = Assets::::default(); - self.world.resource::().register_asset(&assets); - if self.world.contains_resource::() { - let processor = self.world.resource::(); + self.world() + .resource::() + .register_asset(&assets); + if self.world().contains_resource::() { + let processor = self.world().resource::(); // The processor should have its own handle provider separate from the Asset storage // to ensure the id spaces are entirely separate. Not _strictly_ necessary, but // desirable. @@ -394,7 +398,7 @@ impl AssetApp for App { where A: Asset + Reflect + FromReflect + GetTypeRegistration, { - let type_registry = self.world.resource::(); + let type_registry = self.world().resource::(); { let mut type_registry = type_registry.write(); @@ -408,7 +412,7 @@ impl AssetApp for App { } fn preregister_asset_loader(&mut self, extensions: &[&str]) -> &mut Self { - self.world + self.world_mut() .resource_mut::() .preregister_loader::(extensions); self @@ -628,7 +632,7 @@ mod tests { pub fn run_app_until(app: &mut App, mut predicate: impl FnMut(&mut World) -> Option<()>) { for _ in 0..LARGE_ITERATION_COUNT { app.update(); - if predicate(&mut app.world).is_some() { + if predicate(app.world_mut()).is_some() { return; } } @@ -718,13 +722,13 @@ mod tests { .init_resource::() .register_asset_loader(CoolTextLoader) .add_systems(Update, store_asset_events); - let asset_server = app.world.resource::().clone(); + let asset_server = app.world().resource::().clone(); let handle: Handle = asset_server.load(a_path); let a_id = handle.id(); - let entity = app.world.spawn(handle).id(); + let entity = app.world_mut().spawn(handle).id(); app.update(); { - let a_text = get::(&app.world, a_id); + let a_text = get::(app.world(), a_id); let (a_load, a_deps, a_rec_deps) = asset_server.get_load_states(a_id).unwrap(); assert!(a_text.is_none(), "a's asset should not exist yet"); assert_eq!(a_load, LoadState::Loading, "a should still be loading"); @@ -906,27 +910,27 @@ mod tests { }); { - let mut texts = app.world.resource_mut::>(); + let mut texts = app.world_mut().resource_mut::>(); let a = texts.get_mut(a_id).unwrap(); a.text = "Changed".to_string(); } - app.world.despawn(entity); + app.world_mut().despawn(entity); app.update(); assert_eq!( - app.world.resource::>().len(), + app.world().resource::>().len(), 0, "CoolText asset entities should be despawned when no more handles exist" ); app.update(); // this requires a second update because the parent asset was freed in the previous app.update() assert_eq!( - app.world.resource::>().len(), + app.world().resource::>().len(), 0, "SubText asset entities should be despawned when no more handles exist" ); - let events = app.world.remove_resource::().unwrap(); - let id_results = app.world.remove_resource::().unwrap(); + let events = app.world_mut().remove_resource::().unwrap(); + let id_results = app.world_mut().remove_resource::().unwrap(); let expected_events = vec![ AssetEvent::Added { id: a_id }, AssetEvent::LoadedWithDependencies { @@ -1030,7 +1034,7 @@ mod tests { let (mut app, gate_opener) = test_app(dir); app.init_asset::() .register_asset_loader(CoolTextLoader); - let asset_server = app.world.resource::().clone(); + let asset_server = app.world().resource::().clone(); let handle: Handle = asset_server.load(a_path); let a_id = handle.id(); { @@ -1046,7 +1050,7 @@ mod tests { ); } - app.world.spawn(handle); + app.world_mut().spawn(handle); gate_opener.open(a_path); gate_opener.open(b_path); gate_opener.open(c_path); @@ -1117,7 +1121,7 @@ mod tests { let id = { let handle = { - let mut texts = app.world.resource_mut::>(); + let mut texts = app.world_mut().resource_mut::>(); let handle = texts.add(CoolText::default()); texts.get_strong_handle(handle.id()).unwrap() }; @@ -1125,7 +1129,7 @@ mod tests { app.update(); { - let text = app.world.resource::>().get(&handle); + let text = app.world().resource::>().get(&handle); assert!(text.is_some()); } handle.id() @@ -1133,7 +1137,7 @@ mod tests { // handle is dropped app.update(); assert!( - app.world.resource::>().get(id).is_none(), + app.world().resource::>().get(id).is_none(), "asset has no handles, so it should have been dropped last update" ); } @@ -1161,7 +1165,7 @@ mod tests { let id = { let handle = { - let mut texts = app.world.resource_mut::>(); + let mut texts = app.world_mut().resource_mut::>(); texts.add(CoolText { text: hello.clone(), embedded: empty.clone(), @@ -1174,7 +1178,7 @@ mod tests { { let text = app - .world + .world() .resource::>() .get(&handle) .unwrap(); @@ -1185,12 +1189,12 @@ mod tests { // handle is dropped app.update(); assert!( - app.world.resource::>().get(id).is_none(), + app.world().resource::>().get(id).is_none(), "asset has no handles, so it should have been dropped last update" ); // remove event is emitted app.update(); - let events = std::mem::take(&mut app.world.resource_mut::().0); + let events = std::mem::take(&mut app.world_mut().resource_mut::().0); let expected_events = vec![ AssetEvent::Added { id }, AssetEvent::Unused { id }, @@ -1198,7 +1202,7 @@ mod tests { ]; assert_eq!(events, expected_events); - let dep_handle = app.world.resource::().load(dep_path); + let dep_handle = app.world().resource::().load(dep_path); let a = CoolText { text: "a".to_string(), embedded: empty, @@ -1206,19 +1210,19 @@ mod tests { dependencies: vec![dep_handle.clone()], sub_texts: Vec::new(), }; - let a_handle = app.world.resource::().load_asset(a); + let a_handle = app.world().resource::().load_asset(a); app.update(); // TODO: ideally it doesn't take two updates for the added event to emit app.update(); - let events = std::mem::take(&mut app.world.resource_mut::().0); + let events = std::mem::take(&mut app.world_mut().resource_mut::().0); let expected_events = vec![AssetEvent::Added { id: a_handle.id() }]; assert_eq!(events, expected_events); gate_opener.open(dep_path); loop { app.update(); - let events = std::mem::take(&mut app.world.resource_mut::().0); + let events = std::mem::take(&mut app.world_mut().resource_mut::().0); if events.is_empty() { continue; } @@ -1232,7 +1236,7 @@ mod tests { break; } app.update(); - let events = std::mem::take(&mut app.world.resource_mut::().0); + let events = std::mem::take(&mut app.world_mut().resource_mut::().0); let expected_events = vec![AssetEvent::Added { id: dep_handle.id(), }]; @@ -1283,7 +1287,7 @@ mod tests { app.init_asset::() .init_asset::() .register_asset_loader(CoolTextLoader); - let asset_server = app.world.resource::().clone(); + let asset_server = app.world().resource::().clone(); let handle: Handle = asset_server.load_folder("text"); gate_opener.open(a_path); gate_opener.open(b_path); @@ -1438,12 +1442,12 @@ mod tests { (asset_event_handler, asset_load_error_event_handler).chain(), ); - let asset_server = app.world.resource::().clone(); + let asset_server = app.world().resource::().clone(); let a_path = format!("unstable://{a_path}"); let a_handle: Handle = asset_server.load(a_path); let a_id = a_handle.id(); - app.world.spawn(a_handle); + app.world_mut().spawn(a_handle); run_app_until(&mut app, |world| { let tracker = world.resource::(); @@ -1476,7 +1480,7 @@ mod tests { }); // running schedule does not error on ambiguity between the 2 uses_assets systems - app.world.run_schedule(Update); + app.world_mut().run_schedule(Update); } // validate the Asset derive macro for various asset types diff --git a/crates/bevy_asset/src/reflect.rs b/crates/bevy_asset/src/reflect.rs index 02e8cd85278b1..306ddd7db3cad 100644 --- a/crates/bevy_asset/src/reflect.rs +++ b/crates/bevy_asset/src/reflect.rs @@ -257,7 +257,7 @@ mod tests { .register_asset_reflect::(); let reflect_asset = { - let type_registry = app.world.resource::(); + let type_registry = app.world().resource::(); let type_registry = type_registry.read(); type_registry @@ -270,9 +270,9 @@ mod tests { field: "test".into(), }; - let handle = reflect_asset.add(&mut app.world, &value); + let handle = reflect_asset.add(app.world_mut(), &value); let ReflectMut::Struct(strukt) = reflect_asset - .get_mut(&mut app.world, handle) + .get_mut(app.world_mut(), handle) .unwrap() .reflect_mut() else { @@ -283,19 +283,19 @@ mod tests { .unwrap() .apply(&String::from("edited")); - assert_eq!(reflect_asset.len(&app.world), 1); - let ids: Vec<_> = reflect_asset.ids(&app.world).collect(); + assert_eq!(reflect_asset.len(app.world()), 1); + let ids: Vec<_> = reflect_asset.ids(app.world()).collect(); assert_eq!(ids.len(), 1); let fetched_handle = UntypedHandle::Weak(ids[0]); let asset = reflect_asset - .get(&app.world, fetched_handle.clone_weak()) + .get(app.world(), fetched_handle.clone_weak()) .unwrap(); assert_eq!(asset.downcast_ref::().unwrap().field, "edited"); reflect_asset - .remove(&mut app.world, fetched_handle) + .remove(app.world_mut(), fetched_handle) .unwrap(); - assert_eq!(reflect_asset.len(&app.world), 0); + assert_eq!(reflect_asset.len(app.world()), 0); } } diff --git a/crates/bevy_core/src/lib.rs b/crates/bevy_core/src/lib.rs index b636122c17b23..073460b3b3796 100644 --- a/crates/bevy_core/src/lib.rs +++ b/crates/bevy_core/src/lib.rs @@ -149,7 +149,7 @@ mod tests { )); app.update(); - let frame_count = app.world.resource::(); + let frame_count = app.world().resource::(); assert_eq!(1, frame_count.0); } } diff --git a/crates/bevy_core_pipeline/src/blit/mod.rs b/crates/bevy_core_pipeline/src/blit/mod.rs index 91258c034de59..d11a99cbb329d 100644 --- a/crates/bevy_core_pipeline/src/blit/mod.rs +++ b/crates/bevy_core_pipeline/src/blit/mod.rs @@ -21,16 +21,15 @@ impl Plugin for BlitPlugin { fn build(&self, app: &mut App) { load_internal_asset!(app, BLIT_SHADER_HANDLE, "blit.wgsl", Shader::from_wgsl); - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { render_app.allow_ambiguous_resource::>(); } } fn finish(&self, app: &mut App) { - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; - render_app .init_resource::() .init_resource::>(); diff --git a/crates/bevy_core_pipeline/src/bloom/mod.rs b/crates/bevy_core_pipeline/src/bloom/mod.rs index 382e58a48675f..419a82ff6013f 100644 --- a/crates/bevy_core_pipeline/src/bloom/mod.rs +++ b/crates/bevy_core_pipeline/src/bloom/mod.rs @@ -56,10 +56,9 @@ impl Plugin for BloomPlugin { UniformComponentPlugin::::default(), )); - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; - render_app .init_resource::>() .init_resource::>() @@ -87,10 +86,9 @@ impl Plugin for BloomPlugin { } fn finish(&self, app: &mut App) { - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; - render_app .init_resource::() .init_resource::(); diff --git a/crates/bevy_core_pipeline/src/contrast_adaptive_sharpening/mod.rs b/crates/bevy_core_pipeline/src/contrast_adaptive_sharpening/mod.rs index d1b5ddf37e5ca..5be54e87bc00f 100644 --- a/crates/bevy_core_pipeline/src/contrast_adaptive_sharpening/mod.rs +++ b/crates/bevy_core_pipeline/src/contrast_adaptive_sharpening/mod.rs @@ -116,7 +116,7 @@ impl Plugin for CASPlugin { UniformComponentPlugin::::default(), )); - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; render_app @@ -160,7 +160,7 @@ impl Plugin for CASPlugin { } fn finish(&self, app: &mut App) { - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; render_app.init_resource::(); diff --git a/crates/bevy_core_pipeline/src/core_2d/mod.rs b/crates/bevy_core_pipeline/src/core_2d/mod.rs index bc872950701fd..48d2bb5a4f609 100644 --- a/crates/bevy_core_pipeline/src/core_2d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_2d/mod.rs @@ -56,10 +56,9 @@ impl Plugin for Core2dPlugin { app.register_type::() .add_plugins(ExtractComponentPlugin::::default()); - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; - render_app .init_resource::>() .add_systems(ExtractSchedule, extract_core_2d_camera_phases) diff --git a/crates/bevy_core_pipeline/src/core_3d/mod.rs b/crates/bevy_core_pipeline/src/core_3d/mod.rs index 7c622141a6464..39aee1657136f 100644 --- a/crates/bevy_core_pipeline/src/core_3d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_3d/mod.rs @@ -99,10 +99,9 @@ impl Plugin for Core3dPlugin { .add_plugins((SkyboxPlugin, ExtractComponentPlugin::::default())) .add_systems(PostUpdate, check_msaa); - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; - render_app .init_resource::>() .init_resource::>() diff --git a/crates/bevy_core_pipeline/src/deferred/copy_lighting_id.rs b/crates/bevy_core_pipeline/src/deferred/copy_lighting_id.rs index 37c6da5a11ca7..4f5462d1f52ba 100644 --- a/crates/bevy_core_pipeline/src/deferred/copy_lighting_id.rs +++ b/crates/bevy_core_pipeline/src/deferred/copy_lighting_id.rs @@ -35,7 +35,7 @@ impl Plugin for CopyDeferredLightingIdPlugin { "copy_deferred_lighting_id.wgsl", Shader::from_wgsl ); - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; render_app.add_systems( @@ -45,7 +45,7 @@ impl Plugin for CopyDeferredLightingIdPlugin { } fn finish(&self, app: &mut App) { - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; diff --git a/crates/bevy_core_pipeline/src/fxaa/mod.rs b/crates/bevy_core_pipeline/src/fxaa/mod.rs index f1442910850eb..f338a564375d0 100644 --- a/crates/bevy_core_pipeline/src/fxaa/mod.rs +++ b/crates/bevy_core_pipeline/src/fxaa/mod.rs @@ -89,7 +89,7 @@ impl Plugin for FxaaPlugin { app.register_type::(); app.add_plugins(ExtractComponentPlugin::::default()); - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; render_app @@ -116,7 +116,7 @@ impl Plugin for FxaaPlugin { } fn finish(&self, app: &mut App) { - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; render_app.init_resource::(); diff --git a/crates/bevy_core_pipeline/src/msaa_writeback.rs b/crates/bevy_core_pipeline/src/msaa_writeback.rs index 01ca12a4fe47f..8d19a6df136f0 100644 --- a/crates/bevy_core_pipeline/src/msaa_writeback.rs +++ b/crates/bevy_core_pipeline/src/msaa_writeback.rs @@ -21,10 +21,9 @@ pub struct MsaaWritebackPlugin; impl Plugin for MsaaWritebackPlugin { fn build(&self, app: &mut App) { - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; - render_app.add_systems( Render, prepare_msaa_writeback_pipelines.in_set(RenderSet::Prepare), diff --git a/crates/bevy_core_pipeline/src/skybox/mod.rs b/crates/bevy_core_pipeline/src/skybox/mod.rs index 0144a988db9f0..6dfe1af4dcd9f 100644 --- a/crates/bevy_core_pipeline/src/skybox/mod.rs +++ b/crates/bevy_core_pipeline/src/skybox/mod.rs @@ -38,10 +38,9 @@ impl Plugin for SkyboxPlugin { UniformComponentPlugin::::default(), )); - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; - render_app .init_resource::>() .add_systems( @@ -54,12 +53,10 @@ impl Plugin for SkyboxPlugin { } fn finish(&self, app: &mut App) { - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; - - let render_device = render_app.world.resource::().clone(); - + let render_device = render_app.world().resource::().clone(); render_app.insert_resource(SkyboxPipeline::new(&render_device)); } } diff --git a/crates/bevy_core_pipeline/src/taa/mod.rs b/crates/bevy_core_pipeline/src/taa/mod.rs index 59138aa9fd7cb..7565c1a65a6ee 100644 --- a/crates/bevy_core_pipeline/src/taa/mod.rs +++ b/crates/bevy_core_pipeline/src/taa/mod.rs @@ -49,10 +49,9 @@ impl Plugin for TemporalAntiAliasPlugin { app.insert_resource(Msaa::Off) .register_type::(); - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; - render_app .init_resource::>() .add_systems(ExtractSchedule, extract_taa_settings) @@ -77,7 +76,7 @@ impl Plugin for TemporalAntiAliasPlugin { } fn finish(&self, app: &mut App) { - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; diff --git a/crates/bevy_core_pipeline/src/tonemapping/mod.rs b/crates/bevy_core_pipeline/src/tonemapping/mod.rs index 7726ff1e36a15..ca525f8a21d9b 100644 --- a/crates/bevy_core_pipeline/src/tonemapping/mod.rs +++ b/crates/bevy_core_pipeline/src/tonemapping/mod.rs @@ -52,8 +52,8 @@ impl Plugin for TonemappingPlugin { Shader::from_wgsl ); - if !app.world.is_resource_added::() { - let mut images = app.world.resource_mut::>(); + if !app.world().is_resource_added::() { + let mut images = app.world_mut().resource_mut::>(); #[cfg(feature = "tonemapping_luts")] let tonemapping_luts = { @@ -96,20 +96,22 @@ impl Plugin for TonemappingPlugin { ExtractComponentPlugin::::default(), )); - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { - render_app - .init_resource::>() - .add_systems( - Render, - prepare_view_tonemapping_pipelines.in_set(RenderSet::Prepare), - ); - } + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { + return; + }; + render_app + .init_resource::>() + .add_systems( + Render, + prepare_view_tonemapping_pipelines.in_set(RenderSet::Prepare), + ); } fn finish(&self, app: &mut App) { - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { - render_app.init_resource::(); - } + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { + return; + }; + render_app.init_resource::(); } } diff --git a/crates/bevy_core_pipeline/src/upscaling/mod.rs b/crates/bevy_core_pipeline/src/upscaling/mod.rs index a22db16013bc6..6220843883ac8 100644 --- a/crates/bevy_core_pipeline/src/upscaling/mod.rs +++ b/crates/bevy_core_pipeline/src/upscaling/mod.rs @@ -13,7 +13,7 @@ pub struct UpscalingPlugin; impl Plugin for UpscalingPlugin { fn build(&self, app: &mut App) { - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { render_app.add_systems( Render, prepare_view_upscaling_pipelines.in_set(RenderSet::Prepare), diff --git a/crates/bevy_dev_tools/src/ci_testing.rs b/crates/bevy_dev_tools/src/ci_testing.rs index c35288759468a..88dc2fc48dd74 100644 --- a/crates/bevy_dev_tools/src/ci_testing.rs +++ b/crates/bevy_dev_tools/src/ci_testing.rs @@ -62,7 +62,7 @@ pub(crate) fn setup_app(app: &mut App) -> &mut App { }; if let Some(frame_time) = config.frame_time { - app.world + app.world_mut() .insert_resource(TimeUpdateStrategy::ManualDuration(Duration::from_secs_f32( frame_time, ))); diff --git a/crates/bevy_diagnostic/src/diagnostic.rs b/crates/bevy_diagnostic/src/diagnostic.rs index d202ffff33207..b45ee593cfab3 100644 --- a/crates/bevy_diagnostic/src/diagnostic.rs +++ b/crates/bevy_diagnostic/src/diagnostic.rs @@ -1,7 +1,7 @@ use std::hash::{Hash, Hasher}; use std::{borrow::Cow, collections::VecDeque}; -use bevy_app::App; +use bevy_app::{App, SubApp}; use bevy_ecs::system::{Deferred, Res, Resource, SystemBuffer, SystemParam}; use bevy_utils::{hashbrown::HashMap, Duration, Instant, PassHash}; use const_fnv1a_hash::fnv1a_hash_str_64; @@ -383,10 +383,6 @@ impl SystemBuffer for DiagnosticsBuffer { /// Extend [`App`] with new `register_diagnostic` function. pub trait RegisterDiagnostic { - fn register_diagnostic(&mut self, diagnostic: Diagnostic) -> &mut Self; -} - -impl RegisterDiagnostic for App { /// Register a new [`Diagnostic`] with an [`App`]. /// /// Will initialize a [`DiagnosticsStore`] if it doesn't exist. @@ -402,11 +398,22 @@ impl RegisterDiagnostic for App { /// .add_plugins(DiagnosticsPlugin) /// .run(); /// ``` + fn register_diagnostic(&mut self, diagnostic: Diagnostic) -> &mut Self; +} + +impl RegisterDiagnostic for SubApp { fn register_diagnostic(&mut self, diagnostic: Diagnostic) -> &mut Self { self.init_resource::(); - let mut diagnostics = self.world.resource_mut::(); + let mut diagnostics = self.world_mut().resource_mut::(); diagnostics.add(diagnostic); self } } + +impl RegisterDiagnostic for App { + fn register_diagnostic(&mut self, diagnostic: Diagnostic) -> &mut Self { + SubApp::register_diagnostic(self.main_mut(), diagnostic); + self + } +} diff --git a/crates/bevy_gizmos/src/lib.rs b/crates/bevy_gizmos/src/lib.rs index 3d03eac725cc8..5bc45ead6bd35 100644 --- a/crates/bevy_gizmos/src/lib.rs +++ b/crates/bevy_gizmos/src/lib.rs @@ -136,7 +136,7 @@ impl Plugin for GizmoPlugin { .add_plugins(AabbGizmoPlugin) .add_plugins(LightGizmoPlugin); - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; @@ -162,11 +162,11 @@ impl Plugin for GizmoPlugin { } fn finish(&self, app: &mut bevy_app::App) { - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; - let render_device = render_app.world.resource::(); + let render_device = render_app.world().resource::(); let line_layout = render_device.create_bind_group_layout( "LineGizmoUniform layout", &BindGroupLayoutEntries::single( @@ -200,12 +200,12 @@ pub trait AppGizmoBuilder { impl AppGizmoBuilder for App { fn init_gizmo_group(&mut self) -> &mut Self { - if self.world.contains_resource::>() { + if self.world().contains_resource::>() { return self; } let mut handles = self - .world + .world_mut() .get_resource_or_insert_with::(Default::default); handles.list.insert(TypeId::of::(), None); handles.strip.insert(TypeId::of::(), None); @@ -213,7 +213,7 @@ impl AppGizmoBuilder for App { self.init_resource::>() .add_systems(Last, update_gizmo_meshes::); - self.world + self.world_mut() .get_resource_or_insert_with::(Default::default) .register::(); @@ -225,16 +225,16 @@ impl AppGizmoBuilder for App { group: T, config: GizmoConfig, ) -> &mut Self { - self.world + self.world_mut() .get_resource_or_insert_with::(Default::default) .insert(config, group); - if self.world.contains_resource::>() { + if self.world().contains_resource::>() { return self; } let mut handles = self - .world + .world_mut() .get_resource_or_insert_with::(Default::default); handles.list.insert(TypeId::of::(), None); handles.strip.insert(TypeId::of::(), None); diff --git a/crates/bevy_gizmos/src/pipeline_2d.rs b/crates/bevy_gizmos/src/pipeline_2d.rs index 4a886485e8be1..5110779078cbf 100644 --- a/crates/bevy_gizmos/src/pipeline_2d.rs +++ b/crates/bevy_gizmos/src/pipeline_2d.rs @@ -30,7 +30,7 @@ pub struct LineGizmo2dPlugin; impl Plugin for LineGizmo2dPlugin { fn build(&self, app: &mut App) { - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; @@ -57,7 +57,7 @@ impl Plugin for LineGizmo2dPlugin { } fn finish(&self, app: &mut App) { - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; diff --git a/crates/bevy_gizmos/src/pipeline_3d.rs b/crates/bevy_gizmos/src/pipeline_3d.rs index 1fe8d9977b2f1..ca53a1cdf2d54 100644 --- a/crates/bevy_gizmos/src/pipeline_3d.rs +++ b/crates/bevy_gizmos/src/pipeline_3d.rs @@ -32,7 +32,7 @@ use bevy_utils::tracing::error; pub struct LineGizmo3dPlugin; impl Plugin for LineGizmo3dPlugin { fn build(&self, app: &mut App) { - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; @@ -56,7 +56,7 @@ impl Plugin for LineGizmo3dPlugin { } fn finish(&self, app: &mut App) { - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; diff --git a/crates/bevy_gltf/src/lib.rs b/crates/bevy_gltf/src/lib.rs index 9133f3128430b..b95064af9a20b 100644 --- a/crates/bevy_gltf/src/lib.rs +++ b/crates/bevy_gltf/src/lib.rs @@ -63,9 +63,8 @@ impl Plugin for GltfPlugin { } fn finish(&self, app: &mut App) { - let supported_compressed_formats = match app.world.get_resource::() { + let supported_compressed_formats = match app.world().get_resource::() { Some(render_device) => CompressedImageFormats::from_features(render_device.features()), - None => CompressedImageFormats::NONE, }; app.register_asset_loader(GltfLoader { diff --git a/crates/bevy_log/src/lib.rs b/crates/bevy_log/src/lib.rs index 7391ffe039123..f37f0729a847e 100644 --- a/crates/bevy_log/src/lib.rs +++ b/crates/bevy_log/src/lib.rs @@ -177,7 +177,7 @@ impl Plugin for LogPlugin { } })) .build(); - app.world.insert_non_send_resource(guard); + app.insert_non_send_resource(guard); chrome_layer }; diff --git a/crates/bevy_pbr/src/deferred/mod.rs b/crates/bevy_pbr/src/deferred/mod.rs index 0bd154a779479..6fbe30e8f02a8 100644 --- a/crates/bevy_pbr/src/deferred/mod.rs +++ b/crates/bevy_pbr/src/deferred/mod.rs @@ -105,7 +105,7 @@ impl Plugin for DeferredPbrLightingPlugin { Shader::from_wgsl ); - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; @@ -130,7 +130,7 @@ impl Plugin for DeferredPbrLightingPlugin { } fn finish(&self, app: &mut App) { - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index 0a7f095b2782d..c87f14c6afe05 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -352,16 +352,18 @@ impl Plugin for PbrPlugin { app.add_plugins(DeferredPbrLightingPlugin); } - app.world.resource_mut::>().insert( - &Handle::::default(), - StandardMaterial { - base_color: Color::srgb(1.0, 0.0, 0.5), - unlit: true, - ..Default::default() - }, - ); + app.world_mut() + .resource_mut::>() + .insert( + &Handle::::default(), + StandardMaterial { + base_color: Color::srgb(1.0, 0.0, 0.5), + unlit: true, + ..Default::default() + }, + ); - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; @@ -379,8 +381,8 @@ impl Plugin for PbrPlugin { ) .init_resource::(); - let shadow_pass_node = ShadowPassNode::new(&mut render_app.world); - let mut graph = render_app.world.resource_mut::(); + let shadow_pass_node = ShadowPassNode::new(render_app.world_mut()); + let mut graph = render_app.world_mut().resource_mut::(); let draw_3d_graph = graph.get_sub_graph_mut(Core3d).unwrap(); draw_3d_graph.add_node(NodePbr::ShadowPass, shadow_pass_node); draw_3d_graph.add_node_edge(NodePbr::ShadowPass, Node3d::StartMainPass); @@ -396,7 +398,7 @@ impl Plugin for PbrPlugin { } fn finish(&self, app: &mut App) { - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; diff --git a/crates/bevy_pbr/src/light_probe/mod.rs b/crates/bevy_pbr/src/light_probe/mod.rs index 4d681f19ca9d2..1a5df1b2caff0 100644 --- a/crates/bevy_pbr/src/light_probe/mod.rs +++ b/crates/bevy_pbr/src/light_probe/mod.rs @@ -323,7 +323,7 @@ impl Plugin for LightProbePlugin { } fn finish(&self, app: &mut App) { - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; diff --git a/crates/bevy_pbr/src/lightmap/mod.rs b/crates/bevy_pbr/src/lightmap/mod.rs index 30c4bc631fb21..7299e9e946ec2 100644 --- a/crates/bevy_pbr/src/lightmap/mod.rs +++ b/crates/bevy_pbr/src/lightmap/mod.rs @@ -126,7 +126,7 @@ impl Plugin for LightmapPlugin { } fn finish(&self, app: &mut App) { - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index 774aff0745dfa..5b758265f688b 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -253,7 +253,7 @@ where app.init_asset::() .add_plugins(ExtractInstancesPlugin::>::extract_visible()); - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { render_app .init_resource::>() .add_render_command::>() @@ -310,7 +310,7 @@ where } fn finish(&self, app: &mut App) { - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { render_app.init_resource::>(); } } diff --git a/crates/bevy_pbr/src/meshlet/mod.rs b/crates/bevy_pbr/src/meshlet/mod.rs index 128a183c98edc..3770675a9a149 100644 --- a/crates/bevy_pbr/src/meshlet/mod.rs +++ b/crates/bevy_pbr/src/meshlet/mod.rs @@ -164,7 +164,7 @@ impl Plugin for MeshletPlugin { } fn finish(&self, app: &mut App) { - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; diff --git a/crates/bevy_pbr/src/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index ab8ea45d080d1..b6df62aaaca1f 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -90,7 +90,7 @@ where Shader::from_wgsl ); - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; @@ -106,7 +106,7 @@ where } fn finish(&self, app: &mut App) { - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; @@ -130,7 +130,10 @@ where M::Data: PartialEq + Eq + Hash + Clone, { fn build(&self, app: &mut App) { - let no_prepass_plugin_loaded = app.world.get_resource::().is_none(); + let no_prepass_plugin_loaded = app + .world() + .get_resource::() + .is_none(); if no_prepass_plugin_loaded { app.insert_resource(AnyPrepassPluginLoaded) @@ -145,7 +148,7 @@ where ); } - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; diff --git a/crates/bevy_pbr/src/render/fog.rs b/crates/bevy_pbr/src/render/fog.rs index 27dce7eb4a5be..16ae9a1007d98 100644 --- a/crates/bevy_pbr/src/render/fog.rs +++ b/crates/bevy_pbr/src/render/fog.rs @@ -135,7 +135,7 @@ impl Plugin for FogPlugin { app.register_type::(); app.add_plugins(ExtractComponentPlugin::::default()); - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { render_app .init_resource::() .add_systems(Render, prepare_fog.in_set(RenderSet::PrepareResources)); diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index bd06ceca9c5c3..f6d512625be79 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -109,7 +109,7 @@ impl Plugin for MeshRenderPlugin { (no_automatic_skin_batching, no_automatic_morph_batching), ); - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { render_app .init_resource::() .init_resource::() @@ -160,9 +160,9 @@ impl Plugin for MeshRenderPlugin { fn finish(&self, app: &mut App) { let mut mesh_bindings_shader_defs = Vec::with_capacity(1); - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { if let Some(per_object_buffer_batch_size) = GpuArrayBuffer::::batch_size( - render_app.world.resource::(), + render_app.world().resource::(), ) { mesh_bindings_shader_defs.push(ShaderDefVal::UInt( "PER_OBJECT_BUFFER_BATCH_SIZE".into(), @@ -172,7 +172,7 @@ impl Plugin for MeshRenderPlugin { render_app .insert_resource(GpuArrayBuffer::::new( - render_app.world.resource::(), + render_app.world().resource::(), )) .init_resource::(); } diff --git a/crates/bevy_pbr/src/ssao/mod.rs b/crates/bevy_pbr/src/ssao/mod.rs index 5017b5a2752b1..5ef3fc372aba4 100644 --- a/crates/bevy_pbr/src/ssao/mod.rs +++ b/crates/bevy_pbr/src/ssao/mod.rs @@ -72,12 +72,12 @@ impl Plugin for ScreenSpaceAmbientOcclusionPlugin { } fn finish(&self, app: &mut App) { - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; if !render_app - .world + .world() .resource::() .get_texture_format_features(TextureFormat::R16Float) .allowed_usages @@ -88,7 +88,7 @@ impl Plugin for ScreenSpaceAmbientOcclusionPlugin { } if render_app - .world + .world() .resource::() .limits() .max_storage_textures_per_shader_stage diff --git a/crates/bevy_render/src/camera/mod.rs b/crates/bevy_render/src/camera/mod.rs index 579129bb0f014..83c882cc3ed8e 100644 --- a/crates/bevy_render/src/camera/mod.rs +++ b/crates/bevy_render/src/camera/mod.rs @@ -41,13 +41,13 @@ impl Plugin for CameraPlugin { ExtractComponentPlugin::::default(), )); - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { render_app .init_resource::() .add_systems(ExtractSchedule, extract_cameras) .add_systems(Render, sort_cameras.in_set(RenderSet::ManageViews)); - let camera_driver_node = CameraDriverNode::new(&mut render_app.world); - let mut render_graph = render_app.world.resource_mut::(); + let camera_driver_node = CameraDriverNode::new(render_app.world_mut()); + let mut render_graph = render_app.world_mut().resource_mut::(); render_graph.add_node(crate::graph::CameraDriverLabel, camera_driver_node); } } diff --git a/crates/bevy_render/src/diagnostic/mod.rs b/crates/bevy_render/src/diagnostic/mod.rs index af6e4a1c3784c..f64d17a1884c4 100644 --- a/crates/bevy_render/src/diagnostic/mod.rs +++ b/crates/bevy_render/src/diagnostic/mod.rs @@ -52,18 +52,18 @@ impl Plugin for RenderDiagnosticsPlugin { app.insert_resource(render_diagnostics_mutex.clone()) .add_systems(PreUpdate, sync_diagnostics); - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { render_app.insert_resource(render_diagnostics_mutex); } } fn finish(&self, app: &mut App) { - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; - let device = render_app.world.resource::(); - let queue = render_app.world.resource::(); + let device = render_app.world().resource::(); + let queue = render_app.world().resource::(); render_app.insert_resource(DiagnosticsRecorder::new(device, queue)); } } diff --git a/crates/bevy_render/src/extract_component.rs b/crates/bevy_render/src/extract_component.rs index 98be77b2b9689..a19f93539b93d 100644 --- a/crates/bevy_render/src/extract_component.rs +++ b/crates/bevy_render/src/extract_component.rs @@ -80,7 +80,7 @@ impl Default for UniformComponentPlugin { impl Plugin for UniformComponentPlugin { fn build(&self, app: &mut App) { - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { render_app .insert_resource(ComponentUniforms::::default()) .add_systems( @@ -184,7 +184,7 @@ impl ExtractComponentPlugin { impl Plugin for ExtractComponentPlugin { fn build(&self, app: &mut App) { - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { if self.only_extract_visible { render_app.add_systems(ExtractSchedule, extract_visible_components::); } else { diff --git a/crates/bevy_render/src/extract_instances.rs b/crates/bevy_render/src/extract_instances.rs index 5f09184d22ec3..537ca5f11e439 100644 --- a/crates/bevy_render/src/extract_instances.rs +++ b/crates/bevy_render/src/extract_instances.rs @@ -94,7 +94,7 @@ where EI: ExtractInstance, { fn build(&self, app: &mut App) { - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { render_app.init_resource::>(); if self.only_extract_visible { render_app.add_systems(ExtractSchedule, extract_visible::); diff --git a/crates/bevy_render/src/extract_resource.rs b/crates/bevy_render/src/extract_resource.rs index 038a612fe21f6..30f6493d8f2a8 100644 --- a/crates/bevy_render/src/extract_resource.rs +++ b/crates/bevy_render/src/extract_resource.rs @@ -31,7 +31,7 @@ impl Default for ExtractResourcePlugin { impl Plugin for ExtractResourcePlugin { fn build(&self, app: &mut App) { - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { render_app.add_systems(ExtractSchedule, extract_resource::); } } diff --git a/crates/bevy_render/src/globals.rs b/crates/bevy_render/src/globals.rs index 11f0c5295efff..e89648fb56877 100644 --- a/crates/bevy_render/src/globals.rs +++ b/crates/bevy_render/src/globals.rs @@ -21,7 +21,7 @@ impl Plugin for GlobalsPlugin { load_internal_asset!(app, GLOBALS_TYPE_HANDLE, "globals.wgsl", Shader::from_wgsl); app.register_type::(); - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { render_app .init_resource::() .init_resource::)); } } diff --git a/crates/bevy_render/src/render_graph/app.rs b/crates/bevy_render/src/render_graph/app.rs index 94082590aa86b..80ffcdb2a1f8d 100644 --- a/crates/bevy_render/src/render_graph/app.rs +++ b/crates/bevy_render/src/render_graph/app.rs @@ -1,10 +1,10 @@ -use bevy_app::App; +use bevy_app::{App, SubApp}; use bevy_ecs::world::FromWorld; use bevy_utils::tracing::warn; use super::{IntoRenderNodeArray, Node, RenderGraph, RenderLabel, RenderSubGraph}; -/// Adds common [`RenderGraph`] operations to [`App`]. +/// Adds common [`RenderGraph`] operations to [`SubApp`] (and [`App`]). pub trait RenderGraphApp { // Add a sub graph to the [`RenderGraph`] fn add_render_sub_graph(&mut self, sub_graph: impl RenderSubGraph) -> &mut Self; @@ -32,23 +32,15 @@ pub trait RenderGraphApp { ) -> &mut Self; } -impl RenderGraphApp for App { - fn add_render_sub_graph(&mut self, sub_graph: impl RenderSubGraph) -> &mut Self { - let mut render_graph = self.world.get_resource_mut::().expect( - "RenderGraph not found. Make sure you are using add_render_sub_graph on the RenderApp", - ); - render_graph.add_sub_graph(sub_graph, RenderGraph::default()); - self - } - +impl RenderGraphApp for SubApp { fn add_render_graph_node( &mut self, sub_graph: impl RenderSubGraph, node_label: impl RenderLabel, ) -> &mut Self { let sub_graph = sub_graph.intern(); - let node = T::from_world(&mut self.world); - let mut render_graph = self.world.get_resource_mut::().expect( + let node = T::from_world(self.world_mut()); + let mut render_graph = self.world_mut().get_resource_mut::().expect( "RenderGraph not found. Make sure you are using add_render_graph_node on the RenderApp", ); if let Some(graph) = render_graph.get_sub_graph_mut(sub_graph) { @@ -67,7 +59,7 @@ impl RenderGraphApp for App { edges: impl IntoRenderNodeArray, ) -> &mut Self { let sub_graph = sub_graph.intern(); - let mut render_graph = self.world.get_resource_mut::().expect( + let mut render_graph = self.world_mut().get_resource_mut::().expect( "RenderGraph not found. Make sure you are using add_render_graph_edges on the RenderApp", ); if let Some(graph) = render_graph.get_sub_graph_mut(sub_graph) { @@ -87,7 +79,7 @@ impl RenderGraphApp for App { input_node: impl RenderLabel, ) -> &mut Self { let sub_graph = sub_graph.intern(); - let mut render_graph = self.world.get_resource_mut::().expect( + let mut render_graph = self.world_mut().get_resource_mut::().expect( "RenderGraph not found. Make sure you are using add_render_graph_edge on the RenderApp", ); if let Some(graph) = render_graph.get_sub_graph_mut(sub_graph) { @@ -99,4 +91,47 @@ impl RenderGraphApp for App { } self } + + fn add_render_sub_graph(&mut self, sub_graph: impl RenderSubGraph) -> &mut Self { + let mut render_graph = self.world_mut().get_resource_mut::().expect( + "RenderGraph not found. Make sure you are using add_render_sub_graph on the RenderApp", + ); + render_graph.add_sub_graph(sub_graph, RenderGraph::default()); + self + } +} + +impl RenderGraphApp for App { + fn add_render_graph_node( + &mut self, + sub_graph: impl RenderSubGraph, + node_label: impl RenderLabel, + ) -> &mut Self { + SubApp::add_render_graph_node::(self.main_mut(), sub_graph, node_label); + self + } + + fn add_render_graph_edge( + &mut self, + sub_graph: impl RenderSubGraph, + output_node: impl RenderLabel, + input_node: impl RenderLabel, + ) -> &mut Self { + SubApp::add_render_graph_edge(self.main_mut(), sub_graph, output_node, input_node); + self + } + + fn add_render_graph_edges( + &mut self, + sub_graph: impl RenderSubGraph, + edges: impl IntoRenderNodeArray, + ) -> &mut Self { + SubApp::add_render_graph_edges(self.main_mut(), sub_graph, edges); + self + } + + fn add_render_sub_graph(&mut self, sub_graph: impl RenderSubGraph) -> &mut Self { + SubApp::add_render_sub_graph(self.main_mut(), sub_graph); + self + } } diff --git a/crates/bevy_render/src/render_phase/draw.rs b/crates/bevy_render/src/render_phase/draw.rs index 4dc6fc3e0822b..980899b64bc44 100644 --- a/crates/bevy_render/src/render_phase/draw.rs +++ b/crates/bevy_render/src/render_phase/draw.rs @@ -1,5 +1,5 @@ use crate::render_phase::{PhaseItem, TrackedRenderPass}; -use bevy_app::App; +use bevy_app::{App, SubApp}; use bevy_ecs::{ entity::Entity, query::{QueryState, ROQueryItem, ReadOnlyQueryData}, @@ -310,16 +310,16 @@ pub trait AddRenderCommand { C::Param: ReadOnlySystemParam; } -impl AddRenderCommand for App { +impl AddRenderCommand for SubApp { fn add_render_command + Send + Sync + 'static>( &mut self, ) -> &mut Self where C::Param: ReadOnlySystemParam, { - let draw_function = RenderCommandState::::new(&mut self.world); + let draw_function = RenderCommandState::::new(self.world_mut()); let draw_functions = self - .world + .world() .get_resource::>() .unwrap_or_else(|| { panic!( @@ -332,3 +332,15 @@ impl AddRenderCommand for App { self } } + +impl AddRenderCommand for App { + fn add_render_command + Send + Sync + 'static>( + &mut self, + ) -> &mut Self + where + C::Param: ReadOnlySystemParam, + { + SubApp::add_render_command::(self.main_mut()); + self + } +} diff --git a/crates/bevy_render/src/texture/mod.rs b/crates/bevy_render/src/texture/mod.rs index 0d186f9aa6139..880a988449e28 100644 --- a/crates/bevy_render/src/texture/mod.rs +++ b/crates/bevy_render/src/texture/mod.rs @@ -88,12 +88,13 @@ impl Plugin for ImagePlugin { .register_type::() .init_asset::() .register_asset_reflect::(); - app.world + + app.world_mut() .resource_mut::>() .insert(&Handle::default(), Image::default()); #[cfg(feature = "basis-universal")] if let Some(processor) = app - .world + .world() .get_resource::() { processor.register_processor::>( @@ -103,7 +104,7 @@ impl Plugin for ImagePlugin { .set_default_processor::>("png"); } - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { render_app.init_resource::().add_systems( Render, update_texture_cache_system.in_set(RenderSet::Cleanup), @@ -140,9 +141,9 @@ impl Plugin for ImagePlugin { app.init_asset_loader::(); } - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { let default_sampler = { - let device = render_app.world.resource::(); + let device = render_app.world().resource::(); device.create_sampler(&self.default_sampler.as_wgpu()) }; render_app diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index 3b888e33e69dd..0d1958bf7d4de 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -55,7 +55,7 @@ impl Plugin for ViewPlugin { // NOTE: windows.is_changed() handles cases where a window was resized .add_plugins((ExtractResourcePlugin::::default(), VisibilityPlugin)); - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { render_app.init_resource::().add_systems( Render, ( diff --git a/crates/bevy_render/src/view/visibility/mod.rs b/crates/bevy_render/src/view/visibility/mod.rs index 65ffeda9637b1..d7fd09ba7a353 100644 --- a/crates/bevy_render/src/view/visibility/mod.rs +++ b/crates/bevy_render/src/view/visibility/mod.rs @@ -475,42 +475,51 @@ mod test { let mut app = App::new(); app.add_systems(Update, visibility_propagate_system); - let root1 = app.world.spawn(visibility_bundle(Visibility::Hidden)).id(); - let root1_child1 = app.world.spawn(VisibilityBundle::default()).id(); - let root1_child2 = app.world.spawn(visibility_bundle(Visibility::Hidden)).id(); - let root1_child1_grandchild1 = app.world.spawn(VisibilityBundle::default()).id(); - let root1_child2_grandchild1 = app.world.spawn(VisibilityBundle::default()).id(); - - app.world + let root1 = app + .world_mut() + .spawn(visibility_bundle(Visibility::Hidden)) + .id(); + let root1_child1 = app.world_mut().spawn(VisibilityBundle::default()).id(); + let root1_child2 = app + .world_mut() + .spawn(visibility_bundle(Visibility::Hidden)) + .id(); + let root1_child1_grandchild1 = app.world_mut().spawn(VisibilityBundle::default()).id(); + let root1_child2_grandchild1 = app.world_mut().spawn(VisibilityBundle::default()).id(); + + app.world_mut() .entity_mut(root1) .push_children(&[root1_child1, root1_child2]); - app.world + app.world_mut() .entity_mut(root1_child1) .push_children(&[root1_child1_grandchild1]); - app.world + app.world_mut() .entity_mut(root1_child2) .push_children(&[root1_child2_grandchild1]); - let root2 = app.world.spawn(VisibilityBundle::default()).id(); - let root2_child1 = app.world.spawn(VisibilityBundle::default()).id(); - let root2_child2 = app.world.spawn(visibility_bundle(Visibility::Hidden)).id(); - let root2_child1_grandchild1 = app.world.spawn(VisibilityBundle::default()).id(); - let root2_child2_grandchild1 = app.world.spawn(VisibilityBundle::default()).id(); + let root2 = app.world_mut().spawn(VisibilityBundle::default()).id(); + let root2_child1 = app.world_mut().spawn(VisibilityBundle::default()).id(); + let root2_child2 = app + .world_mut() + .spawn(visibility_bundle(Visibility::Hidden)) + .id(); + let root2_child1_grandchild1 = app.world_mut().spawn(VisibilityBundle::default()).id(); + let root2_child2_grandchild1 = app.world_mut().spawn(VisibilityBundle::default()).id(); - app.world + app.world_mut() .entity_mut(root2) .push_children(&[root2_child1, root2_child2]); - app.world + app.world_mut() .entity_mut(root2_child1) .push_children(&[root2_child1_grandchild1]); - app.world + app.world_mut() .entity_mut(root2_child2) .push_children(&[root2_child2_grandchild1]); app.update(); let is_visible = |e: Entity| { - app.world + app.world() .entity(e) .get::() .unwrap() @@ -566,29 +575,29 @@ mod test { let mut app = App::new(); app.add_systems(Update, visibility_propagate_system); - let root1 = app.world.spawn(visibility_bundle(Visible)).id(); - let root1_child1 = app.world.spawn(visibility_bundle(Inherited)).id(); - let root1_child2 = app.world.spawn(visibility_bundle(Hidden)).id(); - let root1_child1_grandchild1 = app.world.spawn(visibility_bundle(Visible)).id(); - let root1_child2_grandchild1 = app.world.spawn(visibility_bundle(Visible)).id(); + let root1 = app.world_mut().spawn(visibility_bundle(Visible)).id(); + let root1_child1 = app.world_mut().spawn(visibility_bundle(Inherited)).id(); + let root1_child2 = app.world_mut().spawn(visibility_bundle(Hidden)).id(); + let root1_child1_grandchild1 = app.world_mut().spawn(visibility_bundle(Visible)).id(); + let root1_child2_grandchild1 = app.world_mut().spawn(visibility_bundle(Visible)).id(); - let root2 = app.world.spawn(visibility_bundle(Inherited)).id(); - let root3 = app.world.spawn(visibility_bundle(Hidden)).id(); + let root2 = app.world_mut().spawn(visibility_bundle(Inherited)).id(); + let root3 = app.world_mut().spawn(visibility_bundle(Hidden)).id(); - app.world + app.world_mut() .entity_mut(root1) .push_children(&[root1_child1, root1_child2]); - app.world + app.world_mut() .entity_mut(root1_child1) .push_children(&[root1_child1_grandchild1]); - app.world + app.world_mut() .entity_mut(root1_child2) .push_children(&[root1_child2_grandchild1]); app.update(); let is_visible = |e: Entity| { - app.world + app.world() .entity(e) .get::() .unwrap() diff --git a/crates/bevy_render/src/view/window/mod.rs b/crates/bevy_render/src/view/window/mod.rs index d184f08e12d8c..ef940901e06ae 100644 --- a/crates/bevy_render/src/view/window/mod.rs +++ b/crates/bevy_render/src/view/window/mod.rs @@ -37,7 +37,7 @@ impl Plugin for WindowRenderPlugin { fn build(&self, app: &mut App) { app.add_plugins(ScreenshotPlugin); - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { render_app .init_resource::() .init_resource::() @@ -53,7 +53,7 @@ impl Plugin for WindowRenderPlugin { } fn finish(&self, app: &mut App) { - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { render_app.init_resource::(); } } diff --git a/crates/bevy_render/src/view/window/screenshot.rs b/crates/bevy_render/src/view/window/screenshot.rs index 64eb3d70169bf..ebf9b3e223a30 100644 --- a/crates/bevy_render/src/view/window/screenshot.rs +++ b/crates/bevy_render/src/view/window/screenshot.rs @@ -137,7 +137,7 @@ impl Plugin for ScreenshotPlugin { } fn finish(&self, app: &mut bevy_app::App) { - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { render_app.init_resource::>(); } } diff --git a/crates/bevy_scene/src/bundle.rs b/crates/bevy_scene/src/bundle.rs index b1d4bba4f575a..0da7d17e75160 100644 --- a/crates/bevy_scene/src/bundle.rs +++ b/crates/bevy_scene/src/bundle.rs @@ -132,15 +132,18 @@ mod tests { let mut scene_world = World::new(); // create a new DynamicScene manually - let type_registry = app.world.resource::().clone(); + let type_registry = app.world().resource::().clone(); scene_world.insert_resource(type_registry); scene_world.spawn(ComponentA { x: 3.0, y: 4.0 }); let scene = DynamicScene::from_world(&scene_world); - let scene_handle = app.world.resource_mut::>().add(scene); + let scene_handle = app + .world_mut() + .resource_mut::>() + .add(scene); // spawn the scene as a child of `entity` using the `DynamicSceneBundle` let entity = app - .world + .world_mut() .spawn(DynamicSceneBundle { scene: scene_handle.clone(), ..default() @@ -152,24 +155,27 @@ mod tests { // make sure that the scene was added as a child of the root entity let (scene_entity, scene_component_a) = app - .world + .world_mut() .query::<(Entity, &ComponentA)>() - .single(&app.world); + .single(app.world()); assert_eq!(scene_component_a.x, 3.0); assert_eq!(scene_component_a.y, 4.0); - assert_eq!(app.world.entity(entity).get::().unwrap().len(), 1); + assert_eq!( + app.world().entity(entity).get::().unwrap().len(), + 1 + ); // let's try to delete the scene - let mut scene_spawner = app.world.resource_mut::(); + let mut scene_spawner = app.world_mut().resource_mut::(); scene_spawner.despawn(&scene_handle); // run the scene spawner system to despawn the scene app.update(); // the scene entity does not exist anymore - assert!(app.world.get_entity(scene_entity).is_none()); + assert!(app.world().get_entity(scene_entity).is_none()); // the root entity does not have any children anymore - assert!(app.world.entity(entity).get::().is_none()); + assert!(app.world().entity(entity).get::().is_none()); } } diff --git a/crates/bevy_scene/src/lib.rs b/crates/bevy_scene/src/lib.rs index a05e9a5f8619e..fa00fde5cc510 100644 --- a/crates/bevy_scene/src/lib.rs +++ b/crates/bevy_scene/src/lib.rs @@ -61,7 +61,7 @@ impl Plugin for ScenePlugin { .add_systems(SpawnScene, (scene_spawner, scene_spawner_system).chain()); // Register component hooks for DynamicScene - app.world + app.world_mut() .register_component_hooks::>() .on_remove(|mut world, entity, _| { let Some(handle) = world.get::>(entity) else { @@ -80,7 +80,7 @@ impl Plugin for ScenePlugin { }); // Register component hooks for Scene - app.world + app.world_mut() .register_component_hooks::>() .on_remove(|mut world, entity, _| { if let Some(&SceneInstance(scene_instance)) = world.get::(entity) { diff --git a/crates/bevy_scene/src/scene_spawner.rs b/crates/bevy_scene/src/scene_spawner.rs index 5aa918e2c0ea0..1bf154e27ab2c 100644 --- a/crates/bevy_scene/src/scene_spawner.rs +++ b/crates/bevy_scene/src/scene_spawner.rs @@ -512,18 +512,18 @@ mod tests { app.add_plugins((AssetPlugin::default(), ScenePlugin)); app.register_type::(); - app.world.spawn(ComponentA); - app.world.spawn(ComponentA); + app.world_mut().spawn(ComponentA); + app.world_mut().spawn(ComponentA); // Build scene. let scene = - app.world + app.world_mut() .run_system_once(|world: &World, asset_server: Res<'_, AssetServer>| { asset_server.add(DynamicScene::from_world(world)) }); // Spawn scene. - let scene_entity = app.world.run_system_once( + let scene_entity = app.world_mut().run_system_once( move |mut commands: Commands<'_, '_>, mut scene_spawner: ResMut<'_, SceneSpawner>| { let scene_entity = commands.spawn_empty().id(); scene_spawner.spawn_dynamic_as_child(scene.clone(), scene_entity); @@ -533,7 +533,7 @@ mod tests { // Check for event arrival. app.update(); - app.world.run_system_once( + app.world_mut().run_system_once( move |mut ev_scene: EventReader<'_, '_, SceneInstanceReady>| { let mut events = ev_scene.read(); @@ -555,7 +555,7 @@ mod tests { app.add_plugins((AssetPlugin::default(), ScenePlugin)); app.register_type::(); - let asset_server = app.world.resource::(); + let asset_server = app.world().resource::(); // Build scene. let scene = asset_server.add(DynamicScene::default()); @@ -573,14 +573,14 @@ mod tests { // Spawn scene. for _ in 0..count { - app.world.spawn((ComponentA, scene.clone())); + app.world_mut().spawn((ComponentA, scene.clone())); } app.update(); - check(&mut app.world, count); + check(app.world_mut(), count); // Despawn scene. - app.world.run_system_once( + app.world_mut().run_system_once( |mut commands: Commands, query: Query>| { for entity in query.iter() { commands.entity(entity).despawn_recursive(); @@ -589,6 +589,6 @@ mod tests { ); app.update(); - check(&mut app.world, 0); + check(app.world_mut(), 0); } } diff --git a/crates/bevy_sprite/src/lib.rs b/crates/bevy_sprite/src/lib.rs index de6e35e4489b6..78bed9c8f6a5a 100644 --- a/crates/bevy_sprite/src/lib.rs +++ b/crates/bevy_sprite/src/lib.rs @@ -97,7 +97,7 @@ impl Plugin for SpritePlugin { ), ); - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { render_app .init_resource::() .init_resource::>() @@ -125,7 +125,7 @@ impl Plugin for SpritePlugin { } fn finish(&self, app: &mut App) { - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { render_app.init_resource::(); } } @@ -202,11 +202,14 @@ mod test { app.add_systems(Update, calculate_bounds_2d); // Add entities - let entity = app.world.spawn((Sprite::default(), image_handle)).id(); + let entity = app + .world_mut() + .spawn((Sprite::default(), image_handle)) + .id(); // Verify that the entity does not have an AABB assert!(!app - .world + .world() .get_entity(entity) .expect("Could not find entity") .contains::()); @@ -216,7 +219,7 @@ mod test { // Verify the AABB exists assert!(app - .world + .world() .get_entity(entity) .expect("Could not find entity") .contains::()); @@ -241,7 +244,7 @@ mod test { // Add entities let entity = app - .world + .world_mut() .spawn(( Sprite { custom_size: Some(Vec2::ZERO), @@ -256,7 +259,7 @@ mod test { // Get the initial AABB let first_aabb = *app - .world + .world() .get_entity(entity) .expect("Could not find entity") .get::() @@ -264,7 +267,7 @@ mod test { // Change `custom_size` of sprite let mut binding = app - .world + .world_mut() .get_entity_mut(entity) .expect("Could not find entity"); let mut sprite = binding @@ -277,7 +280,7 @@ mod test { // Get the re-calculated AABB let second_aabb = *app - .world + .world() .get_entity(entity) .expect("Could not find entity") .get::() diff --git a/crates/bevy_sprite/src/mesh2d/color_material.rs b/crates/bevy_sprite/src/mesh2d/color_material.rs index 6a5f26463d864..4e5f9ae64aef2 100644 --- a/crates/bevy_sprite/src/mesh2d/color_material.rs +++ b/crates/bevy_sprite/src/mesh2d/color_material.rs @@ -24,13 +24,15 @@ impl Plugin for ColorMaterialPlugin { app.add_plugins(Material2dPlugin::::default()) .register_asset_reflect::(); - app.world.resource_mut::>().insert( - &Handle::::default(), - ColorMaterial { - color: Color::srgb(1.0, 0.0, 1.0), - ..Default::default() - }, - ); + app.world_mut() + .resource_mut::>() + .insert( + &Handle::::default(), + ColorMaterial { + color: Color::srgb(1.0, 0.0, 1.0), + ..Default::default() + }, + ); } } diff --git a/crates/bevy_sprite/src/mesh2d/material.rs b/crates/bevy_sprite/src/mesh2d/material.rs index a4533665e4c37..4481dc2bb5c02 100644 --- a/crates/bevy_sprite/src/mesh2d/material.rs +++ b/crates/bevy_sprite/src/mesh2d/material.rs @@ -150,7 +150,7 @@ where fn build(&self, app: &mut App) { app.init_asset::(); - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { render_app .add_render_command::>() .init_resource::>() @@ -176,7 +176,7 @@ where } fn finish(&self, app: &mut App) { - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { render_app.init_resource::>(); } } diff --git a/crates/bevy_sprite/src/mesh2d/mesh.rs b/crates/bevy_sprite/src/mesh2d/mesh.rs index b014f10071ca7..d62255263c591 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh.rs +++ b/crates/bevy_sprite/src/mesh2d/mesh.rs @@ -93,7 +93,7 @@ impl Plugin for Mesh2dRenderPlugin { ); load_internal_asset!(app, MESH2D_SHADER_HANDLE, "mesh2d.wgsl", Shader::from_wgsl); - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { render_app .init_resource::() .init_resource::>() @@ -115,9 +115,9 @@ impl Plugin for Mesh2dRenderPlugin { fn finish(&self, app: &mut bevy_app::App) { let mut mesh_bindings_shader_defs = Vec::with_capacity(1); - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { if let Some(per_object_buffer_batch_size) = GpuArrayBuffer::::batch_size( - render_app.world.resource::(), + render_app.world().resource::(), ) { mesh_bindings_shader_defs.push(ShaderDefVal::UInt( "PER_OBJECT_BUFFER_BATCH_SIZE".into(), @@ -127,7 +127,7 @@ impl Plugin for Mesh2dRenderPlugin { render_app .insert_resource(GpuArrayBuffer::::new( - render_app.world.resource::(), + render_app.world().resource::(), )) .init_resource::(); } diff --git a/crates/bevy_text/src/lib.rs b/crates/bevy_text/src/lib.rs index 839418174330c..014f56818ba7b 100644 --- a/crates/bevy_text/src/lib.rs +++ b/crates/bevy_text/src/lib.rs @@ -104,7 +104,7 @@ impl Plugin for TextPlugin { ), ); - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { render_app.add_systems( ExtractSchedule, extract_text2d_sprite.after(SpriteSystem::ExtractSprites), diff --git a/crates/bevy_text/src/text2d.rs b/crates/bevy_text/src/text2d.rs index b653e259843c7..5212e39e2a86d 100644 --- a/crates/bevy_text/src/text2d.rs +++ b/crates/bevy_text/src/text2d.rs @@ -301,7 +301,7 @@ mod tests { ); let entity = app - .world + .world_mut() .spawn((Text2dBundle { text: Text::from_section(FIRST_TEXT, default()), ..default() @@ -316,7 +316,7 @@ mod tests { let (mut app, entity) = setup(); assert!(!app - .world + .world() .get_entity(entity) .expect("Could not find entity") .contains::()); @@ -325,7 +325,7 @@ mod tests { app.update(); let aabb = app - .world + .world() .get_entity(entity) .expect("Could not find entity") .get::() @@ -347,14 +347,14 @@ mod tests { app.update(); let first_aabb = *app - .world + .world() .get_entity(entity) .expect("Could not find entity") .get::() .expect("Could not find initial AABB"); let mut entity_ref = app - .world + .world_mut() .get_entity_mut(entity) .expect("Could not find entity"); *entity_ref @@ -365,7 +365,7 @@ mod tests { app.update(); let second_aabb = *app - .world + .world() .get_entity(entity) .expect("Could not find entity") .get::() diff --git a/crates/bevy_transform/src/helper.rs b/crates/bevy_transform/src/helper.rs index d1eca1d1df160..b1be978f196c6 100644 --- a/crates/bevy_transform/src/helper.rs +++ b/crates/bevy_transform/src/helper.rs @@ -121,7 +121,7 @@ mod tests { let mut entity = None; for transform in transforms { - let mut e = app.world.spawn(TransformBundle::from(transform)); + let mut e = app.world_mut().spawn(TransformBundle::from(transform)); if let Some(entity) = entity { e.set_parent(entity); @@ -134,10 +134,10 @@ mod tests { app.update(); - let transform = *app.world.get::(leaf_entity).unwrap(); + let transform = *app.world().get::(leaf_entity).unwrap(); - let mut state = SystemState::::new(&mut app.world); - let helper = state.get(&app.world); + let mut state = SystemState::::new(app.world_mut()); + let helper = state.get(app.world()); let computed_transform = helper.compute_global_transform(leaf_entity).unwrap(); diff --git a/crates/bevy_transform/src/systems.rs b/crates/bevy_transform/src/systems.rs index 4e0a2efe8fa2e..12e44b193b837 100644 --- a/crates/bevy_transform/src/systems.rs +++ b/crates/bevy_transform/src/systems.rs @@ -416,7 +416,7 @@ mod test { let mut child = Entity::from_raw(0); let mut grandchild = Entity::from_raw(1); let parent = app - .world + .world_mut() .spawn(( Transform::from_translation(translation), GlobalTransform::IDENTITY, @@ -434,13 +434,16 @@ mod test { app.update(); // check the `Children` structure is spawned - assert_eq!(&**app.world.get::(parent).unwrap(), &[child]); - assert_eq!(&**app.world.get::(child).unwrap(), &[grandchild]); + assert_eq!(&**app.world().get::(parent).unwrap(), &[child]); + assert_eq!( + &**app.world().get::(child).unwrap(), + &[grandchild] + ); // Note that at this point, the `GlobalTransform`s will not have updated yet, due to `Commands` delay app.update(); - let mut state = app.world.query::<&GlobalTransform>(); - for global in state.iter(&app.world) { + let mut state = app.world_mut().query::<&GlobalTransform>(); + for global in state.iter(app.world()) { assert_eq!(global, &GlobalTransform::from_translation(translation)); } } @@ -468,16 +471,16 @@ mod test { } let (temp_child, temp_grandchild) = setup_world(&mut temp); - let (child, grandchild) = setup_world(&mut app.world); + let (child, grandchild) = setup_world(app.world_mut()); assert_eq!(temp_child, child); assert_eq!(temp_grandchild, grandchild); - app.world + app.world_mut() .spawn(TransformBundle::IDENTITY) .push_children(&[child]); std::mem::swap( - &mut *app.world.get_mut::(child).unwrap(), + &mut *app.world_mut().get_mut::(child).unwrap(), &mut *temp.get_mut::(grandchild).unwrap(), ); diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index a8f127980d3fc..32cefb8299c8e 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -173,7 +173,7 @@ impl Plugin for UiPlugin { } fn finish(&self, app: &mut App) { - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 5ba9ba77ede78..9c4ce3ae18796 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -69,7 +69,7 @@ pub enum RenderUiSystem { pub fn build_ui_render(app: &mut App) { load_internal_asset!(app, UI_SHADER_HANDLE, "ui.wgsl", Shader::from_wgsl); - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; @@ -116,7 +116,7 @@ pub fn build_ui_render(app: &mut App) { // Render graph let ui_graph_2d = get_ui_graph(render_app); let ui_graph_3d = get_ui_graph(render_app); - let mut graph = render_app.world.resource_mut::(); + let mut graph = render_app.world_mut().resource_mut::(); if let Some(graph_2d) = graph.get_sub_graph_mut(Core2d) { graph_2d.add_sub_graph(SubGraphUi, ui_graph_2d); @@ -135,8 +135,8 @@ pub fn build_ui_render(app: &mut App) { } } -fn get_ui_graph(render_app: &mut App) -> RenderGraph { - let ui_pass_node = UiPassNode::new(&mut render_app.world); +fn get_ui_graph(render_app: &mut SubApp) -> RenderGraph { + let ui_pass_node = UiPassNode::new(render_app.world_mut()); let mut ui_graph = RenderGraph::default(); ui_graph.add_node(NodeUi::UiPass, ui_pass_node); ui_graph diff --git a/crates/bevy_ui/src/render/ui_material_pipeline.rs b/crates/bevy_ui/src/render/ui_material_pipeline.rs index 2ab70e72235d6..33e293bab6096 100644 --- a/crates/bevy_ui/src/render/ui_material_pipeline.rs +++ b/crates/bevy_ui/src/render/ui_material_pipeline.rs @@ -62,7 +62,7 @@ where app.init_asset::() .add_plugins(ExtractComponentPlugin::>::extract_visible()); - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { render_app .add_render_command::>() .init_resource::>() @@ -89,7 +89,7 @@ where } fn finish(&self, app: &mut App) { - if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { render_app.init_resource::>(); } } diff --git a/crates/bevy_window/src/lib.rs b/crates/bevy_window/src/lib.rs index f32e8b75c86b7..c9ae32dca55dc 100644 --- a/crates/bevy_window/src/lib.rs +++ b/crates/bevy_window/src/lib.rs @@ -107,11 +107,11 @@ impl Plugin for WindowPlugin { if let Some(primary_window) = &self.primary_window { let initial_focus = app - .world + .world_mut() .spawn(primary_window.clone()) .insert(PrimaryWindow) .id(); - if let Some(mut focus) = app.world.get_resource_mut::() { + if let Some(mut focus) = app.world_mut().get_resource_mut::() { **focus = Some(initial_focus); } } diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index 893937d5f2696..9de6073649b65 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -158,9 +158,9 @@ impl Plugin for WinitPlugin { // Otherwise, we want to create a window before `bevy_render` initializes the renderer // so that we have a surface to use as a hint. This improves compatibility with `wgpu` // backends, especially WASM/WebGL2. - let mut create_window = SystemState::::from_world(&mut app.world); - create_windows(&event_loop, create_window.get_mut(&mut app.world)); - create_window.apply(&mut app.world); + let mut create_window = SystemState::::from_world(app.world_mut()); + create_windows(&event_loop, create_window.get_mut(app.world_mut())); + create_window.apply(app.world_mut()); } // `winit`'s windows are bound to the event loop that created them, so the event loop must @@ -270,11 +270,11 @@ pub fn winit_runner(mut app: App) { } let event_loop = app - .world + .world_mut() .remove_non_send_resource::>() .unwrap(); - app.world + app.world_mut() .insert_non_send_resource(event_loop.create_proxy()); let mut runner_state = WinitAppRunnerState::default(); @@ -284,17 +284,17 @@ pub fn winit_runner(mut app: App) { let mut redraw_event_reader = ManualEventReader::::default(); let mut focused_windows_state: SystemState<(Res, Query<&Window>)> = - SystemState::new(&mut app.world); + SystemState::new(app.world_mut()); let mut event_writer_system_state: SystemState<( EventWriter, NonSend, Query<(&mut Window, &mut CachedWindow)>, NonSend, - )> = SystemState::new(&mut app.world); + )> = SystemState::new(app.world_mut()); let mut create_window = - SystemState::>>::from_world(&mut app.world); + SystemState::>>::from_world(app.world_mut()); let mut winit_events = Vec::default(); // set up the event loop let event_handler = move |event, event_loop: &EventLoopWindowTarget| { @@ -350,7 +350,7 @@ fn handle_winit_event( } runner_state.redraw_requested = true; - if let Some(app_exit_events) = app.world.get_resource::>() { + if let Some(app_exit_events) = app.world().get_resource::>() { if app_exit_event_reader.read(app_exit_events).last().is_some() { event_loop.exit(); return; @@ -360,7 +360,7 @@ fn handle_winit_event( match event { Event::AboutToWait => { - let (config, windows) = focused_windows_state.get(&app.world); + let (config, windows) = focused_windows_state.get(app.world()); let focused = windows.iter().any(|window| window.focused); let mut should_update = match config.update_mode(focused) { UpdateMode::Continuous => { @@ -394,7 +394,7 @@ fn handle_winit_event( if should_update { let visible = windows.iter().any(|window| window.visible); - let (_, winit_windows, _, _) = event_writer_system_state.get_mut(&mut app.world); + let (_, winit_windows, _, _) = event_writer_system_state.get_mut(app.world_mut()); if visible && runner_state.active != ActiveState::WillSuspend { for window in winit_windows.windows.values() { window.request_redraw(); @@ -431,7 +431,7 @@ fn handle_winit_event( event, window_id, .. } => { let (mut window_resized, winit_windows, mut windows, access_kit_adapters) = - event_writer_system_state.get_mut(&mut app.world); + event_writer_system_state.get_mut(app.world_mut()); let Some(window) = winit_windows.get_window_entity(window_id) else { warn!("Skipped event {event:?} for unknown winit Window Id {window_id:?}"); @@ -643,8 +643,8 @@ fn handle_winit_event( _ => {} } - let mut windows = app.world.query::<(&mut Window, &mut CachedWindow)>(); - if let Ok((window_component, mut cache)) = windows.get_mut(&mut app.world, window) { + let mut windows = app.world_mut().query::<(&mut Window, &mut CachedWindow)>(); + if let Ok((window_component, mut cache)) = windows.get_mut(app.world_mut(), window) { if window_component.is_changed() { cache.window = window_component.clone(); } @@ -667,8 +667,8 @@ fn handle_winit_event( #[cfg(any(target_os = "android", target_os = "ios", target_os = "macos"))] { if runner_state.active == ActiveState::NotYetStarted { - create_windows(event_loop, create_window.get_mut(&mut app.world)); - create_window.apply(&mut app.world); + create_windows(event_loop, create_window.get_mut(app.world_mut())); + create_window.apply(app.world_mut()); } } @@ -683,9 +683,9 @@ fn handle_winit_event( // Get windows that are cached but without raw handles. Those window were already created, but got their // handle wrapper removed when the app was suspended. let mut query = app - .world + .world_mut() .query_filtered::<(Entity, &Window), (With, Without)>(); - if let Ok((entity, window)) = query.get_single(&app.world) { + if let Ok((entity, window)) = query.get_single(app.world()) { use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; let window = window.clone(); @@ -695,7 +695,7 @@ fn handle_winit_event( mut adapters, mut handlers, accessibility_requested, - ) = create_window.get_mut(&mut app.world); + ) = create_window.get_mut(app.world_mut()); let winit_window = winit_windows.create_window( event_loop, @@ -711,7 +711,7 @@ fn handle_winit_event( display_handle: winit_window.display_handle().unwrap().as_raw(), }; - app.world.entity_mut(entity).insert(wrapper); + app.world_mut().entity_mut(entity).insert(wrapper); } event_loop.set_control_flow(ControlFlow::Wait); } @@ -753,9 +753,13 @@ fn run_app_update_if_should( { // Remove the `RawHandleWrapper` from the primary window. // This will trigger the surface destruction. - let mut query = app.world.query_filtered::>(); - let entity = query.single(&app.world); - app.world.entity_mut(entity).remove::(); + let mut query = app + .world_mut() + .query_filtered::>(); + let entity = query.single(app.world()); + app.world_mut() + .entity_mut(entity) + .remove::(); event_loop.set_control_flow(ControlFlow::Wait); } } @@ -766,7 +770,7 @@ fn run_app_update_if_should( app.update(); // decide when to run the next update - let (config, windows) = focused_windows_state.get(&app.world); + let (config, windows) = focused_windows_state.get(app.world()); let focused = windows.iter().any(|window| window.focused); match config.update_mode(focused) { UpdateMode::Continuous => { @@ -787,13 +791,13 @@ fn run_app_update_if_should( } } - if let Some(app_redraw_events) = app.world.get_resource::>() { + if let Some(app_redraw_events) = app.world().get_resource::>() { if redraw_event_reader.read(app_redraw_events).last().is_some() { runner_state.redraw_requested = true; } } - if let Some(app_exit_events) = app.world.get_resource::>() { + if let Some(app_exit_events) = app.world().get_resource::>() { if app_exit_event_reader.read(app_exit_events).last().is_some() { event_loop.exit(); } @@ -802,8 +806,8 @@ fn run_app_update_if_should( // create any new windows // (even if app did not update, some may have been created by plugin setup) - create_windows(event_loop, create_window.get_mut(&mut app.world)); - create_window.apply(&mut app.world); + create_windows(event_loop, create_window.get_mut(app.world_mut())); + create_window.apply(app.world_mut()); } fn react_to_resize( diff --git a/crates/bevy_winit/src/winit_event.rs b/crates/bevy_winit/src/winit_event.rs index 9592a5e12b7f0..3e47ff17df994 100644 --- a/crates/bevy_winit/src/winit_event.rs +++ b/crates/bevy_winit/src/winit_event.rs @@ -197,83 +197,83 @@ pub(crate) fn forward_winit_events(buffered_events: &mut Vec, app: & for winit_event in buffered_events.iter() { match winit_event.clone() { WinitEvent::ApplicationLifetime(e) => { - app.world.send_event(e); + app.world_mut().send_event(e); } WinitEvent::CursorEntered(e) => { - app.world.send_event(e); + app.world_mut().send_event(e); } WinitEvent::CursorLeft(e) => { - app.world.send_event(e); + app.world_mut().send_event(e); } WinitEvent::CursorMoved(e) => { - app.world.send_event(e); + app.world_mut().send_event(e); } WinitEvent::FileDragAndDrop(e) => { - app.world.send_event(e); + app.world_mut().send_event(e); } WinitEvent::Ime(e) => { - app.world.send_event(e); + app.world_mut().send_event(e); } WinitEvent::ReceivedCharacter(e) => { - app.world.send_event(e); + app.world_mut().send_event(e); } WinitEvent::RequestRedraw(e) => { - app.world.send_event(e); + app.world_mut().send_event(e); } WinitEvent::WindowBackendScaleFactorChanged(e) => { - app.world.send_event(e); + app.world_mut().send_event(e); } WinitEvent::WindowCloseRequested(e) => { - app.world.send_event(e); + app.world_mut().send_event(e); } WinitEvent::WindowCreated(e) => { - app.world.send_event(e); + app.world_mut().send_event(e); } WinitEvent::WindowDestroyed(e) => { - app.world.send_event(e); + app.world_mut().send_event(e); } WinitEvent::WindowFocused(e) => { - app.world.send_event(e); + app.world_mut().send_event(e); } WinitEvent::WindowMoved(e) => { - app.world.send_event(e); + app.world_mut().send_event(e); } WinitEvent::WindowOccluded(e) => { - app.world.send_event(e); + app.world_mut().send_event(e); } WinitEvent::WindowResized(e) => { - app.world.send_event(e); + app.world_mut().send_event(e); } WinitEvent::WindowScaleFactorChanged(e) => { - app.world.send_event(e); + app.world_mut().send_event(e); } WinitEvent::WindowThemeChanged(e) => { - app.world.send_event(e); + app.world_mut().send_event(e); } WinitEvent::MouseButtonInput(e) => { - app.world.send_event(e); + app.world_mut().send_event(e); } WinitEvent::MouseMotion(e) => { - app.world.send_event(e); + app.world_mut().send_event(e); } WinitEvent::MouseWheel(e) => { - app.world.send_event(e); + app.world_mut().send_event(e); } WinitEvent::TouchpadMagnify(e) => { - app.world.send_event(e); + app.world_mut().send_event(e); } WinitEvent::TouchpadRotate(e) => { - app.world.send_event(e); + app.world_mut().send_event(e); } WinitEvent::TouchInput(e) => { - app.world.send_event(e); + app.world_mut().send_event(e); } WinitEvent::KeyboardInput(e) => { - app.world.send_event(e); + app.world_mut().send_event(e); } } } - app.world + app.world_mut() .resource_mut::>() .send_batch(buffered_events.drain(..)); } diff --git a/examples/2d/mesh2d_manual.rs b/examples/2d/mesh2d_manual.rs index 25af819323cb7..860bea5d60670 100644 --- a/examples/2d/mesh2d_manual.rs +++ b/examples/2d/mesh2d_manual.rs @@ -285,7 +285,7 @@ pub const COLORED_MESH2D_SHADER_HANDLE: Handle = impl Plugin for ColoredMesh2dPlugin { fn build(&self, app: &mut App) { // Load our custom shader - let mut shaders = app.world.resource_mut::>(); + let mut shaders = app.world_mut().resource_mut::>(); shaders.insert( &COLORED_MESH2D_SHADER_HANDLE, Shader::from_wgsl(COLORED_MESH2D_SHADER, file!()), diff --git a/examples/app/custom_loop.rs b/examples/app/custom_loop.rs index 17f70ae9f6d38..3d76bed2dff91 100644 --- a/examples/app/custom_loop.rs +++ b/examples/app/custom_loop.rs @@ -11,7 +11,7 @@ fn my_runner(mut app: App) { println!("Type stuff into the console"); for line in io::stdin().lines() { { - let mut input = app.world.resource_mut::(); + let mut input = app.world_mut().resource_mut::(); input.0 = line.unwrap(); } app.update(); diff --git a/examples/ecs/custom_schedule.rs b/examples/ecs/custom_schedule.rs index f85da26e8fed9..564d00f401cca 100644 --- a/examples/ecs/custom_schedule.rs +++ b/examples/ecs/custom_schedule.rs @@ -33,14 +33,14 @@ fn main() { // // Note that we modify `MainScheduleOrder` directly in `main` and not in a startup system. The reason for this is // that the `MainScheduleOrder` cannot be modified from systems that are run as part of the `Main` schedule. - let mut main_schedule_order = app.world.resource_mut::(); + let mut main_schedule_order = app.world_mut().resource_mut::(); main_schedule_order.insert_after(Update, SingleThreadedUpdate); // Adding a custom startup schedule works similarly, but needs to use `insert_startup_after` // instead of `insert_after`. app.add_schedule(Schedule::new(CustomStartup)); - let mut main_schedule_order = app.world.resource_mut::(); + let mut main_schedule_order = app.world_mut().resource_mut::(); main_schedule_order.insert_startup_after(PreStartup, CustomStartup); app.add_systems(SingleThreadedUpdate, single_threaded_update_system) diff --git a/examples/ecs/system_stepping.rs b/examples/ecs/system_stepping.rs index 0732e76eb0ee2..a4e4437362e9e 100644 --- a/examples/ecs/system_stepping.rs +++ b/examples/ecs/system_stepping.rs @@ -54,7 +54,7 @@ fn main() { * Stepping::continue_frame() is called * System has been configured to always run"# ); - let mut stepping = app.world.resource_mut::(); + let mut stepping = app.world_mut().resource_mut::(); stepping.add_schedule(Update).enable(); app.update(); @@ -65,7 +65,7 @@ fn main() { Stepping, step means run the next system across all the schedules that have been added to the Stepping resource."# ); - let mut stepping = app.world.resource_mut::(); + let mut stepping = app.world_mut().resource_mut::(); stepping.step_frame(); app.update(); @@ -89,7 +89,7 @@ fn main() { case, we previously performed a step, running one system in Update. This continue will cause all remaining systems in Update to run."# ); - let mut stepping = app.world.resource_mut::(); + let mut stepping = app.world_mut().resource_mut::(); stepping.continue_frame(); app.update(); @@ -102,7 +102,7 @@ fn main() { systems."# ); for _ in 0..4 { - let mut stepping = app.world.resource_mut::(); + let mut stepping = app.world_mut().resource_mut::(); stepping.step_frame(); app.update(); } @@ -116,10 +116,10 @@ fn main() { execute all systems in the frame. Stepping::always_run() allows us to granularly allow systems to run when stepping is enabled."# ); - let mut stepping = app.world.resource_mut::(); + let mut stepping = app.world_mut().resource_mut::(); stepping.always_run(Update, update_system_two); for _ in 0..3 { - let mut stepping = app.world.resource_mut::(); + let mut stepping = app.world_mut().resource_mut::(); stepping.step_frame(); app.update(); } @@ -132,7 +132,7 @@ fn main() { Stepping::never_run() allows us to disable systems while Stepping is enabled."# ); - let mut stepping = app.world.resource_mut::(); + let mut stepping = app.world_mut().resource_mut::(); stepping.never_run(Update, update_system_two); stepping.continue_frame(); app.update(); @@ -155,14 +155,14 @@ fn main() { During the final continue pre_update_system() and update_system_three() run."# ); - let mut stepping = app.world.resource_mut::(); + let mut stepping = app.world_mut().resource_mut::(); stepping.set_breakpoint(Update, update_system_two); stepping.continue_frame(); app.update(); - let mut stepping = app.world.resource_mut::(); + let mut stepping = app.world_mut().resource_mut::(); stepping.step_frame(); app.update(); - let mut stepping = app.world.resource_mut::(); + let mut stepping = app.world_mut().resource_mut::(); stepping.continue_frame(); app.update(); @@ -172,7 +172,7 @@ fn main() { through all systems Result: All systems will run"# ); - let mut stepping = app.world.resource_mut::(); + let mut stepping = app.world_mut().resource_mut::(); stepping.clear_breakpoint(Update, update_system_two); stepping.continue_frame(); app.update(); @@ -184,7 +184,7 @@ fn main() { call Stepping::step_frame() or Stepping::continue_frame() to run systems in the Update schedule."# ); - let mut stepping = app.world.resource_mut::(); + let mut stepping = app.world_mut().resource_mut::(); stepping.disable(); app.update(); } diff --git a/examples/games/stepping.rs b/examples/games/stepping.rs index 05e7081e5d3ab..61918c330dce6 100644 --- a/examples/games/stepping.rs +++ b/examples/games/stepping.rs @@ -36,7 +36,7 @@ impl Plugin for SteppingPlugin { // We need an independent schedule so we have access to all other // schedules through the `Stepping` resource app.init_schedule(DebugSchedule); - let mut order = app.world.resource_mut::(); + let mut order = app.world_mut().resource_mut::(); order.insert_after(Update, DebugSchedule); // create our stepping resource diff --git a/examples/shader/compute_shader_game_of_life.rs b/examples/shader/compute_shader_game_of_life.rs index 95751be4e3663..21b5cc92c4479 100644 --- a/examples/shader/compute_shader_game_of_life.rs +++ b/examples/shader/compute_shader_game_of_life.rs @@ -84,7 +84,7 @@ impl Plugin for GameOfLifeComputePlugin { prepare_bind_group.in_set(RenderSet::PrepareBindGroups), ); - let mut render_graph = render_app.world.resource_mut::(); + let mut render_graph = render_app.world_mut().resource_mut::(); render_graph.add_node(GameOfLifeLabel, GameOfLifeNode::default()); render_graph.add_node_edge(GameOfLifeLabel, bevy::render::graph::CameraDriverLabel); } diff --git a/examples/shader/post_processing.rs b/examples/shader/post_processing.rs index 7760d07963145..b3b7330bca8fe 100644 --- a/examples/shader/post_processing.rs +++ b/examples/shader/post_processing.rs @@ -59,7 +59,7 @@ impl Plugin for PostProcessPlugin { )); // We need to get the render app from the main app - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; @@ -97,7 +97,7 @@ impl Plugin for PostProcessPlugin { fn finish(&self, app: &mut App) { // We need to get the render app from the main app - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; diff --git a/examples/shader/texture_binding_array.rs b/examples/shader/texture_binding_array.rs index 6332beda6f1c6..252172f5838ae 100644 --- a/examples/shader/texture_binding_array.rs +++ b/examples/shader/texture_binding_array.rs @@ -33,11 +33,11 @@ impl Plugin for GpuFeatureSupportChecker { fn build(&self, _app: &mut App) {} fn finish(&self, app: &mut App) { - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; - let render_device = render_app.world.resource::(); + let render_device = render_app.world().resource::(); // Check if the device support the required feature. If not, exit the example. // In a real application, you should setup a fallback for the missing feature diff --git a/examples/stress_tests/many_lights.rs b/examples/stress_tests/many_lights.rs index 4157869197d67..b9134c42c4f5e 100644 --- a/examples/stress_tests/many_lights.rs +++ b/examples/stress_tests/many_lights.rs @@ -157,7 +157,7 @@ struct LogVisibleLights; impl Plugin for LogVisibleLights { fn build(&self, app: &mut App) { - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; diff --git a/examples/time/time.rs b/examples/time/time.rs index 01e5eefa1b07b..c473f9fa21375 100644 --- a/examples/time/time.rs +++ b/examples/time/time.rs @@ -45,29 +45,29 @@ fn runner(mut app: App) { } "f" => { println!("FAST: setting relative speed to 2x"); - app.world + app.world_mut() .resource_mut::>() .set_relative_speed(2.0); } "n" => { println!("NORMAL: setting relative speed to 1x"); - app.world + app.world_mut() .resource_mut::>() .set_relative_speed(1.0); } "s" => { println!("SLOW: setting relative speed to 0.5x"); - app.world + app.world_mut() .resource_mut::>() .set_relative_speed(0.5); } "p" => { println!("PAUSE: pausing virtual clock"); - app.world.resource_mut::>().pause(); + app.world_mut().resource_mut::>().pause(); } "u" => { println!("UNPAUSE: resuming virtual clock"); - app.world.resource_mut::>().unpause(); + app.world_mut().resource_mut::>().unpause(); } "q" => { println!("QUITTING!"); diff --git a/tests/how_to_test_systems.rs b/tests/how_to_test_systems.rs index b71e5471bd1e1..f14a14a60eba9 100644 --- a/tests/how_to_test_systems.rs +++ b/tests/how_to_test_systems.rs @@ -62,7 +62,7 @@ fn did_hurt_enemy() { // Setup test entities let enemy_id = app - .world + .world_mut() .spawn(Enemy { hit_points: 5, score_value: 3, @@ -73,8 +73,8 @@ fn did_hurt_enemy() { app.update(); // Check resulting changes - assert!(app.world.get::(enemy_id).is_some()); - assert_eq!(app.world.get::(enemy_id).unwrap().hit_points, 4); + assert!(app.world().get::(enemy_id).is_some()); + assert_eq!(app.world().get::(enemy_id).unwrap().hit_points, 4); } #[test] @@ -93,7 +93,7 @@ fn did_despawn_enemy() { // Setup test entities let enemy_id = app - .world + .world_mut() .spawn(Enemy { hit_points: 1, score_value: 1, @@ -104,10 +104,10 @@ fn did_despawn_enemy() { app.update(); // Check enemy was despawned - assert!(app.world.get::(enemy_id).is_none()); + assert!(app.world().get::(enemy_id).is_none()); // Get `EnemyDied` event reader - let enemy_died_events = app.world.resource::>(); + let enemy_died_events = app.world().resource::>(); let mut enemy_died_reader = enemy_died_events.get_reader(); let enemy_died = enemy_died_reader.read(enemy_died_events).next().unwrap(); @@ -132,16 +132,18 @@ fn spawn_enemy_using_input_resource() { app.update(); // Check resulting changes, one entity has been spawned with `Enemy` component - assert_eq!(app.world.query::<&Enemy>().iter(&app.world).len(), 1); + assert_eq!(app.world_mut().query::<&Enemy>().iter(app.world()).len(), 1); // Clear the `just_pressed` status for all `KeyCode`s - app.world.resource_mut::>().clear(); + app.world_mut() + .resource_mut::>() + .clear(); // Run systems app.update(); // Check resulting changes, no new entity has been spawned - assert_eq!(app.world.query::<&Enemy>().iter(&app.world).len(), 1); + assert_eq!(app.world_mut().query::<&Enemy>().iter(app.world()).len(), 1); } #[test] @@ -159,7 +161,7 @@ fn update_score_on_event() { app.add_systems(Update, update_score); // Send an `EnemyDied` event - app.world + app.world_mut() .resource_mut::>() .send(EnemyDied(3)); @@ -167,5 +169,5 @@ fn update_score_on_event() { app.update(); // Check resulting changes - assert_eq!(app.world.resource::().0, 3); + assert_eq!(app.world().resource::().0, 3); }