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