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::{
7 InternedScheduleLabel, InternedSystemSet, ScheduleBuildSettings, ScheduleCleanupPolicy,
8 ScheduleError, ScheduleLabel,
9 },
10 system::{ScheduleSystem, SystemId, SystemInput},
11};
12use bevy_platform::collections::{HashMap, HashSet};
13use core::fmt::Debug;
14
15#[cfg(feature = "trace")]
16use tracing::info_span;
17
18type ExtractFn = Box<dyn FnMut(&mut World, &mut World) + Send>;
19
20pub struct SubApp {
65 world: World,
67 pub(crate) plugin_registry: Vec<Box<dyn Plugin>>,
69 pub(crate) plugin_names: HashSet<String>,
72 pub(crate) plugin_build_depth: usize,
74 pub(crate) plugins_state: PluginsState,
75 pub update_schedule: Option<InternedScheduleLabel>,
77 extract: Option<ExtractFn>,
80}
81
82impl Debug for SubApp {
83 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
84 write!(f, "SubApp")
85 }
86}
87
88impl 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
104impl SubApp {
105 pub fn new() -> Self {
107 Self::default()
108 }
109
110 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 pub fn world(&self) -> &World {
124 &self.world
125 }
126
127 pub fn world_mut(&mut self) -> &mut World {
129 &mut self.world
130 }
131
132 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 pub fn update(&mut self) {
147 self.run_default_schedule();
148 self.world.clear_trackers();
149 }
150
151 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 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 pub fn take_extract(&mut self) -> Option<ExtractFn> {
198 self.extract.take()
199 }
200
201 pub fn insert_resource<R: Resource>(&mut self, resource: R) -> &mut Self {
203 self.world.insert_resource(resource);
204 self
205 }
206
207 pub fn init_resource<R: Resource + FromWorld>(&mut self) -> &mut Self {
209 self.world.init_resource::<R>();
210 self
211 }
212
213 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 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 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 #[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 pub fn add_schedule(&mut self, schedule: Schedule) -> &mut Self {
263 let mut schedules = self.world.resource_mut::<Schedules>();
264 schedules.insert(schedule);
265 self
266 }
267
268 pub fn init_schedule(&mut self, label: impl ScheduleLabel) -> &mut Self {
270 let label = label.intern();
271 let mut schedules = self.world.resource_mut::<Schedules>();
272 if !schedules.contains(label) {
273 schedules.insert(Schedule::new(label));
274 }
275 self
276 }
277
278 pub fn get_schedule(&self, label: impl ScheduleLabel) -> Option<&Schedule> {
280 let schedules = self.world.get_resource::<Schedules>()?;
281 schedules.get(label)
282 }
283
284 pub fn get_schedule_mut(&mut self, label: impl ScheduleLabel) -> Option<&mut Schedule> {
286 let schedules = self.world.get_resource_mut::<Schedules>()?;
287 schedules.into_inner().get_mut(label)
290 }
291
292 pub fn edit_schedule(
294 &mut self,
295 label: impl ScheduleLabel,
296 mut f: impl FnMut(&mut Schedule),
297 ) -> &mut Self {
298 let label = label.intern();
299 let mut schedules = self.world.resource_mut::<Schedules>();
300 if !schedules.contains(label) {
301 schedules.insert(Schedule::new(label));
302 }
303
304 let schedule = schedules.get_mut(label).unwrap();
305 f(schedule);
306
307 self
308 }
309
310 pub fn configure_schedules(
312 &mut self,
313 schedule_build_settings: ScheduleBuildSettings,
314 ) -> &mut Self {
315 self.world_mut()
316 .resource_mut::<Schedules>()
317 .configure_schedules(schedule_build_settings);
318 self
319 }
320
321 pub fn allow_ambiguous_component<T: Component>(&mut self) -> &mut Self {
323 self.world_mut().allow_ambiguous_component::<T>();
324 self
325 }
326
327 pub fn allow_ambiguous_resource<T: Resource>(&mut self) -> &mut Self {
329 self.world_mut().allow_ambiguous_resource::<T>();
330 self
331 }
332
333 #[track_caller]
335 pub fn ignore_ambiguity<M1, M2, S1, S2>(
336 &mut self,
337 schedule: impl ScheduleLabel,
338 a: S1,
339 b: S2,
340 ) -> &mut Self
341 where
342 S1: IntoSystemSet<M1>,
343 S2: IntoSystemSet<M2>,
344 {
345 let schedule = schedule.intern();
346 let mut schedules = self.world.resource_mut::<Schedules>();
347
348 schedules.ignore_ambiguity(schedule, a, b);
349
350 self
351 }
352
353 pub fn add_message<T>(&mut self) -> &mut Self
355 where
356 T: Message,
357 {
358 if !self.world.contains_resource::<Messages<T>>() {
359 MessageRegistry::register_message::<T>(self.world_mut());
360 }
361
362 self
363 }
364
365 pub fn add_plugins<M>(&mut self, plugins: impl Plugins<M>) -> &mut Self {
367 self.run_as_app(|app| plugins.add_to_app(app));
368 self
369 }
370
371 pub fn is_plugin_added<T>(&self) -> bool
373 where
374 T: Plugin,
375 {
376 self.plugin_names.contains(core::any::type_name::<T>())
377 }
378
379 pub fn get_added_plugins<T>(&self) -> Vec<&T>
381 where
382 T: Plugin,
383 {
384 self.plugin_registry
385 .iter()
386 .filter_map(|p| p.downcast_ref())
387 .collect()
388 }
389
390 pub(crate) fn is_building_plugins(&self) -> bool {
392 self.plugin_build_depth > 0
393 }
394
395 #[inline]
397 pub fn plugins_state(&mut self) -> PluginsState {
398 match self.plugins_state {
399 PluginsState::Adding => {
400 let mut state = PluginsState::Ready;
401 let plugins = core::mem::take(&mut self.plugin_registry);
402 self.run_as_app(|app| {
403 for plugin in &plugins {
404 if !plugin.ready(app) {
405 state = PluginsState::Adding;
406 return;
407 }
408 }
409 });
410 self.plugin_registry = plugins;
411 state
412 }
413 state => state,
414 }
415 }
416
417 pub fn finish(&mut self) {
419 let mut hokeypokey: Box<dyn Plugin> = Box::new(crate::HokeyPokey);
421 for i in 0..self.plugin_registry.len() {
422 core::mem::swap(&mut self.plugin_registry[i], &mut hokeypokey);
423 #[cfg(feature = "trace")]
424 let _plugin_finish_span =
425 info_span!("plugin finish", plugin = hokeypokey.name()).entered();
426 self.run_as_app(|app| {
427 hokeypokey.finish(app);
428 });
429 core::mem::swap(&mut self.plugin_registry[i], &mut hokeypokey);
430 }
431 self.plugins_state = PluginsState::Finished;
432 }
433
434 pub fn cleanup(&mut self) {
436 let mut hokeypokey: Box<dyn Plugin> = Box::new(crate::HokeyPokey);
438 for i in 0..self.plugin_registry.len() {
439 core::mem::swap(&mut self.plugin_registry[i], &mut hokeypokey);
440 #[cfg(feature = "trace")]
441 let _plugin_cleanup_span =
442 info_span!("plugin cleanup", plugin = hokeypokey.name()).entered();
443 self.run_as_app(|app| {
444 hokeypokey.cleanup(app);
445 });
446 core::mem::swap(&mut self.plugin_registry[i], &mut hokeypokey);
447 }
448 self.plugins_state = PluginsState::Cleaned;
449 }
450
451 #[cfg(feature = "bevy_reflect")]
453 pub fn register_type<T: bevy_reflect::GetTypeRegistration>(&mut self) -> &mut Self {
454 let registry = self.world.resource_mut::<AppTypeRegistry>();
455 registry.write().register::<T>();
456 self
457 }
458
459 #[cfg(feature = "bevy_reflect")]
461 pub fn register_type_data<
462 T: bevy_reflect::Reflect + bevy_reflect::TypePath,
463 D: bevy_reflect::TypeData + bevy_reflect::FromType<T>,
464 >(
465 &mut self,
466 ) -> &mut Self {
467 let registry = self.world.resource_mut::<AppTypeRegistry>();
468 registry.write().register_type_data::<T, D>();
469 self
470 }
471
472 #[cfg(feature = "reflect_functions")]
474 pub fn register_function<F, Marker>(&mut self, function: F) -> &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(function).unwrap();
480 self
481 }
482
483 #[cfg(feature = "reflect_functions")]
485 pub fn register_function_with_name<F, Marker>(
486 &mut self,
487 name: impl Into<alloc::borrow::Cow<'static, str>>,
488 function: F,
489 ) -> &mut Self
490 where
491 F: bevy_reflect::func::IntoFunction<'static, Marker> + 'static,
492 {
493 let registry = self.world.resource_mut::<AppFunctionRegistry>();
494 registry.write().register_with_name(name, function).unwrap();
495 self
496 }
497}
498
499#[derive(Default)]
501pub struct SubApps {
502 pub main: SubApp,
504 pub sub_apps: HashMap<InternedAppLabel, SubApp>,
506}
507
508impl SubApps {
509 pub fn update(&mut self) {
512 #[cfg(feature = "trace")]
513 let _bevy_update_span = info_span!("update").entered();
514 {
515 #[cfg(feature = "trace")]
516 let _bevy_frame_update_span = info_span!("main app").entered();
517 self.main.run_default_schedule();
518 }
519 for (_label, sub_app) in self.sub_apps.iter_mut() {
520 #[cfg(feature = "trace")]
521 let _sub_app_span = info_span!("sub app", name = ?_label).entered();
522 sub_app.extract(&mut self.main.world);
523 sub_app.update();
524 }
525
526 self.main.world.clear_trackers();
527 }
528
529 pub fn iter(&self) -> impl Iterator<Item = &SubApp> + '_ {
531 core::iter::once(&self.main).chain(self.sub_apps.values())
532 }
533
534 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut SubApp> + '_ {
536 core::iter::once(&mut self.main).chain(self.sub_apps.values_mut())
537 }
538
539 pub fn update_subapp_by_label(&mut self, label: impl AppLabel) {
541 if let Some(sub_app) = self.sub_apps.get_mut(&label.intern()) {
542 sub_app.extract(&mut self.main.world);
543 sub_app.update();
544 }
545 }
546}