use crate::{App, AppLabel, InternedAppLabel, Plugin, Plugins, PluginsState};
use alloc::{boxed::Box, string::String, vec::Vec};
use bevy_ecs::{
event::EventRegistry,
prelude::*,
schedule::{InternedScheduleLabel, InternedSystemSet, ScheduleBuildSettings, ScheduleLabel},
system::{ScheduleSystem, SystemId, SystemInput},
};
use bevy_platform::collections::{HashMap, HashSet};
use core::fmt::Debug;
#[cfg(feature = "trace")]
use tracing::info_span;
type ExtractFn = Box<dyn Fn(&mut World, &mut World) + Send>;
pub struct SubApp {
world: World,
pub(crate) plugin_registry: Vec<Box<dyn Plugin>>,
pub(crate) plugin_names: HashSet<String>,
pub(crate) plugin_build_depth: usize,
pub(crate) plugins_state: PluginsState,
pub update_schedule: Option<InternedScheduleLabel>,
extract: Option<ExtractFn>,
}
impl Debug for SubApp {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "SubApp")
}
}
impl Default for SubApp {
fn default() -> Self {
let mut world = World::new();
world.init_resource::<Schedules>();
Self {
world,
plugin_registry: Vec::default(),
plugin_names: HashSet::default(),
plugin_build_depth: 0,
plugins_state: PluginsState::Adding,
update_schedule: None,
extract: None,
}
}
}
impl SubApp {
pub fn new() -> Self {
Self::default()
}
fn run_as_app<F>(&mut self, f: F)
where
F: FnOnce(&mut App),
{
let mut app = App::empty();
core::mem::swap(self, &mut app.sub_apps.main);
f(&mut app);
core::mem::swap(self, &mut app.sub_apps.main);
}
pub fn world(&self) -> &World {
&self.world
}
pub fn world_mut(&mut self) -> &mut World {
&mut self.world
}
pub fn run_default_schedule(&mut self) {
if self.is_building_plugins() {
panic!("SubApp::update() was called while a plugin was building.");
}
if let Some(label) = self.update_schedule {
self.world.run_schedule(label);
}
}
pub fn update(&mut self) {
self.run_default_schedule();
self.world.clear_trackers();
}
pub fn extract(&mut self, world: &mut World) {
if let Some(f) = self.extract.as_mut() {
f(world, &mut self.world);
}
}
pub fn set_extract<F>(&mut self, extract: F) -> &mut Self
where
F: Fn(&mut World, &mut World) + Send + 'static,
{
self.extract = Some(Box::new(extract));
self
}
pub fn take_extract(&mut self) -> Option<ExtractFn> {
self.extract.take()
}
pub fn insert_resource<R: Resource>(&mut self, resource: R) -> &mut Self {
self.world.insert_resource(resource);
self
}
pub fn init_resource<R: Resource + FromWorld>(&mut self) -> &mut Self {
self.world.init_resource::<R>();
self
}
pub fn add_systems<M>(
&mut self,
schedule: impl ScheduleLabel,
systems: impl IntoScheduleConfigs<ScheduleSystem, M>,
) -> &mut Self {
let mut schedules = self.world.resource_mut::<Schedules>();
schedules.add_systems(schedule, systems);
self
}
pub fn register_system<I, O, M>(
&mut self,
system: impl IntoSystem<I, O, M> + 'static,
) -> SystemId<I, O>
where
I: SystemInput + 'static,
O: 'static,
{
self.world.register_system(system)
}
#[track_caller]
pub fn configure_sets<M>(
&mut self,
schedule: impl ScheduleLabel,
sets: impl IntoScheduleConfigs<InternedSystemSet, M>,
) -> &mut Self {
let mut schedules = self.world.resource_mut::<Schedules>();
schedules.configure_sets(schedule, sets);
self
}
pub fn add_schedule(&mut self, schedule: Schedule) -> &mut Self {
let mut schedules = self.world.resource_mut::<Schedules>();
schedules.insert(schedule);
self
}
pub fn init_schedule(&mut self, label: impl ScheduleLabel) -> &mut Self {
let label = label.intern();
let mut schedules = self.world.resource_mut::<Schedules>();
if !schedules.contains(label) {
schedules.insert(Schedule::new(label));
}
self
}
pub fn get_schedule(&self, label: impl ScheduleLabel) -> Option<&Schedule> {
let schedules = self.world.get_resource::<Schedules>()?;
schedules.get(label)
}
pub fn get_schedule_mut(&mut self, label: impl ScheduleLabel) -> Option<&mut Schedule> {
let schedules = self.world.get_resource_mut::<Schedules>()?;
schedules.into_inner().get_mut(label)
}
pub fn edit_schedule(
&mut self,
label: impl ScheduleLabel,
mut f: impl FnMut(&mut Schedule),
) -> &mut Self {
let label = label.intern();
let mut schedules = self.world.resource_mut::<Schedules>();
if !schedules.contains(label) {
schedules.insert(Schedule::new(label));
}
let schedule = schedules.get_mut(label).unwrap();
f(schedule);
self
}
pub fn configure_schedules(
&mut self,
schedule_build_settings: ScheduleBuildSettings,
) -> &mut Self {
self.world_mut()
.resource_mut::<Schedules>()
.configure_schedules(schedule_build_settings);
self
}
pub fn allow_ambiguous_component<T: Component>(&mut self) -> &mut Self {
self.world_mut().allow_ambiguous_component::<T>();
self
}
pub fn allow_ambiguous_resource<T: Resource>(&mut self) -> &mut Self {
self.world_mut().allow_ambiguous_resource::<T>();
self
}
#[track_caller]
pub fn ignore_ambiguity<M1, M2, S1, S2>(
&mut self,
schedule: impl ScheduleLabel,
a: S1,
b: S2,
) -> &mut Self
where
S1: IntoSystemSet<M1>,
S2: IntoSystemSet<M2>,
{
let schedule = schedule.intern();
let mut schedules = self.world.resource_mut::<Schedules>();
schedules.ignore_ambiguity(schedule, a, b);
self
}
pub fn add_event<T>(&mut self) -> &mut Self
where
T: Event,
{
if !self.world.contains_resource::<Events<T>>() {
EventRegistry::register_event::<T>(self.world_mut());
}
self
}
pub fn add_plugins<M>(&mut self, plugins: impl Plugins<M>) -> &mut Self {
self.run_as_app(|app| plugins.add_to_app(app));
self
}
pub fn is_plugin_added<T>(&self) -> bool
where
T: Plugin,
{
self.plugin_names.contains(core::any::type_name::<T>())
}
pub fn get_added_plugins<T>(&self) -> Vec<&T>
where
T: Plugin,
{
self.plugin_registry
.iter()
.filter_map(|p| p.downcast_ref())
.collect()
}
pub(crate) fn is_building_plugins(&self) -> bool {
self.plugin_build_depth > 0
}
#[inline]
pub fn plugins_state(&mut self) -> PluginsState {
match self.plugins_state {
PluginsState::Adding => {
let mut state = PluginsState::Ready;
let plugins = core::mem::take(&mut self.plugin_registry);
self.run_as_app(|app| {
for plugin in &plugins {
if !plugin.ready(app) {
state = PluginsState::Adding;
return;
}
}
});
self.plugin_registry = plugins;
state
}
state => state,
}
}
pub fn finish(&mut self) {
let plugins = core::mem::take(&mut self.plugin_registry);
self.run_as_app(|app| {
for plugin in &plugins {
plugin.finish(app);
}
});
self.plugin_registry = plugins;
self.plugins_state = PluginsState::Finished;
}
pub fn cleanup(&mut self) {
let plugins = core::mem::take(&mut self.plugin_registry);
self.run_as_app(|app| {
for plugin in &plugins {
plugin.cleanup(app);
}
});
self.plugin_registry = plugins;
self.plugins_state = PluginsState::Cleaned;
}
#[cfg(feature = "bevy_reflect")]
pub fn register_type<T: bevy_reflect::GetTypeRegistration>(&mut self) -> &mut Self {
let registry = self.world.resource_mut::<AppTypeRegistry>();
registry.write().register::<T>();
self
}
#[cfg(feature = "bevy_reflect")]
pub fn register_type_data<
T: bevy_reflect::Reflect + bevy_reflect::TypePath,
D: bevy_reflect::TypeData + bevy_reflect::FromType<T>,
>(
&mut self,
) -> &mut Self {
let registry = self.world.resource_mut::<AppTypeRegistry>();
registry.write().register_type_data::<T, D>();
self
}
#[cfg(feature = "reflect_functions")]
pub fn register_function<F, Marker>(&mut self, function: F) -> &mut Self
where
F: bevy_reflect::func::IntoFunction<'static, Marker> + 'static,
{
let registry = self.world.resource_mut::<AppFunctionRegistry>();
registry.write().register(function).unwrap();
self
}
#[cfg(feature = "reflect_functions")]
pub fn register_function_with_name<F, Marker>(
&mut self,
name: impl Into<alloc::borrow::Cow<'static, str>>,
function: F,
) -> &mut Self
where
F: bevy_reflect::func::IntoFunction<'static, Marker> + 'static,
{
let registry = self.world.resource_mut::<AppFunctionRegistry>();
registry.write().register_with_name(name, function).unwrap();
self
}
}
#[derive(Default)]
pub struct SubApps {
pub main: SubApp,
pub sub_apps: HashMap<InternedAppLabel, SubApp>,
}
impl SubApps {
pub fn update(&mut self) {
#[cfg(feature = "trace")]
let _bevy_update_span = info_span!("update").entered();
{
#[cfg(feature = "trace")]
let _bevy_frame_update_span = info_span!("main app").entered();
self.main.run_default_schedule();
}
for (_label, sub_app) in self.sub_apps.iter_mut() {
#[cfg(feature = "trace")]
let _sub_app_span = info_span!("sub app", name = ?_label).entered();
sub_app.extract(&mut self.main.world);
sub_app.update();
}
self.main.world.clear_trackers();
}
pub fn iter(&self) -> impl Iterator<Item = &SubApp> + '_ {
core::iter::once(&self.main).chain(self.sub_apps.values())
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut SubApp> + '_ {
core::iter::once(&mut self.main).chain(self.sub_apps.values_mut())
}
pub fn update_subapp_by_label(&mut self, label: impl AppLabel) {
if let Some(sub_app) = self.sub_apps.get_mut(&label.intern()) {
sub_app.extract(&mut self.main.world);
sub_app.update();
}
}
}