bevy_app/
sub_app.rs

1use crate::{App, AppLabel, InternedAppLabel, Plugin, Plugins, PluginsState};
2use alloc::{boxed::Box, string::String, vec::Vec};
3use bevy_ecs::{
4    message::MessageRegistry,
5    prelude::*,
6    schedule::{InternedScheduleLabel, InternedSystemSet, ScheduleBuildSettings, ScheduleLabel},
7    system::{ScheduleSystem, SystemId, SystemInput},
8};
9use bevy_platform::collections::{HashMap, HashSet};
10use core::fmt::Debug;
11
12#[cfg(feature = "trace")]
13use tracing::info_span;
14
15type ExtractFn = Box<dyn FnMut(&mut World, &mut World) + Send>;
16
17/// A secondary application with its own [`World`]. These can run independently of each other.
18///
19/// These are useful for situations where certain processes (e.g. a render thread) need to be kept
20/// separate from the main application.
21///
22/// # Example
23///
24/// ```
25/// # use bevy_app::{App, AppLabel, SubApp, Main};
26/// # use bevy_ecs::prelude::*;
27/// # use bevy_ecs::schedule::ScheduleLabel;
28///
29/// #[derive(Resource, Default)]
30/// struct Val(pub i32);
31///
32/// #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, AppLabel)]
33/// struct ExampleApp;
34///
35/// // Create an app with a certain resource.
36/// let mut app = App::new();
37/// app.insert_resource(Val(10));
38///
39/// // Create a sub-app with the same resource and a single schedule.
40/// let mut sub_app = SubApp::new();
41/// sub_app.update_schedule = Some(Main.intern());
42/// sub_app.insert_resource(Val(100));
43///
44/// // Setup an extract function to copy the resource's value in the main world.
45/// sub_app.set_extract(|main_world, sub_world| {
46///     sub_world.resource_mut::<Val>().0 = main_world.resource::<Val>().0;
47/// });
48///
49/// // Schedule a system that will verify extraction is working.
50/// sub_app.add_systems(Main, |counter: Res<Val>| {
51///     // The value will be copied during extraction, so we should see 10 instead of 100.
52///     assert_eq!(counter.0, 10);
53/// });
54///
55/// // Add the sub-app to the main app.
56/// app.insert_sub_app(ExampleApp, sub_app);
57///
58/// // Update the application once (using the default runner).
59/// app.run();
60/// ```
61pub struct SubApp {
62    /// The data of this application.
63    world: World,
64    /// List of plugins that have been added.
65    pub(crate) plugin_registry: Vec<Box<dyn Plugin>>,
66    /// The names of plugins that have been added to this app. (used to track duplicates and
67    /// already-registered plugins)
68    pub(crate) plugin_names: HashSet<String>,
69    /// Panics if an update is attempted while plugins are building.
70    pub(crate) plugin_build_depth: usize,
71    pub(crate) plugins_state: PluginsState,
72    /// The schedule that will be run by [`update`](Self::update).
73    pub update_schedule: Option<InternedScheduleLabel>,
74    /// A function that gives mutable access to two app worlds. This is primarily
75    /// intended for copying data from the main world to secondary worlds.
76    extract: Option<ExtractFn>,
77}
78
79impl Debug for SubApp {
80    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
81        write!(f, "SubApp")
82    }
83}
84
85impl Default for SubApp {
86    fn default() -> Self {
87        let mut world = World::new();
88        world.init_resource::<Schedules>();
89        Self {
90            world,
91            plugin_registry: Vec::default(),
92            plugin_names: HashSet::default(),
93            plugin_build_depth: 0,
94            plugins_state: PluginsState::Adding,
95            update_schedule: None,
96            extract: None,
97        }
98    }
99}
100
101impl SubApp {
102    /// Returns a default, empty [`SubApp`].
103    pub fn new() -> Self {
104        Self::default()
105    }
106
107    /// This method is a workaround. Each [`SubApp`] can have its own plugins, but [`Plugin`]
108    /// works on an [`App`] as a whole.
109    fn run_as_app<F>(&mut self, f: F)
110    where
111        F: FnOnce(&mut App),
112    {
113        let mut app = App::empty();
114        core::mem::swap(self, &mut app.sub_apps.main);
115        f(&mut app);
116        core::mem::swap(self, &mut app.sub_apps.main);
117    }
118
119    /// Returns a reference to the [`World`].
120    pub fn world(&self) -> &World {
121        &self.world
122    }
123
124    /// Returns a mutable reference to the [`World`].
125    pub fn world_mut(&mut self) -> &mut World {
126        &mut self.world
127    }
128
129    /// Runs the default schedule.
130    ///
131    /// Does not clear internal trackers used for change detection.
132    pub fn run_default_schedule(&mut self) {
133        if self.is_building_plugins() {
134            panic!("SubApp::update() was called while a plugin was building.");
135        }
136
137        if let Some(label) = self.update_schedule {
138            self.world.run_schedule(label);
139        }
140    }
141
142    /// Runs the default schedule and updates internal component trackers.
143    pub fn update(&mut self) {
144        self.run_default_schedule();
145        self.world.clear_trackers();
146    }
147
148    /// Extracts data from `world` into the app's world using the registered extract method.
149    ///
150    /// **Note:** There is no default extract method. Calling `extract` does nothing if
151    /// [`set_extract`](Self::set_extract) has not been called.
152    pub fn extract(&mut self, world: &mut World) {
153        if let Some(f) = self.extract.as_mut() {
154            f(world, &mut self.world);
155        }
156    }
157
158    /// Sets the method that will be called by [`extract`](Self::extract).
159    ///
160    /// The first argument is the `World` to extract data from, the second argument is the app `World`.
161    pub fn set_extract<F>(&mut self, extract: F) -> &mut Self
162    where
163        F: FnMut(&mut World, &mut World) + Send + 'static,
164    {
165        self.extract = Some(Box::new(extract));
166        self
167    }
168
169    /// Take the function that will be called by [`extract`](Self::extract) out of the app, if any was set,
170    /// and replace it with `None`.
171    ///
172    /// If you use Bevy, `bevy_render` will set a default extract function used to extract data from
173    /// the main world into the render world as part of the Extract phase. In that case, you cannot replace
174    /// it with your own function. Instead, take the Bevy default function with this, and install your own
175    /// instead which calls the Bevy default.
176    ///
177    /// ```
178    /// # use bevy_app::SubApp;
179    /// # let mut app = SubApp::new();
180    /// let mut default_fn = app.take_extract();
181    /// app.set_extract(move |main, render| {
182    ///     // Do pre-extract custom logic
183    ///     // [...]
184    ///
185    ///     // Call Bevy's default, which executes the Extract phase
186    ///     if let Some(f) = default_fn.as_mut() {
187    ///         f(main, render);
188    ///     }
189    ///
190    ///     // Do post-extract custom logic
191    ///     // [...]
192    /// });
193    /// ```
194    pub fn take_extract(&mut self) -> Option<ExtractFn> {
195        self.extract.take()
196    }
197
198    /// See [`App::insert_resource`].
199    pub fn insert_resource<R: Resource>(&mut self, resource: R) -> &mut Self {
200        self.world.insert_resource(resource);
201        self
202    }
203
204    /// See [`App::init_resource`].
205    pub fn init_resource<R: Resource + FromWorld>(&mut self) -> &mut Self {
206        self.world.init_resource::<R>();
207        self
208    }
209
210    /// See [`App::add_systems`].
211    pub fn add_systems<M>(
212        &mut self,
213        schedule: impl ScheduleLabel,
214        systems: impl IntoScheduleConfigs<ScheduleSystem, M>,
215    ) -> &mut Self {
216        let mut schedules = self.world.resource_mut::<Schedules>();
217        schedules.add_systems(schedule, systems);
218
219        self
220    }
221
222    /// See [`App::register_system`].
223    pub fn register_system<I, O, M>(
224        &mut self,
225        system: impl IntoSystem<I, O, M> + 'static,
226    ) -> SystemId<I, O>
227    where
228        I: SystemInput + 'static,
229        O: 'static,
230    {
231        self.world.register_system(system)
232    }
233
234    /// See [`App::configure_sets`].
235    #[track_caller]
236    pub fn configure_sets<M>(
237        &mut self,
238        schedule: impl ScheduleLabel,
239        sets: impl IntoScheduleConfigs<InternedSystemSet, M>,
240    ) -> &mut Self {
241        let mut schedules = self.world.resource_mut::<Schedules>();
242        schedules.configure_sets(schedule, sets);
243        self
244    }
245
246    /// See [`App::add_schedule`].
247    pub fn add_schedule(&mut self, schedule: Schedule) -> &mut Self {
248        let mut schedules = self.world.resource_mut::<Schedules>();
249        schedules.insert(schedule);
250        self
251    }
252
253    /// See [`App::init_schedule`].
254    pub fn init_schedule(&mut self, label: impl ScheduleLabel) -> &mut Self {
255        let label = label.intern();
256        let mut schedules = self.world.resource_mut::<Schedules>();
257        if !schedules.contains(label) {
258            schedules.insert(Schedule::new(label));
259        }
260        self
261    }
262
263    /// See [`App::get_schedule`].
264    pub fn get_schedule(&self, label: impl ScheduleLabel) -> Option<&Schedule> {
265        let schedules = self.world.get_resource::<Schedules>()?;
266        schedules.get(label)
267    }
268
269    /// See [`App::get_schedule_mut`].
270    pub fn get_schedule_mut(&mut self, label: impl ScheduleLabel) -> Option<&mut Schedule> {
271        let schedules = self.world.get_resource_mut::<Schedules>()?;
272        // We must call `.into_inner` here because the borrow checker only understands reborrows
273        // using ordinary references, not our `Mut` smart pointers.
274        schedules.into_inner().get_mut(label)
275    }
276
277    /// See [`App::edit_schedule`].
278    pub fn edit_schedule(
279        &mut self,
280        label: impl ScheduleLabel,
281        mut f: impl FnMut(&mut Schedule),
282    ) -> &mut Self {
283        let label = label.intern();
284        let mut schedules = self.world.resource_mut::<Schedules>();
285        if !schedules.contains(label) {
286            schedules.insert(Schedule::new(label));
287        }
288
289        let schedule = schedules.get_mut(label).unwrap();
290        f(schedule);
291
292        self
293    }
294
295    /// See [`App::configure_schedules`].
296    pub fn configure_schedules(
297        &mut self,
298        schedule_build_settings: ScheduleBuildSettings,
299    ) -> &mut Self {
300        self.world_mut()
301            .resource_mut::<Schedules>()
302            .configure_schedules(schedule_build_settings);
303        self
304    }
305
306    /// See [`App::allow_ambiguous_component`].
307    pub fn allow_ambiguous_component<T: Component>(&mut self) -> &mut Self {
308        self.world_mut().allow_ambiguous_component::<T>();
309        self
310    }
311
312    /// See [`App::allow_ambiguous_resource`].
313    pub fn allow_ambiguous_resource<T: Resource>(&mut self) -> &mut Self {
314        self.world_mut().allow_ambiguous_resource::<T>();
315        self
316    }
317
318    /// See [`App::ignore_ambiguity`].
319    #[track_caller]
320    pub fn ignore_ambiguity<M1, M2, S1, S2>(
321        &mut self,
322        schedule: impl ScheduleLabel,
323        a: S1,
324        b: S2,
325    ) -> &mut Self
326    where
327        S1: IntoSystemSet<M1>,
328        S2: IntoSystemSet<M2>,
329    {
330        let schedule = schedule.intern();
331        let mut schedules = self.world.resource_mut::<Schedules>();
332
333        schedules.ignore_ambiguity(schedule, a, b);
334
335        self
336    }
337
338    /// See [`App::add_message`].
339    #[deprecated(since = "0.17.0", note = "Use `add_message` instead.")]
340    pub fn add_event<T>(&mut self) -> &mut Self
341    where
342        T: Message,
343    {
344        self.add_message::<T>()
345    }
346
347    /// See [`App::add_message`].
348    pub fn add_message<T>(&mut self) -> &mut Self
349    where
350        T: Message,
351    {
352        if !self.world.contains_resource::<Messages<T>>() {
353            MessageRegistry::register_message::<T>(self.world_mut());
354        }
355
356        self
357    }
358
359    /// See [`App::add_plugins`].
360    pub fn add_plugins<M>(&mut self, plugins: impl Plugins<M>) -> &mut Self {
361        self.run_as_app(|app| plugins.add_to_app(app));
362        self
363    }
364
365    /// See [`App::is_plugin_added`].
366    pub fn is_plugin_added<T>(&self) -> bool
367    where
368        T: Plugin,
369    {
370        self.plugin_names.contains(core::any::type_name::<T>())
371    }
372
373    /// See [`App::get_added_plugins`].
374    pub fn get_added_plugins<T>(&self) -> Vec<&T>
375    where
376        T: Plugin,
377    {
378        self.plugin_registry
379            .iter()
380            .filter_map(|p| p.downcast_ref())
381            .collect()
382    }
383
384    /// Returns `true` if there is no plugin in the middle of being built.
385    pub(crate) fn is_building_plugins(&self) -> bool {
386        self.plugin_build_depth > 0
387    }
388
389    /// Return the state of plugins.
390    #[inline]
391    pub fn plugins_state(&mut self) -> PluginsState {
392        match self.plugins_state {
393            PluginsState::Adding => {
394                let mut state = PluginsState::Ready;
395                let plugins = core::mem::take(&mut self.plugin_registry);
396                self.run_as_app(|app| {
397                    for plugin in &plugins {
398                        if !plugin.ready(app) {
399                            state = PluginsState::Adding;
400                            return;
401                        }
402                    }
403                });
404                self.plugin_registry = plugins;
405                state
406            }
407            state => state,
408        }
409    }
410
411    /// Runs [`Plugin::finish`] for each plugin.
412    pub fn finish(&mut self) {
413        // do hokey pokey with a boxed zst plugin (doesn't allocate)
414        let mut hokeypokey: Box<dyn Plugin> = Box::new(crate::HokeyPokey);
415        for i in 0..self.plugin_registry.len() {
416            core::mem::swap(&mut self.plugin_registry[i], &mut hokeypokey);
417            #[cfg(feature = "trace")]
418            let _plugin_finish_span =
419                info_span!("plugin finish", plugin = hokeypokey.name()).entered();
420            self.run_as_app(|app| {
421                hokeypokey.finish(app);
422            });
423            core::mem::swap(&mut self.plugin_registry[i], &mut hokeypokey);
424        }
425        self.plugins_state = PluginsState::Finished;
426    }
427
428    /// Runs [`Plugin::cleanup`] for each plugin.
429    pub fn cleanup(&mut self) {
430        // do hokey pokey with a boxed zst plugin (doesn't allocate)
431        let mut hokeypokey: Box<dyn Plugin> = Box::new(crate::HokeyPokey);
432        for i in 0..self.plugin_registry.len() {
433            core::mem::swap(&mut self.plugin_registry[i], &mut hokeypokey);
434            #[cfg(feature = "trace")]
435            let _plugin_cleanup_span =
436                info_span!("plugin cleanup", plugin = hokeypokey.name()).entered();
437            self.run_as_app(|app| {
438                hokeypokey.cleanup(app);
439            });
440            core::mem::swap(&mut self.plugin_registry[i], &mut hokeypokey);
441        }
442        self.plugins_state = PluginsState::Cleaned;
443    }
444
445    /// See [`App::register_type`].
446    #[cfg(feature = "bevy_reflect")]
447    pub fn register_type<T: bevy_reflect::GetTypeRegistration>(&mut self) -> &mut Self {
448        let registry = self.world.resource_mut::<AppTypeRegistry>();
449        registry.write().register::<T>();
450        self
451    }
452
453    /// See [`App::register_type_data`].
454    #[cfg(feature = "bevy_reflect")]
455    pub fn register_type_data<
456        T: bevy_reflect::Reflect + bevy_reflect::TypePath,
457        D: bevy_reflect::TypeData + bevy_reflect::FromType<T>,
458    >(
459        &mut self,
460    ) -> &mut Self {
461        let registry = self.world.resource_mut::<AppTypeRegistry>();
462        registry.write().register_type_data::<T, D>();
463        self
464    }
465
466    /// See [`App::register_function`].
467    #[cfg(feature = "reflect_functions")]
468    pub fn register_function<F, Marker>(&mut self, function: F) -> &mut Self
469    where
470        F: bevy_reflect::func::IntoFunction<'static, Marker> + 'static,
471    {
472        let registry = self.world.resource_mut::<AppFunctionRegistry>();
473        registry.write().register(function).unwrap();
474        self
475    }
476
477    /// See [`App::register_function_with_name`].
478    #[cfg(feature = "reflect_functions")]
479    pub fn register_function_with_name<F, Marker>(
480        &mut self,
481        name: impl Into<alloc::borrow::Cow<'static, str>>,
482        function: F,
483    ) -> &mut Self
484    where
485        F: bevy_reflect::func::IntoFunction<'static, Marker> + 'static,
486    {
487        let registry = self.world.resource_mut::<AppFunctionRegistry>();
488        registry.write().register_with_name(name, function).unwrap();
489        self
490    }
491}
492
493/// The collection of sub-apps that belong to an [`App`].
494#[derive(Default)]
495pub struct SubApps {
496    /// The primary sub-app that contains the "main" world.
497    pub main: SubApp,
498    /// Other, labeled sub-apps.
499    pub sub_apps: HashMap<InternedAppLabel, SubApp>,
500}
501
502impl SubApps {
503    /// Calls [`update`](SubApp::update) for the main sub-app, and then calls
504    /// [`extract`](SubApp::extract) and [`update`](SubApp::update) for the rest.
505    pub fn update(&mut self) {
506        #[cfg(feature = "trace")]
507        let _bevy_update_span = info_span!("update").entered();
508        {
509            #[cfg(feature = "trace")]
510            let _bevy_frame_update_span = info_span!("main app").entered();
511            self.main.run_default_schedule();
512        }
513        for (_label, sub_app) in self.sub_apps.iter_mut() {
514            #[cfg(feature = "trace")]
515            let _sub_app_span = info_span!("sub app", name = ?_label).entered();
516            sub_app.extract(&mut self.main.world);
517            sub_app.update();
518        }
519
520        self.main.world.clear_trackers();
521    }
522
523    /// Returns an iterator over the sub-apps (starting with the main one).
524    pub fn iter(&self) -> impl Iterator<Item = &SubApp> + '_ {
525        core::iter::once(&self.main).chain(self.sub_apps.values())
526    }
527
528    /// Returns a mutable iterator over the sub-apps (starting with the main one).
529    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut SubApp> + '_ {
530        core::iter::once(&mut self.main).chain(self.sub_apps.values_mut())
531    }
532
533    /// Extract data from the main world into the [`SubApp`] with the given label and perform an update if it exists.
534    pub fn update_subapp_by_label(&mut self, label: impl AppLabel) {
535        if let Some(sub_app) = self.sub_apps.get_mut(&label.intern()) {
536            sub_app.extract(&mut self.main.world);
537            sub_app.update();
538        }
539    }
540}