@@ -15,11 +15,11 @@ pub struct Time {
15
15
context : TimeContext ,
16
16
update : Clock ,
17
17
fixed_update : Clock ,
18
- // settings
19
18
paused : bool ,
20
19
next_paused : Option < bool > ,
21
20
relative_speed : f64 , // using `f64` instead of `f32` to minimize drift from rounding errors
22
21
next_relative_speed : Option < f64 > ,
22
+ max_delta : Duration ,
23
23
}
24
24
25
25
/// [`Time`] stores two clocks that are synchronized but advance at different rates.
@@ -41,6 +41,7 @@ impl Default for Time {
41
41
next_paused : None ,
42
42
relative_speed : 1.0 ,
43
43
next_relative_speed : None ,
44
+ max_delta : Duration :: from_millis ( 500 ) ,
44
45
}
45
46
}
46
47
}
@@ -156,8 +157,17 @@ impl Time {
156
157
self . apply_pending_changes ( ) ;
157
158
158
159
// zero for first update
159
- let dt = instant - self . current_clock ( ) . last_update ( ) . unwrap_or ( instant) ;
160
- let dt = if self . paused {
160
+ let mut dt = instant - self . update . last_update ( ) . unwrap_or ( instant) ;
161
+
162
+ if dt > self . max_delta {
163
+ warn ! (
164
+ "update delta time {:?} is larger than the maximum {:?}" ,
165
+ dt, self . max_delta
166
+ ) ;
167
+ dt = self . max_delta ;
168
+ }
169
+
170
+ dt = if self . paused {
161
171
Duration :: ZERO
162
172
} else if self . relative_speed != 1.0 {
163
173
dt. mul_f64 ( self . relative_speed )
@@ -166,7 +176,7 @@ impl Time {
166
176
dt
167
177
} ;
168
178
169
- self . update . tick ( dt, instant) ;
179
+ self . update . update ( dt, instant) ;
170
180
}
171
181
TimeContext :: FixedUpdate => {
172
182
warn ! ( "In the `FixedUpdate` context, `Time` can only be advanced via `tick`." ) ;
@@ -175,7 +185,7 @@ impl Time {
175
185
}
176
186
177
187
pub ( crate ) fn tick ( & mut self , dt : Duration , instant : Instant ) {
178
- self . current_clock_mut ( ) . tick ( dt, instant) ;
188
+ self . current_clock_mut ( ) . update ( dt, instant) ;
179
189
}
180
190
181
191
/// Applies pending pause or relative speed changes.
@@ -376,6 +386,27 @@ impl Time {
376
386
pub fn is_paused ( & self ) -> bool {
377
387
self . paused
378
388
}
389
+
390
+ /// Returns the maximum value of [`delta`](#method.delta) (*before* scaling) for any given update.
391
+ #[ inline]
392
+ pub fn max_delta ( & self ) -> Duration {
393
+ self . max_delta
394
+ }
395
+
396
+ /// Sets the maximum value of [`delta`](#method.delta) (*before* scaling) for any given update.
397
+ ///
398
+ /// After an unusually long update, this value limits the value of [`delta`](#method.delta)
399
+ /// in the next update to avoid the app falling into a "death spiral" where each update has an
400
+ /// ever-increasing number of `FixedUpdate` steps to run.
401
+ ///
402
+ /// Without this setting, anything that results in an exceptionally long time passing between
403
+ /// app updates (e.g. the program was suspended) would cause time to jump forward a lot and
404
+ /// rack up a large simulation debt.
405
+ #[ inline]
406
+ pub fn set_max_delta ( & mut self , max_delta : Duration ) {
407
+ assert ! ( !max_delta. is_zero( ) ) ;
408
+ self . max_delta = max_delta;
409
+ }
379
410
}
380
411
381
412
/// A clock that tracks how much actual time has elasped since the last update and since startup.
@@ -407,7 +438,7 @@ impl RealTime {
407
438
pub fn update_with_instant ( & mut self , instant : Instant ) {
408
439
// zero for first update
409
440
let dt = instant - self . 0 . last_update ( ) . unwrap_or ( instant) ;
410
- self . 0 . tick ( dt, instant) ;
441
+ self . 0 . update ( dt, instant) ;
411
442
}
412
443
413
444
/// Returns the [`Instant`] the clock was created.
@@ -608,6 +639,9 @@ mod tests {
608
639
let mut time = Time :: new ( startup) ;
609
640
time. set_wrap_period ( Duration :: from_secs ( 3 ) ) ;
610
641
642
+ // disable maximum delta for this test
643
+ time. set_max_delta ( Duration :: MAX ) ;
644
+
611
645
assert_eq ! ( time. elapsed_seconds_wrapped( ) , 0.0 ) ;
612
646
613
647
// time starts counting from first update
@@ -633,6 +667,9 @@ mod tests {
633
667
let mut time = Time :: new ( startup) ;
634
668
let mut real_time = RealTime :: new ( startup) ;
635
669
670
+ // disable maximum delta for this test
671
+ time. set_max_delta ( Duration :: MAX ) ;
672
+
636
673
let first_update = Instant :: now ( ) ;
637
674
time. update_with_instant ( first_update) ;
638
675
real_time. update_with_instant ( first_update) ;
0 commit comments