clockRewindBlockedCount top-level property

int clockRewindBlockedCount
getter/setter pair

A "never goes backward" clock — a monotonic day/ms clock shared by every time-based reward system (daily reward, streaks, season...). It's a standalone helper here, rather than logic scattered across each place that needs it, so every date-based reward system goes through the same protection layer.

What this defends against: the "advance the clock → claim the reward → wind it back → repeat" loop. Once clamped, winding the clock back has no effect, so every cheat attempt permanently burns the player's real future time (losing the corresponding daily/streak/season checkpoint). That's the strongest protection a client can offer without a trusted server time source.

What this does NOT defend against: jumping the clock forward one-way. So don't apply this clamp to anything where "staying in the future" is exactly what the cheater wants — e.g. a weekend event: setting the device to Saturday and leaving it there already achieves their goal, and a monotonic clamp would make that state permanent, which makes things worse. Counts how many times a clock-rewind attempt was actually blocked by the clamp (i.e. current < maxSeen, the device clock went strictly backward) in nowMsClamped/todayEpochDayClamped. Not incremented on the normal "clock moved forward" path, nor on a same-value re-read (current == maxSeen, e.g. calling again later the same day) — neither is a rewind attempt (BUG-48). Exposed for the debug/QA overlay to show as a live diagnostic; tests reset it directly.

Implementation

/// Counts how many times a clock-rewind attempt was actually blocked by the
/// clamp (i.e. `current < maxSeen`, the device clock went strictly
/// backward) in [nowMsClamped]/[todayEpochDayClamped]. Not incremented on
/// the normal "clock moved forward" path, nor on a same-value re-read
/// (`current == maxSeen`, e.g. calling again later the same day) — neither
/// is a rewind attempt (BUG-48). Exposed for the debug/QA overlay to show
/// as a live diagnostic; tests reset it directly.
int clockRewindBlockedCount = 0;