bevy_mod_async/
time.rs

1use std::{future::Future, time::Duration};
2
3use bevy_app::{App, Update};
4use bevy_ecs::{
5    component::Component,
6    entity::Entity,
7    system::{Commands, Query, Res},
8    world::World,
9};
10use bevy_time::Time;
11use futures::TryFutureExt;
12use tokio::sync::oneshot;
13
14use crate::TaskContext;
15
16pub fn time_plugin(app: &mut App) {
17    app.add_systems(Update, (advance_timeout_after, advance_timeout_at));
18}
19
20pub trait TimingTaskExt {
21    fn sleep(&self, duration: Duration) -> impl Future<Output = ()>;
22    fn sleep_until(&self, duration: Duration) -> impl Future<Output = ()>;
23}
24
25impl TimingTaskExt for TaskContext {
26    fn sleep(&self, duration: Duration) -> impl Future<Output = ()> {
27        let (tx, rx) = oneshot::channel();
28        self.with_world(move |world| {
29            world.spawn(TimeoutAfter(duration, tx));
30        })
31        .detach();
32        rx.unwrap_or_else(|_| ())
33    }
34
35    fn sleep_until(&self, elapsed_since_startup: Duration) -> impl Future<Output = ()> {
36        let (tx, rx) = oneshot::channel();
37        self.with_world(move |world| {
38            world.spawn(TimeoutAt(elapsed_since_startup, tx));
39        })
40        .detach();
41        rx.unwrap_or_else(|_| ())
42    }
43}
44
45#[derive(Component)]
46pub struct TimeoutAfter(Duration, oneshot::Sender<()>);
47
48#[derive(Component)]
49pub struct TimeoutAt(Duration, oneshot::Sender<()>);
50
51pub fn advance_timeout_after(
52    mut timeouts: Query<(Entity, &mut TimeoutAfter)>,
53    time: Res<Time>,
54    mut commands: Commands,
55) {
56    for (e, mut timeout) in &mut timeouts {
57        if let Some(new_timeout) = timeout.0.checked_sub(time.delta()) {
58            timeout.0 = new_timeout;
59        } else {
60            commands.queue(move |world: &mut World| {
61                let Ok(mut e) = world.get_entity_mut(e) else {
62                    return;
63                };
64                if let Some(timeout) = e.take::<TimeoutAfter>() {
65                    timeout.1.send(()).ok();
66                }
67                e.despawn();
68            });
69        }
70    }
71}
72
73pub fn advance_timeout_at(
74    timeouts: Query<(Entity, &TimeoutAt)>,
75    time: Res<Time>,
76    mut commands: Commands,
77) {
78    for (e, timeout) in &timeouts {
79        if time.elapsed() >= timeout.0 {
80            commands.queue(move |world: &mut World| {
81                let Ok(mut e) = world.get_entity_mut(e) else {
82                    return;
83                };
84                if let Some(timeout) = e.take::<TimeoutAt>() {
85                    timeout.1.send(()).ok();
86                }
87                e.despawn();
88            });
89        }
90    }
91}