Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #11878 from LillyJadeKatrin/retroachievements-achi…
…evements-tab

RetroAchievements - Achievement Progress Tab
  • Loading branch information
AdmiralCurtiss committed Jul 3, 2023
2 parents da1a3d3 + ccc9d0e commit da2784a
Show file tree
Hide file tree
Showing 10 changed files with 535 additions and 64 deletions.
180 changes: 131 additions & 49 deletions Source/Core/Core/AchievementManager.cpp
Expand Up @@ -38,11 +38,24 @@ void AchievementManager::Init()
}
}

void AchievementManager::SetUpdateCallback(UpdateCallback callback)
{
m_update_callback = std::move(callback);
m_update_callback();
}

AchievementManager::ResponseType AchievementManager::Login(const std::string& password)
{
if (!m_is_runtime_initialized)
return AchievementManager::ResponseType::MANAGER_NOT_INITIALIZED;
return 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;
}

void AchievementManager::LoginAsync(const std::string& password, const ResponseCallback& callback)
Expand All @@ -52,7 +65,14 @@ void AchievementManager::LoginAsync(const std::string& password, const ResponseC
callback(AchievementManager::ResponseType::MANAGER_NOT_INITIALIZED);
return;
}
m_queue.EmplaceItem([this, password, callback] { callback(VerifyCredentials(password)); });
m_queue.EmplaceItem([this, password, callback] {
{
std::lock_guard lg{m_lock};
callback(VerifyCredentials(password));
}
if (m_update_callback)
m_update_callback();
});
}

bool AchievementManager::IsLoggedIn() const
Expand Down Expand Up @@ -141,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 @@ -154,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 All @@ -179,10 +200,17 @@ void AchievementManager::LoadGameByFilenameAsync(const std::string& iso_path,
// Reset this to zero so that RP immediately triggers on the first frame
m_last_ping_time = 0;

if (m_update_callback)
m_update_callback();
callback(fetch_game_data_response);
});
}

bool AchievementManager::IsGameLoaded() const
{
return m_is_game_loaded;
}

void AchievementManager::LoadUnlockData(const ResponseCallback& callback)
{
m_queue.EmplaceItem([this, callback] {
Expand All @@ -194,6 +222,8 @@ void AchievementManager::LoadUnlockData(const ResponseCallback& callback)
}

callback(FetchUnlockData(false));
if (m_update_callback)
m_update_callback();
});
}

Expand Down Expand Up @@ -296,39 +326,114 @@ 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 : "";
}

u32 AchievementManager::GetPlayerScore() const
{
return IsLoggedIn() ? m_player_score : 0;
}

std::string AchievementManager::GetGameDisplayName() const
{
return IsGameLoaded() ? m_game_data.title : "";
}

AchievementManager::PointSpread AchievementManager::TallyScore() const
{
PointSpread spread{};
if (!IsGameLoaded())
return spread;
for (const auto& entry : m_unlock_map)
{
u32 points = entry.second.points;
spread.total_count++;
spread.total_points += points;
if (entry.second.remote_unlock_status == UnlockStatus::UnlockType::HARDCORE ||
(hardcore_mode_enabled && entry.second.session_unlock_count > 0))
{
spread.hard_unlocks++;
spread.hard_points += points;
}
else if (entry.second.remote_unlock_status == UnlockStatus::UnlockType::SOFTCORE ||
entry.second.session_unlock_count > 0)
{
spread.soft_unlocks++;
spread.soft_points += points;
}
}
return spread;
}

rc_api_fetch_game_data_response_t* AchievementManager::GetGameData()
{
return &m_game_data;
}

AchievementManager::UnlockStatus
AchievementManager::GetUnlockStatus(AchievementId achievement_id) const
{
return m_unlock_map.at(achievement_id);
}

void AchievementManager::GetAchievementProgress(AchievementId achievement_id, u32* value,
u32* target)
{
rc_runtime_get_achievement_measured(&m_runtime, achievement_id, value, target);
}

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();
}

void AchievementManager::Logout()
{
CloseGame();
Config::SetBaseOrCurrent(Config::RA_API_TOKEN, "");
if (m_update_callback)
m_update_callback();
}

