Cheevos: Support logging in while disabled

This commit is contained in:
Connor McLaughlin 2021-02-28 19:00:37 +10:00
parent bc9ca302b8
commit 98be448140
5 changed files with 123 additions and 82 deletions

View file

@ -53,6 +53,7 @@ void AchievementSettingsWidget::updateEnableState()
const bool enabled = m_host_interface->GetBoolSettingValue("Cheevos", "Enabled", false); const bool enabled = m_host_interface->GetBoolSettingValue("Cheevos", "Enabled", false);
m_ui.testMode->setEnabled(enabled); m_ui.testMode->setEnabled(enabled);
m_ui.useFirstDiscFromPlaylist->setEnabled(enabled); m_ui.useFirstDiscFromPlaylist->setEnabled(enabled);
m_ui.richPresence->setEnabled(enabled);
} }
void AchievementSettingsWidget::updateLoginState() void AchievementSettingsWidget::updateLoginState()
@ -80,7 +81,7 @@ void AchievementSettingsWidget::updateLoginState()
void AchievementSettingsWidget::onLoginLogoutPressed() void AchievementSettingsWidget::onLoginLogoutPressed()
{ {
if (Cheevos::IsLoggedIn()) if (!m_host_interface->GetStringSettingValue("Cheevos", "Username").empty())
{ {
m_host_interface->executeOnEmulationThread([]() { Cheevos::Logout(); }, true); m_host_interface->executeOnEmulationThread([]() { Cheevos::Logout(); }, true);
updateLoginState(); updateLoginState();

View file

@ -57,7 +57,6 @@ static bool s_use_first_disc_from_playlist = true;
static bool s_hardcode_mode = false; static bool s_hardcode_mode = false;
static bool s_rich_presence_enabled = false; static bool s_rich_presence_enabled = false;
static CommonHostInterface* s_host_interface;
static rc_runtime_t s_rcheevos_runtime; static rc_runtime_t s_rcheevos_runtime;
static std::unique_ptr<FrontendCommon::HTTPDownloader> s_http_downloader; static std::unique_ptr<FrontendCommon::HTTPDownloader> s_http_downloader;
@ -81,11 +80,13 @@ static u32 s_total_image_downloads;
static u32 s_completed_image_downloads; static u32 s_completed_image_downloads;
static bool s_image_download_progress_active; static bool s_image_download_progress_active;
static ALWAYS_INLINE CommonHostInterface* GetHostInterface()
{
return static_cast<CommonHostInterface*>(g_host_interface);
}
static void FormattedError(const char* format, ...) static void FormattedError(const char* format, ...)
{ {
if (!s_host_interface)
return;
std::va_list ap; std::va_list ap;
va_start(ap, format); va_start(ap, format);
@ -95,7 +96,7 @@ static void FormattedError(const char* format, ...)
va_end(ap); va_end(ap);
s_host_interface->AddOSDMessage(str.GetCharArray(), 10.0f); GetHostInterface()->AddOSDMessage(str.GetCharArray(), 10.0f);
Log_ErrorPrint(str.GetCharArray()); Log_ErrorPrint(str.GetCharArray());
} }
@ -195,7 +196,7 @@ static void ClearGameInfo()
g_game_id = 0; g_game_id = 0;
if (had_game) if (had_game)
s_host_interface->OnAchievementsRefreshed(); GetHostInterface()->OnAchievementsRefreshed();
} }
static void ClearGamePath() static void ClearGamePath()
@ -204,7 +205,12 @@ static void ClearGamePath()
std::string().swap(s_game_hash); std::string().swap(s_game_hash);
} }
bool Initialize(CommonHostInterface* hi, bool test_mode, bool use_first_disc_from_playlist, bool enable_rich_presence) static std::string GetUserAgent()
{
return StringUtil::StdStringFromFormat("DuckStation %s", g_scm_tag_str);
}
bool Initialize(bool test_mode, bool use_first_disc_from_playlist, bool enable_rich_presence)
{ {
s_http_downloader = FrontendCommon::HTTPDownloader::Create(); s_http_downloader = FrontendCommon::HTTPDownloader::Create();
if (!s_http_downloader) if (!s_http_downloader)
@ -213,8 +219,7 @@ bool Initialize(CommonHostInterface* hi, bool test_mode, bool use_first_disc_fro
return false; return false;
} }
s_http_downloader->SetUserAgent(StringUtil::StdStringFromFormat("DuckStation %s", g_scm_tag_str)); s_http_downloader->SetUserAgent(GetUserAgent());
s_host_interface = hi;
g_active = true; g_active = true;
s_test_mode = test_mode; s_test_mode = test_mode;
s_use_first_disc_from_playlist = use_first_disc_from_playlist; s_use_first_disc_from_playlist = use_first_disc_from_playlist;
@ -222,8 +227,8 @@ bool Initialize(CommonHostInterface* hi, bool test_mode, bool use_first_disc_fro
rc_runtime_init(&s_rcheevos_runtime); rc_runtime_init(&s_rcheevos_runtime);
s_last_ping_time.Reset(); s_last_ping_time.Reset();
s_username = hi->GetStringSettingValue("Cheevos", "Username"); s_username = GetHostInterface()->GetStringSettingValue("Cheevos", "Username");
s_login_token = hi->GetStringSettingValue("Cheevos", "Token"); s_login_token = GetHostInterface()->GetStringSettingValue("Cheevos", "Token");
s_logged_in = (!s_username.empty() && !s_login_token.empty()); s_logged_in = (!s_username.empty() && !s_login_token.empty());
if (IsLoggedIn() && System::IsValid()) if (IsLoggedIn() && System::IsValid())
@ -255,9 +260,8 @@ void Shutdown()
std::string().swap(s_username); std::string().swap(s_username);
std::string().swap(s_login_token); std::string().swap(s_login_token);
s_logged_in = false; s_logged_in = false;
s_host_interface->OnAchievementsRefreshed(); GetHostInterface()->OnAchievementsRefreshed();
s_host_interface = nullptr;
g_active = false; g_active = false;
rc_runtime_destroy(&s_rcheevos_runtime); rc_runtime_destroy(&s_rcheevos_runtime);
@ -324,25 +328,40 @@ static void LoginASyncCallback(s32 status_code, const FrontendCommon::HTTPDownlo
return; return;
} }
s_username = doc["User"].GetString(); std::string username = doc["User"].GetString();
s_login_token = doc["Token"].GetString(); std::string login_token = doc["Token"].GetString();
s_logged_in = true;
s_host_interface->ReportFormattedMessage("Logged into RetroAchievements using username '%s'.", s_username.c_str());
// save to config // save to config
std::lock_guard<std::recursive_mutex> guard(s_host_interface->GetSettingsLock());
{ {
s_host_interface->GetSettingsInterface()->SetStringValue("Cheevos", "Username", s_username.c_str()); std::lock_guard<std::recursive_mutex> guard(GetHostInterface()->GetSettingsLock());
s_host_interface->GetSettingsInterface()->SetStringValue("Cheevos", "Token", s_login_token.c_str()); GetHostInterface()->GetSettingsInterface()->SetStringValue("Cheevos", "Username", username.c_str());
s_host_interface->GetSettingsInterface()->SetStringValue( GetHostInterface()->GetSettingsInterface()->SetStringValue("Cheevos", "Token", login_token.c_str());
GetHostInterface()->GetSettingsInterface()->SetStringValue(
"Cheevos", "LoginTimestamp", TinyString::FromFormat("%" PRIu64, Timestamp::Now().AsUnixTimestamp())); "Cheevos", "LoginTimestamp", TinyString::FromFormat("%" PRIu64, Timestamp::Now().AsUnixTimestamp()));
s_host_interface->GetSettingsInterface()->Save(); GetHostInterface()->GetSettingsInterface()->Save();
} }
GetHostInterface()->ReportFormattedMessage("Logged into RetroAchievements using username '%s'.", username.c_str());
if (g_active)
{
s_username = std::move(username);
s_login_token = std::move(login_token);
s_logged_in = true;
// If we have a game running, set it up. // If we have a game running, set it up.
if (System::IsValid()) if (System::IsValid())
GameChanged(); GameChanged();
}
}
static void SendLogin(const char* username, const char* password, FrontendCommon::HTTPDownloader* http_downloader)
{
char url[256] = {};
int res = rc_url_login_with_password(url, sizeof(url), username, password);
Assert(res == 0);
http_downloader->CreateRequest(url, LoginASyncCallback);
} }
bool LoginAsync(const char* username, const char* password) bool LoginAsync(const char* username, const char* password)
@ -352,41 +371,55 @@ bool LoginAsync(const char* username, const char* password)
if (s_logged_in || std::strlen(username) == 0 || std::strlen(password) == 0) if (s_logged_in || std::strlen(username) == 0 || std::strlen(password) == 0)
return false; return false;
char url[256] = {}; SendLogin(username, password, s_http_downloader.get());
int res = rc_url_login_with_password(url, sizeof(url), username, password);
Assert(res == 0);
s_http_downloader->CreateRequest(url, LoginASyncCallback);
return true; return true;
} }
bool Login(const char* username, const char* password) bool Login(const char* username, const char* password)
{ {
if (g_active)
{
if (!LoginAsync(username, password)) if (!LoginAsync(username, password))
return false; return false;
s_http_downloader->WaitForAllRequests(); s_http_downloader->WaitForAllRequests();
return IsLoggedIn(); return IsLoggedIn();
}
// create a temporary downloader if we're not initialized
Assert(!g_active);
std::unique_ptr<FrontendCommon::HTTPDownloader> http_downloader = FrontendCommon::HTTPDownloader::Create();
if (!http_downloader)
return false;
http_downloader->SetUserAgent(GetUserAgent());
SendLogin(username, password, http_downloader.get());
http_downloader->WaitForAllRequests();
return !GetHostInterface()->GetStringSettingValue("Cheevos", "Token").empty();
} }
void Logout() void Logout()
{ {
if (g_active)
{
s_http_downloader->WaitForAllRequests(); s_http_downloader->WaitForAllRequests();
if (!s_logged_in) if (s_logged_in)
return; {
ClearGameInfo(); ClearGameInfo();
std::string().swap(s_username); std::string().swap(s_username);
std::string().swap(s_login_token); std::string().swap(s_login_token);
s_logged_in = false; s_logged_in = false;
s_host_interface->OnAchievementsRefreshed(); GetHostInterface()->OnAchievementsRefreshed();
}
}
// remove from config // remove from config
std::lock_guard<std::recursive_mutex> guard(s_host_interface->GetSettingsLock()); std::lock_guard<std::recursive_mutex> guard(GetHostInterface()->GetSettingsLock());
{ {
s_host_interface->GetSettingsInterface()->DeleteValue("Cheevos", "Username"); GetHostInterface()->GetSettingsInterface()->DeleteValue("Cheevos", "Username");
s_host_interface->GetSettingsInterface()->DeleteValue("Cheevos", "Token"); GetHostInterface()->GetSettingsInterface()->DeleteValue("Cheevos", "Token");
s_host_interface->GetSettingsInterface()->Save(); GetHostInterface()->GetSettingsInterface()->Save();
} }
} }
@ -408,7 +441,7 @@ static void UpdateImageDownloadProgress()
return; return;
} }
if (!s_host_interface->IsFullscreenUIEnabled()) if (!GetHostInterface()->IsFullscreenUIEnabled())
return; return;
std::string message("Downloading achievement resources..."); std::string message("Downloading achievement resources...");
@ -463,7 +496,7 @@ static std::string GetBadgeImageFilename(const char* badge_name, bool locked, bo
// well, this comes from the internet.... :) // well, this comes from the internet.... :)
SmallString clean_name(badge_name); SmallString clean_name(badge_name);
FileSystem::SanitizeFileName(clean_name); FileSystem::SanitizeFileName(clean_name);
return s_host_interface->GetUserDirectoryRelativePath("cache" FS_OSPATH_SEPARATOR_STR return GetHostInterface()->GetUserDirectoryRelativePath("cache" FS_OSPATH_SEPARATOR_STR
"achievement_badge" FS_OSPATH_SEPARATOR_STR "%s%s.png", "achievement_badge" FS_OSPATH_SEPARATOR_STR "%s%s.png",
clean_name.GetCharArray(), locked ? "_lock" : ""); clean_name.GetCharArray(), locked ? "_lock" : "");
} }
@ -548,7 +581,7 @@ static void GetUserUnlocksCallback(s32 status_code, const FrontendCommon::HTTPDo
DisplayAchievementSummary(); DisplayAchievementSummary();
SendPlaying(); SendPlaying();
SendPing(); SendPing();
s_host_interface->OnAchievementsRefreshed(); GetHostInterface()->OnAchievementsRefreshed();
} }
static void GetUserUnlocks() static void GetUserUnlocks()
@ -593,7 +626,7 @@ static void GetPatchesCallback(s32 status_code, const FrontendCommon::HTTPDownlo
std::string icon_name(GetOptionalString(patch_data, "ImageIcon")); std::string icon_name(GetOptionalString(patch_data, "ImageIcon"));
if (!icon_name.empty()) if (!icon_name.empty())
{ {
s_game_icon = s_host_interface->GetUserDirectoryRelativePath( s_game_icon = GetHostInterface()->GetUserDirectoryRelativePath(
"cache" FS_OSPATH_SEPARATOR_STR "achievement_gameicon" FS_OSPATH_SEPARATOR_STR "%u.png", g_game_id); "cache" FS_OSPATH_SEPARATOR_STR "achievement_gameicon" FS_OSPATH_SEPARATOR_STR "%u.png", g_game_id);
if (!FileSystem::FileExists(s_game_icon.c_str())) if (!FileSystem::FileExists(s_game_icon.c_str()))
{ {
@ -628,22 +661,22 @@ static void GetPatchesCallback(s32 status_code, const FrontendCommon::HTTPDownlo
continue; continue;
} }
Achievement achievement; Achievement cheevo;
achievement.id = id; cheevo.id = id;
achievement.memaddr = memaddr; cheevo.memaddr = memaddr;
achievement.title = std::move(title); cheevo.title = std::move(title);
achievement.description = std::move(description); cheevo.description = std::move(description);
achievement.locked = true; cheevo.locked = true;
achievement.active = false; cheevo.active = false;
achievement.points = points; cheevo.points = points;
if (!badge_name.empty()) if (!badge_name.empty())
{ {
achievement.locked_badge_path = ResolveBadgePath(badge_name.c_str(), true); cheevo.locked_badge_path = ResolveBadgePath(badge_name.c_str(), true);
achievement.unlocked_badge_path = ResolveBadgePath(badge_name.c_str(), false); cheevo.unlocked_badge_path = ResolveBadgePath(badge_name.c_str(), false);
} }
s_achievements.push_back(std::move(achievement)); s_achievements.push_back(std::move(cheevo));
} }
} }
@ -661,7 +694,7 @@ static void GetPatchesCallback(s32 status_code, const FrontendCommon::HTTPDownlo
Log_InfoPrintf("Game Title: %s", s_game_title.c_str()); Log_InfoPrintf("Game Title: %s", s_game_title.c_str());
Log_InfoPrintf("Game Developer: %s", s_game_developer.c_str()); Log_InfoPrintf("Game Developer: %s", s_game_developer.c_str());
Log_InfoPrintf("Game Publisher: %s", s_game_publisher.c_str()); Log_InfoPrintf("Game Publisher: %s", s_game_publisher.c_str());
Log_InfoPrintf("Achievements: %u", s_achievements.size()); Log_InfoPrintf("Achievements: %zu", s_achievements.size());
if (!s_achievements.empty() || s_has_rich_presence) if (!s_achievements.empty() || s_has_rich_presence)
{ {
@ -673,7 +706,7 @@ static void GetPatchesCallback(s32 status_code, const FrontendCommon::HTTPDownlo
{ {
ActivateLockedAchievements(); ActivateLockedAchievements();
DisplayAchievementSummary(); DisplayAchievementSummary();
s_host_interface->OnAchievementsRefreshed(); GetHostInterface()->OnAchievementsRefreshed();
} }
} }
else else
@ -775,6 +808,19 @@ void GameChanged(const std::string& path, CDImage* image)
if (s_game_path == path) if (s_game_path == path)
return; return;
std::string game_hash;
if (image)
{
game_hash = GetGameHash(image);
if (s_game_hash == game_hash)
{
// only the path has changed - different format/save state/etc.
Log_InfoPrintf("Detected path change from '%s' to '%s'", s_game_path.c_str(), path.c_str());
s_game_path = path;
return;
}
}
s_http_downloader->WaitForAllRequests(); s_http_downloader->WaitForAllRequests();
const u32 playlist_count = System::GetMediaPlaylistCount(); const u32 playlist_count = System::GetMediaPlaylistCount();
@ -803,15 +849,14 @@ void GameChanged(const std::string& path, CDImage* image)
ClearGameInfo(); ClearGameInfo();
ClearGamePath(); ClearGamePath();
s_game_path = path; s_game_path = path;
s_game_hash = std::move(game_hash);
if (!image) if (!image)
return; return;
#if 1
s_game_hash = GetGameHash(image);
if (s_game_hash.empty()) if (s_game_hash.empty())
{ {
s_host_interface->AddOSDMessage( GetHostInterface()->AddOSDMessage(
s_host_interface->TranslateStdString("OSDMessage", "Failed to read executable from disc. Achievements disabled."), GetHostInterface()->TranslateStdString("OSDMessage", "Failed to read executable from disc. Achievements disabled."),
10.0f); 10.0f);
return; return;
} }
@ -821,10 +866,6 @@ void GameChanged(const std::string& path, CDImage* image)
Assert(res == 0); Assert(res == 0);
s_http_downloader->CreateRequest(url, GetGameIdCallback); s_http_downloader->CreateRequest(url, GetGameIdCallback);
#else
g_game_id = 10434;
GetPatches();
#endif
} }
static void SendPlayingCallback(s32 status_code, const FrontendCommon::HTTPDownloader::Request::Data& data) static void SendPlayingCallback(s32 status_code, const FrontendCommon::HTTPDownloader::Request::Data& data)
@ -857,7 +898,7 @@ static void UpdateRichPresence()
const bool had_rich_presence = !s_rich_presence_string.empty(); const bool had_rich_presence = !s_rich_presence_string.empty();
s_rich_presence_string.clear(); s_rich_presence_string.clear();
if (had_rich_presence) if (had_rich_presence)
s_host_interface->OnAchievementsRefreshed(); GetHostInterface()->OnAchievementsRefreshed();
return; return;
} }
@ -866,7 +907,7 @@ static void UpdateRichPresence()
return; return;
s_rich_presence_string.assign(buffer); s_rich_presence_string.assign(buffer);
s_host_interface->OnAchievementsRefreshed(); GetHostInterface()->OnAchievementsRefreshed();
} }
static void SendPingCallback(s32 status_code, const FrontendCommon::HTTPDownloader::Request::Data& data) static void SendPingCallback(s32 status_code, const FrontendCommon::HTTPDownloader::Request::Data& data)

