Achievements: Attempt relogin on system boot

And display an error notification if login fails, instead of
invalidating the token.
This commit is contained in:
Stenzek 2025-01-12 17:13:28 +10:00
parent 0f76543685
commit e43d7046ba
No known key found for this signature in database
5 changed files with 70 additions and 25 deletions

View File

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 122.88 111.24"><defs><style>.cls-1{fill:#fec901;fill-rule:evenodd;}.cls-2{fill:#010101;}</style></defs><title>risk</title><path d="M2.5,85l43-74.41h0a22.59,22.59,0,0,1,8-8.35,15.72,15.72,0,0,1,16,0,22.52,22.52,0,0,1,7.93,8.38l.23.44,42.08,73.07a20.91,20.91,0,0,1,3,10.84A16.44,16.44,0,0,1,121,102.4a15.45,15.45,0,0,1-5.74,6,21,21,0,0,1-11.35,2.78v0H17.7c-.21,0-.43,0-.64,0a19,19,0,0,1-7.83-1.74,15.83,15.83,0,0,1-6.65-5.72A16.26,16.26,0,0,1,0,95.18a21.66,21.66,0,0,1,2.2-9.62c.1-.2.2-.4.31-.59Z"/><path class="cls-1" d="M9.09,88.78l43-74.38c5.22-8.94,13.49-9.2,18.81,0l42.32,73.49c4.12,6.79,2.41,15.9-9.31,15.72H17.7C9.78,103.79,5,97.44,9.09,88.78Z"/><path class="cls-2" d="M57.55,83.15a5.47,5.47,0,0,1,5.85-1.22,5.65,5.65,0,0,1,2,1.3A5.49,5.49,0,0,1,67,86.77a5.12,5.12,0,0,1-.08,1.4,5.22,5.22,0,0,1-.42,1.34,5.51,5.51,0,0,1-5.2,3.25,5.63,5.63,0,0,1-2.26-.53,5.51,5.51,0,0,1-2.81-2.92A6,6,0,0,1,55.9,88a5.28,5.28,0,0,1,0-1.31h0a6,6,0,0,1,.56-2,4.6,4.6,0,0,1,1.14-1.56Zm8.12-10.21c-.19,4.78-8.28,4.78-8.46,0-.82-8.19-2.92-27.63-2.85-35.32.07-2.37,2-3.78,4.55-4.31a11.65,11.65,0,0,1,2.48-.25,12.54,12.54,0,0,1,2.5.25c2.59.56,4.63,2,4.63,4.43V38l-2.84,35Z"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -137,6 +137,7 @@ static void ReportRCError(int err, fmt::format_string<T...> fmt, T&&... args);
static void ClearGameInfo();
static void ClearGameHash();
static std::string GetGameHash(CDImage* image);
static bool TryLoggingInWithToken();
static void SetHardcoreMode(bool enabled, bool force_display_message);
static bool IsLoggedInOrLoggingIn();
static bool CanEnableHardcoreMode();
@ -583,24 +584,7 @@ bool Achievements::Initialize()
if (System::IsValid())
IdentifyGame(System::GetDiscPath(), nullptr);
std::string username = Host::GetBaseStringSettingValue("Cheevos", "Username");
std::string api_token = Host::GetBaseStringSettingValue("Cheevos", "Token");
if (!username.empty() && !api_token.empty())
{
INFO_LOG("Attempting login with user '{}'...", username);
// If we can't decrypt the token, it was an old config and we need to re-login.
if (const TinyString decrypted_api_token = DecryptLoginToken(api_token, username); !decrypted_api_token.empty())
{
s_state.login_request = rc_client_begin_login_with_token(
s_state.client, username.c_str(), decrypted_api_token.c_str(), ClientLoginWithTokenCallback, nullptr);
}
else
{
WARNING_LOG("Invalid encrypted login token, requesitng a new one.");
Host::OnAchievementsLoginRequested(LoginRequestReason::TokenInvalid);
}
}
TryLoggingInWithToken();
// Hardcore mode isn't enabled when achievements first starts, if a game is already running.
if (System::IsValid() && IsLoggedInOrLoggingIn() && g_settings.achievements_hardcore_mode)
@ -649,6 +633,36 @@ void Achievements::DestroyClient(rc_client_t** client, std::unique_ptr<HTTPDownl
http->reset();
}
bool Achievements::TryLoggingInWithToken()
{
std::string username = Host::GetBaseStringSettingValue("Cheevos", "Username");
std::string api_token = Host::GetBaseStringSettingValue("Cheevos", "Token");
if (username.empty() || api_token.empty())
return false;
INFO_LOG("Attempting token login with user '{}'...", username);
// If we can't decrypt the token, it was an old config and we need to re-login.
if (const TinyString decrypted_api_token = DecryptLoginToken(api_token, username); !decrypted_api_token.empty())
{
s_state.login_request = rc_client_begin_login_with_token(
s_state.client, username.c_str(), decrypted_api_token.c_str(), ClientLoginWithTokenCallback, nullptr);
if (!s_state.login_request)
{
WARNING_LOG("Creating login request failed.");
return false;
}
return true;
}
else
{
WARNING_LOG("Invalid encrypted login token, requesitng a new one.");
Host::OnAchievementsLoginRequested(LoginRequestReason::TokenInvalid);
return false;
}
}
void Achievements::UpdateSettings(const Settings& old_config)
{
if (IsUsingRAIntegration())
@ -976,7 +990,7 @@ void Achievements::UpdateRichPresence(std::unique_lock<std::recursive_mutex>& lo
lock.lock();
}
void Achievements::GameChanged(const std::string& path, CDImage* image)
void Achievements::GameChanged(const std::string& path, CDImage* image, bool booting)
{
std::unique_lock lock(s_state.mutex);
@ -984,6 +998,15 @@ void Achievements::GameChanged(const std::string& path, CDImage* image)
return;
IdentifyGame(path, image);
// if we're not logged in, and there's no login request, retry logging in
// this'll happen if we had no network connection on startup, but gained it before starting a game.
// follow the same order as Initialize() - identify, then log in
if (!IsLoggedInOrLoggingIn() && booting)
{
WARNING_LOG("Not logged in on game booting, trying again.");
TryLoggingInWithToken();
}
}
void Achievements::IdentifyGame(const std::string& path, CDImage* image)
@ -1985,12 +2008,32 @@ void Achievements::ClientLoginWithTokenCallback(int result, const char* error_me
{
s_state.login_request = nullptr;
if (result != RC_OK)
if (result == RC_INVALID_CREDENTIALS || result == RC_EXPIRED_TOKEN)
{
ReportFmtError("Login failed: {}", error_message);
ERROR_LOG("Login failed due to invalid token: {}: {}", rc_error_str(result), error_message);
Host::OnAchievementsLoginRequested(LoginRequestReason::TokenInvalid);
return;
}
else if (result != RC_OK)
{
ERROR_LOG("Login failed: {}: {}", rc_error_str(result), error_message);
// only display user error if they've started a game
if (System::IsValid())
{
std::string message =
fmt::format("Achievement unlocks will not be submitted for this session.\nError: {}", error_message);
GPUThread::RunOnThread([message = std::move(message)]() mutable {
if (!GPUThread::HasGPUBackend() || !FullscreenUI::Initialize())
return;
ImGuiFullscreen::AddNotification("AchievementsLoginFailed", Host::OSD_ERROR_DURATION,
"RetroAchievements Login Failed", std::move(message), "images/warning.svg");
});
}
return;
}
ShowLoginSuccess(client);

View File

@ -72,7 +72,7 @@ bool Login(const char* username, const char* password, Error* error);
void Logout();
/// Called when the system changes game, or is booting.
void GameChanged(const std::string& path, CDImage* image);
void GameChanged(const std::string& path, CDImage* image, bool booting);
/// Re-enables hardcore mode if it is enabled in the settings.
bool ResetHardcoreMode(bool is_booting);

View File

@ -2014,7 +2014,7 @@ void System::ClearRunningGame()
Host::OnGameChanged(s_state.running_game_path, s_state.running_game_serial, s_state.running_game_title);
Achievements::GameChanged(s_state.running_game_path, nullptr);
Achievements::GameChanged(s_state.running_game_path, nullptr, false);
UpdateRichPresence(true);
}
@ -4175,7 +4175,7 @@ void System::UpdateRunningGame(const std::string& path, CDImage* image, bool boo
if (booting)
Achievements::ResetHardcoreMode(true);
Achievements::GameChanged(s_state.running_game_path, image);
Achievements::GameChanged(s_state.running_game_path, image, booting);
// game layer reloads cheats, but only the active list, we need new files
Cheats::ReloadCheats(true, false, false, true);

View File

@ -3219,7 +3219,8 @@ void ImGuiFullscreen::DrawNotifications(ImVec2& position, float spacing)
const ImVec2 badge_max(badge_min.x + badge_size, badge_min.y + badge_size);
if (!notif.badge_path.empty())
{
GPUTexture* tex = GetCachedTexture(notif.badge_path.c_str());
GPUTexture* tex =
GetCachedTexture(notif.badge_path.c_str(), static_cast<u32>(badge_size), static_cast<u32>(badge_size));
if (tex)
{
dl->AddImage(tex, badge_min, badge_max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f),