void AchievementManager::Shutdown()
Expand All @@ -353,6 +458,7 @@ AchievementManager::ResponseType AchievementManager::VerifyCredentials(const std
{
Config::SetBaseOrCurrent(Config::RA_API_TOKEN, login_data.api_token);
m_display_name = login_data.display_name;
m_player_score = login_data.score;
}
rc_api_destroy_login_response(&login_data);
return r_type;
Expand Down Expand Up @@ -618,30 +724,6 @@ void AchievementManager::HandleLeaderboardTriggeredEvent(const rc_runtime_event_
}
}

AchievementManager::PointSpread AchievementManager::TallyScore() const
{
PointSpread spread{};
for (const auto& entry : m_unlock_map)
{
u32 points = entry.second.points;
spread.total_count++;
spread.total_points += points;
if (entry.second.remote_unlock_status == UnlockStatus::UnlockType::HARDCORE ||
(hardcore_mode_enabled && entry.second.session_unlock_count > 0))
{
spread.hard_unlocks++;
spread.hard_points += points;
}
else if (entry.second.remote_unlock_status == UnlockStatus::UnlockType::SOFTCORE ||
entry.second.session_unlock_count > 0)
{
spread.soft_unlocks++;
spread.soft_points += points;
}
}
return spread;
}

// Every RetroAchievements API call, with only a partial exception for fetch_image, follows
// the same design pattern (here, X is the name of the call):
// Create a specific rc_api_X_request_t struct and populate with the necessary values
Expand Down
41 changes: 27 additions & 14 deletions Source/Core/Core/AchievementManager.h
Expand Up @@ -41,6 +41,7 @@ class AchievementManager
UNKNOWN_FAILURE
};
using ResponseCallback = std::function<void(ResponseType)>;
using UpdateCallback = std::function<void()>;

struct PointSpread
{
Expand All @@ -52,12 +53,27 @@ class AchievementManager
u32 soft_points;
};

struct UnlockStatus
{
AchievementId game_data_index = 0;
enum class UnlockType
{
LOCKED,
SOFTCORE,
HARDCORE
} remote_unlock_status = UnlockType::LOCKED;
u32 session_unlock_count = 0;
u32 points = 0;
};

static AchievementManager* GetInstance();
void Init();
void SetUpdateCallback(UpdateCallback callback);
ResponseType Login(const std::string& password);
void LoginAsync(const std::string& password, const ResponseCallback& callback);
bool IsLoggedIn() const;
void LoadGameByFilenameAsync(const std::string& iso_path, const ResponseCallback& callback);
bool IsGameLoaded() const;

void LoadUnlockData(const ResponseCallback& callback);
void ActivateDeactivateAchievements();
Expand All @@ -68,6 +84,15 @@ 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;
PointSpread TallyScore() const;
rc_api_fetch_game_data_response_t* GetGameData();
UnlockStatus GetUnlockStatus(AchievementId achievement_id) const;
void GetAchievementProgress(AchievementId achievement_id, u32* value, u32* target);

void CloseGame();
void Logout();
void Shutdown();
Expand Down Expand Up @@ -95,8 +120,6 @@ class AchievementManager
void HandleLeaderboardCanceledEvent(const rc_runtime_event_t* runtime_event);
void HandleLeaderboardTriggeredEvent(const rc_runtime_event_t* runtime_event);

PointSpread TallyScore() const;

template <typename RcRequest, typename RcResponse>
ResponseType Request(RcRequest rc_request, RcResponse* rc_response,
const std::function<int(rc_api_request_t*, const RcRequest*)>& init_request,
Expand All @@ -105,25 +128,15 @@ class AchievementManager
rc_runtime_t m_runtime{};
Core::System* m_system{};
bool m_is_runtime_initialized = false;
UpdateCallback m_update_callback;
std::string m_display_name;
u32 m_player_score = 0;
std::array<char, HASH_LENGTH> m_game_hash{};
u32 m_game_id = 0;
rc_api_fetch_game_data_response_t m_game_data{};
bool m_is_game_loaded = false;
time_t m_last_ping_time = 0;

struct UnlockStatus
{
AchievementId game_data_index = 0;
enum class UnlockType
{
LOCKED,
SOFTCORE,
HARDCORE
} remote_unlock_status = UnlockType::LOCKED;
u32 session_unlock_count = 0;
u32 points = 0;
};
std::unordered_map<AchievementId, UnlockStatus> m_unlock_map;

Common::WorkQueueThread<std::function<void()>> m_queue;
Expand Down

0 comments on commit da2784a

Please sign in to comment.