View file

@ -4,8 +4,6 @@
#include <string> #include <string>
class CDImage; class CDImage;
class CommonHostInterface;
class SettingsInterface;
namespace Cheevos { namespace Cheevos {
@ -40,7 +38,7 @@ ALWAYS_INLINE u32 GetGameID()
return g_game_id; return g_game_id;
} }
bool Initialize(CommonHostInterface* hi, bool test_mode, bool use_first_disc_from_playlist, bool enable_rich_presence); bool Initialize(bool test_mode, bool use_first_disc_from_playlist, bool enable_rich_presence);
void Reset(); void Reset();
void Shutdown(); void Shutdown();
void Update(); void Update();

View file

@ -3311,7 +3311,7 @@ void CommonHostInterface::UpdateCheevosActive()
Cheevos::Shutdown(); Cheevos::Shutdown();
if (cheevos_enabled) if (cheevos_enabled)
{ {
if (!Cheevos::Initialize(this, cheevos_test_mode, cheevos_use_first_disc_from_playlist, cheevos_rich_presence)) if (!Cheevos::Initialize(cheevos_test_mode, cheevos_use_first_disc_from_playlist, cheevos_rich_presence))
ReportError("Failed to initialize cheevos after settings change."); ReportError("Failed to initialize cheevos after settings change.");
} }
} }

View file

@ -2,6 +2,7 @@
#include "common/types.h" #include "common/types.h"
#include <atomic> #include <atomic>
#include <functional> #include <functional>
#include <memory>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <vector> #include <vector>