#![warn(missing_docs)]
#![cfg_attr(all(doc, CHANNEL_NIGHTLY), feature(doc_cfg))]
#[cfg(feature = "bevy_app")]
use bevy_app::prelude::*;
#[cfg(feature = "debug")]
use bevy_ecs::component::ComponentId;
use bevy_ecs::prelude::*;
#[cfg(feature = "bevy_app")]
use bevy_ecs::schedule::{InternedScheduleLabel, ScheduleLabel};
mod time_runner;
mod time_span;
#[cfg(feature = "debug")]
use std::any::TypeId;
#[cfg(feature = "bevy_app")]
use std::marker::PhantomData;
pub use time_runner::*;
pub use time_span::*;
#[cfg(feature = "bevy_app")]
pub struct TimeRunnerPlugin<TimeCtx = ()>
where
TimeCtx: Default + Send + Sync + 'static,
{
pub schedule: InternedScheduleLabel,
marker: PhantomData<TimeCtx>,
#[cfg(feature = "debug")]
pub enable_debug: bool,
}
#[cfg(feature = "bevy_app")]
impl<TimeCtx> TimeRunnerPlugin<TimeCtx>
where
TimeCtx: Default + Send + Sync + 'static,
{
pub fn in_schedule(schedule: InternedScheduleLabel) -> Self {
Self {
schedule,
..Default::default()
}
}
}
#[cfg(feature = "bevy_app")]
impl<TimeCtx> Default for TimeRunnerPlugin<TimeCtx>
where
TimeCtx: Default + Send + Sync + 'static,
{
fn default() -> Self {
TimeRunnerPlugin {
schedule: PostUpdate.intern(),
#[cfg(feature = "debug")]
enable_debug: true,
marker: PhantomData::default(),
}
}
}
#[cfg(feature = "bevy_app")]
impl<TimeCtx> Plugin for TimeRunnerPlugin<TimeCtx>
where
TimeCtx: Default + Send + Sync + 'static,
{
fn build(&self, app: &mut App) {
app.add_message::<TimeRunnerEnded>();
#[cfg(feature = "bevy_app")]
app.configure_sets(
self.schedule,
(
TimeRunnerSet::Tagging,
TimeRunnerSet::TickTimer,
TimeRunnerSet::Progress,
)
.chain(),
)
.add_systems(
self.schedule,
(
tag_time_runner_children_with_context::<TimeCtx>.in_set(TimeRunnerSet::Tagging),
tick_time_runner_system::<TimeCtx>.in_set(TimeRunnerSet::TickTimer),
time_runner_system::<TimeCtx>.in_set(TimeRunnerSet::Progress),
),
);
#[cfg(feature = "bevy_reflect")]
app.register_type::<TimeRunner>()
.register_type::<SkipTimeRunner>()
.register_type::<TimeRunnerElasped>()
.register_type::<TimeRunnerEnded>()
.register_type::<TimeSpan>()
.register_type::<TimeSpanProgress>()
.register_type::<Repeat>()
.register_type::<RepeatStyle>()
.register_type::<TimeBound>()
.register_type::<TimeDirection>();
#[cfg(feature = "debug")]
if self.enable_debug && !app.is_plugin_added::<TimeRunnerDebugPlugin>() {
app.add_plugins(TimeRunnerDebugPlugin::default());
}
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, SystemSet)]
pub enum TimeRunnerSet {
Tagging,
TickTimer,
Progress,
}
#[cfg(feature = "debug")]
#[derive(Debug, Default, Clone, Resource)]
pub struct TimeRunnerDebugInfo {
time_steps: Vec<ComponentId>,
}
#[cfg(feature = "debug")]
pub struct TimeRunnerDebugPlugin {
time_step_markers: Vec<(TypeId, &'static str)>,
}
#[cfg(feature = "debug")]
impl Default for TimeRunnerDebugPlugin {
fn default() -> Self {
let mut a = TimeRunnerDebugPlugin {
time_step_markers: Vec::new(),
};
a.add_time_step::<()>();
a.add_time_step::<bevy_time::Fixed>();
a.add_time_step::<bevy_time::Real>();
a.add_time_step::<bevy_time::Virtual>();
a
}
}
#[cfg(feature = "debug")]
impl TimeRunnerDebugPlugin {
pub fn add_time_step<TimeCtx>(&mut self)
where
TimeCtx: Default + Send + Sync + 'static,
{
self.time_step_markers.push((
TypeId::of::<TimeContext<TimeCtx>>(),
std::any::type_name::<TimeContext<TimeCtx>>(),
));
}
}
#[cfg(all(feature = "debug", feature = "bevy_app"))]
impl Plugin for TimeRunnerDebugPlugin {
fn build(&self, app: &mut App) {
let mut info = TimeRunnerDebugInfo::default();
let world_mut = app.world_mut();
world_mut.register_component::<TimeContext<()>>();
world_mut.register_component::<TimeContext<bevy_time::Fixed>>();
world_mut.register_component::<TimeContext<bevy_time::Real>>();
world_mut.register_component::<TimeContext<bevy_time::Virtual>>();
for (type_id, type_name) in &self.time_step_markers {
let Some(component_id) = app.world().components().get_id(*type_id) else {
panic!(
"{type_name} have not been registered as a componenet yet. It is required for `TimeRunnerDebugPlugin`."
)
};
info.time_steps.push(component_id);
}
app.world_mut().insert_resource(info);
}
}