Merge pull request #12216 from LillyJadeKatrin/retroachievements-rehash

RetroAchievements - Improved Game Loading
This commit is contained in:
Mai 2023-12-05 18:22:38 -05:00 committed by GitHub
commit b4276049fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 346 additions and 115 deletions

View File

@ -20,7 +20,7 @@
#include "Core/Core.h"
#include "Core/PowerPC/MMU.h"
#include "Core/System.h"
#include "DiscIO/Volume.h"
#include "DiscIO/Blob.h"
#include "VideoCommon/OnScreenDisplay.h"
#include "VideoCommon/VideoEvents.h"
@ -91,8 +91,7 @@ bool AchievementManager::IsLoggedIn() const
return !Config::Get(Config::RA_API_TOKEN).empty();
}
void AchievementManager::LoadGameByFilenameAsync(const std::string& iso_path,
const ResponseCallback& callback)
void AchievementManager::HashGame(const std::string& file_path, const ResponseCallback& callback)
{
if (!m_is_runtime_initialized)
{
@ -101,117 +100,215 @@ void AchievementManager::LoadGameByFilenameAsync(const std::string& iso_path,
callback(AchievementManager::ResponseType::MANAGER_NOT_INITIALIZED);
return;
}
if (m_disabled)
{
INFO_LOG_FMT(ACHIEVEMENTS, "Achievement Manager is disabled until core is rebooted.");
OSD::AddMessage("Achievements are disabled until you restart emulation.",
OSD::Duration::VERY_LONG, OSD::Color::RED);
return;
}
m_system = &Core::System::GetInstance();
m_queue.EmplaceItem([this, callback, file_path] {
Hash new_hash;
{
std::lock_guard lg{m_filereader_lock};
rc_hash_filereader volume_reader{
.open = &AchievementManager::FilereaderOpenByFilepath,
.seek = &AchievementManager::FilereaderSeek,
.tell = &AchievementManager::FilereaderTell,
.read = &AchievementManager::FilereaderRead,
.close = &AchievementManager::FilereaderClose,
};
rc_hash_init_custom_filereader(&volume_reader);
if (!rc_hash_generate_from_file(new_hash.data(), RC_CONSOLE_GAMECUBE, file_path.c_str()))
{
ERROR_LOG_FMT(ACHIEVEMENTS, "Unable to generate achievement hash from game file {}.",
file_path);
callback(AchievementManager::ResponseType::MALFORMED_OBJECT);
}
}
{
std::lock_guard lg{m_lock};
if (m_disabled)
{
INFO_LOG_FMT(ACHIEVEMENTS, "Achievements disabled while hash was resolving.");
callback(AchievementManager::ResponseType::EXPIRED_CONTEXT);
return;
}
m_game_hash = std::move(new_hash);
}
LoadGameSync(callback);
});
}
void AchievementManager::HashGame(const DiscIO::Volume* volume, const ResponseCallback& callback)
{
if (!m_is_runtime_initialized)
{
ERROR_LOG_FMT(ACHIEVEMENTS,
"Attempted to load game achievements without Achievement Manager initialized.");
callback(AchievementManager::ResponseType::MANAGER_NOT_INITIALIZED);
return;
}
if (volume == nullptr)
{
INFO_LOG_FMT(ACHIEVEMENTS, "New volume is empty.");
return;
}
if (m_disabled)
{
INFO_LOG_FMT(ACHIEVEMENTS, "Achievement Manager is disabled until core is rebooted.");
OSD::AddMessage("Achievements are disabled until core is rebooted.", OSD::Duration::VERY_LONG,
OSD::Color::RED);
return;
}
// Need to SetDisabled outside a lock because it uses m_lock internally.
bool disable = false;
{
std::lock_guard lg{m_lock};
if (m_loading_volume.get() != nullptr)
{
disable = true;
}
else
{
m_loading_volume = DiscIO::CreateVolume(volume->GetBlobReader().CopyReader());
}
}
if (disable)
{
INFO_LOG_FMT(ACHIEVEMENTS, "Disabling Achievement Manager due to hash spam.");
SetDisabled(true);
callback(AchievementManager::ResponseType::EXPIRED_CONTEXT);
return;
}
m_system = &Core::System::GetInstance();
struct FilereaderState
{
int64_t position = 0;
std::unique_ptr<DiscIO::Volume> volume;
};
rc_hash_filereader volume_reader{
.open = [](const char* path_utf8) -> void* {
auto state = std::make_unique<FilereaderState>();
state->volume = DiscIO::CreateVolume(path_utf8);
if (!state->volume)
return nullptr;
return state.release();
},
.seek =
[](void* file_handle, int64_t offset, int origin) {
switch (origin)
{
case SEEK_SET:
reinterpret_cast<FilereaderState*>(file_handle)->position = offset;
break;
case SEEK_CUR:
reinterpret_cast<FilereaderState*>(file_handle)->position += offset;
break;
case SEEK_END:
// Unused
break;
}
},
.tell =
[](void* file_handle) {
return reinterpret_cast<FilereaderState*>(file_handle)->position;
},
.read =
[](void* file_handle, void* buffer, size_t requested_bytes) {
FilereaderState* filereader_state = reinterpret_cast<FilereaderState*>(file_handle);
bool success = (filereader_state->volume->Read(
filereader_state->position, requested_bytes, reinterpret_cast<u8*>(buffer),
DiscIO::PARTITION_NONE));
if (success)
{
filereader_state->position += requested_bytes;
return requested_bytes;
}
else
{
return static_cast<size_t>(0);
}
},
.close = [](void* file_handle) { delete reinterpret_cast<FilereaderState*>(file_handle); }};
rc_hash_init_custom_filereader(&volume_reader);
if (!rc_hash_generate_from_file(m_game_hash.data(), RC_CONSOLE_GAMECUBE, iso_path.c_str()))
{
ERROR_LOG_FMT(ACHIEVEMENTS, "Unable to generate achievement hash from game file.");
return;
}
m_queue.EmplaceItem([this, callback] {
const auto resolve_hash_response = ResolveHash(this->m_game_hash);
if (resolve_hash_response != ResponseType::SUCCESS || m_game_id == 0)
Hash new_hash;
{
callback(resolve_hash_response);
INFO_LOG_FMT(ACHIEVEMENTS, "No RetroAchievements data found for this game.");
OSD::AddMessage("No RetroAchievements data found for this game.", OSD::Duration::VERY_LONG,
OSD::Color::RED);
return;
std::lock_guard lg{m_filereader_lock};
rc_hash_filereader volume_reader{
.open = &AchievementManager::FilereaderOpenByVolume,
.seek = &AchievementManager::FilereaderSeek,
.tell = &AchievementManager::FilereaderTell,
.read = &AchievementManager::FilereaderRead,
.close = &AchievementManager::FilereaderClose,
};
rc_hash_init_custom_filereader(&volume_reader);
if (!rc_hash_generate_from_file(new_hash.data(), RC_CONSOLE_GAMECUBE, ""))
{
ERROR_LOG_FMT(ACHIEVEMENTS, "Unable to generate achievement hash from volume.");
callback(AchievementManager::ResponseType::MALFORMED_OBJECT);
return;
}
}
const auto start_session_response = StartRASession();
if (start_session_response != ResponseType::SUCCESS)
{
callback(start_session_response);
WARN_LOG_FMT(ACHIEVEMENTS, "Failed to connect to RetroAchievements server.");
OSD::AddMessage("Failed to connect to RetroAchievements server.", OSD::Duration::VERY_LONG,
OSD::Color::RED);
return;
}
const auto fetch_game_data_response = FetchGameData();
if (fetch_game_data_response != ResponseType::SUCCESS)
{
ERROR_LOG_FMT(ACHIEVEMENTS, "Unable to retrieve data from RetroAchievements server.");
OSD::AddMessage("Unable to retrieve data from RetroAchievements server.",
OSD::Duration::VERY_LONG, OSD::Color::RED);
return;
}
INFO_LOG_FMT(ACHIEVEMENTS, "Loading achievements for {}.", m_game_data.title);
// Claim the lock, then queue the fetch unlock data calls, then initialize the unlock map in
// ActivateDeactiveAchievements. This allows the calls to process while initializing the
// unlock map but then forces them to wait until it's initialized before making modifications to
// it.
{
std::lock_guard lg{m_lock};
m_is_game_loaded = true;
m_framecount = 0;
LoadUnlockData([](ResponseType r_type) {});
ActivateDeactivateAchievements();
ActivateDeactivateLeaderboards();
ActivateDeactivateRichPresence();
if (m_disabled)
{
INFO_LOG_FMT(ACHIEVEMENTS, "Achievements disabled while hash was resolving.");
callback(AchievementManager::ResponseType::EXPIRED_CONTEXT);
return;
}
m_game_hash = std::move(new_hash);
m_loading_volume.reset();
}
FetchBadges();
// Reset this to zero so that RP immediately triggers on the first frame
m_last_ping_time = 0;
INFO_LOG_FMT(ACHIEVEMENTS, "RetroAchievements successfully loaded for {}.", m_game_data.title);
if (m_update_callback)
m_update_callback();
callback(fetch_game_data_response);
LoadGameSync(callback);
});
}
void AchievementManager::LoadGameSync(const ResponseCallback& callback)
{
u32 new_game_id = 0;
Hash current_hash;
{
std::lock_guard lg{m_lock};
current_hash = m_game_hash;
}
const auto resolve_hash_response = ResolveHash(current_hash, &new_game_id);
if (resolve_hash_response != ResponseType::SUCCESS || new_game_id == 0)
{
INFO_LOG_FMT(ACHIEVEMENTS, "No RetroAchievements data found for this game.");
OSD::AddMessage("No RetroAchievements data found for this game.", OSD::Duration::VERY_LONG,
OSD::Color::RED);
SetDisabled(true);
callback(resolve_hash_response);
return;
}
u32 old_game_id;
{
std::lock_guard lg{m_lock};
old_game_id = m_game_id;
}
if (new_game_id == old_game_id)
{
INFO_LOG_FMT(ACHIEVEMENTS, "Alternate hash resolved for current game {}.", old_game_id);
callback(ResponseType::SUCCESS);
return;
}
else if (old_game_id != 0)
{
INFO_LOG_FMT(ACHIEVEMENTS, "Swapping game {} for game {}; achievements disabled.", old_game_id,
new_game_id);
OSD::AddMessage("Achievements are now disabled. Please close emulation to re-enable.",
OSD::Duration::VERY_LONG, OSD::Color::RED);
SetDisabled(true);
callback(ResponseType::EXPIRED_CONTEXT);
return;
}
{
std::lock_guard lg{m_lock};
m_game_id = new_game_id;
}
const auto start_session_response = StartRASession();
if (start_session_response != ResponseType::SUCCESS)
{
WARN_LOG_FMT(ACHIEVEMENTS, "Failed to connect to RetroAchievements server.");
OSD::AddMessage("Failed to connect to RetroAchievements server.", OSD::Duration::VERY_LONG,
OSD::Color::RED);
callback(start_session_response);
return;
}
const auto fetch_game_data_response = FetchGameData();
if (fetch_game_data_response != ResponseType::SUCCESS)
{
ERROR_LOG_FMT(ACHIEVEMENTS, "Unable to retrieve data from RetroAchievements server.");
OSD::AddMessage("Unable to retrieve data from RetroAchievements server.",
OSD::Duration::VERY_LONG, OSD::Color::RED);
return;
}
INFO_LOG_FMT(ACHIEVEMENTS, "Loading achievements for {}.", m_game_data.title);
// Claim the lock, then queue the fetch unlock data calls, then initialize the unlock map in
// ActivateDeactiveAchievements. This allows the calls to process while initializing the
// unlock map but then forces them to wait until it's initialized before making modifications to
// it.
{
std::lock_guard lg{m_lock};
m_is_game_loaded = true;
m_framecount = 0;
LoadUnlockData([](ResponseType r_type) {});
ActivateDeactivateAchievements();
ActivateDeactivateLeaderboards();
ActivateDeactivateRichPresence();
}
FetchBadges();
// Reset this to zero so that RP immediately triggers on the first frame
m_last_ping_time = 0;
INFO_LOG_FMT(ACHIEVEMENTS, "RetroAchievements successfully loaded for {}.", m_game_data.title);
if (m_update_callback)
m_update_callback();
callback(fetch_game_data_response);
}
bool AchievementManager::IsGameLoaded() const
{
return m_is_game_loaded;
@ -741,6 +838,25 @@ AchievementManager::RichPresence AchievementManager::GetRichPresence()
return rich_presence;
}
void AchievementManager::SetDisabled(bool disable)
{
{
std::lock_guard lg{m_lock};
m_disabled = disable;
}
if (disable)
{
INFO_LOG_FMT(ACHIEVEMENTS, "Achievement Manager has been disabled.");
OSD::AddMessage("Please close all games to re-enable achievements.", OSD::Duration::VERY_LONG,
OSD::Color::RED);
CloseGame();
}
else
{
INFO_LOG_FMT(ACHIEVEMENTS, "Achievement Manager has been re-enabled.");
}
};
void AchievementManager::CloseGame()
{
{
@ -790,6 +906,69 @@ void AchievementManager::Shutdown()
INFO_LOG_FMT(ACHIEVEMENTS, "Achievement Manager shut down.");
}
void* AchievementManager::FilereaderOpenByFilepath(const char* path_utf8)
{
auto state = std::make_unique<FilereaderState>();
state->volume = DiscIO::CreateVolume(path_utf8);
if (!state->volume)
return nullptr;
return state.release();
}
void* AchievementManager::FilereaderOpenByVolume(const char* path_utf8)
{
auto state = std::make_unique<FilereaderState>();
{
std::lock_guard lg{*AchievementManager::GetInstance()->GetLock()};
state->volume = std::move(AchievementManager::GetInstance()->GetLoadingVolume());
}
if (!state->volume)
return nullptr;
return state.release();
}
void AchievementManager::FilereaderSeek(void* file_handle, int64_t offset, int origin)
{
switch (origin)
{
case SEEK_SET:
static_cast<FilereaderState*>(file_handle)->position = offset;
break;
case SEEK_CUR:
static_cast<FilereaderState*>(file_handle)->position += offset;
break;
case SEEK_END:
// Unused
break;
}
}
int64_t AchievementManager::FilereaderTell(void* file_handle)
{
return static_cast<FilereaderState*>(file_handle)->position;
}
size_t AchievementManager::FilereaderRead(void* file_handle, void* buffer, size_t requested_bytes)
{
FilereaderState* filereader_state = static_cast<FilereaderState*>(file_handle);
bool success = (filereader_state->volume->Read(filereader_state->position, requested_bytes,
static_cast<u8*>(buffer), DiscIO::PARTITION_NONE));
if (success)
{
filereader_state->position += requested_bytes;
return requested_bytes;
}
else
{
return 0;
}
}
void AchievementManager::FilereaderClose(void* file_handle)
{
delete static_cast<FilereaderState*>(file_handle);
}
AchievementManager::ResponseType AchievementManager::VerifyCredentials(const std::string& password)
{
rc_api_login_response_t login_data{};
@ -826,8 +1005,8 @@ AchievementManager::ResponseType AchievementManager::VerifyCredentials(const std
return r_type;
}
AchievementManager::ResponseType
AchievementManager::ResolveHash(std::array<char, HASH_LENGTH> game_hash)
AchievementManager::ResponseType AchievementManager::ResolveHash(const Hash& game_hash,
u32* game_id)
{
rc_api_resolve_hash_response_t hash_data{};
std::string username, api_token;
@ -843,9 +1022,8 @@ AchievementManager::ResolveHash(std::array<char, HASH_LENGTH> game_hash)
rc_api_process_resolve_hash_response);
if (r_type == ResponseType::SUCCESS)
{
std::lock_guard lg{m_lock};
m_game_id = hash_data.game_id;
INFO_LOG_FMT(ACHIEVEMENTS, "Hashed game ID {} for RetroAchievements.", m_game_id);
*game_id = hash_data.game_id;
INFO_LOG_FMT(ACHIEVEMENTS, "Hashed game ID {} for RetroAchievements.", *game_id);
}
else
{

View File

@ -18,6 +18,7 @@
#include "Common/Event.h"
#include "Common/WorkQueueThread.h"
#include "DiscIO/Volume.h"
namespace Core
{
@ -51,6 +52,8 @@ public:
u32 soft_points;
};
static constexpr size_t HASH_SIZE = 33;
using Hash = std::array<char, HASH_SIZE>;
using AchievementId = u32;
static constexpr size_t FORMAT_SIZE = 24;
using FormattedValue = std::array<char, FORMAT_SIZE>;
@ -105,7 +108,8 @@ public:
ResponseType Login(const std::string& password);
void LoginAsync(const std::string& password, const ResponseCallback& callback);
bool IsLoggedIn() const;
void LoadGameByFilenameAsync(const std::string& iso_path, const ResponseCallback& callback);
void HashGame(const std::string& file_path, const ResponseCallback& callback);
void HashGame(const DiscIO::Volume* volume, const ResponseCallback& callback);
bool IsGameLoaded() const;
void LoadUnlockData(const ResponseCallback& callback);
@ -132,6 +136,8 @@ public:
u32* target);
const std::unordered_map<AchievementId, LeaderboardStatus>& GetLeaderboardsInfo() const;
RichPresence GetRichPresence();
bool IsDisabled() const { return m_disabled; };
void SetDisabled(bool disabled);
void CloseGame();
void Logout();
@ -140,15 +146,29 @@ public:
private:
AchievementManager() = default;
static constexpr int HASH_LENGTH = 33;
struct FilereaderState
{
int64_t position = 0;
std::unique_ptr<DiscIO::Volume> volume;
};
static void* FilereaderOpenByFilepath(const char* path_utf8);
static void* FilereaderOpenByVolume(const char* path_utf8);
static void FilereaderSeek(void* file_handle, int64_t offset, int origin);
static int64_t FilereaderTell(void* file_handle);
static size_t FilereaderRead(void* file_handle, void* buffer, size_t requested_bytes);
static void FilereaderClose(void* file_handle);
ResponseType VerifyCredentials(const std::string& password);
ResponseType ResolveHash(std::array<char, HASH_LENGTH> game_hash);
ResponseType ResolveHash(const Hash& game_hash, u32* game_id);
void LoadGameSync(const ResponseCallback& callback);
ResponseType StartRASession();
ResponseType FetchGameData();
ResponseType FetchUnlockData(bool hardcore);
ResponseType FetchBoardInfo(AchievementId leaderboard_id);
std::unique_ptr<DiscIO::Volume>& GetLoadingVolume() { return m_loading_volume; };
void ActivateDeactivateAchievement(AchievementId id, bool enabled, bool unofficial, bool encore);
void GenerateRichPresence();
@ -174,10 +194,12 @@ private:
Core::System* m_system{};
bool m_is_runtime_initialized = false;
UpdateCallback m_update_callback;
std::unique_ptr<DiscIO::Volume> m_loading_volume;
bool m_disabled = false;
std::string m_display_name;
u32 m_player_score = 0;
BadgeStatus m_player_badge;
std::array<char, HASH_LENGTH> m_game_hash{};
Hash m_game_hash{};
u32 m_game_id = 0;
rc_api_fetch_game_data_response_t m_game_data{};
bool m_is_game_loaded = false;
@ -192,6 +214,7 @@ private:
Common::WorkQueueThread<std::function<void()>> m_queue;
Common::WorkQueueThread<std::function<void()>> m_image_queue;
mutable std::recursive_mutex m_lock;
std::recursive_mutex m_filereader_lock;
}; // class AchievementManager
#endif // USE_RETRO_ACHIEVEMENTS

View File

@ -25,6 +25,7 @@
#include "Common/MsgHandler.h"
#include "Common/StringUtil.h"
#include "Core/AchievementManager.h"
#include "Core/Boot/DolReader.h"
#include "Core/Boot/ElfReader.h"
#include "Core/CommonTitles.h"
@ -558,6 +559,11 @@ bool CBoot::BootUp(Core::System& system, const Core::CPUThreadGuard& guard,
SetupGCMemory(system, guard);
}
#ifdef USE_RETRO_ACHIEVEMENTS
AchievementManager::GetInstance()->HashGame(executable.path,
[](AchievementManager::ResponseType r_type) {});
#endif // USE_RETRO_ACHIEVEMENTS
if (!executable.reader->LoadIntoMemory(system))
{
PanicAlertFmtT("Failed to load the executable to memory.");

View File

@ -165,13 +165,7 @@ bool BootCore(std::unique_ptr<BootParameters> boot, const WindowSystemInfo& wsi)
}
#ifdef USE_RETRO_ACHIEVEMENTS
std::string path = "";
if (std::holds_alternative<BootParameters::Disc>(boot->parameters))
{
path = std::get<BootParameters::Disc>(boot->parameters).path;
}
AchievementManager::GetInstance()->LoadGameByFilenameAsync(
path, [](AchievementManager::ResponseType r_type) {});
AchievementManager::GetInstance()->SetDisabled(false);
#endif // USE_RETRO_ACHIEVEMENTS
const bool load_ipl = !StartUp.bWii && !Config::Get(Config::MAIN_SKIP_IPL) &&

View File

@ -30,6 +30,7 @@
#include "Common/StringUtil.h"
#include "Common/Version.h"
#include "Core/AchievementManager.h"
#include "Core/Boot/Boot.h"
#include "Core/CommonTitles.h"
#include "Core/Config/DefaultLocale.h"
@ -168,6 +169,10 @@ void SConfig::SetRunningGameMetadata(const std::string& game_id, const std::stri
if (!was_changed)
return;
#ifdef USE_RETRO_ACHIEVEMENTS
AchievementManager::GetInstance()->SetDisabled(true);
#endif // USE_RETRO_ACHIEVEMENTS
if (game_id == "00000000")
{
m_title_name.clear();

View File

@ -20,6 +20,7 @@
#include "Common/Config/Config.h"
#include "Common/Logging/Log.h"
#include "Core/AchievementManager.h"
#include "Core/Config/MainSettings.h"
#include "Core/Config/SessionSettings.h"
#include "Core/CoreTiming.h"
@ -396,6 +397,11 @@ void DVDInterface::SetDisc(std::unique_ptr<DiscIO::VolumeDisc> disc,
m_auto_disc_change_index = 0;
}
#ifdef USE_RETRO_ACHIEVEMENTS
AchievementManager::GetInstance()->HashGame(disc.get(),
[](AchievementManager::ResponseType r_type) {});
#endif // USE_RETRO_ACHIEVEMENTS
// Assume that inserting a disc requires having an empty disc before
if (had_disc != has_disc)
ExpansionInterface::g_rtc_flags[ExpansionInterface::RTCFlag::DiscChanged] = true;

View File

@ -16,6 +16,7 @@
#include "Common/NandPaths.h"
#include "Common/ScopeGuard.h"
#include "Common/StringUtil.h"
#include "Core/AchievementManager.h"
#include "Core/CommonTitles.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
@ -476,6 +477,12 @@ bool ESDevice::LaunchPPCTitle(u64 title_id)
if (!Core::IsRunningAndStarted())
return BootstrapPPC();
#ifdef USE_RETRO_ACHIEVEMENTS
INFO_LOG_FMT(ACHIEVEMENTS,
"WAD and NAND formats not currently supported by Achievement Manager.");
AchievementManager::GetInstance()->SetDisabled(true);
#endif // USE_RETRO_ACHIEVEMENTS
core_timing.RemoveEvent(s_bootstrap_ppc_for_launch_event);
core_timing.ScheduleEvent(ticks, s_bootstrap_ppc_for_launch_event);
return true;

View File

@ -37,6 +37,11 @@ AchievementHeaderWidget::AchievementHeaderWidget(QWidget* parent) : QWidget(pare
m_game_progress_hard = new QProgressBar();
m_game_progress_soft = new QProgressBar();
m_rich_presence = new QLabel();
m_locked_warning = new QLabel();
m_locked_warning->setText(tr("Achievements have been disabled.<br>Please close all running "
"games to re-enable achievements."));
m_locked_warning->setStyleSheet(QStringLiteral("QLabel { color : red; }"));
QSizePolicy sp_retain = m_game_progress_hard->sizePolicy();
sp_retain.setRetainSizeWhenHidden(true);
@ -54,6 +59,7 @@ AchievementHeaderWidget::AchievementHeaderWidget(QWidget* parent) : QWidget(pare
text_col->addWidget(m_game_progress_hard);
text_col->addWidget(m_game_progress_soft);
text_col->addWidget(m_rich_presence);
text_col->addWidget(m_locked_warning);
QHBoxLayout* header_layout = new QHBoxLayout();
header_layout->addLayout(icon_col);
header_layout->addLayout(text_col);
@ -148,6 +154,7 @@ void AchievementHeaderWidget::UpdateData()
QString::fromUtf8(AchievementManager::GetInstance()->GetRichPresence().data()));
if (!m_rich_presence->isVisible())
m_rich_presence->setVisible(Config::Get(Config::RA_RICH_PRESENCE_ENABLED));
m_locked_warning->setVisible(false);
}
else
{
@ -157,6 +164,10 @@ void AchievementHeaderWidget::UpdateData()
m_game_progress_hard->setVisible(false);
m_game_progress_soft->setVisible(false);
m_rich_presence->setVisible(false);
if (AchievementManager::GetInstance()->IsDisabled())
{
m_locked_warning->setVisible(true);
}
}
}

View File

@ -30,6 +30,7 @@ private:
QProgressBar* m_game_progress_hard;
QProgressBar* m_game_progress_soft;
QLabel* m_rich_presence;
QLabel* m_locked_warning;
QGroupBox* m_header_box;
};