Merge pull request #13892 from JosJuice/retroachievements-hookableevent

AchievementManager: Use HookableEvent instead of std::function callbacks
This commit is contained in:
JMC47 2025-09-16 14:48:43 -04:00 committed by GitHub
commit 5a153c3d41
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 39 additions and 44 deletions

View File

@ -122,16 +122,6 @@ picojson::value AchievementManager::LoadApprovedList()
return temp; return temp;
} }
void AchievementManager::SetUpdateCallback(UpdateCallback callback)
{
m_update_callback = std::move(callback);
if (!m_update_callback)
m_update_callback = [](UpdatedItems) {};
m_update_callback(UpdatedItems{.all = true});
}
void AchievementManager::Login(const std::string& password) void AchievementManager::Login(const std::string& password)
{ {
if (!m_client) if (!m_client)
@ -354,7 +344,7 @@ void AchievementManager::DoFrame()
{ {
m_last_rp_time = current_time; m_last_rp_time = current_time;
rc_client_get_rich_presence_message(m_client, m_rich_presence.data(), RP_SIZE); rc_client_get_rich_presence_message(m_client, m_rich_presence.data(), RP_SIZE);
m_update_callback(UpdatedItems{.rich_presence = true}); UpdateEvent::Trigger(UpdatedItems{.rich_presence = true});
if (Config::Get(Config::RA_DISCORD_PRESENCE_ENABLED)) if (Config::Get(Config::RA_DISCORD_PRESENCE_ENABLED))
Discord::UpdateDiscordPresence(); Discord::UpdateDiscordPresence();
} }
@ -753,7 +743,7 @@ void AchievementManager::CloseGame()
INFO_LOG_FMT(ACHIEVEMENTS, "Game closed."); INFO_LOG_FMT(ACHIEVEMENTS, "Game closed.");
} }
m_update_callback(UpdatedItems{.all = true}); UpdateEvent::Trigger(UpdatedItems{.all = true});
} }
void AchievementManager::Logout() void AchievementManager::Logout()
@ -767,7 +757,7 @@ void AchievementManager::Logout()
Config::SetBaseOrCurrent(Config::RA_API_TOKEN, ""); Config::SetBaseOrCurrent(Config::RA_API_TOKEN, "");
} }
m_update_callback(UpdatedItems{.all = true}); UpdateEvent::Trigger(UpdatedItems{.all = true});
INFO_LOG_FMT(ACHIEVEMENTS, "Logged out from server."); INFO_LOG_FMT(ACHIEVEMENTS, "Logged out from server.");
} }
@ -910,7 +900,7 @@ void AchievementManager::LoginCallback(int result, const char* error_message, rc
{ {
WARN_LOG_FMT(ACHIEVEMENTS, "Failed to login {} to RetroAchievements server.", WARN_LOG_FMT(ACHIEVEMENTS, "Failed to login {} to RetroAchievements server.",
Config::Get(Config::RA_USERNAME)); Config::Get(Config::RA_USERNAME));
AchievementManager::GetInstance().m_update_callback({.failed_login_code = result}); UpdateEvent::Trigger({.failed_login_code = result});
return; return;
} }
@ -922,7 +912,7 @@ void AchievementManager::LoginCallback(int result, const char* error_message, rc
if (!user) if (!user)
{ {
WARN_LOG_FMT(ACHIEVEMENTS, "Failed to retrieve user information from client."); WARN_LOG_FMT(ACHIEVEMENTS, "Failed to retrieve user information from client.");
AchievementManager::GetInstance().m_update_callback({.failed_login_code = RC_INVALID_STATE}); UpdateEvent::Trigger({.failed_login_code = RC_INVALID_STATE});
return; return;
} }
@ -941,7 +931,7 @@ void AchievementManager::LoginCallback(int result, const char* error_message, rc
INFO_LOG_FMT(ACHIEVEMENTS, "Attempted to login prior user {}; current user is {}.", INFO_LOG_FMT(ACHIEVEMENTS, "Attempted to login prior user {}; current user is {}.",
user->username, Config::Get(Config::RA_USERNAME)); user->username, Config::Get(Config::RA_USERNAME));
rc_client_logout(client); rc_client_logout(client);
AchievementManager::GetInstance().m_update_callback({.failed_login_code = RC_INVALID_STATE}); UpdateEvent::Trigger({.failed_login_code = RC_INVALID_STATE});
return; return;
} }
} }
@ -987,7 +977,7 @@ void AchievementManager::LeaderboardEntriesCallback(int result, const char* erro
if (static_cast<int32_t>(ix) == list->user_index) if (static_cast<int32_t>(ix) == list->user_index)
leaderboard.player_index = response_entry.rank; leaderboard.player_index = response_entry.rank;
} }
AchievementManager::GetInstance().m_update_callback({.leaderboards = {*leaderboard_id}}); UpdateEvent::Trigger({.leaderboards = {*leaderboard_id}});
} }
void AchievementManager::LoadGameCallback(int result, const char* error_message, void AchievementManager::LoadGameCallback(int result, const char* error_message,
@ -1033,7 +1023,7 @@ void AchievementManager::LoadGameCallback(int result, const char* error_message,
rc_client_set_read_memory_function(instance.m_client, MemoryPeeker); rc_client_set_read_memory_function(instance.m_client, MemoryPeeker);
instance.FetchGameBadges(); instance.FetchGameBadges();
instance.m_system.store(&Core::System::GetInstance(), std::memory_order_release); instance.m_system.store(&Core::System::GetInstance(), std::memory_order_release);
instance.m_update_callback({.all = true}); UpdateEvent::Trigger({.all = true});
// Set this to a value that will immediately trigger RP // Set this to a value that will immediately trigger RP
instance.m_last_rp_time = std::chrono::steady_clock::now() - std::chrono::minutes{2}; instance.m_last_rp_time = std::chrono::steady_clock::now() - std::chrono::minutes{2};
@ -1121,8 +1111,7 @@ void AchievementManager::HandleAchievementTriggeredEvent(const rc_client_event_t
(rc_client_get_hardcore_enabled(instance.m_client)) ? OSD::Color::YELLOW : (rc_client_get_hardcore_enabled(instance.m_client)) ? OSD::Color::YELLOW :
OSD::Color::CYAN, OSD::Color::CYAN,
&instance.GetAchievementBadge(client_event->achievement->id, false)); &instance.GetAchievementBadge(client_event->achievement->id, false));
AchievementManager::GetInstance().m_update_callback( UpdateEvent::Trigger(UpdatedItems{.achievements = {client_event->achievement->id}});
UpdatedItems{.achievements = {client_event->achievement->id}});
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION #ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
switch (rc_client_raintegration_get_achievement_state(instance.m_client, switch (rc_client_raintegration_get_achievement_state(instance.m_client,
client_event->achievement->id)) client_event->achievement->id))
@ -1171,8 +1160,7 @@ void AchievementManager::HandleLeaderboardSubmittedEvent(const rc_client_event_t
client_event->leaderboard->title), client_event->leaderboard->title),
OSD::Duration::VERY_LONG, OSD::Color::YELLOW); OSD::Duration::VERY_LONG, OSD::Color::YELLOW);
AchievementManager::GetInstance().FetchBoardInfo(client_event->leaderboard->id); AchievementManager::GetInstance().FetchBoardInfo(client_event->leaderboard->id);
AchievementManager::GetInstance().m_update_callback( UpdateEvent::Trigger(UpdatedItems{.leaderboards = {client_event->leaderboard->id}});
UpdatedItems{.leaderboards = {client_event->leaderboard->id}});
} }
void AchievementManager::HandleLeaderboardTrackerUpdateEvent(const rc_client_event_t* client_event) void AchievementManager::HandleLeaderboardTrackerUpdateEvent(const rc_client_event_t* client_event)
@ -1209,7 +1197,7 @@ void AchievementManager::HandleAchievementChallengeIndicatorShowEvent(
const auto [iter, inserted] = instance.m_active_challenges.insert(client_event->achievement->id); const auto [iter, inserted] = instance.m_active_challenges.insert(client_event->achievement->id);
if (inserted) if (inserted)
instance.m_challenges_updated = true; instance.m_challenges_updated = true;
AchievementManager::GetInstance().m_update_callback(UpdatedItems{.rich_presence = true}); UpdateEvent::Trigger(UpdatedItems{.rich_presence = true});
} }
void AchievementManager::HandleAchievementChallengeIndicatorHideEvent( void AchievementManager::HandleAchievementChallengeIndicatorHideEvent(
@ -1219,7 +1207,7 @@ void AchievementManager::HandleAchievementChallengeIndicatorHideEvent(
const auto removed = instance.m_active_challenges.erase(client_event->achievement->id); const auto removed = instance.m_active_challenges.erase(client_event->achievement->id);
if (removed > 0) if (removed > 0)
instance.m_challenges_updated = true; instance.m_challenges_updated = true;
AchievementManager::GetInstance().m_update_callback(UpdatedItems{.rich_presence = true}); UpdateEvent::Trigger(UpdatedItems{.rich_presence = true});
} }
void AchievementManager::HandleAchievementProgressIndicatorShowEvent( void AchievementManager::HandleAchievementProgressIndicatorShowEvent(
@ -1235,8 +1223,7 @@ void AchievementManager::HandleAchievementProgressIndicatorShowEvent(
OSD::Duration::SHORT, OSD::Color::GREEN, OSD::Duration::SHORT, OSD::Color::GREEN,
&instance.GetAchievementBadge(client_event->achievement->id, false)); &instance.GetAchievementBadge(client_event->achievement->id, false));
instance.m_last_progress_message = current_time; instance.m_last_progress_message = current_time;
AchievementManager::GetInstance().m_update_callback( UpdateEvent::Trigger(UpdatedItems{.achievements = {client_event->achievement->id}});
UpdatedItems{.achievements = {client_event->achievement->id}});
} }
void AchievementManager::HandleGameCompletedEvent(const rc_client_event_t* client_event, void AchievementManager::HandleGameCompletedEvent(const rc_client_event_t* client_event,
@ -1375,7 +1362,7 @@ void AchievementManager::FetchBadge(AchievementManager::Badge* badge, u32 badge_
{ {
if (!m_client || !HasAPIToken()) if (!m_client || !HasAPIToken())
{ {
m_update_callback(callback_data); UpdateEvent::Trigger(callback_data);
if (m_display_welcome_message && badge_type == RC_IMAGE_TYPE_GAME) if (m_display_welcome_message && badge_type == RC_IMAGE_TYPE_GAME)
DisplayWelcomeMessage(); DisplayWelcomeMessage();
return; return;
@ -1421,7 +1408,7 @@ void AchievementManager::FetchBadge(AchievementManager::Badge* badge, u32 badge_
"RetroAchievements connection failed on image request.\n URL: {}", "RetroAchievements connection failed on image request.\n URL: {}",
api_request.url); api_request.url);
rc_api_destroy_request(&api_request); rc_api_destroy_request(&api_request);
m_update_callback(callback_data); UpdateEvent::Trigger(callback_data);
return; return;
} }
@ -1454,7 +1441,7 @@ void AchievementManager::FetchBadge(AchievementManager::Badge* badge, u32 badge_
} }
*badge = std::move(tmp_badge); *badge = std::move(tmp_badge);
m_update_callback(callback_data); UpdateEvent::Trigger(callback_data);
if (badge_type == RC_IMAGE_TYPE_ACHIEVEMENT && if (badge_type == RC_IMAGE_TYPE_ACHIEVEMENT &&
m_active_challenges.contains(*callback_data.achievements.begin())) m_active_challenges.contains(*callback_data.achievements.begin()))
{ {
@ -1530,7 +1517,7 @@ void AchievementManager::LoadIntegrationCallback(int result, const char* error_m
rc_client_raintegration_set_event_handler(instance.m_client, RAIntegrationEventHandler); rc_client_raintegration_set_event_handler(instance.m_client, RAIntegrationEventHandler);
rc_client_raintegration_set_write_memory_function(instance.m_client, MemoryPoker); rc_client_raintegration_set_write_memory_function(instance.m_client, MemoryPoker);
rc_client_raintegration_set_get_game_name_function(instance.m_client, GameTitleEstimateHandler); rc_client_raintegration_set_get_game_name_function(instance.m_client, GameTitleEstimateHandler);
instance.m_dev_menu_callback(); DevMenuUpdateEvent::Trigger();
// TODO: hook up menu and dll event handlers // TODO: hook up menu and dll event handlers
break; break;
@ -1552,12 +1539,11 @@ void AchievementManager::LoadIntegrationCallback(int result, const char* error_m
void AchievementManager::RAIntegrationEventHandler(const rc_client_raintegration_event_t* event, void AchievementManager::RAIntegrationEventHandler(const rc_client_raintegration_event_t* event,
rc_client_t* client) rc_client_t* client)
{ {
auto& instance = AchievementManager::GetInstance();
switch (event->type) switch (event->type)
{ {
case RC_CLIENT_RAINTEGRATION_EVENT_MENU_CHANGED: case RC_CLIENT_RAINTEGRATION_EVENT_MENU_CHANGED:
case RC_CLIENT_RAINTEGRATION_EVENT_MENUITEM_CHECKED_CHANGED: case RC_CLIENT_RAINTEGRATION_EVENT_MENUITEM_CHECKED_CHANGED:
instance.m_dev_menu_callback(); DevMenuUpdateEvent::Trigger();
break; break;
case RC_CLIENT_RAINTEGRATION_EVENT_PAUSE: case RC_CLIENT_RAINTEGRATION_EVENT_PAUSE:
{ {

View File

@ -29,6 +29,7 @@
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/Config/Config.h" #include "Common/Config/Config.h"
#include "Common/Event.h" #include "Common/Event.h"
#include "Common/HookableEvent.h"
#include "Common/HttpRequest.h" #include "Common/HttpRequest.h"
#include "Common/JsonUtil.h" #include "Common/JsonUtil.h"
#include "Common/Lazy.h" #include "Common/Lazy.h"
@ -119,11 +120,10 @@ public:
bool rich_presence = false; bool rich_presence = false;
int failed_login_code = 0; int failed_login_code = 0;
}; };
using UpdateCallback = std::function<void(const UpdatedItems&)>; using UpdateEvent = Common::HookableEvent<"AchievementManagerUpdate", const UpdatedItems&>;
static AchievementManager& GetInstance(); static AchievementManager& GetInstance();
void Init(void* hwnd); void Init(void* hwnd);
void SetUpdateCallback(UpdateCallback callback);
void Login(const std::string& password); void Login(const std::string& password);
bool HasAPIToken() const; bool HasAPIToken() const;
void LoadGame(const DiscIO::Volume* volume); void LoadGame(const DiscIO::Volume* volume);
@ -170,12 +170,9 @@ public:
std::vector<std::string> GetActiveLeaderboards() const; std::vector<std::string> GetActiveLeaderboards() const;
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION #ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
using DevMenuUpdateEvent = Common::HookableEvent<"AchievementManagerDevMenuUpdate">;
const rc_client_raintegration_menu_t* GetDevelopmentMenu(); const rc_client_raintegration_menu_t* GetDevelopmentMenu();
u32 ActivateDevMenuItem(u32 menu_item_id); u32 ActivateDevMenuItem(u32 menu_item_id);
void SetDevMenuUpdateCallback(std::function<void(void)> callback)
{
m_dev_menu_callback = callback;
}
bool CheckForModifications() { return rc_client_raintegration_has_modifications(m_client); } bool CheckForModifications() { return rc_client_raintegration_has_modifications(m_client); }
#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION #endif // RC_CLIENT_SUPPORTS_RAINTEGRATION
@ -267,7 +264,6 @@ private:
rc_client_t* m_client{}; rc_client_t* m_client{};
std::atomic<Core::System*> m_system{}; std::atomic<Core::System*> m_system{};
bool m_is_runtime_initialized = false; bool m_is_runtime_initialized = false;
UpdateCallback m_update_callback = [](const UpdatedItems&) {};
std::unique_ptr<DiscIO::Volume> m_loading_volume; std::unique_ptr<DiscIO::Volume> m_loading_volume;
Config::ConfigChangedCallbackID m_config_changed_callback_id; Config::ConfigChangedCallbackID m_config_changed_callback_id;
Badge m_default_player_badge; Badge m_default_player_badge;
@ -294,7 +290,6 @@ private:
bool m_dll_found = false; bool m_dll_found = false;
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION #ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
std::function<void(void)> m_dev_menu_callback;
std::vector<u8> m_cloned_memory; std::vector<u8> m_cloned_memory;
std::recursive_mutex m_memory_lock; std::recursive_mutex m_memory_lock;
std::string m_title_estimate; std::string m_title_estimate;

View File

@ -30,12 +30,16 @@ AchievementsWindow::AchievementsWindow(QWidget* parent) : QDialog(parent)
CreateMainLayout(); CreateMainLayout();
ConnectWidgets(); ConnectWidgets();
AchievementManager::GetInstance().SetUpdateCallback(
m_event_hook = AchievementManager::UpdateEvent::Register(
[this](AchievementManager::UpdatedItems updated_items) { [this](AchievementManager::UpdatedItems updated_items) {
QueueOnObject(this, [this, updated_items = std::move(updated_items)] { QueueOnObject(this, [this, updated_items = std::move(updated_items)] {
AchievementsWindow::UpdateData(std::move(updated_items)); AchievementsWindow::UpdateData(std::move(updated_items));
}); });
}); },
"AchievementsWindow");
UpdateData(AchievementManager::UpdatedItems{.all = true});
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
[this] { m_settings_widget->UpdateData(RC_OK); }); [this] { m_settings_widget->UpdateData(RC_OK); });
} }

View File

@ -6,6 +6,7 @@
#ifdef USE_RETRO_ACHIEVEMENTS #ifdef USE_RETRO_ACHIEVEMENTS
#include <QDialog> #include <QDialog>
#include "Common/HookableEvent.h"
#include "Core/AchievementManager.h" #include "Core/AchievementManager.h"
class AchievementHeaderWidget; class AchievementHeaderWidget;
@ -35,6 +36,8 @@ private:
AchievementProgressWidget* m_progress_widget; AchievementProgressWidget* m_progress_widget;
AchievementLeaderboardWidget* m_leaderboard_widget; AchievementLeaderboardWidget* m_leaderboard_widget;
QDialogButtonBox* m_button_box; QDialogButtonBox* m_button_box;
Common::EventHook m_event_hook;
}; };
#endif // USE_RETRO_ACHIEVEMENTS #endif // USE_RETRO_ACHIEVEMENTS

View File

@ -294,8 +294,8 @@ void MenuBar::AddToolsMenu()
tools_menu->addAction(tr("Achievements"), this, [this] { emit ShowAchievementsWindow(); }); tools_menu->addAction(tr("Achievements"), this, [this] { emit ShowAchievementsWindow(); });
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION #ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
m_achievements_dev_menu = tools_menu->addMenu(tr("RetroAchievements Development")); m_achievements_dev_menu = tools_menu->addMenu(tr("RetroAchievements Development"));
AchievementManager::GetInstance().SetDevMenuUpdateCallback( m_raintegration_event_hook = AchievementManager::DevMenuUpdateEvent::Register(
[this] { QueueOnObject(this, [this] { this->UpdateAchievementDevelopmentMenu(); }); }); [this] { QueueOnObject(this, [this] { UpdateAchievementDevelopmentMenu(); }); }, "MenuBar");
m_achievements_dev_menu->menuAction()->setVisible(false); m_achievements_dev_menu->menuAction()->setVisible(false);
#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION #endif // RC_CLIENT_SUPPORTS_RAINTEGRATION
tools_menu->addSeparator(); tools_menu->addSeparator();

View File

@ -12,6 +12,9 @@
#include <QPointer> #include <QPointer>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
#include "Common/HookableEvent.h"
#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION
class QMenu; class QMenu;
class ParallelProgressDialog; class ParallelProgressDialog;
@ -299,4 +302,8 @@ private:
QAction* m_jit_register_cache_off; QAction* m_jit_register_cache_off;
bool m_game_selected = false; bool m_game_selected = false;
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
Common::EventHook m_raintegration_event_hook;
#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION
}; };