Handle Achievement Triggered Client Event

Also deletes the old runtime-based Achievement Triggered event from the old handler, and the methods used by it to publish to the server and reactivate/deactivate achievements in the runtime.
This commit is contained in:
LillyJadeKatrin 2024-03-30 18:04:00 -04:00
parent c88d4cf040
commit bc3e429dd9
2 changed files with 26 additions and 132 deletions

View File

@ -234,9 +234,6 @@ void AchievementManager::AchievementEventHandler(const rc_runtime_event_t* runti
{ {
switch (runtime_event->type) switch (runtime_event->type)
{ {
case RC_RUNTIME_EVENT_ACHIEVEMENT_TRIGGERED:
HandleAchievementTriggeredEvent(runtime_event);
break;
case RC_RUNTIME_EVENT_ACHIEVEMENT_PROGRESS_UPDATED: case RC_RUNTIME_EVENT_ACHIEVEMENT_PROGRESS_UPDATED:
HandleAchievementProgressUpdatedEvent(runtime_event); HandleAchievementProgressUpdatedEvent(runtime_event);
break; break;
@ -665,56 +662,6 @@ AchievementManager::ResponseType AchievementManager::FetchBoardInfo(AchievementI
return ResponseType::SUCCESS; return ResponseType::SUCCESS;
} }
void AchievementManager::ActivateDeactivateAchievement(AchievementId id, bool enabled,
bool unofficial, bool encore)
{
auto it = m_unlock_map.find(id);
if (it == m_unlock_map.end())
{
ERROR_LOG_FMT(ACHIEVEMENTS, "Attempted to unlock unknown achievement id {}.", id);
return;
}
const UnlockStatus& status = it->second;
u32 index = status.game_data_index;
bool active = (rc_runtime_get_achievement(&m_runtime, id) != nullptr);
bool hardcore_mode_enabled = Config::Get(Config::RA_HARDCORE_ENABLED);
// Deactivate achievements if game is not loaded
bool activate = m_is_game_loaded;
// Activate achievements only if achievements are enabled
if (activate && !enabled)
activate = false;
// Deactivate if achievement is unofficial, unless unofficial achievements are enabled
if (activate && !unofficial &&
m_game_data.achievements[index].category == RC_ACHIEVEMENT_CATEGORY_UNOFFICIAL)
{
activate = false;
}
// If encore mode is on, activate/deactivate regardless of current unlock status
if (activate && !encore)
{
// Encore is off, achievement has been unlocked in this session, deactivate
activate = (status.session_unlock_count == 0);
// Encore is off, achievement has been hardcore unlocked on site, deactivate
if (activate && status.remote_unlock_status == UnlockStatus::UnlockType::HARDCORE)
activate = false;
// Encore is off, hardcore is off, achievement has been softcore unlocked on site, deactivate
if (activate && !hardcore_mode_enabled &&
status.remote_unlock_status == UnlockStatus::UnlockType::SOFTCORE)
{
activate = false;
}
}
if (!active && activate)
{
rc_runtime_activate_achievement(&m_runtime, id, m_game_data.achievements[index].definition,
nullptr, 0);
}
if (active && !activate)
rc_runtime_deactivate_achievement(&m_runtime, id);
}
void AchievementManager::GenerateRichPresence(const Core::CPUThreadGuard& guard) void AchievementManager::GenerateRichPresence(const Core::CPUThreadGuard& guard)
{ {
std::lock_guard lg{m_lock}; std::lock_guard lg{m_lock};
@ -723,33 +670,6 @@ void AchievementManager::GenerateRichPresence(const Core::CPUThreadGuard& guard)
[](unsigned address, unsigned num_bytes, void* ud) { return 0u; }, this, nullptr); [](unsigned address, unsigned num_bytes, void* ud) { return 0u; }, this, nullptr);
} }
AchievementManager::ResponseType AchievementManager::AwardAchievement(AchievementId achievement_id)
{
std::string username = Config::Get(Config::RA_USERNAME);
std::string api_token = Config::Get(Config::RA_API_TOKEN);
bool hardcore_mode_enabled = Config::Get(Config::RA_HARDCORE_ENABLED);
rc_api_award_achievement_request_t award_request = {.username = username.c_str(),
.api_token = api_token.c_str(),
.achievement_id = achievement_id,
.hardcore = hardcore_mode_enabled,
.game_hash = m_game_hash.data()};
rc_api_award_achievement_response_t award_response = {};
ResponseType r_type =
Request<rc_api_award_achievement_request_t, rc_api_award_achievement_response_t>(
award_request, &award_response, rc_api_init_award_achievement_request,
rc_api_process_award_achievement_response);
rc_api_destroy_award_achievement_response(&award_response);
if (r_type == ResponseType::SUCCESS)
{
INFO_LOG_FMT(ACHIEVEMENTS, "Awarded achievement ID {}.", achievement_id);
}
else
{
WARN_LOG_FMT(ACHIEVEMENTS, "Failed to award achievement ID {}.", achievement_id);
}
return r_type;
}
AchievementManager::ResponseType AchievementManager::SubmitLeaderboard(AchievementId leaderboard_id, AchievementManager::ResponseType AchievementManager::SubmitLeaderboard(AchievementId leaderboard_id,
int value) int value)
{ {
@ -851,55 +771,6 @@ void AchievementManager::DisplayWelcomeMessage()
OSD::Duration::VERY_LONG, color); OSD::Duration::VERY_LONG, color);
} }
void AchievementManager::HandleAchievementTriggeredEvent(const rc_runtime_event_t* runtime_event)
{
bool hardcore_mode_enabled = Config::Get(Config::RA_HARDCORE_ENABLED);
const auto event_id = runtime_event->id;
auto it = m_unlock_map.find(event_id);
if (it == m_unlock_map.end())
{
ERROR_LOG_FMT(ACHIEVEMENTS, "Invalid achievement triggered event with id {}.", event_id);
return;
}
it->second.session_unlock_count++;
AchievementId game_data_index = it->second.game_data_index;
OSD::AddMessage(fmt::format("Unlocked: {} ({})", m_game_data.achievements[game_data_index].title,
m_game_data.achievements[game_data_index].points),
OSD::Duration::VERY_LONG,
(hardcore_mode_enabled) ? OSD::Color::YELLOW : OSD::Color::CYAN,
(Config::Get(Config::RA_BADGES_ENABLED)) ?
DecodeBadgeToOSDIcon(it->second.unlocked_badge.badge) :
nullptr);
if (m_game_data.achievements[game_data_index].category == RC_ACHIEVEMENT_CATEGORY_CORE)
{
auto* user = rc_client_get_user_info(m_client);
m_queue.EmplaceItem([this, event_id] { AwardAchievement(event_id); });
PointSpread spread = TallyScore();
if (spread.hard_points == spread.total_points &&
it->second.remote_unlock_status != UnlockStatus::UnlockType::HARDCORE)
{
OSD::AddMessage(
fmt::format("Congratulations! {} has mastered {}", user->display_name, m_game_data.title),
OSD::Duration::VERY_LONG, OSD::Color::YELLOW,
(Config::Get(Config::RA_BADGES_ENABLED)) ? DecodeBadgeToOSDIcon(m_game_badge.badge) :
nullptr);
}
else if (spread.hard_points + spread.soft_points == spread.total_points &&
it->second.remote_unlock_status == UnlockStatus::UnlockType::LOCKED)
{
OSD::AddMessage(fmt::format("Congratulations! {} has completed {}", user->display_name,
m_game_data.title),
OSD::Duration::VERY_LONG, OSD::Color::CYAN,
(Config::Get(Config::RA_BADGES_ENABLED)) ?
DecodeBadgeToOSDIcon(m_game_badge.badge) :
nullptr);
}
}
ActivateDeactivateAchievement(event_id, Config::Get(Config::RA_ACHIEVEMENTS_ENABLED),
Config::Get(Config::RA_UNOFFICIAL_ENABLED),
Config::Get(Config::RA_ENCORE_ENABLED));
}
void AchievementManager::HandleAchievementProgressUpdatedEvent( void AchievementManager::HandleAchievementProgressUpdatedEvent(
const rc_runtime_event_t* runtime_event) const rc_runtime_event_t* runtime_event)
{ {
@ -1019,6 +890,21 @@ void AchievementManager::HandleLeaderboardTriggeredEvent(const rc_runtime_event_
ERROR_LOG_FMT(ACHIEVEMENTS, "Invalid leaderboard triggered event with id {}.", event_id); ERROR_LOG_FMT(ACHIEVEMENTS, "Invalid leaderboard triggered event with id {}.", event_id);
} }
void AchievementManager::HandleAchievementTriggeredEvent(const rc_client_event_t* client_event)
{
OSD::AddMessage(fmt::format("Unlocked: {} ({})", client_event->achievement->title,
client_event->achievement->points),
OSD::Duration::VERY_LONG,
(rc_client_get_hardcore_enabled(AchievementManager::GetInstance().m_client)) ?
OSD::Color::YELLOW :
OSD::Color::CYAN,
(Config::Get(Config::RA_BADGES_ENABLED)) ?
DecodeBadgeToOSDIcon(AchievementManager::GetInstance()
.m_unlocked_badges[client_event->achievement->id]
.badge) :
nullptr);
}
// Every RetroAchievements API call, with only a partial exception for fetch_image, follows // 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): // 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 // Create a specific rc_api_X_request_t struct and populate with the necessary values
@ -1201,6 +1087,15 @@ void AchievementManager::FetchBadge(AchievementManager::BadgeStatus* badge, u32
void AchievementManager::EventHandlerV2(const rc_client_event_t* event, rc_client_t* client) void AchievementManager::EventHandlerV2(const rc_client_event_t* event, rc_client_t* client)
{ {
switch (event->type)
{
case RC_CLIENT_EVENT_ACHIEVEMENT_TRIGGERED:
HandleAchievementTriggeredEvent(event);
break;
default:
INFO_LOG_FMT(ACHIEVEMENTS, "Event triggered of unhandled type {}", event->type);
break;
}
} }
#endif // USE_RETRO_ACHIEVEMENTS #endif // USE_RETRO_ACHIEVEMENTS

View File

@ -174,10 +174,8 @@ private:
std::unique_ptr<DiscIO::Volume>& GetLoadingVolume() { return m_loading_volume; }; std::unique_ptr<DiscIO::Volume>& GetLoadingVolume() { return m_loading_volume; };
void ActivateDeactivateAchievement(AchievementId id, bool enabled, bool unofficial, bool encore);
void GenerateRichPresence(const Core::CPUThreadGuard& guard); void GenerateRichPresence(const Core::CPUThreadGuard& guard);
ResponseType AwardAchievement(AchievementId achievement_id);
ResponseType SubmitLeaderboard(AchievementId leaderboard_id, int value); ResponseType SubmitLeaderboard(AchievementId leaderboard_id, int value);
ResponseType PingRichPresence(const RichPresence& rich_presence); ResponseType PingRichPresence(const RichPresence& rich_presence);
@ -185,7 +183,6 @@ private:
void* userdata); void* userdata);
void DisplayWelcomeMessage(); void DisplayWelcomeMessage();
void HandleAchievementTriggeredEvent(const rc_runtime_event_t* runtime_event);
void HandleAchievementProgressUpdatedEvent(const rc_runtime_event_t* runtime_event); void HandleAchievementProgressUpdatedEvent(const rc_runtime_event_t* runtime_event);
void HandleAchievementPrimedEvent(const rc_runtime_event_t* runtime_event); void HandleAchievementPrimedEvent(const rc_runtime_event_t* runtime_event);
void HandleAchievementUnprimedEvent(const rc_runtime_event_t* runtime_event); void HandleAchievementUnprimedEvent(const rc_runtime_event_t* runtime_event);
@ -193,6 +190,8 @@ private:
void HandleLeaderboardCanceledEvent(const rc_runtime_event_t* runtime_event); void HandleLeaderboardCanceledEvent(const rc_runtime_event_t* runtime_event);
void HandleLeaderboardTriggeredEvent(const rc_runtime_event_t* runtime_event); void HandleLeaderboardTriggeredEvent(const rc_runtime_event_t* runtime_event);
static void HandleAchievementTriggeredEvent(const rc_client_event_t* client_event);
template <typename RcRequest, typename RcResponse> template <typename RcRequest, typename RcResponse>
ResponseType Request(RcRequest rc_request, RcResponse* rc_response, ResponseType Request(RcRequest rc_request, RcResponse* rc_response,
const std::function<int(rc_api_request_t*, const RcRequest*)>& init_request, const std::function<int(rc_api_request_t*, const RcRequest*)>& init_request,