1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use crate::*;

use riddle_common::define_handles;

use std::sync::Mutex;

/// The Riddle time system core state.

///

/// Manages tracking framerate and timer state.

///

/// It is possible to manage the audio system state independantly - the most important

/// thing to note is that [`TimeSystem::process_frame`] must be called once per "frame".

/// The default Riddle integration calls that method whenever the

/// `riddle::Event::ProcessFrame` event is fired.

pub struct TimeSystem {
    weak_self: TimeSystemWeak,
    frame_time: Mutex<FrameTime>,
    timers: TimerSet,
}

define_handles!(<TimeSystem>::weak_self, pub TimeSystemHandle, pub TimeSystemWeak);

impl TimeSystem {
    /// Create a new time system. The time the system is created is used as the time

    /// of the 0th frame.

    pub fn new() -> TimeSystemHandle {
        TimeSystemHandle::new(|weak_self| Self {
            weak_self,
            frame_time: Mutex::new(FrameTime::new()),
            timers: TimerSet::new(),
        })
    }

    /// Update the time system state, marking the beginning of a the next frame.

    ///

    /// The instant that this method is called is taken as the reference time for

    /// the frame that is about to be executed.

    ///

    /// Timers will also be triggered during this function call if they are due

    /// to trigger.

    ///

    /// **Do not** call this function directly if you are using this through the

    /// `riddle` crate.

    ///

    /// # Example

    ///

    /// ```

    /// # use riddle_time::*; doctest::simple(|time_system| {

    /// let frame_1 = time_system.frame_instant();

    ///

    /// // A while later

    /// # doctest::pump_for_secs(time_system, 1);

    /// let frame_n = time_system.frame_instant();

    ///

    /// assert_eq!(true, frame_n - frame_1 > std::time::Duration::from_secs(0));

    /// # });

    /// ```

    pub fn process_frame(&self) {
        let mut locked_time = self.frame_time.lock().unwrap();
        locked_time.update();
        let delta = locked_time.frame_delta;
        drop(locked_time);

        self.timers.update(delta);
    }

    /// Get the current FPS as calculated based on previous frame durations.

    pub fn fps(&self) -> f32 {
        self.frame_time.lock().unwrap().fps
    }

    /// Get the current delta t as calculated based on previous frame durations.

    pub fn delta_secs(&self) -> f32 {
        1.0 / self.frame_time.lock().unwrap().fps
    }

    /// Get the reference time for this frame. Captured during [`TimeSystem::process_frame`].

    pub fn frame_instant(&self) -> std::time::Instant {
        self.frame_time.lock().unwrap().frame_instant
    }

    /// Register a timer with a callback which will be fired when the time elpases.

    ///

    /// The returned handle may be dropped without cancelling the timer.

    ///

    /// # Example

    ///

    /// ```

    /// # use std::sync::{Arc, atomic::{AtomicBool, Ordering}};

    /// # use riddle_time::*; doctest::simple(|time_system| {

    /// let val = Arc::new(AtomicBool::new(false));

    ///

    /// time_system.register_timer(std::time::Duration::from_millis(200), {

    ///     let val = val.clone();

    ///     move || { val.store(true, Ordering::Relaxed); }

    /// });

    ///

    /// // A while later

    /// # doctest::pump_for_secs(time_system, 1);

    /// assert_eq!(true, val.load(Ordering::Relaxed));

    /// # });

    /// ```

    pub fn register_timer<F>(&self, duration: std::time::Duration, callback: F) -> TimerHandle
    where
        F: FnOnce() + Send + 'static,
    {
        self.timers.register_timer(duration, Box::new(callback))
    }
}

struct FrameTime {
    frame_instant: std::time::Instant,
    frame_delta: std::time::Duration,
    fps: f32,
}

impl FrameTime {
    fn new() -> Self {
        Self {
            frame_instant: std::time::Instant::now(),
            frame_delta: Default::default(),
            fps: 0.0,
        }
    }

    fn update(&mut self) {
        let now = std::time::Instant::now();
        self.frame_delta = now.duration_since(self.frame_instant);
        self.fps = 1.0 / self.frame_delta.as_secs_f32().max(0.001);
        self.frame_instant = now;
    }
}