bevy_time/
lib.rs

1#![doc = include_str!("../README.md")]
2#![cfg_attr(docsrs, feature(doc_auto_cfg))]
3#![forbid(unsafe_code)]
4#![doc(
5    html_logo_url = "https://bevyengine.org/assets/icon.png",
6    html_favicon_url = "https://bevyengine.org/assets/icon.png"
7)]
8
9/// Common run conditions
10pub mod common_conditions;
11mod fixed;
12mod real;
13mod stopwatch;
14#[allow(clippy::module_inception)]
15mod time;
16mod timer;
17mod virt;
18
19pub use fixed::*;
20pub use real::*;
21pub use stopwatch::*;
22pub use time::*;
23pub use timer::*;
24pub use virt::*;
25
26pub mod prelude {
27    //! The Bevy Time Prelude.
28    #[doc(hidden)]
29    pub use crate::{Fixed, Real, Time, Timer, TimerMode, Virtual};
30}
31
32use bevy_app::{prelude::*, RunFixedMainLoop};
33use bevy_ecs::event::{signal_event_update_system, EventRegistry, ShouldUpdateEvents};
34use bevy_ecs::prelude::*;
35use bevy_utils::{tracing::warn, Duration, Instant};
36pub use crossbeam_channel::TrySendError;
37use crossbeam_channel::{Receiver, Sender};
38
39/// Adds time functionality to Apps.
40#[derive(Default)]
41pub struct TimePlugin;
42
43#[derive(Debug, PartialEq, Eq, Clone, Hash, SystemSet)]
44/// Updates the elapsed time. Any system that interacts with [`Time`] component should run after
45/// this.
46pub struct TimeSystem;
47
48impl Plugin for TimePlugin {
49    fn build(&self, app: &mut App) {
50        app.init_resource::<Time>()
51            .init_resource::<Time<Real>>()
52            .init_resource::<Time<Virtual>>()
53            .init_resource::<Time<Fixed>>()
54            .init_resource::<TimeUpdateStrategy>();
55
56        #[cfg(feature = "bevy_reflect")]
57        {
58            app.register_type::<Time>()
59                .register_type::<Time<Real>>()
60                .register_type::<Time<Virtual>>()
61                .register_type::<Time<Fixed>>()
62                .register_type::<Timer>();
63        }
64
65        app.add_systems(First, time_system.in_set(TimeSystem))
66            .add_systems(RunFixedMainLoop, run_fixed_main_schedule);
67
68        // Ensure the events are not dropped until `FixedMain` systems can observe them
69        app.add_systems(FixedPostUpdate, signal_event_update_system);
70        let mut event_registry = app.world_mut().resource_mut::<EventRegistry>();
71        // We need to start in a waiting state so that the events are not updated until the first fixed update
72        event_registry.should_update = ShouldUpdateEvents::Waiting;
73    }
74}
75
76/// Configuration resource used to determine how the time system should run.
77///
78/// For most cases, [`TimeUpdateStrategy::Automatic`] is fine. When writing tests, dealing with
79/// networking or similar, you may prefer to set the next [`Time`] value manually.
80#[derive(Resource, Default)]
81pub enum TimeUpdateStrategy {
82    /// [`Time`] will be automatically updated each frame using an [`Instant`] sent from the render world via a [`TimeSender`].
83    /// If nothing is sent, the system clock will be used instead.
84    #[default]
85    Automatic,
86    /// [`Time`] will be updated to the specified [`Instant`] value each frame.
87    /// In order for time to progress, this value must be manually updated each frame.
88    ///
89    /// Note that the `Time` resource will not be updated until [`TimeSystem`] runs.
90    ManualInstant(Instant),
91    /// [`Time`] will be incremented by the specified [`Duration`] each frame.
92    ManualDuration(Duration),
93}
94
95/// Channel resource used to receive time from the render world.
96#[derive(Resource)]
97pub struct TimeReceiver(pub Receiver<Instant>);
98
99/// Channel resource used to send time from the render world.
100#[derive(Resource)]
101pub struct TimeSender(pub Sender<Instant>);
102
103/// Creates channels used for sending time between the render world and the main world.
104pub fn create_time_channels() -> (TimeSender, TimeReceiver) {
105    // bound the channel to 2 since when pipelined the render phase can finish before
106    // the time system runs.
107    let (s, r) = crossbeam_channel::bounded::<Instant>(2);
108    (TimeSender(s), TimeReceiver(r))
109}
110
111/// The system used to update the [`Time`] used by app logic. If there is a render world the time is
112/// sent from there to this system through channels. Otherwise the time is updated in this system.
113pub fn time_system(
114    mut real_time: ResMut<Time<Real>>,
115    mut virtual_time: ResMut<Time<Virtual>>,
116    mut time: ResMut<Time>,
117    update_strategy: Res<TimeUpdateStrategy>,
118    time_recv: Option<Res<TimeReceiver>>,
119    mut has_received_time: Local<bool>,
120) {
121    let new_time = if let Some(time_recv) = time_recv {
122        // TODO: Figure out how to handle this when using pipelined rendering.
123        if let Ok(new_time) = time_recv.0.try_recv() {
124            *has_received_time = true;
125            new_time
126        } else {
127            if *has_received_time {
128                warn!("time_system did not receive the time from the render world! Calculations depending on the time may be incorrect.");
129            }
130            Instant::now()
131        }
132    } else {
133        Instant::now()
134    };
135
136    match update_strategy.as_ref() {
137        TimeUpdateStrategy::Automatic => real_time.update_with_instant(new_time),
138        TimeUpdateStrategy::ManualInstant(instant) => real_time.update_with_instant(*instant),
139        TimeUpdateStrategy::ManualDuration(duration) => real_time.update_with_duration(*duration),
140    }
141
142    update_virtual_time(&mut time, &mut virtual_time, &real_time);
143}
144
145#[cfg(test)]
146mod tests {
147    use crate::{Fixed, Time, TimePlugin, TimeUpdateStrategy, Virtual};
148    use bevy_app::{App, FixedUpdate, Startup, Update};
149    use bevy_ecs::{
150        event::{Event, EventReader, EventRegistry, EventWriter, Events, ShouldUpdateEvents},
151        system::{Local, Res, ResMut, Resource},
152    };
153    use bevy_utils::Duration;
154    use std::error::Error;
155
156    #[derive(Event)]
157    struct TestEvent<T: Default> {
158        sender: std::sync::mpsc::Sender<T>,
159    }
160
161    impl<T: Default> Drop for TestEvent<T> {
162        fn drop(&mut self) {
163            self.sender
164                .send(T::default())
165                .expect("Failed to send drop signal");
166        }
167    }
168
169    #[derive(Event)]
170    struct DummyEvent;
171
172    #[derive(Resource, Default)]
173    struct FixedUpdateCounter(u8);
174
175    fn count_fixed_updates(mut counter: ResMut<FixedUpdateCounter>) {
176        counter.0 += 1;
177    }
178
179    fn report_time(
180        mut frame_count: Local<u64>,
181        virtual_time: Res<Time<Virtual>>,
182        fixed_time: Res<Time<Fixed>>,
183    ) {
184        println!(
185            "Virtual time on frame {}: {:?}",
186            *frame_count,
187            virtual_time.elapsed()
188        );
189        println!(
190            "Fixed time on frame {}: {:?}",
191            *frame_count,
192            fixed_time.elapsed()
193        );
194
195        *frame_count += 1;
196    }
197
198    #[test]
199    fn fixed_main_schedule_should_run_with_time_plugin_enabled() {
200        // Set the time step to just over half the fixed update timestep
201        // This way, it will have not accumulated enough time to run the fixed update after one update
202        // But will definitely have enough time after two updates
203        let fixed_update_timestep = Time::<Fixed>::default().timestep();
204        let time_step = fixed_update_timestep / 2 + Duration::from_millis(1);
205
206        let mut app = App::new();
207        app.add_plugins(TimePlugin)
208            .add_systems(FixedUpdate, count_fixed_updates)
209            .add_systems(Update, report_time)
210            .init_resource::<FixedUpdateCounter>()
211            .insert_resource(TimeUpdateStrategy::ManualDuration(time_step));
212
213        // Frame 0
214        // Fixed update should not have run yet
215        app.update();
216
217        assert!(Duration::ZERO < fixed_update_timestep);
218        let counter = app.world().resource::<FixedUpdateCounter>();
219        assert_eq!(counter.0, 0, "Fixed update should not have run yet");
220
221        // Frame 1
222        // Fixed update should not have run yet
223        app.update();
224
225        assert!(time_step < fixed_update_timestep);
226        let counter = app.world().resource::<FixedUpdateCounter>();
227        assert_eq!(counter.0, 0, "Fixed update should not have run yet");
228
229        // Frame 2
230        // Fixed update should have run now
231        app.update();
232
233        assert!(2 * time_step > fixed_update_timestep);
234        let counter = app.world().resource::<FixedUpdateCounter>();
235        assert_eq!(counter.0, 1, "Fixed update should have run once");
236
237        // Frame 3
238        // Fixed update should have run exactly once still
239        app.update();
240
241        assert!(3 * time_step < 2 * fixed_update_timestep);
242        let counter = app.world().resource::<FixedUpdateCounter>();
243        assert_eq!(counter.0, 1, "Fixed update should have run once");
244
245        // Frame 4
246        // Fixed update should have run twice now
247        app.update();
248
249        assert!(4 * time_step > 2 * fixed_update_timestep);
250        let counter = app.world().resource::<FixedUpdateCounter>();
251        assert_eq!(counter.0, 2, "Fixed update should have run twice");
252    }
253
254    #[test]
255    fn events_get_dropped_regression_test_11528() -> Result<(), impl Error> {
256        let (tx1, rx1) = std::sync::mpsc::channel();
257        let (tx2, rx2) = std::sync::mpsc::channel();
258        let mut app = App::new();
259        app.add_plugins(TimePlugin)
260            .add_event::<TestEvent<i32>>()
261            .add_event::<TestEvent<()>>()
262            .add_systems(Startup, move |mut ev2: EventWriter<TestEvent<()>>| {
263                ev2.send(TestEvent {
264                    sender: tx2.clone(),
265                });
266            })
267            .add_systems(Update, move |mut ev1: EventWriter<TestEvent<i32>>| {
268                // Keep adding events so this event type is processed every update
269                ev1.send(TestEvent {
270                    sender: tx1.clone(),
271                });
272            })
273            .add_systems(
274                Update,
275                |mut ev1: EventReader<TestEvent<i32>>, mut ev2: EventReader<TestEvent<()>>| {
276                    // Read events so they can be dropped
277                    for _ in ev1.read() {}
278                    for _ in ev2.read() {}
279                },
280            )
281            .insert_resource(TimeUpdateStrategy::ManualDuration(
282                Time::<Fixed>::default().timestep(),
283            ));
284
285        for _ in 0..10 {
286            app.update();
287        }
288
289        // Check event type 1 as been dropped at least once
290        let _drop_signal = rx1.try_recv()?;
291        // Check event type 2 has been dropped
292        rx2.try_recv()
293    }
294
295    #[test]
296    fn event_update_should_wait_for_fixed_main() {
297        // Set the time step to just over half the fixed update timestep
298        // This way, it will have not accumulated enough time to run the fixed update after one update
299        // But will definitely have enough time after two updates
300        let fixed_update_timestep = Time::<Fixed>::default().timestep();
301        let time_step = fixed_update_timestep / 2 + Duration::from_millis(1);
302
303        fn send_event(mut events: ResMut<Events<DummyEvent>>) {
304            events.send(DummyEvent);
305        }
306
307        let mut app = App::new();
308        app.add_plugins(TimePlugin)
309            .add_event::<DummyEvent>()
310            .init_resource::<FixedUpdateCounter>()
311            .add_systems(Startup, send_event)
312            .add_systems(FixedUpdate, count_fixed_updates)
313            .insert_resource(TimeUpdateStrategy::ManualDuration(time_step));
314
315        for frame in 0..10 {
316            app.update();
317            let fixed_updates_seen = app.world().resource::<FixedUpdateCounter>().0;
318            let events = app.world().resource::<Events<DummyEvent>>();
319            let n_total_events = events.len();
320            let n_current_events = events.iter_current_update_events().count();
321            let event_registry = app.world().resource::<EventRegistry>();
322            let should_update = event_registry.should_update;
323
324            println!("Frame {frame}, {fixed_updates_seen} fixed updates seen. Should update: {should_update:?}");
325            println!("Total events: {n_total_events} | Current events: {n_current_events}",);
326
327            match frame {
328                0 | 1 => {
329                    assert_eq!(fixed_updates_seen, 0);
330                    assert_eq!(n_total_events, 1);
331                    assert_eq!(n_current_events, 1);
332                    assert_eq!(should_update, ShouldUpdateEvents::Waiting);
333                }
334                2 => {
335                    assert_eq!(fixed_updates_seen, 1); // Time to trigger event updates
336                    assert_eq!(n_total_events, 1);
337                    assert_eq!(n_current_events, 1);
338                    assert_eq!(should_update, ShouldUpdateEvents::Ready); // Prepping first update
339                }
340                3 => {
341                    assert_eq!(fixed_updates_seen, 1);
342                    assert_eq!(n_total_events, 1);
343                    assert_eq!(n_current_events, 0); // First update has occurred
344                    assert_eq!(should_update, ShouldUpdateEvents::Waiting);
345                }
346                4 => {
347                    assert_eq!(fixed_updates_seen, 2); // Time to trigger the second update
348                    assert_eq!(n_total_events, 1);
349                    assert_eq!(n_current_events, 0);
350                    assert_eq!(should_update, ShouldUpdateEvents::Ready); // Prepping second update
351                }
352                5 => {
353                    assert_eq!(fixed_updates_seen, 2);
354                    assert_eq!(n_total_events, 0); // Second update has occurred
355                    assert_eq!(n_current_events, 0);
356                    assert_eq!(should_update, ShouldUpdateEvents::Waiting);
357                }
358                _ => {
359                    assert_eq!(n_total_events, 0); // No more events are sent
360                    assert_eq!(n_current_events, 0);
361                }
362            }
363        }
364    }
365}