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::{InternedScheduleLabel, InternedSystemSet, ScheduleBuildSettings, ScheduleLabel},
7 system::{ScheduleSystem, SystemId, SystemInput},
8};
9use bevy_platform::collections::{HashMap, HashSet};
10use core::fmt::Debug;
11
12#[cfg(feature = "trace")]
13use tracing::info_span;
14
15type ExtractFn = Box<dyn FnMut(&mut World, &mut World) + Send>;
16
17pub struct SubApp {
62 world: World,
64 pub(crate) plugin_registry: Vec<Box<dyn Plugin>>,
66 pub(crate) plugin_names: HashSet<String>,
69 pub(crate) plugin_build_depth: usize,
71 pub(crate) plugins_state: PluginsState,
72 pub update_schedule: Option<InternedScheduleLabel>,
74 extract: Option<ExtractFn>,
77}
78
79impl Debug for SubApp {
80 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
81 write!(f, "SubApp")
82 }
83}
84
85impl 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
101impl SubApp {
102 pub fn new() -> Self {
104 Self::default()
105 }
106
107 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 pub fn world(&self) -> &World {
121 &self.world
122 }
123
124 pub fn world_mut(&mut self) -> &mut World {
126 &mut self.world
127 }
128
129 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 pub fn update(&mut self) {
144 self.run_default_schedule();
145 self.world.clear_trackers();
146 }
147
148 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 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 pub fn take_extract(&mut self) -> Option<ExtractFn> {
195 self.extract.take()
196 }
197
198 pub fn insert_resource<R: Resource>(&mut self, resource: R) -> &mut Self {
200 self.world.insert_resource(resource);
201 self
202 }
203
204 pub fn init_resource<R: Resource + FromWorld>(&mut self) -> &mut Self {
206 self.world.init_resource::<R>();
207 self
208 }
209
210 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 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 #[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 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 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 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 pub fn get_schedule_mut(&mut self, label: impl ScheduleLabel) -> Option<&mut Schedule> {
271 let schedules = self.world.get_resource_mut::<Schedules>()?;
272 schedules.into_inner().get_mut(label)
275 }
276
277 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 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 pub fn allow_ambiguous_component<T: Component>(&mut self) -> &mut Self {
308 self.world_mut().allow_ambiguous_component::<T>();
309 self
310 }
311
312 pub fn allow_ambiguous_resource<T: Resource>(&mut self) -> &mut Self {
314 self.world_mut().allow_ambiguous_resource::<T>();
315 self
316 }
317
318 #[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 #[deprecated(since = "0.17.0", note = "Use `add_message` instead.")]
340 pub fn add_event<T>(&mut self) -> &mut Self
341 where
342 T: Message,
343 {
344 self.add_message::<T>()
345 }
346
347 pub fn add_message<T>(&mut self) -> &mut Self
349 where
350 T: Message,
351 {
352 if !self.world.contains_resource::<Messages<T>>() {
353 MessageRegistry::register_message::<T>(self.world_mut());
354 }
355
356 self
357 }
358
359 pub fn add_plugins<M>(&mut self, plugins: impl Plugins<M>) -> &mut Self {
361 self.run_as_app(|app| plugins.add_to_app(app));
362 self
363 }
364
365 pub fn is_plugin_added<T>(&self) -> bool
367 where
368 T: Plugin,
369 {
370 self.plugin_names.contains(core::any::type_name::<T>())
371 }
372
373 pub fn get_added_plugins<T>(&self) -> Vec<&T>
375 where
376 T: Plugin,
377 {
378 self.plugin_registry
379 .iter()
380 .filter_map(|p| p.downcast_ref())
381 .collect()
382 }
383
384 pub(crate) fn is_building_plugins(&self) -> bool {
386 self.plugin_build_depth > 0
387 }
388
389 #[inline]
391 pub fn plugins_state(&mut self) -> PluginsState {
392 match self.plugins_state {
393 PluginsState::Adding => {
394 let mut state = PluginsState::Ready;
395 let plugins = core::mem::take(&mut self.plugin_registry);
396 self.run_as_app(|app| {
397 for plugin in &plugins {
398 if !plugin.ready(app) {
399 state = PluginsState::Adding;
400 return;
401 }
402 }
403 });
404 self.plugin_registry = plugins;
405 state
406 }
407 state => state,
408 }
409 }
410
411 pub fn finish(&mut self) {
413 let mut hokeypokey: Box<dyn Plugin> = Box::new(crate::HokeyPokey);
415 for i in 0..self.plugin_registry.len() {
416 core::mem::swap(&mut self.plugin_registry[i], &mut hokeypokey);
417 #[cfg(feature = "trace")]
418 let _plugin_finish_span =
419 info_span!("plugin finish", plugin = hokeypokey.name()).entered();
420 self.run_as_app(|app| {
421 hokeypokey.finish(app);
422 });
423 core::mem::swap(&mut self.plugin_registry[i], &mut hokeypokey);
424 }
425 self.plugins_state = PluginsState::Finished;
426 }
427
428 pub fn cleanup(&mut self) {
430 let mut hokeypokey: Box<dyn Plugin> = Box::new(crate::HokeyPokey);
432 for i in 0..self.plugin_registry.len() {
433 core::mem::swap(&mut self.plugin_registry[i], &mut hokeypokey);
434 #[cfg(feature = "trace")]
435 let _plugin_cleanup_span =
436 info_span!("plugin cleanup", plugin = hokeypokey.name()).entered();
437 self.run_as_app(|app| {
438 hokeypokey.cleanup(app);
439 });
440 core::mem::swap(&mut self.plugin_registry[i], &mut hokeypokey);
441 }
442 self.plugins_state = PluginsState::Cleaned;
443 }
444
445 #[cfg(feature = "bevy_reflect")]
447 pub fn register_type<T: bevy_reflect::GetTypeRegistration>(&mut self) -> &mut Self {
448 let registry = self.world.resource_mut::<AppTypeRegistry>();
449 registry.write().register::<T>();
450 self
451 }
452
453 #[cfg(feature = "bevy_reflect")]
455 pub fn register_type_data<
456 T: bevy_reflect::Reflect + bevy_reflect::TypePath,
457 D: bevy_reflect::TypeData + bevy_reflect::FromType<T>,
458 >(
459 &mut self,
460 ) -> &mut Self {
461 let registry = self.world.resource_mut::<AppTypeRegistry>();
462 registry.write().register_type_data::<T, D>();
463 self
464 }
465
466 #[cfg(feature = "reflect_functions")]
468 pub fn register_function<F, Marker>(&mut self, function: F) -> &mut Self
469 where
470 F: bevy_reflect::func::IntoFunction<'static, Marker> + 'static,
471 {
472 let registry = self.world.resource_mut::<AppFunctionRegistry>();
473 registry.write().register(function).unwrap();
474 self
475 }
476
477 #[cfg(feature = "reflect_functions")]
479 pub fn register_function_with_name<F, Marker>(
480 &mut self,
481 name: impl Into<alloc::borrow::Cow<'static, str>>,
482 function: F,
483 ) -> &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_with_name(name, function).unwrap();
489 self
490 }
491}
492
493#[derive(Default)]
495pub struct SubApps {
496 pub main: SubApp,
498 pub sub_apps: HashMap<InternedAppLabel, SubApp>,
500}
501
502impl SubApps {
503 pub fn update(&mut self) {
506 #[cfg(feature = "trace")]
507 let _bevy_update_span = info_span!("update").entered();
508 {
509 #[cfg(feature = "trace")]
510 let _bevy_frame_update_span = info_span!("main app").entered();
511 self.main.run_default_schedule();
512 }
513 for (_label, sub_app) in self.sub_apps.iter_mut() {
514 #[cfg(feature = "trace")]
515 let _sub_app_span = info_span!("sub app", name = ?_label).entered();
516 sub_app.extract(&mut self.main.world);
517 sub_app.update();
518 }
519
520 self.main.world.clear_trackers();
521 }
522
523 pub fn iter(&self) -> impl Iterator<Item = &SubApp> + '_ {
525 core::iter::once(&self.main).chain(self.sub_apps.values())
526 }
527
528 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut SubApp> + '_ {
530 core::iter::once(&mut self.main).chain(self.sub_apps.values_mut())
531 }
532
533 pub fn update_subapp_by_label(&mut self, label: impl AppLabel) {
535 if let Some(sub_app) = self.sub_apps.get_mut(&label.intern()) {
536 sub_app.extract(&mut self.main.world);
537 sub_app.update();
538 }
539 }
540}