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,7 +336,11 @@ impl Timer {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn percent(&self) -> f32 {
|
pub fn percent(&self) -> f32 {
|
||||||
self.elapsed().as_secs_f32() / self.duration().as_secs_f32()
|
if self.duration == Duration::ZERO {
|
||||||
|
1.0
|
||||||
|
} else {
|
||||||
|
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