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;
}
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)
{
if (!m_client)
@ -354,7 +344,7 @@ void AchievementManager::DoFrame()
{
m_last_rp_time = current_time;
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))
Discord::UpdateDiscordPresence();
}
@ -753,7 +743,7 @@ void AchievementManager::CloseGame()
INFO_LOG_FMT(ACHIEVEMENTS, "Game closed.");
}
m_update_callback(UpdatedItems{.all = true});
UpdateEvent::Trigger(UpdatedItems{.all = true});
}
void AchievementManager::Logout()
@ -767,7 +757,7 @@ void AchievementManager::Logout()
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.");
}
@ -910,7 +900,7 @@ void AchievementManager::LoginCallback(int result, const char* error_message, rc
{
WARN_LOG_FMT(ACHIEVEMENTS, "Failed to login {} to RetroAchievements server.",
Config::Get(Config::RA_USERNAME));
AchievementManager::GetInstance().m_update_callback({.failed_login_code = result});
UpdateEvent::Trigger({.failed_login_code = result});
return;
}
@ -922,7 +912,7 @@ void AchievementManager::LoginCallback(int result, const char* error_message, rc
if (!user)
{
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;
}
@ -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 {}.",
user->username, Config::Get(Config::RA_USERNAME));
rc_client_logout(client);
AchievementManager::GetInstance().m_update_callback({.failed_login_code = RC_INVALID_STATE});
UpdateEvent::Trigger({.failed_login_code = RC_INVALID_STATE});
return;
}
}
@ -987,7 +977,7 @@ void AchievementManager::LeaderboardEntriesCallback(int result, const char* erro
if (static_cast<int32_t>(ix) == list->user_index)
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,
@ -1033,7 +1023,7 @@ void AchievementManager::LoadGameCallback(int result, const char* error_message,
rc_client_set_read_memory_function(instance.m_client, MemoryPeeker);
instance.FetchGameBadges();
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
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 :
OSD::Color::CYAN,
&instance.GetAchievementBadge(client_event->achievement->id, false));
AchievementManager::GetInstance().m_update_callback(
UpdatedItems{.achievements = {client_event->achievement->id}});
UpdateEvent::Trigger(UpdatedItems{.achievements = {client_event->achievement->id}});
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
switch (rc_client_raintegration_get_achievement_state(instance.m_client,
client_event->achievement->id))
@ -1171,8 +1160,7 @@ void AchievementManager::HandleLeaderboardSubmittedEvent(const rc_client_event_t
client_event->leaderboard->title),
OSD::Duration::VERY_LONG, OSD::Color::YELLOW);
AchievementManager::GetInstance().FetchBoardInfo(client_event->leaderboard->id);
AchievementManager::GetInstance().m_update_callback(
UpdatedItems{.leaderboards = {client_event->leaderboard->id}});
UpdateEvent::Trigger(UpdatedItems{.leaderboards = {client_event->leaderboard->id}});
}
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);
if (inserted)
instance.m_challenges_updated = true;
AchievementManager::GetInstance().m_update_callback(UpdatedItems{.rich_presence = true});
UpdateEvent::Trigger(UpdatedItems{.rich_presence = true});
}
void AchievementManager::HandleAchievementChallengeIndicatorHideEvent(
@ -1219,7 +1207,7 @@ void AchievementManager::HandleAchievementChallengeIndicatorHideEvent(
const auto removed = instance.m_active_challenges.erase(client_event->achievement->id);
if (removed > 0)
instance.m_challenges_updated = true;
AchievementManager::GetInstance().m_update_callback(UpdatedItems{.rich_presence = true});
UpdateEvent::Trigger(UpdatedItems{.rich_presence = true});
}
void AchievementManager::HandleAchievementProgressIndicatorShowEvent(
@ -1235,8 +1223,7 @@ void AchievementManager::HandleAchievementProgressIndicatorShowEvent(
OSD::Duration::SHORT, OSD::Color::GREEN,
&instance.GetAchievementBadge(client_event->achievement->id, false));
instance.m_last_progress_message = current_time;
AchievementManager::GetInstance().m_update_callback(
UpdatedItems{.achievements = {client_event->achievement->id}});
UpdateEvent::Trigger(UpdatedItems{.achievements = {client_event->achievement->id}});
}
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())
{
m_update_callback(callback_data);
UpdateEvent::Trigger(callback_data);
if (m_display_welcome_message && badge_type == RC_IMAGE_TYPE_GAME)
DisplayWelcomeMessage();
return;
@ -1421,7 +1408,7 @@ void AchievementManager::FetchBadge(AchievementManager::Badge* badge, u32 badge_
"RetroAchievements connection failed on image request.\n URL: {}",
api_request.url);
rc_api_destroy_request(&api_request);
m_update_callback(callback_data);
UpdateEvent::Trigger(callback_data);
return;
}
@ -1454,7 +1441,7 @@ void AchievementManager::FetchBadge(AchievementManager::Badge* badge, u32 badge_
}
*badge = std::move(tmp_badge);
m_update_callback(callback_data);
UpdateEvent::Trigger(callback_data);
if (badge_type == RC_IMAGE_TYPE_ACHIEVEMENT &&
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_write_memory_function(instance.m_client, MemoryPoker);
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
break;
@ -1552,12 +1539,11 @@ void AchievementManager::LoadIntegrationCallback(int result, const char* error_m
void AchievementManager::RAIntegrationEventHandler(const rc_client_raintegration_event_t* event,
rc_client_t* client)
{
auto& instance = AchievementManager::GetInstance();
switch (event->type)
{
case RC_CLIENT_RAINTEGRATION_EVENT_MENU_CHANGED:
case RC_CLIENT_RAINTEGRATION_EVENT_MENUITEM_CHECKED_CHANGED:
instance.m_dev_menu_callback();
DevMenuUpdateEvent::Trigger();
break;
case RC_CLIENT_RAINTEGRATION_EVENT_PAUSE:
{

View File

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

View File

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

View File

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

View File

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

View File

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