use std::thread;
use std::time;
use chrono;
use float_duration::{FloatDuration, TimePoint};
use step::TimeStep;
use framerate::FrameCount;
#[derive(Debug, Clone)]
pub struct GameTime {
frame_start_time: chrono::DateTime<chrono::Local>,
total_wall_time: FloatDuration,
total_game_time: FloatDuration,
elapsed_game_time: FloatDuration,
elapsed_wall_time: FloatDuration,
frame_number: u64,
}
#[derive(Debug, Clone)]
pub struct GameClock {
last_frame_time: GameTime,
start_wall_time: chrono::DateTime<chrono::Local>,
total_game_time: time::Duration,
current_frame: u64,
clock_multiplier: f64,
}
#[derive(Debug, Clone)]
pub struct GameClockBuilder {
start_game_time: time::Duration,
start_wall_time: chrono::DateTime<chrono::Local>,
start_frame: u64,
clock_multiplier: f64,
}
impl GameClock {
pub fn new() -> GameClock {
let now = chrono::Local::now();
let start_game_time = GameTime {
frame_start_time: now,
total_wall_time: FloatDuration::zero(),
total_game_time: FloatDuration::zero(),
elapsed_game_time: FloatDuration::zero(),
elapsed_wall_time: FloatDuration::zero(),
frame_number: 0,
};
GameClock {
last_frame_time: start_game_time,
start_wall_time: now,
total_game_time: time::Duration::new(0, 0),
current_frame: 0,
clock_multiplier: 1.0,
}
}
pub fn current_frame_number(&self) -> u64 {
self.current_frame
}
pub fn start_wall_time(&self) -> chrono::DateTime<chrono::Local> {
self.start_wall_time
}
pub fn frame_start_time(&self) -> chrono::DateTime<chrono::Local> {
self.last_frame_time().frame_start_time()
}
pub fn total_wall_time(&self) -> FloatDuration {
self.last_frame_time().total_wall_time()
}
pub fn frame_elapsed_time(&self) -> FloatDuration {
chrono::Local::now()
.float_duration_since(self.last_frame_time().frame_start_time())
.unwrap()
}
pub fn last_frame_time(&self) -> &GameTime {
&self.last_frame_time
}
pub fn clock_multiplier(&self) -> f64 {
self.clock_multiplier
}
pub fn set_clock_multiplier(&mut self, val: f64) -> &mut GameClock {
self.clock_multiplier = val;
self
}
pub fn tick<T>(&mut self, time_step: &T) -> GameTime
where
T: TimeStep + ?Sized,
{
let frame_start = chrono::Local::now();
self.tick_with_wall_time(time_step, frame_start)
}
pub fn tick_with_wall_time<T>(
&mut self,
time_step: &T,
frame_start: chrono::DateTime<chrono::Local>,
) -> GameTime
where
T: TimeStep + ?Sized,
{
self.current_frame += 1;
let elapsed_wall_time = frame_start
.float_duration_since(self.frame_start_time())
.unwrap();
let elapsed_game_time = time_step.time_step(&elapsed_wall_time) * self.clock_multiplier;
let total_game_time = self.total_game_time + elapsed_game_time.to_std().unwrap();
self.total_game_time = total_game_time;
let time = GameTime {
frame_start_time: frame_start,
total_wall_time: FloatDuration::from(
frame_start.signed_duration_since(self.start_wall_time),
),
total_game_time: FloatDuration::from(total_game_time),
elapsed_game_time,
elapsed_wall_time,
frame_number: self.current_frame,
};
self.last_frame_time = time.clone();
time
}
pub fn sleep_remaining_via<C, F>(&mut self, counter: &C, f: F)
where
C: FrameCount + ?Sized,
F: FnOnce(FloatDuration),
{
let remaining_time = counter.target_time_per_frame() -
self.last_frame_time.elapsed_time_since_frame_start();
if !remaining_time.is_negative() {
f(remaining_time)
}
}
pub fn sleep_remaining<C>(&mut self, counter: &C)
where
C: FrameCount + ?Sized,
{
self.sleep_remaining_via(counter, |rem| thread::sleep(rem.to_std().unwrap()))
}
}
impl Default for GameClock {
fn default() -> GameClock {
GameClock::new()
}
}
impl GameTime {
pub fn total_game_time(&self) -> FloatDuration {
self.total_game_time
}
pub fn total_wall_time(&self) -> FloatDuration {
self.total_wall_time
}
pub fn frame_start_time(&self) -> chrono::DateTime<chrono::Local> {
self.frame_start_time
}
pub fn elapsed_game_time(&self) -> FloatDuration {
self.elapsed_game_time
}
pub fn elapsed_wall_time(&self) -> FloatDuration {
self.elapsed_wall_time
}
pub fn elapsed_time_since_frame_start(&self) -> FloatDuration {
chrono::Local::now()
.float_duration_since(self.frame_start_time())
.unwrap()
}
pub fn frame_number(&self) -> u64 {
self.frame_number
}
pub fn instantaneous_frame_rate(&self) -> f64 {
1.0 / self.elapsed_game_time.as_seconds()
}
}
impl GameClockBuilder {
pub fn new() -> GameClockBuilder {
GameClockBuilder {
start_game_time: time::Duration::new(0, 0),
start_wall_time: chrono::Local::now(),
start_frame: 0,
clock_multiplier: 1.0,
}
}
pub fn start_game_time(&mut self, time: time::Duration) -> &mut GameClockBuilder {
self.start_game_time = time;
self
}
pub fn start_wall_time(
&mut self,
time: chrono::DateTime<chrono::Local>,
) -> &mut GameClockBuilder {
self.start_wall_time = time;
self
}
pub fn start_frame(&mut self, frame_num: u64) -> &mut GameClockBuilder {
self.start_frame = frame_num;
self
}
pub fn clock_multiplier(&mut self, multiplier: f64) -> &mut GameClockBuilder {
self.clock_multiplier = multiplier;
self
}
pub fn build(&self) -> GameClock {
let start_game_time = GameTime {
frame_start_time: self.start_wall_time,
total_wall_time: FloatDuration::zero(),
total_game_time: FloatDuration::from(self.start_game_time),
elapsed_game_time: FloatDuration::zero(),
elapsed_wall_time: FloatDuration::zero(),
frame_number: self.start_frame,
};
GameClock {
last_frame_time: start_game_time,
start_wall_time: self.start_wall_time,
total_game_time: self.start_game_time,
current_frame: self.start_frame,
clock_multiplier: self.clock_multiplier,
}
}
}
impl Default for GameClockBuilder {
fn default() -> GameClockBuilder {
GameClockBuilder::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use chrono::Local;
use step;
#[test]
fn test_clock_construct() {
let clock = GameClock::new();
assert_eq!(clock.clock_multiplier(), 1.0);
assert_eq!(
clock.last_frame_time().elapsed_game_time(),
FloatDuration::zero()
);
let clock2 = GameClockBuilder::new()
.clock_multiplier(5.0)
.start_frame(100)
.build();
assert_eq!(clock2.current_frame_number(), 100);
assert_eq!(clock2.clock_multiplier(), 5.0);
assert_eq!(
clock2.last_frame_time().elapsed_game_time(),
FloatDuration::zero()
);
assert_eq!(
clock2.last_frame_time().elapsed_wall_time(),
FloatDuration::zero()
);
assert!(clock2.last_frame_time().total_wall_time() == FloatDuration::zero());
let start_time = Local::today().and_hms(12, 0, 0);
let clock3 = GameClockBuilder::default()
.start_game_time(time::Duration::new(100, 0))
.start_wall_time(start_time)
.clone()
.build();
assert_eq!(clock3.current_frame_number(), 0);
assert_eq!(
clock3.last_frame_time().total_game_time(),
FloatDuration::seconds(100.0)
);
assert_eq!(clock3.last_frame_time().frame_start_time(), start_time);
assert_eq!(GameClock::default().current_frame_number(), 0);
assert_eq!(
GameClock::default().last_frame_time().total_game_time(),
FloatDuration::zero()
);
}
#[test]
fn test_clock_tick() {
let mut clock = GameClock::new();
let time = clock.tick(&step::ConstantStep::new(FloatDuration::seconds(1.0)));
assert_eq!(time.frame_number(), 1);
assert_eq!(time.total_game_time(), FloatDuration::seconds(1.0));
assert!(time.frame_start_time() > clock.start_wall_time());
assert!(time.frame_start_time() < Local::now());
assert_eq!(time.elapsed_game_time(), FloatDuration::seconds(1.0));
assert!(time.elapsed_wall_time() < FloatDuration::seconds(1.0));
let time2 = clock.tick(&step::VariableStep::new());
assert_eq!(time2.frame_number(), 2);
assert!(time2.total_wall_time() > time.total_wall_time());
assert_eq!(time2.elapsed_game_time(), time2.elapsed_wall_time());
assert!(time2.total_game_time() > time.total_game_time());
assert!(time2.elapsed_time_since_frame_start() > FloatDuration::seconds(0.0));
assert!(time2.elapsed_time_since_frame_start() < FloatDuration::seconds(0.01));
}
#[test]
fn test_clock_tick_loop() {
let dt = FloatDuration::milliseconds(50.0);
let step = step::ConstantStep::new(dt);
let mut clock = GameClock::default().clone();
for x in 0..10 {
let frame_time = clock.tick(&step);
assert_eq!(frame_time.frame_number(), x + 1);
assert_eq!(frame_time.elapsed_game_time(), dt);
relative_eq!(
frame_time.total_game_time(),
dt * (x + 1) as f64,
epsilon = 1e-8
);
assert!(frame_time.elapsed_time_since_frame_start() > FloatDuration::zero());
}
let frame_time = clock.last_frame_time();
assert_eq!(frame_time.total_game_time(), FloatDuration::seconds(0.5));
assert!(frame_time.elapsed_time_since_frame_start() < FloatDuration::milliseconds(1.0));
assert!(frame_time.frame_start_time() > clock.start_wall_time());
}
#[test]
fn test_wall_time() {
let mut clock = GameClock::new();
clock.set_clock_multiplier(2.0);
assert_eq!(clock.clock_multiplier(), 2.0);
let step = step::VariableStep::new();
let start_time = clock.start_wall_time();
let wall_dt = chrono::Duration::seconds(1);
for x in 0..10 {
let time = start_time + wall_dt * (x + 1);
let frame_time = clock.tick_with_wall_time(&step, time);
assert_eq!(
frame_time.total_wall_time(),
time.float_duration_since(clock.start_wall_time()).unwrap()
);
assert_eq!(
2.0 * frame_time.elapsed_wall_time(),
frame_time.elapsed_game_time()
);
assert_eq!(
frame_time.elapsed_wall_time(),
FloatDuration::from_chrono(wall_dt)
);
assert_eq!(frame_time.instantaneous_frame_rate(), 0.5);
}
}
}