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