twsnap/
time.rs

1//! Timing for ddnet
2
3use serde::{Deserialize, Serialize};
4use std::fmt;
5use std::ops::{Add, Sub};
6use vek::num_traits::SaturatingSub;
7
8// TODO: make it i64?
9pub type SnapTick = i32;
10
11#[derive(
12    Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
13)]
14/// Time measured in Ticks (1/50 of a second = 20ms)
15pub struct Instant(i64);
16
17impl Add<Duration> for Instant {
18    type Output = Instant;
19    fn add(self, rhs: Duration) -> Self::Output {
20        Instant(self.0 + rhs.0 as i64)
21    }
22}
23
24impl Sub<Duration> for Instant {
25    type Output = Instant;
26    fn sub(self, rhs: Duration) -> Self::Output {
27        Instant(self.0 - rhs.0 as i64)
28    }
29}
30
31impl Instant {
32    /// returns whether the duration has passed since the other instant
33    pub fn duration_passed_since(self, past_time: Instant, duration: Duration) -> bool {
34        past_time.0 + duration.0 as i64 <= self.0
35    }
36
37    pub fn duration_since(self, past_time: Instant) -> Option<Duration> {
38        Some(Duration(self.0.checked_sub(past_time.0)? as u32))
39    }
40
41    pub fn advance(&self) -> Self {
42        Self(self.0 + 1)
43    }
44
45    pub const fn zero() -> Instant {
46        Instant(0)
47    }
48
49    pub fn seconds(&self) -> f32 {
50        self.0 as f32 / 50.0
51    }
52
53    pub fn snap_tick(self) -> SnapTick {
54        self.0.try_into().unwrap()
55    }
56
57    pub fn from_snap_tick(t: SnapTick) -> Self {
58        Self(t.into())
59    }
60}
61
62impl fmt::Display for Duration {
63    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64        let days = self.0 / 50 / 60 / 60 / 24;
65        let hours = self.0 / 50 / 60 / 60 % 24;
66        let minutes = self.0 / 50 / 60 % 60;
67        let seconds = self.0 / 50 % 60;
68        let sub_seconds = (self.0 % 50) * 2;
69        let ticks = self.0;
70        if days > 0 {
71            write!(
72                f,
73                "{days}d {hours:02}:{minutes:02}:{seconds:02}.{sub_seconds:02} ({ticks})"
74            )
75        } else if hours > 0 {
76            write!(
77                f,
78                "{hours:02}:{minutes:02}:{seconds:02}.{sub_seconds:02} ({ticks})"
79            )
80        } else {
81            write!(f, "{minutes:02}:{seconds:02}.{sub_seconds:02} ({ticks})",)
82        }
83    }
84}
85
86impl fmt::Display for Instant {
87    // This trait requires `fmt` with this exact signature.
88    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
89        let days = self.0 / 50 / 60 / 60 / 24;
90        let hours = self.0 / 50 / 60 / 60 % 24;
91        let minutes = self.0 / 50 / 60 % 60;
92        let seconds = self.0 / 50 % 60;
93        let ticks = self.0;
94        if days > 0 {
95            write!(f, "{days}d {hours:02}:{minutes:02}:{seconds:02} ({ticks})")
96        } else if hours > 0 {
97            write!(f, "{hours:02}:{minutes:02}:{seconds:02} ({ticks})")
98        } else {
99            write!(f, "{minutes:02}:{seconds:02} ({ticks})",)
100        }
101    }
102}
103
104/// Duration in Ticks (1/50 of a second = 20ms)
105#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
106pub struct Duration(u32);
107
108impl Add<Duration> for Duration {
109    type Output = Duration;
110    fn add(self, rhs: Duration) -> Self::Output {
111        Duration(self.0 + rhs.0)
112    }
113}
114
115impl Sub<Self> for Duration {
116    type Output = Duration;
117
118    fn sub(self, rhs: Self) -> Self::Output {
119        Duration(self.0.sub(rhs.0))
120    }
121}
122
123impl SaturatingSub for Duration {
124    fn saturating_sub(&self, rhs: &Self) -> Self {
125        Duration(self.0.saturating_sub(rhs.0))
126    }
127}
128
129impl Duration {
130    pub fn decrement(self) -> Option<Duration> {
131        self.0.checked_sub(1).map(Duration)
132    }
133
134    pub fn seconds(self) -> f32 {
135        self.0 as f32 / 50.0
136    }
137
138    pub fn ticks(self) -> SnapTick {
139        self.0.try_into().unwrap()
140    }
141
142    /// Must only be called for loading from save code
143    pub fn from_ticks(n: SnapTick) -> Duration {
144        Duration(if n.is_negative() { 0 } else { n as u32 })
145    }
146
147    pub const fn from_min(n: u32) -> Duration {
148        Duration(n * 50 * 60)
149    }
150
151    pub const fn from_secs(n: u32) -> Duration {
152        Duration(n * 50)
153    }
154
155    pub fn from_secs_f32(s: f32) -> Duration {
156        Duration((s * 50.0) as u32)
157    }
158
159    pub fn from_ms_f32(ms: f32) -> Duration {
160        Duration(ms as u32 / 20)
161    }
162
163    pub const T0MS: Duration = Duration(0);
164    pub const T20MS: Duration = Duration(1);
165    pub const T40MS: Duration = Duration(2);
166    pub const T60MS: Duration = Duration(3);
167    pub const T80MS: Duration = Duration(4);
168    pub const T100MS: Duration = Duration(5);
169    pub const T120MS: Duration = Duration(6);
170    pub const T140MS: Duration = Duration(7);
171    pub const T160MS: Duration = Duration(8);
172    pub const T180MS: Duration = Duration(9);
173    pub const T200MS: Duration = Duration(10);
174    pub const T220MS: Duration = Duration(11);
175    pub const T240MS: Duration = Duration(12);
176    pub const T260MS: Duration = Duration(13);
177    pub const T280MS: Duration = Duration(14);
178    pub const T300MS: Duration = Duration(15);
179    pub const T320MS: Duration = Duration(16);
180    pub const T340MS: Duration = Duration(17);
181    pub const T360MS: Duration = Duration(18);
182    pub const T380MS: Duration = Duration(19);
183    pub const T400MS: Duration = Duration(20);
184    pub const T420MS: Duration = Duration(21);
185    pub const T440MS: Duration = Duration(22);
186    pub const T460MS: Duration = Duration(23);
187    pub const T480MS: Duration = Duration(24);
188    pub const T500MS: Duration = Duration(25);
189    pub const T520MS: Duration = Duration(26);
190    pub const T540MS: Duration = Duration(27);
191    pub const T560MS: Duration = Duration(28);
192    pub const T580MS: Duration = Duration(29);
193    pub const T600MS: Duration = Duration(30);
194    pub const T620MS: Duration = Duration(31);
195    pub const T640MS: Duration = Duration(32);
196    pub const T660MS: Duration = Duration(33);
197    pub const T680MS: Duration = Duration(34);
198    pub const T700MS: Duration = Duration(35);
199    pub const T720MS: Duration = Duration(36);
200    pub const T740MS: Duration = Duration(37);
201    pub const T760MS: Duration = Duration(38);
202    pub const T780MS: Duration = Duration(39);
203    pub const T800MS: Duration = Duration(40);
204    pub const T820MS: Duration = Duration(41);
205    pub const T840MS: Duration = Duration(42);
206    pub const T860MS: Duration = Duration(43);
207    pub const T880MS: Duration = Duration(44);
208    pub const T900MS: Duration = Duration(45);
209    pub const T920MS: Duration = Duration(46);
210    pub const T940MS: Duration = Duration(47);
211    pub const T960MS: Duration = Duration(48);
212    pub const T980MS: Duration = Duration(49);
213}
214
215#[cfg(test)]
216mod test {
217    use super::*;
218
219    #[test]
220    fn simple() {
221        let a = Instant(150);
222        let b = Instant(50);
223        assert!(a.duration_passed_since(b, Duration::from_secs(1)));
224        assert!(a.duration_passed_since(b, Duration::from_secs(2)));
225        assert!(!a.duration_passed_since(b, Duration::from_secs(3)));
226        assert!(!a.duration_passed_since(b, Duration::from_secs(5)));
227    }
228}