diff --git a/Source/Core/Core/AchievementManager.cpp b/Source/Core/Core/AchievementManager.cpp index a40df12104..f73072fa59 100644 --- a/Source/Core/Core/AchievementManager.cpp +++ b/Source/Core/Core/AchievementManager.cpp @@ -358,10 +358,12 @@ AchievementManager::GetAchievementProgress(AchievementId achievement_id, u32* va return ResponseType::SUCCESS; } -const std::unordered_map& -AchievementManager::GetLeaderboardsInfo() const +const AchievementManager::LeaderboardStatus* +AchievementManager::GetLeaderboardInfo(AchievementManager::AchievementId leaderboard_id) const { - return m_leaderboard_map; + if (m_leaderboard_map.count(leaderboard_id) < 1) + return nullptr; + return &m_leaderboard_map.at(leaderboard_id); } AchievementManager::RichPresence AchievementManager::GetRichPresence() const diff --git a/Source/Core/Core/AchievementManager.h b/Source/Core/Core/AchievementManager.h index 6cd7d39a90..785c463fbf 100644 --- a/Source/Core/Core/AchievementManager.h +++ b/Source/Core/Core/AchievementManager.h @@ -143,7 +143,7 @@ public: const UnlockStatus* GetUnlockStatus(AchievementId achievement_id) const; AchievementManager::ResponseType GetAchievementProgress(AchievementId achievement_id, u32* value, u32* target); - const std::unordered_map& GetLeaderboardsInfo() const; + const LeaderboardStatus* GetLeaderboardInfo(AchievementId leaderboard_id) const; RichPresence GetRichPresence() const; bool IsDisabled() const { return m_disabled; }; void SetDisabled(bool disabled); diff --git a/Source/Core/DolphinQt/Achievements/AchievementLeaderboardWidget.cpp b/Source/Core/DolphinQt/Achievements/AchievementLeaderboardWidget.cpp index 13ab205f9c..e9705caffa 100644 --- a/Source/Core/DolphinQt/Achievements/AchievementLeaderboardWidget.cpp +++ b/Source/Core/DolphinQt/Achievements/AchievementLeaderboardWidget.cpp @@ -24,10 +24,7 @@ AchievementLeaderboardWidget::AchievementLeaderboardWidget(QWidget* parent) : QW m_common_box = new QGroupBox(); m_common_layout = new QGridLayout(); - { - std::lock_guard lg{AchievementManager::GetInstance().GetLock()}; - UpdateData(); - } + UpdateData(true); m_common_box->setLayout(m_common_layout); @@ -38,77 +35,121 @@ AchievementLeaderboardWidget::AchievementLeaderboardWidget(QWidget* parent) : QW setLayout(layout); } -void AchievementLeaderboardWidget::UpdateData() +void AchievementLeaderboardWidget::UpdateData(bool clean_all) { - ClearLayoutRecursively(m_common_layout); - - if (!AchievementManager::GetInstance().IsGameLoaded()) - return; - const auto& leaderboards = AchievementManager::GetInstance().GetLeaderboardsInfo(); - int row = 0; - for (const auto& board_row : leaderboards) + if (clean_all) { - const AchievementManager::LeaderboardStatus& board = board_row.second; - QLabel* a_title = new QLabel(QString::fromStdString(board.name)); - QLabel* a_description = new QLabel(QString::fromStdString(board.description)); - QVBoxLayout* a_col_left = new QVBoxLayout(); - a_col_left->addWidget(a_title); - a_col_left->addWidget(a_description); - if (row > 0) + ClearLayoutRecursively(m_common_layout); + + auto& instance = AchievementManager::GetInstance(); + if (!instance.IsGameLoaded()) + return; + + rc_api_fetch_game_data_response_t* game_data; { - QFrame* a_divider = new QFrame(); - a_divider->setFrameShape(QFrame::HLine); - m_common_layout->addWidget(a_divider, row - 1, 0); + std::lock_guard lg{AchievementManager::GetInstance().GetLock()}; + game_data = instance.GetGameData(); } - m_common_layout->addLayout(a_col_left, row, 0); - // Each leaderboard entry is displayed with four values. These are *generally* intended to be, - // in order, the first place entry, the entry one above the player, the player's entry, and - // the entry one below the player. - // Edge cases: - // * If there are fewer than four entries in the leaderboard, all entries will be shown in - // order and the remainder of the list will be padded with empty values. - // * If the player does not currently have a score in the leaderboard, or is in the top 3, - // the four slots will be the top four players in order. - // * If the player is last place, the player will be in the fourth slot, and the second and - // third slots will be the two players above them. The first slot will always be first place. - std::array to_display{1, 2, 3, 4}; - if (board.player_index > to_display.size() - 1) + for (u32 row = 0; row < game_data->num_leaderboards; row += 2) { - // If the rank one below than the player is found, offset = 1. - u32 offset = static_cast(board.entries.count(board.player_index + 1)); - // Example: player is 10th place but not last - // to_display = {1, 10-3+1+1, 10-3+1+2, 10-3+1+3} = {1, 9, 10, 11} - // Example: player is 15th place and is last - // to_display = {1, 15-3+0+1, 15-3+0+2, 15-3+0+3} = {1, 13, 14, 15} - for (size_t i = 1; i < to_display.size(); ++i) - to_display[i] = board.player_index - 3 + offset + static_cast(i); - } - for (size_t i = 0; i < to_display.size(); ++i) - { - u32 index = to_display[i]; - QLabel* a_rank = new QLabel(QStringLiteral("---")); - QLabel* a_username = new QLabel(QStringLiteral("---")); - QLabel* a_score = new QLabel(QStringLiteral("---")); - const auto it = board.entries.find(index); - if (it != board.entries.end()) - { - a_rank->setText(tr("Rank %1").arg(it->second.rank)); - a_username->setText(QString::fromStdString(it->second.username)); - a_score->setText(QString::fromUtf8(it->second.score.data())); - } - QVBoxLayout* a_col = new QVBoxLayout(); - a_col->addWidget(a_rank); - a_col->addWidget(a_username); - a_col->addWidget(a_score); + const auto* leaderboard = game_data->leaderboards + (row / 2); + m_leaderboard_order[leaderboard->id] = row; + QLabel* a_title = new QLabel(QString::fromUtf8(leaderboard->title)); + QLabel* a_description = new QLabel(QString::fromUtf8(leaderboard->description)); + QVBoxLayout* a_col_left = new QVBoxLayout(); + a_col_left->addWidget(a_title); + a_col_left->addWidget(a_description); if (row > 0) { QFrame* a_divider = new QFrame(); a_divider->setFrameShape(QFrame::HLine); - m_common_layout->addWidget(a_divider, row - 1, static_cast(i) + 1); + m_common_layout->addWidget(a_divider, row - 1, 0); + } + m_common_layout->addLayout(a_col_left, row, 0); + for (size_t ix = 0; ix < 4; ix++) + { + QVBoxLayout* a_col = new QVBoxLayout(); + for (size_t jx = 0; jx < 3; jx++) + a_col->addWidget(new QLabel(QStringLiteral("---"))); + if (row > 0) + { + QFrame* a_divider = new QFrame(); + a_divider->setFrameShape(QFrame::HLine); + m_common_layout->addWidget(a_divider, row - 1, static_cast(ix) + 1); + } + m_common_layout->addLayout(a_col, row, static_cast(ix) + 1); } - m_common_layout->addLayout(a_col, row, static_cast(i) + 1); } - row += 2; + } + for (auto row : m_leaderboard_order) + { + UpdateRow(row.second); + } +} + +void AchievementLeaderboardWidget::UpdateData( + const std::set& update_ids) +{ + for (auto row : m_leaderboard_order) + { + if (update_ids.contains(row.first)) + { + UpdateRow(row.second); + } + } +} + +void AchievementLeaderboardWidget::UpdateRow(AchievementManager::AchievementId leaderboard_id) +{ + const auto leaderboard_itr = m_leaderboard_order.find(leaderboard_id); + if (leaderboard_itr == m_leaderboard_order.end()) + return; + const int row = leaderboard_itr->second; + + const AchievementManager::LeaderboardStatus* board; + { + std::lock_guard lg{AchievementManager::GetInstance().GetLock()}; + board = AchievementManager::GetInstance().GetLeaderboardInfo(leaderboard_id); + } + if (!board) + return; + + // Each leaderboard entry is displayed with four values. These are *generally* intended to be, + // in order, the first place entry, the entry one above the player, the player's entry, and + // the entry one below the player. + // Edge cases: + // * If there are fewer than four entries in the leaderboard, all entries will be shown in + // order and the remainder of the list will be padded with empty values. + // * If the player does not currently have a score in the leaderboard, or is in the top 3, + // the four slots will be the top four players in order. + // * If the player is last place, the player will be in the fourth slot, and the second and + // third slots will be the two players above them. The first slot will always be first place. + std::array to_display{1, 2, 3, 4}; + if (board->player_index > to_display.size() - 1) + { + // If the rank one below than the player is found, offset = 1. + u32 offset = static_cast(board->entries.count(board->player_index + 1)); + // Example: player is 10th place but not last + // to_display = {1, 10-3+1+1, 10-3+1+2, 10-3+1+3} = {1, 9, 10, 11} + // Example: player is 15th place and is last + // to_display = {1, 15-3+0+1, 15-3+0+2, 15-3+0+3} = {1, 13, 14, 15} + for (size_t ix = 1; ix < to_display.size(); ++ix) + to_display[ix] = board->player_index - 3 + offset + static_cast(ix); + } + for (size_t ix = 0; ix < to_display.size(); ++ix) + { + const auto it = board->entries.find(to_display[ix]); + if (it != board->entries.end()) + { + QVBoxLayout* a_col = new QVBoxLayout(); + a_col->addWidget(new QLabel(tr("Rank %1").arg(it->second.rank))); + a_col->addWidget(new QLabel(QString::fromStdString(it->second.username))); + a_col->addWidget(new QLabel(QString::fromUtf8(it->second.score.data()))); + auto old_item = m_common_layout->itemAtPosition(row, static_cast(ix) + 1); + m_common_layout->removeItem(old_item); + ClearLayoutRecursively(static_cast(old_item)); + m_common_layout->addLayout(a_col, row, static_cast(ix) + 1); + } } } diff --git a/Source/Core/DolphinQt/Achievements/AchievementLeaderboardWidget.h b/Source/Core/DolphinQt/Achievements/AchievementLeaderboardWidget.h index 055ea6ab3f..cafd2483bd 100644 --- a/Source/Core/DolphinQt/Achievements/AchievementLeaderboardWidget.h +++ b/Source/Core/DolphinQt/Achievements/AchievementLeaderboardWidget.h @@ -6,6 +6,8 @@ #ifdef USE_RETRO_ACHIEVEMENTS #include +#include "Core/AchievementManager.h" + class QGroupBox; class QGridLayout; @@ -14,11 +16,14 @@ class AchievementLeaderboardWidget final : public QWidget Q_OBJECT public: explicit AchievementLeaderboardWidget(QWidget* parent); - void UpdateData(); + void UpdateData(bool clean_all); + void UpdateData(const std::set& update_ids); + void UpdateRow(AchievementManager::AchievementId leaderboard_id); private: QGroupBox* m_common_box; QGridLayout* m_common_layout; + std::map m_leaderboard_order; }; #endif // USE_RETRO_ACHIEVEMENTS diff --git a/Source/Core/DolphinQt/Achievements/AchievementsWindow.cpp b/Source/Core/DolphinQt/Achievements/AchievementsWindow.cpp index a95e844e29..6f26811a49 100644 --- a/Source/Core/DolphinQt/Achievements/AchievementsWindow.cpp +++ b/Source/Core/DolphinQt/Achievements/AchievementsWindow.cpp @@ -83,7 +83,7 @@ void AchievementsWindow::UpdateData() m_settings_widget->UpdateData(); m_progress_widget->UpdateData(true); m_tab_widget->setTabVisible(1, is_game_loaded); - m_leaderboard_widget->UpdateData(); + m_leaderboard_widget->UpdateData(true); m_tab_widget->setTabVisible(2, is_game_loaded); } update();