1use crate::{App, AppLabel, InternedAppLabel, Plugin, Plugins, PluginsState};
2use bevy_ecs::{
3 event::EventRegistry,
4 prelude::*,
5 schedule::{InternedScheduleLabel, ScheduleBuildSettings, ScheduleLabel},
6 system::{SystemId, SystemInput},
7};
8
9#[cfg(feature = "trace")]
10use bevy_utils::tracing::info_span;
11use bevy_utils::{HashMap, HashSet};
12use core::fmt::Debug;
13
14type ExtractFn = Box<dyn Fn(&mut World, &mut World) + Send>;
15
16pub struct SubApp {
60 world: World,
62 pub(crate) plugin_registry: Vec<Box<dyn Plugin>>,
64 pub(crate) plugin_names: HashSet<String>,
67 pub(crate) plugin_build_depth: usize,
69 pub(crate) plugins_state: PluginsState,
70 pub update_schedule: Option<InternedScheduleLabel>,
72 extract: Option<ExtractFn>,
75}
76
77impl Debug for SubApp {
78 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
79 write!(f, "SubApp")
80 }
81}
82
83impl Default for SubApp {
84 fn default() -> Self {
85 let mut world = World::new();
86 world.init_resource::<Schedules>();
87 Self {
88 world,
89 plugin_registry: Vec::default(),
90 plugin_names: HashSet::default(),
91 plugin_build_depth: 0,
92 plugins_state: PluginsState::Adding,
93 update_schedule: None,
94 extract: None,
95 }
96 }
97}
98
99impl SubApp {
100 pub fn new() -> Self {
102 Self::default()
103 }
104
105 fn run_as_app<F>(&mut self, f: F)
108 where
109 F: FnOnce(&mut App),
110 {
111 let mut app = App::empty();
112 core::mem::swap(self, &mut app.sub_apps.main);
113 f(&mut app);
114 core::mem::swap(self, &mut app.sub_apps.main);
115 }
116
117 pub fn world(&self) -> &World {
119 &self.world
120 }
121
122 pub fn world_mut(&mut self) -> &mut World {
124 &mut self.world
125 }
126
127 pub fn run_default_schedule(&mut self) {
131 if self.is_building_plugins() {
132 panic!("SubApp::update() was called while a plugin was building.");
133 }
134
135 if let Some(label) = self.update_schedule {
136 self.world.run_schedule(label);
137 }
138 }
139
140 pub fn update(&mut self) {
142 self.run_default_schedule();
143 self.world.clear_trackers();
144 }
145
146 pub fn extract(&mut self, world: &mut World) {
151 if let Some(f) = self.extract.as_mut() {
152 f(world, &mut self.world);
153 }
154 }
155
156 pub fn set_extract<F>(&mut self, extract: F) -> &mut Self
160 where
161 F: Fn(&mut World, &mut World) + Send + 'static,
162 {
163 self.extract = Some(Box::new(extract));
164 self
165 }
166
167 pub fn take_extract(&mut self) -> Option<ExtractFn> {
193 self.extract.take()
194 }
195
196 pub fn insert_resource<R: Resource>(&mut self, resource: R) -> &mut Self {
198 self.world.insert_resource(resource);
199 self
200 }
201
202 pub fn init_resource<R: Resource + FromWorld>(&mut self) -> &mut Self {
204 self.world.init_resource::<R>();
205 self
206 }
207
208 pub fn add_systems<M>(
210 &mut self,
211 schedule: impl ScheduleLabel,
212 systems: impl IntoSystemConfigs<M>,
213 ) -> &mut Self {
214 let mut schedules = self.world.resource_mut::<Schedules>();
215 schedules.add_systems(schedule, systems);
216
217 self
218 }
219
220 pub fn register_system<I, O, M>(
222 &mut self,
223 system: impl IntoSystem<I, O, M> + 'static,
224 ) -> SystemId<I, O>
225 where
226 I: SystemInput + 'static,
227 O: 'static,
228 {
229 self.world.register_system(system)
230 }
231
232 #[track_caller]
234 pub fn configure_sets(
235 &mut self,
236 schedule: impl ScheduleLabel,
237 sets: impl IntoSystemSetConfigs,
238 ) -> &mut Self {
239 let mut schedules = self.world.resource_mut::<Schedules>();
240 schedules.configure_sets(schedule, sets);
241 self
242 }
243
244 pub fn add_schedule(&mut self, schedule: Schedule) -> &mut Self {
246 let mut schedules = self.world.resource_mut::<Schedules>();
247 schedules.insert(schedule);
248 self
249 }
250
251 pub fn init_schedule(&mut self, label: impl ScheduleLabel) -> &mut Self {
253 let label = label.intern();
254 let mut schedules = self.world.resource_mut::<Schedules>();
255 if !schedules.contains(label) {
256 schedules.insert(Schedule::new(label));
257 }
258 self
259 }
260
261 pub fn get_schedule(&self, label: impl ScheduleLabel) -> Option<&Schedule> {
263 let schedules = self.world.get_resource::<Schedules>()?;
264 schedules.get(label)
265 }
266
267 pub fn get_schedule_mut(&mut self, label: impl ScheduleLabel) -> Option<&mut Schedule> {
269 let schedules = self.world.get_resource_mut::<Schedules>()?;
270 schedules.into_inner().get_mut(label)
273 }
274
275 pub fn edit_schedule(
277 &mut self,
278 label: impl ScheduleLabel,
279 mut f: impl FnMut(&mut Schedule),
280 ) -> &mut Self {
281 let label = label.intern();
282 let mut schedules = self.world.resource_mut::<Schedules>();
283 if !schedules.contains(label) {
284 schedules.insert(Schedule::new(label));
285 }
286
287 let schedule = schedules.get_mut(label).unwrap();
288 f(schedule);
289
290 self
291 }
292
293 pub fn configure_schedules(
295 &mut self,
296 schedule_build_settings: ScheduleBuildSettings,
297 ) -> &mut Self {
298 self.world_mut()
299 .resource_mut::<Schedules>()
300 .configure_schedules(schedule_build_settings);
301 self
302 }
303
304 pub fn allow_ambiguous_component<T: Component>(&mut self) -> &mut Self {
306 self.world_mut().allow_ambiguous_component::<T>();
307 self
308 }
309
310 pub fn allow_ambiguous_resource<T: Resource>(&mut self) -> &mut Self {
312 self.world_mut().allow_ambiguous_resource::<T>();
313 self
314 }
315
316 #[track_caller]
318 pub fn ignore_ambiguity<M1, M2, S1, S2>(
319 &mut self,
320 schedule: impl ScheduleLabel,
321 a: S1,
322 b: S2,
323 ) -> &mut Self
324 where
325 S1: IntoSystemSet<M1>,
326 S2: IntoSystemSet<M2>,
327 {
328 let schedule = schedule.intern();
329 let mut schedules = self.world.resource_mut::<Schedules>();
330
331 schedules.ignore_ambiguity(schedule, a, b);
332
333 self
334 }
335
336 pub fn add_event<T>(&mut self) -> &mut Self
338 where
339 T: Event,
340 {
341 if !self.world.contains_resource::<Events<T>>() {
342 EventRegistry::register_event::<T>(self.world_mut());
343 }
344
345 self
346 }
347
348 pub fn add_plugins<M>(&mut self, plugins: impl Plugins<M>) -> &mut Self {
350 self.run_as_app(|app| plugins.add_to_app(app));
351 self
352 }
353
354 pub fn is_plugin_added<T>(&self) -> bool
356 where
357 T: Plugin,
358 {
359 self.plugin_names.contains(core::any::type_name::<T>())
360 }
361
362 pub fn get_added_plugins<T>(&self) -> Vec<&T>
364 where
365 T: Plugin,
366 {
367 self.plugin_registry
368 .iter()
369 .filter_map(|p| p.downcast_ref())
370 .collect()
371 }
372
373 pub(crate) fn is_building_plugins(&self) -> bool {
375 self.plugin_build_depth > 0
376 }
377
378 #[inline]
380 pub fn plugins_state(&mut self) -> PluginsState {
381 match self.plugins_state {
382 PluginsState::Adding => {
383 let mut state = PluginsState::Ready;
384 let plugins = core::mem::take(&mut self.plugin_registry);
385 self.run_as_app(|app| {
386 for plugin in &plugins {
387 if !plugin.ready(app) {
388 state = PluginsState::Adding;
389 return;
390 }
391 }
392 });
393 self.plugin_registry = plugins;
394 state
395 }
396 state => state,
397 }
398 }
399
400 pub fn finish(&mut self) {
402 let plugins = core::mem::take(&mut self.plugin_registry);
403 self.run_as_app(|app| {
404 for plugin in &plugins {
405 plugin.finish(app);
406 }
407 });
408 self.plugin_registry = plugins;
409 self.plugins_state = PluginsState::Finished;
410 }
411
412 pub fn cleanup(&mut self) {
414 let plugins = core::mem::take(&mut self.plugin_registry);
415 self.run_as_app(|app| {
416 for plugin in &plugins {
417 plugin.cleanup(app);
418 }
419 });
420 self.plugin_registry = plugins;
421 self.plugins_state = PluginsState::Cleaned;
422 }
423
424 #[cfg(feature = "bevy_reflect")]
426 pub fn register_type<T: bevy_reflect::GetTypeRegistration>(&mut self) -> &mut Self {
427 let registry = self.world.resource_mut::<AppTypeRegistry>();
428 registry.write().register::<T>();
429 self
430 }
431
432 #[cfg(feature = "bevy_reflect")]
434 pub fn register_type_data<
435 T: bevy_reflect::Reflect + bevy_reflect::TypePath,
436 D: bevy_reflect::TypeData + bevy_reflect::FromType<T>,
437 >(
438 &mut self,
439 ) -> &mut Self {
440 let registry = self.world.resource_mut::<AppTypeRegistry>();
441 registry.write().register_type_data::<T, D>();
442 self
443 }
444
445 #[cfg(feature = "reflect_functions")]
447 pub fn register_function<F, Marker>(&mut self, function: F) -> &mut Self
448 where
449 F: bevy_reflect::func::IntoFunction<'static, Marker> + 'static,
450 {
451 let registry = self.world.resource_mut::<AppFunctionRegistry>();
452 registry.write().register(function).unwrap();
453 self
454 }
455
456 #[cfg(feature = "reflect_functions")]
458 pub fn register_function_with_name<F, Marker>(
459 &mut self,
460 name: impl Into<alloc::borrow::Cow<'static, str>>,
461 function: F,
462 ) -> &mut Self
463 where
464 F: bevy_reflect::func::IntoFunction<'static, Marker> + 'static,
465 {
466 let registry = self.world.resource_mut::<AppFunctionRegistry>();
467 registry.write().register_with_name(name, function).unwrap();
468 self
469 }
470}
471
472#[derive(Default)]
474pub struct SubApps {
475 pub main: SubApp,
477 pub sub_apps: HashMap<InternedAppLabel, SubApp>,
479}
480
481impl SubApps {
482 pub fn update(&mut self) {
485 #[cfg(feature = "trace")]
486 let _bevy_update_span = info_span!("update").entered();
487 {
488 #[cfg(feature = "trace")]
489 let _bevy_frame_update_span = info_span!("main app").entered();
490 self.main.run_default_schedule();
491 }
492 for (_label, sub_app) in self.sub_apps.iter_mut() {
493 #[cfg(feature = "trace")]
494 let _sub_app_span = info_span!("sub app", name = ?_label).entered();
495 sub_app.extract(&mut self.main.world);
496 sub_app.update();
497 }
498
499 self.main.world.clear_trackers();
500 }
501
502 pub fn iter(&self) -> impl Iterator<Item = &SubApp> + '_ {
504 core::iter::once(&self.main).chain(self.sub_apps.values())
505 }
506
507 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut SubApp> + '_ {
509 core::iter::once(&mut self.main).chain(self.sub_apps.values_mut())
510 }
511
512 pub fn update_subapp_by_label(&mut self, label: impl AppLabel) {
514 if let Some(sub_app) = self.sub_apps.get_mut(&label.intern()) {
515 sub_app.extract(&mut self.main.world);
516 sub_app.update();
517 }
518 }
519}