ui_dialog_verbs: a battleground's run time is the time since its status plus the wire's elapsed ms, read forwards so no Instant precedes the clock's origin (#630)
`GetBattlefieldInstanceRunTime`'s stamp was set backwards, as `now - Duration::from_millis(elapsed_ms)`, with `elapsed_ms` the u32 from `SMSG_BATTLEFIELD_STATUS`'s in-progress tuple. On Windows, std's `Instant` is a `Duration` from the performance counter's origin, so a run longer than the machine's uptime panics there. On macOS and Linux the subtraction cannot panic. The stamp is now the status's arrival and the wire span, and `run_time_ms` reads `now.saturating_duration_since(at) + elapsed`. That is the old value everywhere the old one did not panic, taken forwards the way the queue's time waited is. `a_run_longer_than_any_uptime_reads_forwards` pins a 49.7-day run, and the status-handler test now also checks that the stamp runs on after the status. Built and gated on macOS; the Windows target was not built. Fixes #627 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
57f33aa620
commit
6e9e5e995d
@@ -181,7 +181,9 @@ pub(crate) struct BattlefieldQueue {
|
||||
active: Option<(usize, u32)>,
|
||||
/// The instance's run-time stamp (`[0xb6ebbc]`) and expiration (`[0xb6ebb8]`): set by status
|
||||
/// 3, zeroed by any other non-clearing status for any slot, as the handler `0x4aa850` does.
|
||||
run_started: Option<Instant>,
|
||||
/// The stamp is kept as the status's arrival and the wire's elapsed span, read forwards, so no
|
||||
/// `Instant` precedes the clock's origin.
|
||||
run_started: Option<(Instant, std::time::Duration)>,
|
||||
instance_expiration: Option<Instant>,
|
||||
/// Status 3 fires `UPDATE_BATTLEFIELD_SCORE` before `_STATUS` (`0x4aaa5a`, then `0x4aab05`).
|
||||
score_dirty: bool,
|
||||
@@ -223,7 +225,7 @@ impl BattlefieldQueue {
|
||||
self.instance_expiration = (expires_ms != 0)
|
||||
.then(|| now + std::time::Duration::from_millis(u64::from(expires_ms)));
|
||||
self.run_started = (elapsed_ms != 0)
|
||||
.then(|| now - std::time::Duration::from_millis(u64::from(elapsed_ms)));
|
||||
.then(|| (now, std::time::Duration::from_millis(u64::from(elapsed_ms))));
|
||||
self.score_dirty = true;
|
||||
}
|
||||
None => {
|
||||
@@ -258,8 +260,8 @@ impl BattlefieldQueue {
|
||||
|
||||
/// `GetBattlefieldInstanceRunTime()`: ms since the status-3 stamp, 0 with none.
|
||||
pub(crate) fn run_time_ms(&self, now: Instant) -> u32 {
|
||||
self.run_started.map_or(0, |t| {
|
||||
now.saturating_duration_since(t)
|
||||
self.run_started.map_or(0, |(at, elapsed)| {
|
||||
(now.saturating_duration_since(at) + elapsed)
|
||||
.as_millis()
|
||||
.min(u128::from(u32::MAX)) as u32
|
||||
})
|
||||
@@ -1588,6 +1590,30 @@ mod tests {
|
||||
assert_eq!(q.map_id(2), None, "a zero map clears the slot");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_run_longer_than_any_uptime_reads_forwards() {
|
||||
let mut q = BattlefieldQueue::default();
|
||||
let now = Instant::now();
|
||||
q.apply_at(
|
||||
BattlefieldStatus {
|
||||
slot: 0,
|
||||
map_id: 489,
|
||||
bracket: 0,
|
||||
instance_id: 3,
|
||||
status: 3,
|
||||
time_ms: None,
|
||||
in_progress: Some((0, u32::MAX)),
|
||||
queued: None,
|
||||
},
|
||||
now,
|
||||
);
|
||||
assert_eq!(
|
||||
q.run_time_ms(now),
|
||||
u32::MAX,
|
||||
"49.7 days elapsed, no stamp needed"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_instance_clocks_follow_the_status_handler() {
|
||||
let mut q = BattlefieldQueue::default();
|
||||
@@ -1606,6 +1632,11 @@ mod tests {
|
||||
assert_eq!(q.active_map(), Some(489));
|
||||
assert_eq!(q.instance_expiration_ms(now), 90_000);
|
||||
assert_eq!(q.run_time_ms(now), 30_000);
|
||||
assert_eq!(
|
||||
q.run_time_ms(now + std::time::Duration::from_secs(2)),
|
||||
32_000,
|
||||
"the stamp runs on from the status"
|
||||
);
|
||||
assert!(q.take_score_dirty());
|
||||
// A queued status for slot 2 zeroes both clocks too.
|
||||
let mut queued = active.clone();
|
||||
|
||||
Reference in New Issue
Block a user