bevy_app/
sub_app.rs

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