Skip to content

Commit 04df930

Browse files
LillyJadeKatrinAdmiralCurtiss
authored andcommitted
Added FetchBoardInfo to AchievementManager
FetchBoardInfo is called (via the work queue asynchronously) on a leaderboard every time it is activated or submitted to. It makes two calls to the RetroAchievements API for fetching leaderboard info, one that requests the top four entries in the leaderboard and another that requests the player's entry, the two entries above the player and the two entries below. All of these are inserted into a single map (resolving any overlaps) so the result can be exposed to the UI.
1 parent 61dded7 commit 04df930

File tree

2 files changed

+100
-3
lines changed

2 files changed

+100
-3
lines changed

Source/Core/Core/AchievementManager.cpp

Lines changed: 99 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include <fmt/format.h>
99

10+
#include <rcheevos/include/rc_api_info.h>
1011
#include <rcheevos/include/rc_hash.h>
1112

1213
#include "Common/HttpRequest.h"
@@ -268,9 +269,15 @@ void AchievementManager::ActivateDeactivateLeaderboards()
268269
for (u32 ix = 0; ix < m_game_data.num_leaderboards; ix++)
269270
{
270271
auto leaderboard = m_game_data.leaderboards[ix];
272+
u32 leaderboard_id = leaderboard.id;
271273
if (m_is_game_loaded && leaderboards_enabled && hardcore_mode_enabled)
272274
{
273-
rc_runtime_activate_lboard(&m_runtime, leaderboard.id, leaderboard.definition, nullptr, 0);
275+
rc_runtime_activate_lboard(&m_runtime, leaderboard_id, leaderboard.definition, nullptr, 0);
276+
m_queue.EmplaceItem([this, leaderboard_id] {
277+
FetchBoardInfo(leaderboard_id);
278+
if (m_update_callback)
279+
m_update_callback();
280+
});
274281
}
275282
else
276283
{
@@ -712,7 +719,7 @@ AchievementManager::GetAchievementProgress(AchievementId achievement_id, u32* va
712719
return ResponseType::SUCCESS;
713720
}
714721

715-
const std::unordered_map<AchievementId, AchievementManager::LeaderboardStatus>&
722+
const std::unordered_map<AchievementManager::AchievementId, AchievementManager::LeaderboardStatus>&
716723
AchievementManager::GetLeaderboardsInfo() const
717724
{
718725
return m_leaderboard_map;
@@ -962,6 +969,90 @@ AchievementManager::ResponseType AchievementManager::FetchUnlockData(bool hardco
962969
return r_type;
963970
}
964971

972+
AchievementManager::ResponseType AchievementManager::FetchBoardInfo(AchievementId leaderboard_id)
973+
{
974+
std::string username = Config::Get(Config::RA_USERNAME);
975+
LeaderboardStatus lboard{};
976+
977+
{
978+
rc_api_fetch_leaderboard_info_response_t board_info{};
979+
const rc_api_fetch_leaderboard_info_request_t fetch_board_request = {
980+
.leaderboard_id = leaderboard_id, .count = 4, .first_entry = 1, .username = nullptr};
981+
const ResponseType r_type =
982+
Request<rc_api_fetch_leaderboard_info_request_t, rc_api_fetch_leaderboard_info_response_t>(
983+
fetch_board_request, &board_info, rc_api_init_fetch_leaderboard_info_request,
984+
rc_api_process_fetch_leaderboard_info_response);
985+
if (r_type != ResponseType::SUCCESS)
986+
{
987+
ERROR_LOG_FMT(ACHIEVEMENTS, "Failed to fetch info for leaderboard ID {}.", leaderboard_id);
988+
rc_api_destroy_fetch_leaderboard_info_response(&board_info);
989+
return r_type;
990+
}
991+
lboard.name = board_info.title;
992+
lboard.description = board_info.description;
993+
lboard.entries.clear();
994+
for (u32 i = 0; i < board_info.num_entries; ++i)
995+
{
996+
const auto& org_entry = board_info.entries[i];
997+
LeaderboardEntry dest_entry =
998+
LeaderboardEntry{.username = org_entry.username, .rank = org_entry.rank};
999+
if (rc_runtime_format_lboard_value(dest_entry.score.data(), FORMAT_SIZE, org_entry.score,
1000+
board_info.format) == 0)
1001+
{
1002+
ERROR_LOG_FMT(ACHIEVEMENTS, "Failed to format leaderboard score {}.", org_entry.score);
1003+
strncpy(dest_entry.score.data(), fmt::format("{}", org_entry.score).c_str(), FORMAT_SIZE);
1004+
}
1005+
lboard.entries[org_entry.index] = dest_entry;
1006+
}
1007+
rc_api_destroy_fetch_leaderboard_info_response(&board_info);
1008+
}
1009+
1010+
{
1011+
// Retrieve, if exists, the player's entry, the two entries above the player, and the two
1012+
// entries below the player, for a total of five entries. Technically I only need one entry
1013+
// below, but the API is ambiguous what happens if an even number and a username are provided.
1014+
rc_api_fetch_leaderboard_info_response_t board_info{};
1015+
const rc_api_fetch_leaderboard_info_request_t fetch_board_request = {
1016+
.leaderboard_id = leaderboard_id,
1017+
.count = 5,
1018+
.first_entry = 0,
1019+
.username = username.c_str()};
1020+
const ResponseType r_type =
1021+
Request<rc_api_fetch_leaderboard_info_request_t, rc_api_fetch_leaderboard_info_response_t>(
1022+
fetch_board_request, &board_info, rc_api_init_fetch_leaderboard_info_request,
1023+
rc_api_process_fetch_leaderboard_info_response);
1024+
if (r_type != ResponseType::SUCCESS)
1025+
{
1026+
ERROR_LOG_FMT(ACHIEVEMENTS, "Failed to fetch info for leaderboard ID {}.", leaderboard_id);
1027+
rc_api_destroy_fetch_leaderboard_info_response(&board_info);
1028+
return r_type;
1029+
}
1030+
for (u32 i = 0; i < board_info.num_entries; ++i)
1031+
{
1032+
const auto& org_entry = board_info.entries[i];
1033+
LeaderboardEntry dest_entry =
1034+
LeaderboardEntry{.username = org_entry.username, .rank = org_entry.rank};
1035+
if (rc_runtime_format_lboard_value(dest_entry.score.data(), FORMAT_SIZE, org_entry.score,
1036+
board_info.format) == 0)
1037+
{
1038+
ERROR_LOG_FMT(ACHIEVEMENTS, "Failed to format leaderboard score {}.", org_entry.score);
1039+
strncpy(dest_entry.score.data(), fmt::format("{}", org_entry.score).c_str(), FORMAT_SIZE);
1040+
}
1041+
lboard.entries[org_entry.index] = dest_entry;
1042+
if (org_entry.username == username)
1043+
lboard.player_index = org_entry.index;
1044+
}
1045+
rc_api_destroy_fetch_leaderboard_info_response(&board_info);
1046+
}
1047+
1048+
{
1049+
std::lock_guard lg{m_lock};
1050+
m_leaderboard_map[leaderboard_id] = lboard;
1051+
}
1052+
1053+
return ResponseType::SUCCESS;
1054+
}
1055+
9651056
void AchievementManager::ActivateDeactivateAchievement(AchievementId id, bool enabled,
9661057
bool unofficial, bool encore)
9671058
{
@@ -1205,7 +1296,12 @@ void AchievementManager::HandleLeaderboardTriggeredEvent(const rc_runtime_event_
12051296
m_game_data.leaderboards[ix].title),
12061297
OSD::Duration::VERY_LONG, OSD::Color::YELLOW);
12071298
}
1208-
return;
1299+
m_queue.EmplaceItem([this, event_id] {
1300+
FetchBoardInfo(event_id);
1301+
if (m_update_callback)
1302+
m_update_callback();
1303+
});
1304+
break;
12091305
}
12101306
}
12111307
ERROR_LOG_FMT(ACHIEVEMENTS, "Invalid leaderboard triggered event with id {}.", event_id);

Source/Core/Core/AchievementManager.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ class AchievementManager
146146
ResponseType StartRASession();
147147
ResponseType FetchGameData();
148148
ResponseType FetchUnlockData(bool hardcore);
149+
ResponseType FetchBoardInfo(AchievementId leaderboard_id);
149150

150151
void ActivateDeactivateAchievement(AchievementId id, bool enabled, bool unofficial, bool encore);
151152
void GenerateRichPresence();

0 commit comments

Comments
 (0)