Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_app/src/sub_app.rs
6595 views
1
use crate::{App, AppLabel, InternedAppLabel, Plugin, Plugins, PluginsState};
2
use alloc::{boxed::Box, string::String, vec::Vec};
3
use bevy_ecs::{
4
event::EventRegistry,
5
prelude::*,
6
schedule::{InternedScheduleLabel, InternedSystemSet, ScheduleBuildSettings, ScheduleLabel},
7
system::{ScheduleSystem, SystemId, SystemInput},
8
};
9
use bevy_platform::collections::{HashMap, HashSet};
10
use core::fmt::Debug;
11
12
#[cfg(feature = "trace")]
13
use tracing::info_span;
14
15
type 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
/// ```
61
pub 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
79
impl Debug for SubApp {
80
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
81
write!(f, "SubApp")
82
}
83
}
84
85
impl 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
101
impl 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_event`].
339
pub fn add_event<T>(&mut self) -> &mut Self
340
where
341
T: BufferedEvent,
342
{
343
if !self.world.contains_resource::<Events<T>>() {
344
EventRegistry::register_event::<T>(self.world_mut());
345
}
346
347
self
348
}
349
350
/// See [`App::add_plugins`].
351
pub fn add_plugins<M>(&mut self, plugins: impl Plugins<M>) -> &mut Self {
352
self.run_as_app(|app| plugins.add_to_app(app));
353
self
354
}
355
356
/// See [`App::is_plugin_added`].
357
pub fn is_plugin_added<T>(&self) -> bool
358
where
359
T: Plugin,
360
{
361
self.plugin_names.contains(core::any::type_name::<T>())
362
}
363
364
/// See [`App::get_added_plugins`].
365
pub fn get_added_plugins<T>(&self) -> Vec<&T>
366
where
367
T: Plugin,
368
{
369
self.plugin_registry
370
.iter()
371
.filter_map(|p| p.downcast_ref())
372
.collect()
373
}
374
375
/// Returns `true` if there is no plugin in the middle of being built.
376
pub(crate) fn is_building_plugins(&self) -> bool {
377
self.plugin_build_depth > 0
378
}
379
380
/// Return the state of plugins.
381
#[inline]
382
pub fn plugins_state(&mut self) -> PluginsState {
383
match self.plugins_state {
384
PluginsState::Adding => {
385
let mut state = PluginsState::Ready;
386
let plugins = core::mem::take(&mut self.plugin_registry);
387
self.run_as_app(|app| {
388
for plugin in &plugins {
389
if !plugin.ready(app) {
390
state = PluginsState::Adding;
391
return;
392
}
393
}
394
});
395
self.plugin_registry = plugins;
396
state
397
}
398
state => state,
399
}
400
}
401
402
/// Runs [`Plugin::finish`] for each plugin.
403
pub fn finish(&mut self) {
404
// do hokey pokey with a boxed zst plugin (doesn't allocate)
405
let mut hokeypokey: Box<dyn Plugin> = Box::new(crate::HokeyPokey);
406
for i in 0..self.plugin_registry.len() {
407
core::mem::swap(&mut self.plugin_registry[i], &mut hokeypokey);
408
#[cfg(feature = "trace")]
409
let _plugin_finish_span =
410
info_span!("plugin finish", plugin = hokeypokey.name()).entered();
411
self.run_as_app(|app| {
412
hokeypokey.finish(app);
413
});
414
core::mem::swap(&mut self.plugin_registry[i], &mut hokeypokey);
415
}
416
self.plugins_state = PluginsState::Finished;
417
}
418
419
/// Runs [`Plugin::cleanup`] for each plugin.
420
pub fn cleanup(&mut self) {
421
// do hokey pokey with a boxed zst plugin (doesn't allocate)
422
let mut hokeypokey: Box<dyn Plugin> = Box::new(crate::HokeyPokey);
423
for i in 0..self.plugin_registry.len() {
424
core::mem::swap(&mut self.plugin_registry[i], &mut hokeypokey);
425
#[cfg(feature = "trace")]
426
let _plugin_cleanup_span =
427
info_span!("plugin cleanup", plugin = hokeypokey.name()).entered();
428
self.run_as_app(|app| {
429
hokeypokey.cleanup(app);
430
});
431
core::mem::swap(&mut self.plugin_registry[i], &mut hokeypokey);
432
}
433
self.plugins_state = PluginsState::Cleaned;
434
}
435
436
/// See [`App::register_type`].
437
#[cfg(feature = "bevy_reflect")]
438
pub fn register_type<T: bevy_reflect::GetTypeRegistration>(&mut self) -> &mut Self {
439
let registry = self.world.resource_mut::<AppTypeRegistry>();
440
registry.write().register::<T>();
441
self
442
}
443
444
/// See [`App::register_type_data`].
445
#[cfg(feature = "bevy_reflect")]
446
pub fn register_type_data<
447
T: bevy_reflect::Reflect + bevy_reflect::TypePath,
448
D: bevy_reflect::TypeData + bevy_reflect::FromType<T>,
449
>(
450
&mut self,
451
) -> &mut Self {
452
let registry = self.world.resource_mut::<AppTypeRegistry>();
453
registry.write().register_type_data::<T, D>();
454
self
455
}
456
457
/// See [`App::register_function`].
458
#[cfg(feature = "reflect_functions")]
459
pub fn register_function<F, Marker>(&mut self, function: F) -> &mut Self
460
where
461
F: bevy_reflect::func::IntoFunction<'static, Marker> + 'static,
462
{
463
let registry = self.world.resource_mut::<AppFunctionRegistry>();
464
registry.write().register(function).unwrap();
465
self
466
}
467
468
/// See [`App::register_function_with_name`].
469
#[cfg(feature = "reflect_functions")]
470
pub fn register_function_with_name<F, Marker>(
471
&mut self,
472
name: impl Into<alloc::borrow::Cow<'static, str>>,
473
function: F,
474
) -> &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_with_name(name, function).unwrap();
480
self
481
}
482
}
483
484
/// The collection of sub-apps that belong to an [`App`].
485
#[derive(Default)]
486
pub struct SubApps {
487
/// The primary sub-app that contains the "main" world.
488
pub main: SubApp,
489
/// Other, labeled sub-apps.
490
pub sub_apps: HashMap<InternedAppLabel, SubApp>,
491
}
492
493
impl SubApps {
494
/// Calls [`update`](SubApp::update) for the main sub-app, and then calls
495
/// [`extract`](SubApp::extract) and [`update`](SubApp::update) for the rest.
496
pub fn update(&mut self) {
497
#[cfg(feature = "trace")]
498
let _bevy_update_span = info_span!("update").entered();
499
{
500
#[cfg(feature = "trace")]
501
let _bevy_frame_update_span = info_span!("main app").entered();
502
self.main.run_default_schedule();
503
}
504
for (_label, sub_app) in self.sub_apps.iter_mut() {
505
#[cfg(feature = "trace")]
506
let _sub_app_span = info_span!("sub app", name = ?_label).entered();
507
sub_app.extract(&mut self.main.world);
508
sub_app.update();
509
}
510
511
self.main.world.clear_trackers();
512
}
513
514
/// Returns an iterator over the sub-apps (starting with the main one).
515
pub fn iter(&self) -> impl Iterator<Item = &SubApp> + '_ {
516
core::iter::once(&self.main).chain(self.sub_apps.values())
517
}
518
519
/// Returns a mutable iterator over the sub-apps (starting with the main one).
520
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut SubApp> + '_ {
521
core::iter::once(&mut self.main).chain(self.sub_apps.values_mut())
522
}
523
524
/// Extract data from the main world into the [`SubApp`] with the given label and perform an update if it exists.
525
pub fn update_subapp_by_label(&mut self, label: impl AppLabel) {
526
if let Some(sub_app) = self.sub_apps.get_mut(&label.intern()) {
527
sub_app.extract(&mut self.main.world);
528
sub_app.update();
529
}
530
}
531
}
532
533