Skip to content

Commit

Permalink
Synchronized Achievement Window
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
LillyJadeKatrin committed Jun 11, 2023
1 parent 5b8326c commit 26d0bd2
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 29 deletions.
67 changes: 43 additions & 24 deletions Source/Core/Core/AchievementManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ AchievementManager::ResponseType AchievementManager::Login(const std::string& pa
{
if (!m_is_runtime_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)
m_update_callback();
return r_type;
Expand All @@ -62,7 +66,10 @@ void AchievementManager::LoginAsync(const std::string& password, const ResponseC
return;
}
m_queue.EmplaceItem([this, password, callback] {
{
std::lock_guard lg{m_lock};
callback(VerifyCredentials(password));
}
if (m_update_callback)
m_update_callback();
});
Expand Down Expand Up @@ -154,11 +161,11 @@ void AchievementManager::LoadGameByFilenameAsync(const std::string& iso_path,
}

const auto fetch_game_data_response = FetchGameData();
m_is_game_loaded = fetch_game_data_response == ResponseType::SUCCESS;
if (!m_is_game_loaded)
if (fetch_game_data_response != ResponseType::SUCCESS)
{
OSD::AddMessage("Unable to retrieve data from RetroAchievements server.",
OSD::Duration::VERY_LONG, OSD::Color::RED);
return;
}

// Claim the lock, then queue the fetch unlock data calls, then initialize the unlock map in
Expand All @@ -167,6 +174,7 @@ void AchievementManager::LoadGameByFilenameAsync(const std::string& iso_path,
// it.
{
std::lock_guard lg{m_lock};
m_is_game_loaded = true;
LoadUnlockData([](ResponseType r_type) {});
ActivateDeactivateAchievements();
PointSpread spread = TallyScore();
Expand Down Expand Up @@ -318,25 +326,33 @@ u32 AchievementManager::MemoryPeeker(u32 address, u32 num_bytes, void* ud)

void AchievementManager::AchievementEventHandler(const rc_runtime_event_t* runtime_event)
{
switch (runtime_event->type)
{
case RC_RUNTIME_EVENT_ACHIEVEMENT_TRIGGERED:
HandleAchievementTriggeredEvent(runtime_event);
break;
case RC_RUNTIME_EVENT_LBOARD_STARTED:
HandleLeaderboardStartedEvent(runtime_event);
break;
case RC_RUNTIME_EVENT_LBOARD_CANCELED:
HandleLeaderboardCanceledEvent(runtime_event);
break;
case RC_RUNTIME_EVENT_LBOARD_TRIGGERED:
HandleLeaderboardTriggeredEvent(runtime_event);
break;
std::lock_guard lg{m_lock};
switch (runtime_event->type)
{
case RC_RUNTIME_EVENT_ACHIEVEMENT_TRIGGERED:
HandleAchievementTriggeredEvent(runtime_event);
break;
case RC_RUNTIME_EVENT_LBOARD_STARTED:
HandleLeaderboardStartedEvent(runtime_event);
break;
case RC_RUNTIME_EVENT_LBOARD_CANCELED:
HandleLeaderboardCanceledEvent(runtime_event);
break;
case RC_RUNTIME_EVENT_LBOARD_TRIGGERED:
HandleLeaderboardTriggeredEvent(runtime_event);
break;
}
}
if (m_update_callback)
m_update_callback();
}

std::recursive_mutex* AchievementManager::GetLock()
{
return &m_lock;
}

std::string AchievementManager::GetPlayerDisplayName() const
{
return IsLoggedIn() ? m_display_name : "";
Expand Down Expand Up @@ -397,14 +413,17 @@ void AchievementManager::GetAchievementProgress(AchievementId achievement_id, u3

void AchievementManager::CloseGame()
{
m_is_game_loaded = false;
m_game_id = 0;
m_queue.Cancel();
m_unlock_map.clear();
m_system = nullptr;
ActivateDeactivateAchievements();
ActivateDeactivateLeaderboards();
ActivateDeactivateRichPresence();
{
std::lock_guard lg{m_lock};
m_is_game_loaded = false;
m_game_id = 0;
m_queue.Cancel();
m_unlock_map.clear();
m_system = nullptr;
ActivateDeactivateAchievements();
ActivateDeactivateLeaderboards();
ActivateDeactivateRichPresence();
}
if (m_update_callback)
m_update_callback();
}
Expand Down
1 change: 1 addition & 0 deletions Source/Core/Core/AchievementManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ class AchievementManager
u32 MemoryPeeker(u32 address, u32 num_bytes, void* ud);
void AchievementEventHandler(const rc_runtime_event_t* runtime_event);

std::recursive_mutex* GetLock();
std::string GetPlayerDisplayName() const;
u32 GetPlayerScore() const;
std::string GetGameDisplayName() const;
Expand Down
15 changes: 10 additions & 5 deletions Source/Core/DolphinQt/Achievements/AchievementsWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#ifdef USE_RETRO_ACHIEVEMENTS
#include "DolphinQt/Achievements/AchievementsWindow.h"

#include <mutex>

#include <QDialogButtonBox>
#include <QTabWidget>
#include <QVBoxLayout>
Expand Down Expand Up @@ -60,11 +62,14 @@ void AchievementsWindow::ConnectWidgets()

void AchievementsWindow::UpdateData()
{
m_header_widget->UpdateData();
m_header_widget->setVisible(AchievementManager::GetInstance()->IsLoggedIn());
// Settings tab handles its own updates ... indeed, that calls this
m_progress_widget->UpdateData();
m_tab_widget->setTabEnabled(1, AchievementManager::GetInstance()->IsGameLoaded());
{
std::lock_guard lg{*AchievementManager::GetInstance()->GetLock()};
m_header_widget->UpdateData();
m_header_widget->setVisible(AchievementManager::GetInstance()->IsLoggedIn());
// Settings tab handles its own updates ... indeed, that calls this
m_progress_widget->UpdateData();
m_tab_widget->setTabEnabled(1, AchievementManager::GetInstance()->IsGameLoaded());
}
update();
}

Expand Down

0 comments on commit 26d0bd2

Please sign in to comment.