Skip to main content

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