use std::cmp::Ordering;
use std::fmt::Formatter;
use std::ops::{Add, AddAssign, Mul, Sub, SubAssign};
use std::time::Duration;
use bevy::prelude::{Time, Timer, TimerMode, Virtual};
use bitcode::{Decode, Encode};
use chrono::Duration as ChronoDuration;
use serde::{Deserialize, Serialize};
pub const WRAPPING_TIME_US: u32 = u32::MAX;
pub struct TimeManager {
wrapped_time: WrappedTime,
overstep: Duration,
delta: Duration,
pub base_relative_speed: f32,
pub(crate) sync_relative_speed: f32,
send_timer: Option<Timer>,
}
impl TimeManager {
pub fn new(send_interval: Duration) -> Self {
let send_timer = if send_interval == Duration::default() {
None
} else {
Some(Timer::new(send_interval, TimerMode::Repeating))
};
Self {
wrapped_time: WrappedTime::new(0),
overstep: Duration::default(),
delta: Duration::default(),
base_relative_speed: 1.0,
sync_relative_speed: 1.0,
send_timer,
}
}
pub(crate) fn is_ready_to_send(&self) -> bool {
self.send_timer
.as_ref()
.map_or(true, |timer| timer.finished())
}
pub fn delta(&self) -> Duration {
self.delta
}
pub fn overstep(&self) -> Duration {
self.overstep
}
pub fn update_relative_speed(&self, time: &mut Time<Virtual>) {
time.set_relative_speed(self.base_relative_speed * self.sync_relative_speed)
}
pub fn update(&mut self, delta: Duration, overstep: Duration) {
self.delta = delta;
self.wrapped_time += delta;
self.overstep = overstep;
if let Some(timer) = self.send_timer.as_mut() {
timer.tick(delta);
}
}
pub fn current_time(&self) -> WrappedTime {
self.wrapped_time
}
}
#[derive(Default, Encode, Decode, Serialize, Deserialize, Copy, Clone, Eq, PartialEq)]
pub struct WrappedTime {
pub(crate) elapsed_us_wrapped: u32,
}
impl std::fmt::Debug for WrappedTime {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("WrappedTime")
.field(
"time",
&Duration::from_micros(self.elapsed_us_wrapped as u64),
)
.finish()
}
}
impl WrappedTime {
pub fn new(elapsed_us_wrapped: u32) -> Self {
Self { elapsed_us_wrapped }
}
pub fn from_duration(elapsed_wrapped: Duration) -> Self {
let elapsed_us_wrapped = elapsed_wrapped.as_micros() as u32;
Self { elapsed_us_wrapped }
}
pub fn to_duration(&self) -> Duration {
Duration::from_micros(self.elapsed_us_wrapped as u64)
}
pub fn wrapping_diff(a: &Self, b: &Self) -> i32 {
const MAX: i32 = (WRAPPING_TIME_US / 2 - 1) as i32;
const MIN: i32 = -MAX;
const ADJUST: i32 = WRAPPING_TIME_US as i32;
let a: i32 = a.elapsed_us_wrapped as i32;
let b: i32 = b.elapsed_us_wrapped as i32;
let mut result = b - a;
if (MIN..=MAX).contains(&result) {
result
} else if b > a {
result = b - (a + ADJUST);
if (MIN..=MAX).contains(&result) {
result
} else {
panic!("integer overflow, this shouldn't happen")
}
} else {
result = (b + ADJUST) - a;
if (MIN..=MAX).contains(&result) {
result
} else {
panic!("integer overflow, this shouldn't happen")
}
}
}
}
impl Ord for WrappedTime {
fn cmp(&self, other: &Self) -> Ordering {
match Self::wrapping_diff(self, other) {
0 => Ordering::Equal,
x if x > 0 => Ordering::Less,
x if x < 0 => Ordering::Greater,
_ => unreachable!(),
}
}
}
impl PartialOrd for WrappedTime {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Sub for WrappedTime {
type Output = ChronoDuration;
fn sub(self, rhs: Self) -> Self::Output {
let diff_us = Self::wrapping_diff(&rhs, &self);
ChronoDuration::microseconds(diff_us as i64)
}
}
impl Sub<Duration> for WrappedTime {
type Output = WrappedTime;
fn sub(self, rhs: Duration) -> Self::Output {
let mut result = self;
result -= rhs;
result
}
}
impl Sub<ChronoDuration> for WrappedTime {
type Output = WrappedTime;
fn sub(self, rhs: ChronoDuration) -> Self::Output {
let mut result = self;
result -= rhs;
result
}
}
impl SubAssign<Duration> for WrappedTime {
fn sub_assign(&mut self, rhs: Duration) {
let rhs_micros = rhs.as_micros();
self.elapsed_us_wrapped = self.elapsed_us_wrapped.wrapping_sub(rhs_micros as u32);
}
}
impl SubAssign<ChronoDuration> for WrappedTime {
fn sub_assign(&mut self, rhs: ChronoDuration) {
let rhs_micros = rhs.num_microseconds().unwrap();
if rhs_micros > 0 {
self.elapsed_us_wrapped = self.elapsed_us_wrapped.wrapping_sub(rhs_micros as u32);
} else {
self.elapsed_us_wrapped = self.elapsed_us_wrapped.wrapping_add(rhs_micros as u32);
}
}
}
impl Add<Duration> for WrappedTime {
type Output = Self;
fn add(self, rhs: Duration) -> Self::Output {
Self {
elapsed_us_wrapped: self.elapsed_us_wrapped.wrapping_add(rhs.as_micros() as u32),
}
}
}
impl Add for WrappedTime {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self {
elapsed_us_wrapped: self.elapsed_us_wrapped.wrapping_add(rhs.elapsed_us_wrapped),
}
}
}
impl Add<ChronoDuration> for WrappedTime {
type Output = Self;
fn add(self, rhs: ChronoDuration) -> Self::Output {
let mut result = self;
result += rhs;
result
}
}
impl AddAssign<ChronoDuration> for WrappedTime {
fn add_assign(&mut self, rhs: ChronoDuration) {
let rhs_micros = rhs.num_microseconds().unwrap();
if rhs_micros > 0 {
self.elapsed_us_wrapped = self.elapsed_us_wrapped.wrapping_add(rhs_micros as u32);
} else {
self.elapsed_us_wrapped = self.elapsed_us_wrapped.wrapping_sub(rhs_micros as u32);
}
}
}
impl AddAssign<Duration> for WrappedTime {
fn add_assign(&mut self, rhs: Duration) {
self.elapsed_us_wrapped = self.elapsed_us_wrapped.wrapping_add(rhs.as_micros() as u32);
}
}
impl Mul<f32> for WrappedTime {
type Output = Self;
fn mul(self, rhs: f32) -> Self::Output {
Self {
elapsed_us_wrapped: ((self.elapsed_us_wrapped as f32) * rhs) as u32,
}
}
}
impl From<Duration> for WrappedTime {
fn from(value: Duration) -> Self {
Self::from_duration(value)
}
}
impl From<WrappedTime> for Duration {
fn from(value: WrappedTime) -> Self {
value.to_duration()
}
}
#[cfg(test)]
mod tests {
use crate::shared::time_manager::WrappedTime;
#[test]
fn test_mul() {
let a = WrappedTime::new(u32::MAX);
let b = a * 2.0;
assert_eq!(b.elapsed_us_wrapped, u32::MAX);
}
}