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
21pub struct SubApp {
66 world: World,
68 pub(crate) plugin_registry: Vec<Box<dyn Plugin>>,
70 pub(crate) plugin_names: HashSet<String>,
73 pub(crate) plugin_build_depth: usize,
75 pub(crate) plugins_state: PluginsState,
76 pub update_schedule: Option<InternedScheduleLabel>,
78 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 pub fn new() -> Self {
108 Self::default()
109 }
110
111 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 pub fn world(&self) -> &World {
125 &self.world
126 }
127
128 pub fn world_mut(&mut self) -> &mut World {
130 &mut self.world
131 }
132
133 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 pub fn update(&mut self) {
148 self.run_default_schedule();
149 self.world.clear_trackers();
150 }
151
152 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 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 pub fn take_extract(&mut self) -> Option<ExtractFn> {
199 self.extract.take()
200 }
201
202 pub fn insert_resource<R: Resource>(&mut self, resource: R) -> &mut Self {
204 self.world.insert_resource(resource);
205 self
206 }
207
208 pub fn init_resource<R: Resource + FromWorld>(&mut self) -> &mut Self {
210 self.world.init_resource::<R>();
211 self
212 }
213
214 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 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 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 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 #[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 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 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 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 pub fn get_schedule_mut(&mut self, label: impl ScheduleLabel) -> Option<&mut Schedule> {
308 let schedules = self.world.get_resource_mut::<Schedules>()?;
309 schedules.into_inner().get_mut(label)
312 }
313
314 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 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 pub fn allow_ambiguous_component<T: Component>(&mut self) -> &mut Self {
345 self.world_mut().allow_ambiguous_component::<T>();
346 self
347 }
348
349 pub fn allow_ambiguous_resource<T: Resource>(&mut self) -> &mut Self {
351 self.world_mut().allow_ambiguous_resource::<T>();
352 self
353 }
354
355 #[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 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 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 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 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 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 pub(crate) fn is_building_plugins(&self) -> bool {
420 self.plugin_build_depth > 0
421 }
422
423 #[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 pub fn finish(&mut self) {
447 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 pub fn cleanup(&mut self) {
464 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 #[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 #[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 #[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 #[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 #[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 #[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#[derive(Default)]
556pub struct SubApps {
557 pub main: SubApp,
559 pub sub_apps: HashMap<InternedAppLabel, SubApp>,
561}
562
563impl SubApps {
564 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 pub fn iter(&self) -> impl Iterator<Item = &SubApp> + '_ {
586 core::iter::once(&self.main).chain(self.sub_apps.values())
587 }
588
589 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 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}