Synchronized Achievement Window

Expanded the use of the lock mutex already used for loading the player's existing unlock status to guard against races involving the Achievements dialog window reading from data AchievementManager might be in the process of updating. The lock has been exposed publicly and the AchievementsWindow uses it in its UpdateData method, and anywhere else that might modify data used to render that window has also been wrapped with it.
This commit is contained in:
LillyJadeKatrin 2023-06-09 17:05:52 -04:00
parent fbaeaf305b
commit ccc9d0e5ea
3 changed files with 54 additions and 29 deletions

View File

@ -48,7 +48,11 @@ AchievementManager::ResponseType AchievementManager::Login(const std::string& pa
{ {
if (!m_is_runtime_initialized) if (!m_is_runtime_initialized)
return AchievementManager::ResponseType::MANAGER_NOT_INITIALIZED; return AchievementManager::ResponseType::MANAGER_NOT_INITIALIZED;
AchievementManager::ResponseType r_type = VerifyCredentials(password); AchievementManager::ResponseType r_type = AchievementManager::ResponseType::UNKNOWN_FAILURE;
{
std::lock_guard lg{m_lock};
r_type = VerifyCredentials(password);
}
if (m_update_callback) if (m_update_callback)
m_update_callback(); m_update_callback();
return r_type; return r_type;
@ -62,7 +66,10 @@ void AchievementManager::LoginAsync(const std::string& password, const ResponseC
return; return;
} }
m_queue.EmplaceItem([this, password, callback] { m_queue.EmplaceItem([this, password, callback] {
{
std::lock_guard lg{m_lock};
callback(VerifyCredentials(password)); callback(VerifyCredentials(password));
}
if (m_update_callback) if (m_update_callback)
m_update_callback(); m_update_callback();
}); });
@ -154,11 +161,11 @@ void AchievementManager::LoadGameByFilenameAsync(const std::string& iso_path,
} }
const auto fetch_game_data_response = FetchGameData(); const auto fetch_game_data_response = FetchGameData();
m_is_game_loaded = fetch_game_data_response == ResponseType::SUCCESS; if (fetch_game_data_response != ResponseType::SUCCESS)
if (!m_is_game_loaded)
{ {
OSD::AddMessage("Unable to retrieve data from RetroAchievements server.", OSD::AddMessage("Unable to retrieve data from RetroAchievements server.",
OSD::Duration::VERY_LONG, OSD::Color::RED); OSD::Duration::VERY_LONG, OSD::Color::RED);
return;
} }
// Claim the lock, then queue the fetch unlock data calls, then initialize the unlock map in // Claim the lock, then queue the fetch unlock data calls, then initialize the unlock map in
@ -167,6 +174,7 @@ void AchievementManager::LoadGameByFilenameAsync(const std::string& iso_path,
// it. // it.
{ {
std::lock_guard lg{m_lock}; std::lock_guard lg{m_lock};
m_is_game_loaded = true;
LoadUnlockData([](ResponseType r_type) {}); LoadUnlockData([](ResponseType r_type) {});
ActivateDeactivateAchievements(); ActivateDeactivateAchievements();
PointSpread spread = TallyScore(); PointSpread spread = TallyScore();
@ -318,25 +326,33 @@ u32 AchievementManager::MemoryPeeker(u32 address, u32 num_bytes, void* ud)
void AchievementManager::AchievementEventHandler(const rc_runtime_event_t* runtime_event) void AchievementManager::AchievementEventHandler(const rc_runtime_event_t* runtime_event)
{ {
switch (runtime_event->type)
{ {
case RC_RUNTIME_EVENT_ACHIEVEMENT_TRIGGERED: std::lock_guard lg{m_lock};
HandleAchievementTriggeredEvent(runtime_event); switch (runtime_event->type)
break; {
case RC_RUNTIME_EVENT_LBOARD_STARTED: case RC_RUNTIME_EVENT_ACHIEVEMENT_TRIGGERED:
HandleLeaderboardStartedEvent(runtime_event); HandleAchievementTriggeredEvent(runtime_event);
break; break;
case RC_RUNTIME_EVENT_LBOARD_CANCELED: case RC_RUNTIME_EVENT_LBOARD_STARTED:
HandleLeaderboardCanceledEvent(runtime_event); HandleLeaderboardStartedEvent(runtime_event);
break; break;
case RC_RUNTIME_EVENT_LBOARD_TRIGGERED: case RC_RUNTIME_EVENT_LBOARD_CANCELED:
HandleLeaderboardTriggeredEvent(runtime_event); HandleLeaderboardCanceledEvent(runtime_event);
break; break;
case RC_RUNTIME_EVENT_LBOARD_TRIGGERED:
HandleLeaderboardTriggeredEvent(runtime_event);
break;
}
} }
if (m_update_callback) if (m_update_callback)
m_update_callback(); m_update_callback();
} }
std::recursive_mutex* AchievementManager::GetLock()
{
return &m_lock;
}
std::string AchievementManager::GetPlayerDisplayName() const std::string AchievementManager::GetPlayerDisplayName() const
{ {
return IsLoggedIn() ? m_display_name : ""; return IsLoggedIn() ? m_display_name : "";
@ -397,14 +413,17 @@ void AchievementManager::GetAchievementProgress(AchievementId achievement_id, u3
void AchievementManager::CloseGame() void AchievementManager::CloseGame()
{ {
m_is_game_loaded = false; {
m_game_id = 0; std::lock_guard lg{m_lock};
m_queue.Cancel(); m_is_game_loaded = false;
m_unlock_map.clear(); m_game_id = 0;
m_system = nullptr; m_queue.Cancel();
ActivateDeactivateAchievements(); m_unlock_map.clear();
ActivateDeactivateLeaderboards(); m_system = nullptr;
ActivateDeactivateRichPresence(); ActivateDeactivateAchievements();
ActivateDeactivateLeaderboards();
ActivateDeactivateRichPresence();
}
if (m_update_callback) if (m_update_callback)
m_update_callback(); m_update_callback();
} }

View File

@ -84,6 +84,7 @@ public:
u32 MemoryPeeker(u32 address, u32 num_bytes, void* ud); u32 MemoryPeeker(u32 address, u32 num_bytes, void* ud);
void AchievementEventHandler(const rc_runtime_event_t* runtime_event); void AchievementEventHandler(const rc_runtime_event_t* runtime_event);
std::recursive_mutex* GetLock();
std::string GetPlayerDisplayName() const; std::string GetPlayerDisplayName() const;
u32 GetPlayerScore() const; u32 GetPlayerScore() const;
std::string GetGameDisplayName() const; std::string GetGameDisplayName() const;

View File

@ -4,6 +4,8 @@
#ifdef USE_RETRO_ACHIEVEMENTS #ifdef USE_RETRO_ACHIEVEMENTS
#include "DolphinQt/Achievements/AchievementsWindow.h" #include "DolphinQt/Achievements/AchievementsWindow.h"
#include <mutex>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QTabWidget> #include <QTabWidget>
#include <QVBoxLayout> #include <QVBoxLayout>
@ -60,11 +62,14 @@ void AchievementsWindow::ConnectWidgets()
void AchievementsWindow::UpdateData() void AchievementsWindow::UpdateData()
{ {
m_header_widget->UpdateData(); {
m_header_widget->setVisible(AchievementManager::GetInstance()->IsLoggedIn()); std::lock_guard lg{*AchievementManager::GetInstance()->GetLock()};
// Settings tab handles its own updates ... indeed, that calls this m_header_widget->UpdateData();
m_progress_widget->UpdateData(); m_header_widget->setVisible(AchievementManager::GetInstance()->IsLoggedIn());
m_tab_widget->setTabVisible(1, AchievementManager::GetInstance()->IsGameLoaded()); // Settings tab handles its own updates ... indeed, that calls this
m_progress_widget->UpdateData();
m_tab_widget->setTabVisible(1, AchievementManager::GetInstance()->IsGameLoaded());
}
update(); update();
} }