mirror of
https://github.com/stenzek/duckstation.git
synced 2025-06-08 04:25:37 +00:00
Achievements: Attempt relogin on system boot
And display an error notification if login fails, instead of invalidating the token.
This commit is contained in:
parent
0f76543685
commit
e43d7046ba
1
data/resources/images/warning.svg
Normal file
1
data/resources/images/warning.svg
Normal 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 |
@ -137,6 +137,7 @@ static void ReportRCError(int err, fmt::format_string<T...> fmt, T&&... args);
|
|||||||
static void ClearGameInfo();
|
static void ClearGameInfo();
|
||||||
static void ClearGameHash();
|
static void ClearGameHash();
|
||||||
static std::string GetGameHash(CDImage* image);
|
static std::string GetGameHash(CDImage* image);
|
||||||
|
static bool TryLoggingInWithToken();
|
||||||
static void SetHardcoreMode(bool enabled, bool force_display_message);
|
static void SetHardcoreMode(bool enabled, bool force_display_message);
|
||||||
static bool IsLoggedInOrLoggingIn();
|
static bool IsLoggedInOrLoggingIn();
|
||||||
static bool CanEnableHardcoreMode();
|
static bool CanEnableHardcoreMode();
|
||||||
@ -583,24 +584,7 @@ bool Achievements::Initialize()
|
|||||||
if (System::IsValid())
|
if (System::IsValid())
|
||||||
IdentifyGame(System::GetDiscPath(), nullptr);
|
IdentifyGame(System::GetDiscPath(), nullptr);
|
||||||
|
|
||||||
std::string username = Host::GetBaseStringSettingValue("Cheevos", "Username");
|
TryLoggingInWithToken();
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hardcore mode isn't enabled when achievements first starts, if a game is already running.
|
// Hardcore mode isn't enabled when achievements first starts, if a game is already running.
|
||||||
if (System::IsValid() && IsLoggedInOrLoggingIn() && g_settings.achievements_hardcore_mode)
|
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();
|
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)
|
void Achievements::UpdateSettings(const Settings& old_config)
|
||||||
{
|
{
|
||||||
if (IsUsingRAIntegration())
|
if (IsUsingRAIntegration())
|
||||||
@ -976,7 +990,7 @@ void Achievements::UpdateRichPresence(std::unique_lock<std::recursive_mutex>& lo
|
|||||||
lock.lock();
|
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);
|
std::unique_lock lock(s_state.mutex);
|
||||||
|
|
||||||
@ -984,6 +998,15 @@ void Achievements::GameChanged(const std::string& path, CDImage* image)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
IdentifyGame(path, image);
|
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)
|
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;
|
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);
|
Host::OnAchievementsLoginRequested(LoginRequestReason::TokenInvalid);
|
||||||
return;
|
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);
|
ShowLoginSuccess(client);
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ bool Login(const char* username, const char* password, Error* error);
|
|||||||
void Logout();
|
void Logout();
|
||||||
|
|
||||||
/// Called when the system changes game, or is booting.
|
/// 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.
|
/// Re-enables hardcore mode if it is enabled in the settings.
|
||||||
bool ResetHardcoreMode(bool is_booting);
|
bool ResetHardcoreMode(bool is_booting);
|
||||||
|
@ -2014,7 +2014,7 @@ void System::ClearRunningGame()
|
|||||||
|
|
||||||
Host::OnGameChanged(s_state.running_game_path, s_state.running_game_serial, s_state.running_game_title);
|
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);
|
UpdateRichPresence(true);
|
||||||
}
|
}
|
||||||
@ -4175,7 +4175,7 @@ void System::UpdateRunningGame(const std::string& path, CDImage* image, bool boo
|
|||||||
if (booting)
|
if (booting)
|
||||||
Achievements::ResetHardcoreMode(true);
|
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
|
// game layer reloads cheats, but only the active list, we need new files
|
||||||
Cheats::ReloadCheats(true, false, false, true);
|
Cheats::ReloadCheats(true, false, false, true);
|
||||||
|
@ -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);
|
const ImVec2 badge_max(badge_min.x + badge_size, badge_min.y + badge_size);
|
||||||
if (!notif.badge_path.empty())
|
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)
|
if (tex)
|
||||||
{
|
{
|
||||||
dl->AddImage(tex, badge_min, badge_max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f),
|
dl->AddImage(tex, badge_min, badge_max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user