fix(app): advance logic timers by wall-clock time
Logic timers advanced by io.DeltaTime, which update() clamps to 0.25 s so animations don't jump after a pause. That matched real time only while the main loop rendered continuously: with frames seconds apart, a timer fed the clamped delta runs up to 8x slow. Affected: the refresh scheduler, the seed-migration poll, the chat fast scan, the encryption check, the loading and shutdown timers, the lock-screen countdowns, the Settings debug gate and the mining benchmark. - App::logicDeltaTime(): steady-clock seconds since the previous update(), capped at 6 s so a sleep/resume fires each timer once instead of replaying the gap. Visual interpolation keeps the clamped io.DeltaTime. - The refresh timers tick before the RPC results are drained, so a hold that a callback arms (the connect-retry backoff) starts fresh instead of being charged the gap before it existed. - The shutdown timer is measured from shutdown_start_time_: beginShutdown runs from SDL event handling, so an accumulated delta would count the idle gap before the close request. - The lock-screen error and lockout countdowns subtract only the time since they were armed: a failed unlock arms them from an RPC result, which can land on the frame that ends an idle gap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -472,6 +472,7 @@ void App::applyUnlockFailure(const std::string& errorMessage) {
|
||||
lock_attempts_++;
|
||||
lock_error_msg_ = errorMessage;
|
||||
lock_error_timer_ = 3.0f;
|
||||
lock_timer_armed_at_ = std::chrono::steady_clock::now();
|
||||
memset(lock_passphrase_buf_, 0, sizeof(lock_passphrase_buf_));
|
||||
const float baseDelay = ui::schema::UI().drawElement("security", "lockout-base-delay").sizeOr(2.0f);
|
||||
const int maxAttempts = (int)ui::schema::UI().drawElement("security", "max-attempts-before-lockout").sizeOr(5.0f);
|
||||
@@ -963,9 +964,14 @@ void App::renderLockScreen() {
|
||||
cy += ts.y + 20.0f * dp;
|
||||
}
|
||||
|
||||
// Count down only the time since the timers were armed: a failed unlock arms them from an RPC result,
|
||||
// which can land on a frame that ends an idle gap the countdown would otherwise lose at once.
|
||||
const float lockDt = std::min(logic_dt_, std::chrono::duration<float>(
|
||||
std::chrono::steady_clock::now() - lock_timer_armed_at_).count());
|
||||
|
||||
// Lockout timer
|
||||
if (lock_lockout_timer_ > 0.0f) {
|
||||
lock_lockout_timer_ -= ImGui::GetIO().DeltaTime;
|
||||
lock_lockout_timer_ -= lockDt;
|
||||
if (lock_lockout_timer_ < 0) lock_lockout_timer_ = 0;
|
||||
|
||||
char msg[128];
|
||||
@@ -1072,7 +1078,7 @@ void App::renderLockScreen() {
|
||||
|
||||
// Error message
|
||||
if (!lock_error_msg_.empty() && lock_error_timer_ > 0) {
|
||||
lock_error_timer_ -= ImGui::GetIO().DeltaTime;
|
||||
lock_error_timer_ -= lockDt;
|
||||
ImVec2 es = captionFont->CalcTextSizeA(captionFont->LegacySize, FLT_MAX, 0, lock_error_msg_.c_str());
|
||||
dl->AddText(captionFont, captionFont->LegacySize,
|
||||
ImVec2(cardX + (cardW - es.x) * 0.5f, cy), ui::material::Error(),
|
||||
@@ -1151,6 +1157,7 @@ void App::renderLockScreen() {
|
||||
lock_error_msg_ = TR("pin_not_set");
|
||||
lock_use_pin_ = false;
|
||||
lock_error_timer_ = 3.0f;
|
||||
lock_timer_armed_at_ = std::chrono::steady_clock::now();
|
||||
} else {
|
||||
applyUnlockFailure(TR("incorrect_pin"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user