@@ -135,6 +135,8 @@ static bool SaveStateToBuffer(std::span<u8> data);
135135static std::string GetImageURL (const char * image_name, u32 type);
136136static std::string GetLocalImagePath (const std::string_view image_name, u32 type);
137137static void DownloadImage (std::string url, std::string cache_path);
138+ static void PrefetchNextAchievementBadge ();
139+ static void PrefetchNextAchievementBadge (const rc_client_achievement_t * const last_cheevo);
138140
139141static TinyString DecryptLoginToken (std::string_view encrypted_token, std::string_view username);
140142static TinyString EncryptLoginToken (std::string_view token, std::string_view username);
@@ -458,6 +460,50 @@ void Achievements::DownloadImage(std::string url, std::string cache_path)
458460 s_state.http_downloader ->CreateRequest (std::move (url), std::move (callback));
459461}
460462
463+ void Achievements::PrefetchNextAchievementBadge ()
464+ {
465+ if (!HasAchievements ())
466+ return ;
467+
468+ // Find most recent unlock.
469+ rc_client_achievement_list_t * const achievements =
470+ rc_client_create_achievement_list (Achievements::GetClient (), RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE_AND_UNOFFICIAL,
471+ RC_CLIENT_ACHIEVEMENT_LIST_GROUPING_PROGRESS);
472+ const rc_client_achievement_t * most_recent_unlock = nullptr ;
473+ for (u32 i = 0 ; i < achievements->num_buckets ; i++)
474+ {
475+ const rc_client_achievement_bucket_t & bucket = achievements->buckets [i];
476+ if (bucket.bucket_type != RC_CLIENT_ACHIEVEMENT_BUCKET_UNLOCKED &&
477+ bucket.bucket_type != RC_CLIENT_ACHIEVEMENT_BUCKET_RECENTLY_UNLOCKED)
478+ {
479+ continue ;
480+ }
481+
482+ for (u32 j = 0 ; j < bucket.num_achievements ; j++)
483+ {
484+ const rc_client_achievement_t * const cheevo = bucket.achievements [j];
485+ if (!most_recent_unlock || cheevo->unlock_time > most_recent_unlock->unlock_time )
486+ most_recent_unlock = cheevo;
487+ }
488+ }
489+ rc_client_destroy_achievement_list (achievements);
490+
491+ if (most_recent_unlock)
492+ PrefetchNextAchievementBadge (most_recent_unlock);
493+ }
494+
495+ void Achievements::PrefetchNextAchievementBadge (const rc_client_achievement_t * const last_cheevo)
496+ {
497+ // Precache badge for the likely next achievement to avoid the badge load time.
498+ const rc_client_achievement_t * const next_cheevo =
499+ rc_client_get_next_achievement_info (s_state.client , last_cheevo, RC_CLIENT_ACHIEVEMENT_BUCKET_LOCKED);
500+ if (!next_cheevo)
501+ return ;
502+
503+ VERBOSE_LOG (" Prefetching badge for likely next achievement '{}' ({})" , next_cheevo->title , next_cheevo->badge_url );
504+ GetAchievementBadgePath (next_cheevo, false );
505+ }
506+
461507bool Achievements::IsActive ()
462508{
463509 return (s_state.client != nullptr );
@@ -1248,6 +1294,7 @@ void Achievements::ClientLoadGameCallback(int result, const char* error_message,
12481294
12491295 // update progress database on first load, in case it was played on another PC
12501296 UpdateGameSummary (true );
1297+ PrefetchNextAchievementBadge ();
12511298
12521299 // needed for notifications
12531300 SoundEffectManager::EnsureInitialized ();
@@ -1367,7 +1414,7 @@ void Achievements::HandleResetEvent(const rc_client_event_t* event)
13671414
13681415void Achievements::HandleUnlockEvent (const rc_client_event_t * event)
13691416{
1370- const rc_client_achievement_t * cheevo = event->achievement ;
1417+ const rc_client_achievement_t * const cheevo = event->achievement ;
13711418 DebugAssert (cheevo);
13721419
13731420 INFO_LOG (" Achievement {} ({}) for game {} unlocked" , cheevo->id , cheevo->title , s_state.game_id );
@@ -1391,6 +1438,8 @@ void Achievements::HandleUnlockEvent(const rc_client_event_t* event)
13911438 std::move (title), std::string (cheevo->description ), std::move (note),
13921439 (cheevo->points > 0 ) ? FullscreenUI::AchievementNotificationNoteType::Text :
13931440 FullscreenUI::AchievementNotificationNoteType::None);
1441+
1442+ PrefetchNextAchievementBadge (cheevo);
13941443 }
13951444
13961445 if (g_settings.achievements_sound_effects )
@@ -1926,9 +1975,14 @@ std::string Achievements::GetAchievementBadgePath(const rc_client_achievement_t*
19261975 url = std::string (url_ptr);
19271976
19281977 if (url.empty ()) [[unlikely]]
1929- ReportFmtError (" Acheivement {} with badge name {} has no badge URL" , achievement->id , achievement->badge_name );
1978+ {
1979+ ReportFmtError (" Achievement {} with badge name {} has no badge URL" , achievement->id , achievement->badge_name );
1980+ }
19301981 else
1982+ {
1983+ DEV_LOG (" Downloading badge for achievement {} from URL: {}" , achievement->id , url);
19311984 DownloadImage (std::string (url), path);
1985+ }
19321986 }
19331987
19341988 return path;
0 commit comments