1use naia_serde::{BitReader, BitWrite, ConstBitLength, Serde, SerdeErr, UnsignedInteger};
2use naia_socket_shared::Instant;
3
4const GAME_INSANT_BITS: u8 = 22;
5pub const GAME_TIME_LIMIT: u32 = 4194304; const GAME_TIME_LIMIT_U128: u128 = 4194304;
8const GAME_TIME_MAX: u32 = 4194303; const TIME_OFFSET_MAX: i32 = 2097151; const TIME_OFFSET_MIN: i32 = -2097152; #[derive(PartialEq, Debug, Clone, Copy)]
14pub struct GameInstant {
15 millis: u32,
16}
17
18impl GameInstant {
19 pub fn new(start_instant: &Instant) -> Self {
21 let now = Instant::now();
22 let millis = (start_instant.elapsed(&now).as_millis() % GAME_TIME_LIMIT_U128) as u32;
23
24 Self { millis }
26 }
27
28 pub fn time_since(&self, previous_instant: &GameInstant) -> GameDuration {
30 let previous_millis = previous_instant.millis;
31 let current_millis = self.millis;
32
33 if previous_millis == current_millis {
34 return GameDuration { millis: 0 };
35 }
36
37 if previous_millis < current_millis {
38 GameDuration::from_millis(current_millis - previous_millis)
39 } else {
40 GameDuration::from_millis(GAME_TIME_MAX - previous_millis + current_millis)
41 }
42 }
43
44 pub fn offset_from(&self, other: &GameInstant) -> i32 {
46 const MAX: i32 = TIME_OFFSET_MAX;
47 const MIN: i32 = TIME_OFFSET_MIN;
48 const ADJUST: i32 = GAME_TIME_LIMIT as i32;
49
50 let a: i32 = self.millis as i32;
51 let b: i32 = other.millis as i32;
52
53 let mut result = b - a;
54 if (MIN..=MAX).contains(&result) {
55 result
56 } else if b > a {
57 result = b - (a + ADJUST);
58 if (MIN..=MAX).contains(&result) {
59 result
60 } else {
61 panic!("integer overflow, this shouldn't happen");
62 }
63 } else {
64 result = (b + ADJUST) - a;
65 if (MIN..=MAX).contains(&result) {
66 result
67 } else {
68 panic!("integer overflow, this shouldn't happen");
69 }
70 }
71 }
72
73 pub fn is_more_than(&self, other: &GameInstant) -> bool {
76 self.offset_from(other) < 0
77 }
78
79 pub fn as_millis(&self) -> u32 {
81 self.millis
82 }
83
84 pub fn add_millis(&self, millis: u32) -> Self {
86 Self {
87 millis: (self.millis + millis) % GAME_TIME_LIMIT,
88 }
89 }
90
91 pub fn sub_millis(&self, millis: u32) -> Self {
93 let millis = millis % GAME_TIME_LIMIT;
94 if self.millis >= millis {
95 Self {
96 millis: self.millis - millis,
97 }
98 } else {
99 let delta = millis - self.millis;
101 Self {
102 millis: GAME_TIME_LIMIT - delta,
103 }
104 }
105 }
106
107 pub fn add_signed_millis(&self, millis: i32) -> Self {
109 if millis >= 0 {
110 self.add_millis(millis as u32)
111 } else {
112 self.sub_millis(-millis as u32)
113 }
114 }
115}
116
117impl Serde for GameInstant {
118 fn ser(&self, writer: &mut dyn BitWrite) {
119 let integer = UnsignedInteger::<GAME_INSANT_BITS>::new(self.millis as u64);
120 integer.ser(writer);
121 }
122
123 fn de(reader: &mut BitReader) -> Result<Self, SerdeErr> {
124 let integer = UnsignedInteger::<GAME_INSANT_BITS>::de(reader)?;
125 let millis = integer.get() as u32;
126 Ok(Self { millis })
127 }
128
129 fn bit_length(&self) -> u32 {
130 <Self as ConstBitLength>::const_bit_length()
131 }
132}
133
134impl ConstBitLength for GameInstant {
135 fn const_bit_length() -> u32 {
136 <UnsignedInteger<GAME_INSANT_BITS> as ConstBitLength>::const_bit_length()
137 }
138}
139
140#[derive(PartialEq, PartialOrd, Eq, Clone)]
142pub struct GameDuration {
143 millis: u32,
144}
145
146impl GameDuration {
147 pub fn from_millis(millis: u32) -> Self {
149 Self { millis }
150 }
151
152 pub fn as_millis(&self) -> u32 {
154 self.millis
155 }
156
157 pub fn add_millis(&self, millis: u32) -> Self {
159 Self {
160 millis: self.millis + millis,
161 }
162 }
163
164 pub fn sub_millis(&self, millis: u32) -> Self {
166 Self {
167 millis: self.millis - millis,
168 }
169 }
170}
171
172#[cfg(test)]
174mod wrapping_diff_tests {
175 use super::GameInstant;
176 use crate::game_time::{GAME_TIME_LIMIT, GAME_TIME_MAX};
177
178 #[test]
179 fn simple() {
180 let a = GameInstant { millis: 10 };
181 let b = GameInstant { millis: 12 };
182
183 let result = a.offset_from(&b);
184
185 assert_eq!(result, 2);
186 }
187
188 #[test]
189 fn simple_backwards() {
190 let a = GameInstant { millis: 10 };
191 let b = GameInstant { millis: 12 };
192
193 let result = b.offset_from(&a);
194
195 assert_eq!(result, -2);
196 }
197
198 #[test]
199 fn max_wrap() {
200 let a = GameInstant {
201 millis: GAME_TIME_MAX,
202 };
203 let b = a.add_millis(2);
204
205 let result = a.offset_from(&b);
206
207 assert_eq!(result, 2);
208 }
209
210 #[test]
211 fn min_wrap() {
212 let a = GameInstant { millis: 0 };
213 let b = a.sub_millis(2);
214
215 let result = a.offset_from(&b);
216
217 assert_eq!(result, -2);
218 }
219
220 #[test]
221 fn max_wrap_backwards() {
222 let a = GameInstant {
223 millis: GAME_TIME_MAX,
224 };
225 let b = a.add_millis(2);
226
227 let result = b.offset_from(&a);
228
229 assert_eq!(result, -2);
230 }
231
232 #[test]
233 fn min_wrap_backwards() {
234 let a = GameInstant { millis: 0 };
235 let b = a.sub_millis(2);
236
237 let result = b.offset_from(&a);
238
239 assert_eq!(result, 2);
240 }
241
242 #[test]
243 fn medium_min_wrap() {
244 let diff = GAME_TIME_LIMIT / 2;
245 let a = GameInstant { millis: 0 };
246 let b = a.sub_millis(diff);
247
248 let result = a.offset_from(&b);
249
250 assert_eq!(result as i64, -i64::from(diff));
251 }
252
253 #[test]
254 fn medium_min_wrap_backwards() {
255 let diff = (GAME_TIME_LIMIT / 2) - 1;
256 let a = GameInstant { millis: 0 };
257 let b = a.sub_millis(diff);
258
259 let result = b.offset_from(&a);
260
261 assert_eq!(result as i64, i64::from(diff));
262 }
263
264 #[test]
265 fn medium_max_wrap() {
266 let diff = (GAME_TIME_LIMIT / 2) - 1;
267 let a = GameInstant {
268 millis: GAME_TIME_MAX,
269 };
270 let b = a.add_millis(diff);
271
272 let result = a.offset_from(&b);
273
274 assert_eq!(result as i64, i64::from(diff));
275 }
276
277 #[test]
278 fn medium_max_wrap_backwards() {
279 let diff = GAME_TIME_LIMIT / 2;
280 let a = GameInstant {
281 millis: GAME_TIME_MAX,
282 };
283 let b = a.add_millis(diff);
284
285 let result = b.offset_from(&a);
286
287 assert_eq!(result as i64, -i64::from(diff));
288 }
289}