Fix timer with zero duration (#8467)
# Objective Timer with zero `Duration` panics at `tick()` because of division by zero. This PR Fixes #8463 . ## Solution - Handle division by zero separately with `checked_div` and `checked_rem`. --- ## Changelog - Replace division with `checked_div`. Set `times_finished_this_tick` to u32::MAX when duration is zero. - Set `elapsed` to `Duration::ZERO` when timer duration is zero. - Set `percent` to `1.0` when duration is zero. - `times_finished_this_tick` is [not used anywhere](https://github.com/bevyengine/bevy/search?q=times_finished_this_tick), that's why this change will not affect other parts of the project. - `times_finished_this_tick` is set to `0` after `reset()` and before first `tick()` call.
This commit is contained in:
		
							parent
							
								
									f3360938eb
								
							
						
					
					
						commit
						e2531b2273
					
				| @ -224,10 +224,17 @@ impl Timer { | |||||||
| 
 | 
 | ||||||
|         if self.finished() { |         if self.finished() { | ||||||
|             if self.mode == TimerMode::Repeating { |             if self.mode == TimerMode::Repeating { | ||||||
|                 self.times_finished_this_tick = |                 self.times_finished_this_tick = self | ||||||
|                     (self.elapsed().as_nanos() / self.duration().as_nanos()) as u32; |                     .elapsed() | ||||||
|                 // Duration does not have a modulo
 |                     .as_nanos() | ||||||
|                 self.set_elapsed(self.elapsed() - self.duration() * self.times_finished_this_tick); |                     .checked_div(self.duration().as_nanos()) | ||||||
|  |                     .map_or(u32::MAX, |x| x as u32); | ||||||
|  |                 self.set_elapsed( | ||||||
|  |                     self.elapsed() | ||||||
|  |                         .as_nanos() | ||||||
|  |                         .checked_rem(self.duration().as_nanos()) | ||||||
|  |                         .map_or(Duration::ZERO, |x| Duration::from_nanos(x as u64)), | ||||||
|  |                 ); | ||||||
|             } else { |             } else { | ||||||
|                 self.times_finished_this_tick = 1; |                 self.times_finished_this_tick = 1; | ||||||
|                 self.set_elapsed(self.duration()); |                 self.set_elapsed(self.duration()); | ||||||
| @ -329,8 +336,12 @@ impl Timer { | |||||||
|     /// ```
 |     /// ```
 | ||||||
|     #[inline] |     #[inline] | ||||||
|     pub fn percent(&self) -> f32 { |     pub fn percent(&self) -> f32 { | ||||||
|  |         if self.duration == Duration::ZERO { | ||||||
|  |             1.0 | ||||||
|  |         } else { | ||||||
|             self.elapsed().as_secs_f32() / self.duration().as_secs_f32() |             self.elapsed().as_secs_f32() / self.duration().as_secs_f32() | ||||||
|         } |         } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     /// Returns the percentage of the timer remaining time (goes from 1.0 to 0.0).
 |     /// Returns the percentage of the timer remaining time (goes from 1.0 to 0.0).
 | ||||||
|     ///
 |     ///
 | ||||||
| @ -517,6 +528,26 @@ mod tests { | |||||||
|         assert_eq!(t.times_finished_this_tick(), 0); |         assert_eq!(t.times_finished_this_tick(), 0); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn times_finished_this_tick_repeating_zero_duration() { | ||||||
|  |         let mut t = Timer::from_seconds(0.0, TimerMode::Repeating); | ||||||
|  |         assert_eq!(t.times_finished_this_tick(), 0); | ||||||
|  |         assert_eq!(t.elapsed(), Duration::ZERO); | ||||||
|  |         assert_eq!(t.percent(), 1.0); | ||||||
|  |         t.tick(Duration::from_secs(1)); | ||||||
|  |         assert_eq!(t.times_finished_this_tick(), u32::MAX); | ||||||
|  |         assert_eq!(t.elapsed(), Duration::ZERO); | ||||||
|  |         assert_eq!(t.percent(), 1.0); | ||||||
|  |         t.tick(Duration::from_secs(2)); | ||||||
|  |         assert_eq!(t.times_finished_this_tick(), u32::MAX); | ||||||
|  |         assert_eq!(t.elapsed(), Duration::ZERO); | ||||||
|  |         assert_eq!(t.percent(), 1.0); | ||||||
|  |         t.reset(); | ||||||
|  |         assert_eq!(t.times_finished_this_tick(), 0); | ||||||
|  |         assert_eq!(t.elapsed(), Duration::ZERO); | ||||||
|  |         assert_eq!(t.percent(), 1.0); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     fn times_finished_this_tick_precise() { |     fn times_finished_this_tick_precise() { | ||||||
|         let mut t = Timer::from_seconds(0.01, TimerMode::Repeating); |         let mut t = Timer::from_seconds(0.01, TimerMode::Repeating); | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Rostyslav Toch
						Rostyslav Toch