Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_app/src/sub_app.rs
9358 views
1
use crate::{App, AppLabel, InternedAppLabel, Plugin, Plugins, PluginsState};
2
use alloc::{boxed::Box, string::String, vec::Vec};
3
use bevy_ecs::{
4
message::MessageRegistry,
5
prelude::*,
6
schedule::{
7
InternedScheduleLabel, InternedSystemSet, ScheduleBuildSettings, ScheduleCleanupPolicy,
8
ScheduleError, ScheduleLabel,
9
},
10
system::{ScheduleSystem, SystemId, SystemInput},
11
};
12
use bevy_platform::collections::{HashMap, HashSet};
13
use core::fmt::Debug;
14
15
#[cfg(feature = "trace")]
16
use tracing::{info_span, warn};
17
18
type 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
/// ```
64
pub 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
82
impl Debug for SubApp {
83
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
84
write!(f, "SubApp")
85
}
86
}
87
88
impl 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
104
impl 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
let _old_schedule = schedules.insert(schedule);
265
266
#[cfg(feature = "trace")]
267
if let Some(schedule) = _old_schedule {
268
warn!(
269
"Schedule {:?} was re-inserted, all previous configuration has been removed",
270
schedule.label()
271
);
272
}
273
274
self
275
}
276
277
/// See [`App::init_schedule`].
278
pub fn init_schedule(&mut self, label: impl ScheduleLabel) -> &mut Self {
279
let label = label.intern();
280
let mut schedules = self.world.resource_mut::<Schedules>();
281
if !schedules.contains(label) {
282
schedules.insert(Schedule::new(label));
283
}
284
self
285
}
286
287
/// See [`App::get_schedule`].
288
pub fn get_schedule(&self, label: impl ScheduleLabel) -> Option<&Schedule> {
289
let schedules = self.world.get_resource::<Schedules>()?;
290
schedules.get(label)
291
}
292
293
/// See [`App::get_schedule_mut`].
294
pub fn get_schedule_mut(&mut self, label: impl ScheduleLabel) -> Option<&mut Schedule> {
295
let schedules = self.world.get_resource_mut::<Schedules>()?;
296
// We must call `.into_inner` here because the borrow checker only understands reborrows
297
// using ordinary references, not our `Mut` smart pointers.
298
schedules.into_inner().get_mut(label)
299
}
300
301
/// See [`App::edit_schedule`].
302
pub fn edit_schedule(
303
&mut self,
304
label: impl ScheduleLabel,
305
mut f: impl FnMut(&mut Schedule),
306
) -> &mut Self {
307
let label = label.intern();
308
let mut schedules = self.world.resource_mut::<Schedules>();
309
if !schedules.contains(label) {
310
schedules.insert(Schedule::new(label));
311
}
312
313
let schedule = schedules.get_mut(label).unwrap();
314
f(schedule);
315
316
self
317
}
318
319
/// See [`App::configure_schedules`].
320
pub fn configure_schedules(
321
&mut self,
322
schedule_build_settings: ScheduleBuildSettings,
323
) -> &mut Self {
324
self.world_mut()
325
.resource_mut::<Schedules>()
326
.configure_schedules(schedule_build_settings);
327
self
328
}
329
330
/// See [`App::allow_ambiguous_component`].
331
pub fn allow_ambiguous_component<T: Component>(&mut self) -> &mut Self {
332
self.world_mut().allow_ambiguous_component::<T>();
333
self
334
}
335
336
/// See [`App::allow_ambiguous_resource`].
337
pub fn allow_ambiguous_resource<T: Resource>(&mut self) -> &mut Self {
338
self.world_mut().allow_ambiguous_resource::<T>();
339
self
340
}
341
342
/// See [`App::ignore_ambiguity`].
343
#[track_caller]
344
pub fn ignore_ambiguity<M1, M2, S1, S2>(
345
&mut self,
346
schedule: impl ScheduleLabel,
347
a: S1,
348
b: S2,
349
) -> &mut Self
350
where
351
S1: IntoSystemSet<M1>,
352
S2: IntoSystemSet<M2>,
353
{
354
let schedule = schedule.intern();
355
let mut schedules = self.world.resource_mut::<Schedules>();
356
357
schedules.ignore_ambiguity(schedule, a, b);
358
359
self
360
}
361
362
/// See [`App::add_message`].
363
pub fn add_message<T>(&mut self) -> &mut Self
364
where
365
T: Message,
366
{
367
if !self.world.contains_resource::<Messages<T>>() {
368
MessageRegistry::register_message::<T>(self.world_mut());
369
}
370
371
self
372
}
373
374
/// See [`App::add_plugins`].
375
pub fn add_plugins<M>(&mut self, plugins: impl Plugins<M>) -> &mut Self {
376
self.run_as_app(|app| plugins.add_to_app(app));
377
self
378
}
379
380
/// See [`App::is_plugin_added`].
381
pub fn is_plugin_added<T>(&self) -> bool
382
where
383
T: Plugin,
384
{
385
self.plugin_names.contains(core::any::type_name::<T>())
386
}
387
388
/// See [`App::get_added_plugins`].
389
pub fn get_added_plugins<T>(&self) -> Vec<&T>
390
where
391
T: Plugin,
392
{
393
self.plugin_registry
394
.iter()
395
.filter_map(|p| p.downcast_ref())
396
.collect()
397
}
398
399
/// Returns `true` if there is no plugin in the middle of being built.
400
pub(crate) fn is_building_plugins(&self) -> bool {
401
self.plugin_build_depth > 0
402
}
403
404
/// Return the state of plugins.
405
#[inline]
406
pub fn plugins_state(&mut self) -> PluginsState {
407
match self.plugins_state {
408
PluginsState::Adding => {
409
let mut state = PluginsState::Ready;
410
let plugins = core::mem::take(&mut self.plugin_registry);
411
self.run_as_app(|app| {
412
for plugin in &plugins {
413
if !plugin.ready(app) {
414
state = PluginsState::Adding;
415
return;
416
}
417
}
418
});
419
self.plugin_registry = plugins;
420
state
421
}
422
state => state,
423
}
424
}
425
426
/// Runs [`Plugin::finish`] for each plugin.
427
pub fn finish(&mut self) {
428
// do hokey pokey with a boxed zst plugin (doesn't allocate)
429
let mut hokeypokey: Box<dyn Plugin> = Box::new(crate::HokeyPokey);
430
for i in 0..self.plugin_registry.len() {
431
core::mem::swap(&mut self.plugin_registry[i], &mut hokeypokey);
432
#[cfg(feature = "trace")]
433
let _plugin_finish_span =
434
info_span!("plugin finish", plugin = hokeypokey.name()).entered();
435
self.run_as_app(|app| {
436
hokeypokey.finish(app);
437
});
438
core::mem::swap(&mut self.plugin_registry[i], &mut hokeypokey);
439
}
440
self.plugins_state = PluginsState::Finished;
441
}
442
443
/// Runs [`Plugin::cleanup`] for each plugin.
444
pub fn cleanup(&mut self) {
445
// do hokey pokey with a boxed zst plugin (doesn't allocate)
446
let mut hokeypokey: Box<dyn Plugin> = Box::new(crate::HokeyPokey);
447
for i in 0..self.plugin_registry.len() {
448
core::mem::swap(&mut self.plugin_registry[i], &mut hokeypokey);
449
#[cfg(feature = "trace")]
450
let _plugin_cleanup_span =
451
info_span!("plugin cleanup", plugin = hokeypokey.name()).entered();
452
self.run_as_app(|app| {
453
hokeypokey.cleanup(app);
454
});
455
core::mem::swap(&mut self.plugin_registry[i], &mut hokeypokey);
456
}
457
self.plugins_state = PluginsState::Cleaned;
458
}
459
460
/// See [`App::register_type`].
461
#[cfg(feature = "bevy_reflect")]
462
pub fn register_type<T: bevy_reflect::GetTypeRegistration>(&mut self) -> &mut Self {
463
let registry = self.world.resource_mut::<AppTypeRegistry>();
464
registry.write().register::<T>();
465
self
466
}
467
468
/// See [`App::register_type_data`].
469
#[cfg(feature = "bevy_reflect")]
470
pub fn register_type_data<
471
T: bevy_reflect::Reflect + bevy_reflect::TypePath,
472
D: bevy_reflect::TypeData + bevy_reflect::FromType<T>,
473
>(
474
&mut self,
475
) -> &mut Self {
476
let registry = self.world.resource_mut::<AppTypeRegistry>();
477
registry.write().register_type_data::<T, D>();
478
self
479
}
480
481
/// See [`App::register_function`].
482
#[cfg(feature = "reflect_functions")]
483
pub fn register_function<F, Marker>(&mut self, function: F) -> &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(function).unwrap();
489
self
490
}
491
492
/// See [`App::register_function_with_name`].
493
#[cfg(feature = "reflect_functions")]
494
pub fn register_function_with_name<F, Marker>(
495
&mut self,
496
name: impl Into<alloc::borrow::Cow<'static, str>>,
497
function: F,
498
) -> &mut Self
499
where
500
F: bevy_reflect::func::IntoFunction<'static, Marker> + 'static,
501
{
502
let registry = self.world.resource_mut::<AppFunctionRegistry>();
503
registry.write().register_with_name(name, function).unwrap();
504
self
505
}
506
}
507
508
/// The collection of sub-apps that belong to an [`App`].
509
#[derive(Default)]
510
pub struct SubApps {
511
/// The primary sub-app that contains the "main" world.
512
pub main: SubApp,
513
/// Other, labeled sub-apps.
514
pub sub_apps: HashMap<InternedAppLabel, SubApp>,
515
}
516
517
impl SubApps {
518
/// Calls [`update`](SubApp::update) for the main sub-app, and then calls
519
/// [`extract`](SubApp::extract) and [`update`](SubApp::update) for the rest.
520
pub fn update(&mut self) {
521
#[cfg(feature = "trace")]
522
let _bevy_update_span = info_span!("update").entered();
523
{
524
#[cfg(feature = "trace")]
525
let _bevy_frame_update_span = info_span!("main app").entered();
526
self.main.run_default_schedule();
527
}
528
for (_label, sub_app) in self.sub_apps.iter_mut() {
529
#[cfg(feature = "trace")]
530
let _sub_app_span = info_span!("sub app", name = ?_label).entered();
531
sub_app.extract(&mut self.main.world);
532
sub_app.update();
533
}
534
535
self.main.world.clear_trackers();
536
}
537
538
/// Returns an iterator over the sub-apps (starting with the main one).
539
pub fn iter(&self) -> impl Iterator<Item = &SubApp> + '_ {
540
core::iter::once(&self.main).chain(self.sub_apps.values())
541
}
542
543
/// Returns a mutable iterator over the sub-apps (starting with the main one).
544
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut SubApp> + '_ {
545
core::iter::once(&mut self.main).chain(self.sub_apps.values_mut())
546
}
547
548
/// Extract data from the main world into the [`SubApp`] with the given label and perform an update if it exists.
549
pub fn update_subapp_by_label(&mut self, label: impl AppLabel) {
550
if let Some(sub_app) = self.sub_apps.get_mut(&label.intern()) {
551
sub_app.extract(&mut self.main.world);
552
sub_app.update();
553
}
554
}
555
}
556
557