Achievements: Use rc_client as source of truth for HC mode

This commit is contained in:
Stenzek 2025-04-09 22:26:54 +10:00
parent a0aac8ef17
commit 1bb1354d4e
No known key found for this signature in database
9 changed files with 282 additions and 310 deletions

View File

@ -7,6 +7,7 @@
#include "achievements_private.h" #include "achievements_private.h"
#include "bios.h" #include "bios.h"
#include "bus.h" #include "bus.h"
#include "cheats.h"
#include "cpu_core.h" #include "cpu_core.h"
#include "fullscreen_ui.h" #include "fullscreen_ui.h"
#include "game_list.h" #include "game_list.h"
@ -144,14 +145,14 @@ static void ReportRCError(int err, fmt::format_string<T...> fmt, T&&... args);
static void ClearGameInfo(); static void ClearGameInfo();
static void ClearGameHash(); static void ClearGameHash();
static bool TryLoggingInWithToken(); static bool TryLoggingInWithToken();
static void SetHardcoreMode(bool enabled, bool force_display_message); static void EnableHardcodeMode(bool display_message, bool display_game_summary);
static void OnHardcoreModeChanged(bool enabled, bool display_message, bool display_game_summary);
static bool IsLoggedInOrLoggingIn(); static bool IsLoggedInOrLoggingIn();
static bool CanEnableHardcoreMode();
static void FinishLogin(const rc_client_t* client); static void FinishLogin(const rc_client_t* client);
static void ShowLoginNotification(); static void ShowLoginNotification();
static void IdentifyGame(const std::string& path, CDImage* image); static bool IdentifyGame(CDImage* image);
static bool IdentifyCurrentGame();
static void BeginLoadGame(); static void BeginLoadGame();
static void BeginChangeDisc();
static void UpdateGameSummary(bool update_progress_database, bool force_update_progress_database); static void UpdateGameSummary(bool update_progress_database, bool force_update_progress_database);
static std::string GetLocalImagePath(const std::string_view image_name, int type); static std::string GetLocalImagePath(const std::string_view image_name, int type);
static void DownloadImage(std::string url, std::string cache_path); static void DownloadImage(std::string url, std::string cache_path);
@ -244,7 +245,6 @@ struct PauseMenuAchievementInfo
struct State struct State
{ {
rc_client_t* client = nullptr; rc_client_t* client = nullptr;
bool hardcore_mode = false;
bool has_achievements = false; bool has_achievements = false;
bool has_leaderboards = false; bool has_leaderboards = false;
bool has_rich_presence = false; bool has_rich_presence = false;
@ -330,9 +330,8 @@ const rc_client_user_game_summary_t& Achievements::GetGameSummary()
void Achievements::ReportError(std::string_view sv) void Achievements::ReportError(std::string_view sv)
{ {
std::string error = fmt::format("Achievements error: {}", sv); ERROR_LOG(sv);
ERROR_LOG(error.c_str()); Host::AddIconOSDWarning(std::string(), ICON_EMOJI_WARNING, std::string(sv), Host::OSD_CRITICAL_ERROR_DURATION);
Host::AddOSDMessage(std::move(error), Host::OSD_CRITICAL_ERROR_DURATION);
} }
template<typename... T> template<typename... T>
@ -581,7 +580,11 @@ bool Achievements::IsHardcoreModeActive()
return RA_HardcoreModeIsActive() != 0; return RA_HardcoreModeIsActive() != 0;
#endif #endif
return s_state.hardcore_mode; if (!s_state.client)
return false;
const auto lock = GetLock();
return rc_client_get_hardcore_enabled(s_state.client);
} }
bool Achievements::HasActiveGame() bool Achievements::HasActiveGame()
@ -651,25 +654,27 @@ bool Achievements::Initialize()
if (!CreateClient(&s_state.client, &s_state.http_downloader)) if (!CreateClient(&s_state.client, &s_state.http_downloader))
return false; return false;
// Hardcore starts off. We enable it on first boot.
s_state.hardcore_mode = false;
rc_client_set_event_handler(s_state.client, ClientEventHandler); rc_client_set_event_handler(s_state.client, ClientEventHandler);
rc_client_set_hardcore_enabled(s_state.client, s_state.hardcore_mode); // Hardcore starts off. We enable it on first boot.
rc_client_set_hardcore_enabled(s_state.client, false);
rc_client_set_encore_mode_enabled(s_state.client, g_settings.achievements_encore_mode); rc_client_set_encore_mode_enabled(s_state.client, g_settings.achievements_encore_mode);
rc_client_set_unofficial_enabled(s_state.client, g_settings.achievements_unofficial_test_mode); rc_client_set_unofficial_enabled(s_state.client, g_settings.achievements_unofficial_test_mode);
rc_client_set_spectator_mode_enabled(s_state.client, g_settings.achievements_spectator_mode); rc_client_set_spectator_mode_enabled(s_state.client, g_settings.achievements_spectator_mode);
// Begin disc identification early, before the login finishes. // Start logging in. This can take a while.
if (System::IsValid())
IdentifyGame(System::GetDiscPath(), nullptr);
TryLoggingInWithToken(); TryLoggingInWithToken();
// Hardcore mode isn't enabled when achievements first starts, if a game is already running. // Are we running a game?
if (System::IsValid() && IsLoggedInOrLoggingIn() && g_settings.achievements_hardcore_mode) if (System::IsValid())
DisplayHardcoreDeferredMessage(); {
IdentifyCurrentGame();
BeginLoadGame();
// Hardcore mode isn't enabled when achievements first starts, if a game is already running.
if (IsLoggedInOrLoggingIn() && g_settings.achievements_hardcore_mode)
DisplayHardcoreDeferredMessage();
}
return true; return true;
} }
@ -753,7 +758,7 @@ void Achievements::UpdateSettings(const Settings& old_config)
if (!g_settings.achievements_enabled) if (!g_settings.achievements_enabled)
{ {
// we're done here // we're done here
Shutdown(false); Shutdown();
return; return;
} }
@ -766,26 +771,23 @@ void Achievements::UpdateSettings(const Settings& old_config)
if (g_settings.achievements_hardcore_mode != old_config.achievements_hardcore_mode) if (g_settings.achievements_hardcore_mode != old_config.achievements_hardcore_mode)
{ {
// Hardcore mode can only be enabled through reset (ResetChallengeMode()). // Enables have to wait for reset, disables can go through immediately.
if (s_state.hardcore_mode && !g_settings.achievements_hardcore_mode) if (g_settings.achievements_hardcore_mode)
{ DisplayHardcoreDeferredMessage();
ResetHardcoreMode(false); else
} DisableHardcoreMode(true, true);
else if (!s_state.hardcore_mode && g_settings.achievements_hardcore_mode)
{
if (HasActiveGame())
DisplayHardcoreDeferredMessage();
}
} }
// These cannot be modified while a game is loaded, so just toss state and reload. // These cannot be modified while a game is loaded, so just toss state and reload.
auto lock = GetLock();
if (HasActiveGame()) if (HasActiveGame())
{ {
lock.unlock();
if (g_settings.achievements_encore_mode != old_config.achievements_encore_mode || if (g_settings.achievements_encore_mode != old_config.achievements_encore_mode ||
g_settings.achievements_spectator_mode != old_config.achievements_spectator_mode || g_settings.achievements_spectator_mode != old_config.achievements_spectator_mode ||
g_settings.achievements_unofficial_test_mode != old_config.achievements_unofficial_test_mode) g_settings.achievements_unofficial_test_mode != old_config.achievements_unofficial_test_mode)
{ {
Shutdown(false); Shutdown();
Initialize(); Initialize();
return; return;
} }
@ -801,48 +803,27 @@ void Achievements::UpdateSettings(const Settings& old_config)
} }
} }
bool Achievements::Shutdown(bool allow_cancel) void Achievements::Shutdown()
{ {
#ifdef ENABLE_RAINTEGRATION
if (IsUsingRAIntegration())
{
if (System::IsValid() && allow_cancel && !RA_ConfirmLoadNewRom(true))
return false;
RA_SetPaused(false);
RA_ActivateGame(0);
return true;
}
#endif
if (!IsActive()) if (!IsActive())
return true; return;
auto lock = GetLock(); auto lock = GetLock();
Assert(s_state.client && s_state.http_downloader); Assert(s_state.client && s_state.http_downloader);
ClearGameInfo(); ClearGameInfo();
ClearGameHash(); ClearGameHash();
DisableHardcoreMode(); DisableHardcoreMode(false, false);
UpdateGlyphRanges(); UpdateGlyphRanges();
CancelHashDatabaseRequests(); CancelHashDatabaseRequests();
if (s_state.load_game_request)
{
rc_client_abort_async(s_state.client, s_state.load_game_request);
s_state.load_game_request = nullptr;
}
if (s_state.login_request) if (s_state.login_request)
{ {
rc_client_abort_async(s_state.client, s_state.login_request); rc_client_abort_async(s_state.client, s_state.login_request);
s_state.login_request = nullptr; s_state.login_request = nullptr;
} }
s_state.hardcore_mode = false;
DestroyClient(&s_state.client, &s_state.http_downloader); DestroyClient(&s_state.client, &s_state.http_downloader);
Host::OnAchievementsRefreshed();
return true;
} }
void Achievements::ClientMessageCallback(const char* message, const rc_client_t* client) void Achievements::ClientMessageCallback(const char* message, const rc_client_t* client)
@ -892,7 +873,7 @@ void Achievements::ClientServerCall(const rc_api_request_t* request, rc_client_s
RC_API_SERVER_RESPONSE_RETRYABLE_CLIENT_ERROR) : RC_API_SERVER_RESPONSE_RETRYABLE_CLIENT_ERROR) :
status_code; status_code;
rr.body_length = data.size(); rr.body_length = data.size();
rr.body = reinterpret_cast<const char*>(data.data()); rr.body = data.empty() ? nullptr : reinterpret_cast<const char*>(data.data());
callback(&rr, callback_data); callback(&rr, callback_data);
}; };
@ -1147,40 +1128,109 @@ void Achievements::UpdateRichPresence(std::unique_lock<std::recursive_mutex>& lo
lock.lock(); lock.lock();
} }
void Achievements::GameChanged(const std::string& path, CDImage* image, bool booting) void Achievements::OnSystemStarting(CDImage* image, bool disable_hardcore_mode)
{ {
std::unique_lock lock(s_state.mutex); std::unique_lock lock(s_state.mutex);
if (!IsActive()) if (!IsActive())
return; return;
IdentifyGame(path, image);
// if we're not logged in, and there's no login request, retry logging in // if we're not logged in, and there's no login request, retry logging in
// this'll happen if we had no network connection on startup, but gained it before starting a game. // this'll happen if we had no network connection on startup, but gained it before starting a game.
// follow the same order as Initialize() - identify, then log in if (!IsLoggedInOrLoggingIn())
if (!IsLoggedInOrLoggingIn() && booting)
{ {
WARNING_LOG("Not logged in on game booting, trying again."); WARNING_LOG("Not logged in on game booting, trying again.");
TryLoggingInWithToken(); TryLoggingInWithToken();
} }
// HC should have been disabled, we're now enabling it
// only enable hardcore mode if we're logged in, or waiting for a login response
AssertMsg(!rc_client_get_hardcore_enabled(s_state.client), "Hardcode mode should be disabled prior to boot");
if (!disable_hardcore_mode && g_settings.achievements_hardcore_mode && IsLoggedInOrLoggingIn())
EnableHardcodeMode(false, false);
// now we can finally identify the game
IdentifyGame(image);
BeginLoadGame();
} }
void Achievements::IdentifyGame(const std::string& path, CDImage* image) void Achievements::OnSystemDestroyed()
{ {
if (s_state.game_path == path) ClearGameInfo();
ClearGameHash();
DisableHardcoreMode(false, false);
UpdateGlyphRanges();
}
void Achievements::OnSystemPaused(bool paused)
{
#ifdef ENABLE_RAINTEGRATION
if (IsUsingRAIntegration())
RA_SetPaused(paused);
#endif
}
void Achievements::OnSystemReset()
{
const auto lock = GetLock();
if (!IsActive())
return;
#ifdef ENABLE_RAINTEGRATION
if (IsUsingRAIntegration())
{ {
WARNING_LOG("Game path is unchanged."); RA_OnReset();
return; return;
} }
#endif
std::unique_ptr<CDImage> temp_image; // Do we need to enable hardcore mode?
if (!path.empty() && !image) if (System::IsValid() && g_settings.achievements_hardcore_mode && !rc_client_get_hardcore_enabled(s_state.client))
{ {
temp_image = CDImage::Open(path.c_str(), g_settings.cdrom_load_image_patches, nullptr); // This will raise the silly reset event, but we can safely ignore that since we're immediately resetting the client
image = temp_image.get(); DEV_LOG("Enabling hardcore mode after reset");
if (!temp_image) EnableHardcodeMode(true, true);
ERROR_LOG("Failed to open temporary CD image '{}'", path); }
DEV_LOG("Reset client");
rc_client_reset(s_state.client);
}
void Achievements::GameChanged(CDImage* image)
{
std::unique_lock lock(s_state.mutex);
if (!IsActive())
return;
// disc changed?
if (!IdentifyGame(image))
return;
// cancel previous requests
if (s_state.load_game_request)
{
rc_client_abort_async(s_state.client, s_state.load_game_request);
s_state.load_game_request = nullptr;
}
// Use a hash that will never match if we removed the disc. See rc_client_begin_change_media().
TinyString game_hash_str;
if (s_state.game_hash.has_value())
game_hash_str = GameHashToString(s_state.game_hash.value());
else
game_hash_str = "[NO HASH]";
s_state.load_game_request = rc_client_begin_change_media_from_hash(
s_state.client, game_hash_str.c_str(), ClientLoadGameCallback, reinterpret_cast<void*>(static_cast<uintptr_t>(1)));
}
bool Achievements::IdentifyGame(CDImage* image)
{
if (s_state.game_path == image->GetPath())
{
WARNING_LOG("Game path is unchanged.");
return false;
} }
std::optional<GameHash> game_hash; std::optional<GameHash> game_hash;
@ -1189,63 +1239,64 @@ void Achievements::IdentifyGame(const std::string& path, CDImage* image)
u32 bytes_hashed; u32 bytes_hashed;
game_hash = GetGameHash(image, &bytes_hashed); game_hash = GetGameHash(image, &bytes_hashed);
if (game_hash.has_value()) if (game_hash.has_value())
INFO_LOG("RA Hash: {} ({} bytes hashed)", GameHashToString(game_hash.value()), bytes_hashed); {
INFO_COLOR_LOG(StrongOrange, "RA Hash: {} ({} bytes hashed)", GameHashToString(game_hash.value()), bytes_hashed);
}
else
{
// If we are starting with this game and it's bad, notify the user that this is why.
Host::AddIconOSDWarning(
"AchievementsHashFailed", ICON_EMOJI_WARNING,
TRANSLATE_STR("Achievements", "Failed to read executable from disc. Achievements disabled."),
Host::OSD_ERROR_DURATION);
}
} }
s_state.game_path = image ? image->GetPath() : std::string();
if (s_state.game_hash == game_hash) if (s_state.game_hash == game_hash)
{ {
// only the path has changed - different format/save state/etc. // only the path has changed - different format/save state/etc.
INFO_LOG("Detected path change from '{}' to '{}'", s_state.game_path, path); INFO_LOG("Detected path change to '{}'", s_state.game_path);
s_state.game_path = path; s_state.game_path = image->GetPath();
return; return false;
} }
ClearGameHash(); s_state.game_hash = game_hash;
s_state.game_path = path;
s_state.game_hash = std::move(game_hash);
#ifdef ENABLE_RAINTEGRATION #ifdef ENABLE_RAINTEGRATION
if (IsUsingRAIntegration()) if (IsUsingRAIntegration())
{
RAIntegration::GameChanged(); RAIntegration::GameChanged();
return;
}
#endif #endif
// shouldn't have a load game request when we're not logged in. return true;
Assert(IsLoggedInOrLoggingIn() || !s_state.load_game_request); }
// bail out if we're not logged in, just save the hash bool Achievements::IdentifyCurrentGame()
if (!IsLoggedInOrLoggingIn()) {
DebugAssert(System::IsValid());
// this crap is only needed because we can't grab the image from the reader...
std::unique_ptr<CDImage> temp_image;
if (const std::string& disc_path = System::GetDiscPath(); !disc_path.empty())
{ {
INFO_LOG("Skipping load game because we're not logged in."); Error error;
DisableHardcoreMode(); temp_image = CDImage::Open(disc_path.c_str(), g_settings.cdrom_load_image_patches, &error);
return; if (!temp_image)
ERROR_LOG("Failed to open disc for late game identification: {}", error.GetDescription());
} }
if (!rc_client_is_game_loaded(s_state.client)) return IdentifyGame(temp_image.get());
BeginLoadGame();
else
BeginChangeDisc();
} }
void Achievements::BeginLoadGame() void Achievements::BeginLoadGame()
{ {
ClearGameInfo(); DebugAssert(IsLoggedInOrLoggingIn());
if (!s_state.game_hash.has_value()) if (!s_state.game_hash.has_value())
{ {
// when we're booting the bios, this will fail // no need to go through ClientLoadGameCallback, just bail out straight away
if (!s_state.game_path.empty()) DisableHardcoreMode(false, false);
{
Host::AddKeyedOSDMessage(
"retroachievements_disc_read_failed",
TRANSLATE_STR("Achievements", "Failed to read executable from disc. Achievements disabled."),
Host::OSD_ERROR_DURATION);
}
DisableHardcoreMode();
UpdateGlyphRanges();
return; return;
} }
@ -1253,37 +1304,6 @@ void Achievements::BeginLoadGame()
ClientLoadGameCallback, nullptr); ClientLoadGameCallback, nullptr);
} }
void Achievements::BeginChangeDisc()
{
// cancel previous requests
if (s_state.load_game_request)
{
rc_client_abort_async(s_state.client, s_state.load_game_request);
s_state.load_game_request = nullptr;
}
if (!s_state.game_hash.has_value())
{
// when we're booting the bios, this will fail
if (!s_state.game_path.empty())
{
Host::AddKeyedOSDMessage(
"retroachievements_disc_read_failed",
TRANSLATE_STR("Achievements", "Failed to read executable from disc. Achievements disabled."),
Host::OSD_ERROR_DURATION);
}
ClearGameInfo();
DisableHardcoreMode();
UpdateGlyphRanges();
return;
}
s_state.load_game_request =
rc_client_begin_change_media_from_hash(s_state.client, GameHashToString(s_state.game_hash.value()),
ClientLoadGameCallback, reinterpret_cast<void*>(static_cast<uintptr_t>(1)));
}
void Achievements::ClientLoadGameCallback(int result, const char* error_message, rc_client_t* client, void* userdata) void Achievements::ClientLoadGameCallback(int result, const char* error_message, rc_client_t* client, void* userdata)
{ {
const bool was_disc_change = (userdata != nullptr); const bool was_disc_change = (userdata != nullptr);
@ -1300,7 +1320,7 @@ void Achievements::ClientLoadGameCallback(int result, const char* error_message,
UpdateGlyphRanges(); UpdateGlyphRanges();
} }
DisableHardcoreMode(); DisableHardcoreMode(false, false);
return; return;
} }
else if (result == RC_LOGIN_REQUIRED) else if (result == RC_LOGIN_REQUIRED)
@ -1309,6 +1329,14 @@ void Achievements::ClientLoadGameCallback(int result, const char* error_message,
// Once we've done so, we'll reload the game. // Once we've done so, we'll reload the game.
return; return;
} }
else if (result == RC_HARDCORE_DISABLED)
{
if (error_message)
ReportError(error_message);
OnHardcoreModeChanged(false, true, false);
return;
}
else if (result != RC_OK) else if (result != RC_OK)
{ {
ReportFmtError("Loading game failed: {}", error_message); ReportFmtError("Loading game failed: {}", error_message);
@ -1318,16 +1346,9 @@ void Achievements::ClientLoadGameCallback(int result, const char* error_message,
UpdateGlyphRanges(); UpdateGlyphRanges();
} }
DisableHardcoreMode(); DisableHardcoreMode(false, false);
return; return;
} }
else if (result == RC_HARDCORE_DISABLED)
{
if (error_message)
ReportError(error_message);
DisableHardcoreMode();
}
const rc_client_game_t* info = rc_client_get_game_info(s_state.client); const rc_client_game_t* info = rc_client_get_game_info(s_state.client);
if (!info) if (!info)
@ -1339,7 +1360,7 @@ void Achievements::ClientLoadGameCallback(int result, const char* error_message,
UpdateGlyphRanges(); UpdateGlyphRanges();
} }
DisableHardcoreMode(); DisableHardcoreMode(false, false);
return; return;
} }
@ -1349,13 +1370,10 @@ void Achievements::ClientLoadGameCallback(int result, const char* error_message,
// Only display summary if the game title has changed across discs. // Only display summary if the game title has changed across discs.
const bool display_summary = (s_state.game_id != info->id || s_state.game_title != info->title); const bool display_summary = (s_state.game_id != info->id || s_state.game_title != info->title);
// If the game has a RetroAchievements entry but no achievements or leaderboards, // If the game has a RetroAchievements entry but no achievements or leaderboards, enforcing hardcore mode
// enforcing hardcore mode is pointless. // is pointless. Have to re-query leaderboards because hidden should still trip HC.
if (!has_achievements && !has_leaderboards) if (!has_achievements && !rc_client_has_leaderboards(client, true))
DisableHardcoreMode(); DisableHardcoreMode(false, false);
// We should have matched hardcore mode state.
Assert(s_state.hardcore_mode == (rc_client_get_hardcore_enabled(client) != 0));
s_state.game_id = info->id; s_state.game_id = info->id;
s_state.game_title = info->title; s_state.game_title = info->title;
@ -1469,7 +1487,7 @@ void Achievements::DisplayAchievementSummary()
void Achievements::DisplayHardcoreDeferredMessage() void Achievements::DisplayHardcoreDeferredMessage()
{ {
if (g_settings.achievements_hardcore_mode && !s_state.hardcore_mode && System::IsValid()) if (g_settings.achievements_hardcore_mode && System::IsValid())
{ {
GPUThread::RunOnThread([]() { GPUThread::RunOnThread([]() {
if (!FullscreenUI::Initialize()) if (!FullscreenUI::Initialize())
@ -1484,12 +1502,7 @@ void Achievements::DisplayHardcoreDeferredMessage()
void Achievements::HandleResetEvent(const rc_client_event_t* event) void Achievements::HandleResetEvent(const rc_client_event_t* event)
{ {
// We handle system resets ourselves, but still need to reset the client's state. WARNING_LOG("Ignoring RC_CLIENT_EVENT_RESET.");
INFO_LOG("Resetting runtime due to reset event");
rc_client_reset(s_state.client);
if (HasActiveGame())
UpdateGameSummary(false, false);
} }
void Achievements::HandleUnlockEvent(const rc_client_event_t* event) void Achievements::HandleUnlockEvent(const rc_client_event_t* event)
@ -1806,32 +1819,17 @@ void Achievements::HandleServerReconnectedEvent(const rc_client_event_t* event)
}); });
} }
void Achievements::Reset() void Achievements::EnableHardcodeMode(bool display_message, bool display_game_summary)
{ {
#ifdef ENABLE_RAINTEGRATION DebugAssert(IsActive());
if (IsUsingRAIntegration()) if (rc_client_get_hardcore_enabled(s_state.client))
{
RA_OnReset();
return;
}
#endif
if (!IsActive())
return; return;
DEV_LOG("Reset client"); rc_client_set_hardcore_enabled(s_state.client, true);
rc_client_reset(s_state.client); OnHardcoreModeChanged(true, display_message, display_game_summary);
} }
void Achievements::OnSystemPaused(bool paused) void Achievements::DisableHardcoreMode(bool show_message, bool display_game_summary)
{
#ifdef ENABLE_RAINTEGRATION
if (IsUsingRAIntegration())
RA_SetPaused(paused);
#endif
}
void Achievements::DisableHardcoreMode()
{ {
if (!IsActive()) if (!IsActive())
return; return;
@ -1846,43 +1844,19 @@ void Achievements::DisableHardcoreMode()
} }
#endif #endif
if (!s_state.hardcore_mode)
return;
SetHardcoreMode(false, true);
}
bool Achievements::ResetHardcoreMode(bool is_booting)
{
if (!IsActive())
return false;
const auto lock = GetLock(); const auto lock = GetLock();
if (!rc_client_get_hardcore_enabled(s_state.client))
// If we're not logged in, don't apply hardcore mode restrictions.
// If we later log in, we'll start with it off anyway.
const bool wanted_hardcore_mode = (IsLoggedInOrLoggingIn() || s_state.load_game_request) &&
(HasActiveGame() || s_state.load_game_request) &&
g_settings.achievements_hardcore_mode;
if (s_state.hardcore_mode == wanted_hardcore_mode)
return false;
if (!is_booting && wanted_hardcore_mode && !CanEnableHardcoreMode())
return false;
SetHardcoreMode(wanted_hardcore_mode, false);
return true;
}
void Achievements::SetHardcoreMode(bool enabled, bool force_display_message)
{
if (enabled == s_state.hardcore_mode)
return; return;
// new mode rc_client_set_hardcore_enabled(s_state.client, false);
s_state.hardcore_mode = enabled; OnHardcoreModeChanged(false, show_message, display_game_summary);
}
if (System::IsValid() && (HasActiveGame() || force_display_message)) void Achievements::OnHardcoreModeChanged(bool enabled, bool display_message, bool display_game_summary)
{
INFO_COLOR_LOG(StrongYellow, "Hardcore mode/restrictions are now {}.", enabled ? "ACTIVE" : "inactive");
if (System::IsValid() && display_message)
{ {
GPUThread::RunOnThread([enabled]() { GPUThread::RunOnThread([enabled]() {
if (!FullscreenUI::Initialize()) if (!FullscreenUI::Initialize())
@ -1895,17 +1869,28 @@ void Achievements::SetHardcoreMode(bool enabled, bool force_display_message)
}); });
} }
rc_client_set_hardcore_enabled(s_state.client, enabled); if (HasActiveGame() && display_game_summary)
DebugAssert((rc_client_get_hardcore_enabled(s_state.client) != 0) == enabled);
if (HasActiveGame())
{ {
UpdateGameSummary(true, true); UpdateGameSummary(true, true);
DisplayAchievementSummary(); DisplayAchievementSummary();
} }
DebugAssert((rc_client_get_hardcore_enabled(s_state.client) != 0) == enabled);
// Reload setting to permit cheating-like things if we were just disabled. // Reload setting to permit cheating-like things if we were just disabled.
if (!enabled && System::IsValid()) if (System::IsValid())
{
// Make sure a pre-existing cheat file hasn't been loaded when resetting after enabling HC mode.
Cheats::ReloadCheats(true, true, false, true, true);
// Defer settings update in case something is using it.
Host::RunOnCPUThread([]() { System::ApplySettings(false); }); Host::RunOnCPUThread([]() { System::ApplySettings(false); });
}
else if (System::GetState() == System::State::Starting)
{
// Initial HC enable, activate restrictions.
System::ApplySettings(false);
}
// Toss away UI state, because it's invalid now // Toss away UI state, because it's invalid now
ClearUIState(); ClearUIState();
@ -1947,9 +1932,6 @@ bool Achievements::DoState(StateWrapper& sw)
GPUThread::RunOnThread([]() { FullscreenUI::CloseLoadingScreen(); }); GPUThread::RunOnThread([]() { FullscreenUI::CloseLoadingScreen(); });
} }
// loading an old state without cheevos, so reset the runtime
Achievements::Reset();
u32 data_size = 0; u32 data_size = 0;
sw.DoEx(&data_size, REQUIRED_VERSION, 0u); sw.DoEx(&data_size, REQUIRED_VERSION, 0u);
if (data_size == 0) if (data_size == 0)
@ -2096,12 +2078,6 @@ bool Achievements::IsLoggedInOrLoggingIn()
return (rc_client_get_user_info(s_state.client) != nullptr || s_state.login_request); return (rc_client_get_user_info(s_state.client) != nullptr || s_state.login_request);
} }
bool Achievements::CanEnableHardcoreMode()
{
// have to re-query leaderboards because hidden should still trip HC
return (s_state.load_game_request || s_state.has_achievements || rc_client_has_leaderboards(s_state.client, true));
}
bool Achievements::Login(const char* username, const char* password, Error* error) bool Achievements::Login(const char* username, const char* password, Error* error)
{ {
auto lock = GetLock(); auto lock = GetLock();
@ -2145,7 +2121,10 @@ bool Achievements::Login(const char* username, const char* password, Error* erro
// If we were't a temporary client, get the game loaded. // If we were't a temporary client, get the game loaded.
if (System::IsValid() && !is_temporary_client) if (System::IsValid() && !is_temporary_client)
{
IdentifyCurrentGame();
BeginLoadGame(); BeginLoadGame();
}
return true; return true;
} }
@ -2223,9 +2202,6 @@ void Achievements::ClientLoginWithTokenCallback(int result, const char* error_me
} }
FinishLogin(client); FinishLogin(client);
if (System::IsValid())
BeginLoadGame();
} }
void Achievements::FinishLogin(const rc_client_t* client) void Achievements::FinishLogin(const rc_client_t* client)
@ -2338,7 +2314,7 @@ void Achievements::Logout()
ClearProgressDatabase(); ClearProgressDatabase();
} }
bool Achievements::ConfirmSystemReset() bool Achievements::ConfirmGameChange()
{ {
#ifdef ENABLE_RAINTEGRATION #ifdef ENABLE_RAINTEGRATION
if (IsUsingRAIntegration()) if (IsUsingRAIntegration())
@ -2364,7 +2340,7 @@ bool Achievements::ConfirmHardcoreModeDisable(const char* trigger)
if (!confirmed) if (!confirmed)
return false; return false;
DisableHardcoreMode(); DisableHardcoreMode(true, true);
return true; return true;
} }
@ -2374,7 +2350,7 @@ void Achievements::ConfirmHardcoreModeDisableAsync(const char* trigger, std::fun
// don't run the callback in the middle of rendering the UI // don't run the callback in the middle of rendering the UI
Host::RunOnCPUThread([callback = std::move(callback), res]() { Host::RunOnCPUThread([callback = std::move(callback), res]() {
if (res) if (res)
DisableHardcoreMode(); DisableHardcoreMode(true, true);
callback(res); callback(res);
}); });
}; };
@ -2826,7 +2802,7 @@ void Achievements::DrawAchievementsWindow()
using ImGuiFullscreen::RenderShadowedTextClipped; using ImGuiFullscreen::RenderShadowedTextClipped;
using ImGuiFullscreen::UIStyle; using ImGuiFullscreen::UIStyle;
auto lock = Achievements::GetLock(); const auto lock = Achievements::GetLock();
// achievements can get turned off via the main UI // achievements can get turned off via the main UI
if (!s_state.achievement_list) if (!s_state.achievement_list)
@ -2886,7 +2862,7 @@ void Achievements::DrawAchievementsWindow()
const ImRect title_bb(ImVec2(left, top), ImVec2(right, top + UIStyle.LargeFont->FontSize)); const ImRect title_bb(ImVec2(left, top), ImVec2(right, top + UIStyle.LargeFont->FontSize));
text.assign(s_state.game_title); text.assign(s_state.game_title);
if (s_state.hardcore_mode) if (rc_client_get_hardcore_enabled(s_state.client))
text.append(TRANSLATE_SV("Achievements", " (Hardcore Mode)")); text.append(TRANSLATE_SV("Achievements", " (Hardcore Mode)"));
top += UIStyle.LargeFont->FontSize + spacing; top += UIStyle.LargeFont->FontSize + spacing;

View File

@ -77,18 +77,24 @@ bool Initialize();
/// Updates achievements settings. /// Updates achievements settings.
void UpdateSettings(const Settings& old_config); void UpdateSettings(const Settings& old_config);
/// Resets the internal state of all achievement tracking. Call on system reset. /// Shuts down the RetroAchievements client.
void Reset(); void Shutdown();
/// Called when the system is being reset. If it returns false, the reset should be aborted. /// Called when the system is start. Engages hardcore mode if enabled.
bool ConfirmSystemReset(); void OnSystemStarting(CDImage* image, bool disable_hardcore_mode);
/// Called when the system is being shut down. If Shutdown() returns false, the shutdown should be aborted. /// Called when the system is shutting down. If this returns false, the shutdown should be aborted.
bool Shutdown(bool allow_cancel); void OnSystemDestroyed();
/// Called when the system is being reset. Resets the internal state of all achievement tracking.
void OnSystemReset();
/// Called when the system is being paused and resumed. /// Called when the system is being paused and resumed.
void OnSystemPaused(bool paused); void OnSystemPaused(bool paused);
/// Called when the system changes game.
void GameChanged(CDImage* image);
/// Called once a frame at vsync time on the CPU thread. /// Called once a frame at vsync time on the CPU thread.
void FrameUpdate(); void FrameUpdate();
@ -108,14 +114,8 @@ bool Login(const char* username, const char* password, Error* error);
/// Logs out of RetroAchievements, clearing any credentials. /// Logs out of RetroAchievements, clearing any credentials.
void Logout(); void Logout();
/// Called when the system changes game, or is booting.
void GameChanged(const std::string& path, CDImage* image, bool booting);
/// Re-enables hardcore mode if it is enabled in the settings.
bool ResetHardcoreMode(bool is_booting);
/// Forces hardcore mode off until next reset. /// Forces hardcore mode off until next reset.
void DisableHardcoreMode(); void DisableHardcoreMode(bool show_message, bool display_game_summary);
/// Prompts the user to disable hardcore mode, if they agree, returns true. /// Prompts the user to disable hardcore mode, if they agree, returns true.
bool ConfirmHardcoreModeDisable(const char* trigger); bool ConfirmHardcoreModeDisable(const char* trigger);
@ -127,6 +127,9 @@ bool IsHardcoreModeActive();
/// RAIntegration only exists for Windows, so no point checking it on other platforms. /// RAIntegration only exists for Windows, so no point checking it on other platforms.
bool IsUsingRAIntegration(); bool IsUsingRAIntegration();
/// Hook for RAIntegration to confirm reset/shutdown.
bool ConfirmGameChange();
/// Returns true if the achievement system is active. Achievements can be active without a valid client. /// Returns true if the achievement system is active. Achievements can be active without a valid client.
bool IsActive(); bool IsActive();

View File

@ -542,7 +542,7 @@ void System::CPUThreadShutdown()
ShutdownDiscordPresence(); ShutdownDiscordPresence();
#endif #endif
Achievements::Shutdown(false); Achievements::Shutdown();
InputManager::CloseSources(); InputManager::CloseSources();
@ -1349,12 +1349,17 @@ void System::ApplySettings(bool display_osd_messages)
LoadSettings(display_osd_messages); LoadSettings(display_osd_messages);
// If we've disabled/enabled game settings, we need to reload without it. // If we've disabled/enabled game settings, we need to reload without it.
// Also reload cheats when safe mode is toggled, because patches might change.
if (g_settings.apply_game_settings != old_settings.apply_game_settings) if (g_settings.apply_game_settings != old_settings.apply_game_settings)
{ {
UpdateGameSettingsLayer(); UpdateGameSettingsLayer();
LoadSettings(display_osd_messages); LoadSettings(display_osd_messages);
} }
else if (g_settings.achievements_hardcore_mode != old_settings.achievements_hardcore_mode)
{
// Hardcore mode enabled/disabled. May need to disable restrictions.
Achievements::UpdateSettings(old_settings);
LoadSettings(display_osd_messages);
}
CheckForSettingsChanges(old_settings); CheckForSettingsChanges(old_settings);
Host::CheckForSettingsChanges(old_settings); Host::CheckForSettingsChanges(old_settings);
@ -1546,19 +1551,9 @@ void System::UpdateInputSettingsLayer(std::string input_profile_name, std::uniqu
void System::ResetSystem() void System::ResetSystem()
{ {
if (!IsValid()) if (!IsValid() || !Achievements::ConfirmGameChange())
return; return;
if (!Achievements::ConfirmSystemReset())
return;
if (Achievements::ResetHardcoreMode(false))
{
// Make sure a pre-existing cheat file hasn't been loaded when resetting after enabling HC mode.
Cheats::ReloadCheats(true, true, false, true, true);
ApplySettings(false);
}
InternalReset(); InternalReset();
// Reset boot mode/reload BIOS if needed. Preserve exe/psf boot. // Reset boot mode/reload BIOS if needed. Preserve exe/psf boot.
@ -1731,6 +1726,7 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
// Update running game, this will apply settings as well. // Update running game, this will apply settings as well.
UpdateRunningGame(disc ? disc->GetPath() : parameters.filename, disc.get(), true); UpdateRunningGame(disc ? disc->GetPath() : parameters.filename, disc.get(), true);
Achievements::OnSystemStarting(disc.get(), parameters.disable_achievements_hardcore_mode);
// Determine console region. Has to be done here, because gamesettings can override it. // Determine console region. Has to be done here, because gamesettings can override it.
s_state.region = (g_settings.region == ConsoleRegion::Auto) ? auto_console_region : g_settings.region; s_state.region = (g_settings.region == ConsoleRegion::Auto) ? auto_console_region : g_settings.region;
@ -1755,13 +1751,6 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
// Achievement hardcore checks before committing to anything. // Achievement hardcore checks before committing to anything.
if (disc) if (disc)
{ {
// Check for resuming with hardcore mode.
const bool hc_mode_was_enabled = Achievements::IsHardcoreModeActive();
if (parameters.disable_achievements_hardcore_mode)
Achievements::DisableHardcoreMode();
else
Achievements::ResetHardcoreMode(true);
if ((!parameters.save_state.empty() || !exe_override.empty()) && Achievements::IsHardcoreModeActive()) if ((!parameters.save_state.empty() || !exe_override.empty()) && Achievements::IsHardcoreModeActive())
{ {
const bool is_exe_override_boot = parameters.save_state.empty(); const bool is_exe_override_boot = parameters.save_state.empty();
@ -1794,10 +1783,6 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
return true; return true;
} }
} }
// Need to reinit things like emulation speed, cpu overclock, etc.
if (Achievements::IsHardcoreModeActive() != hc_mode_was_enabled)
ApplySettings(false);
} }
// Are we fast booting? Must be checked after updating game settings. // Are we fast booting? Must be checked after updating game settings.
@ -1994,6 +1979,7 @@ void System::DestroySystem()
CPU::Shutdown(); CPU::Shutdown();
Bus::Shutdown(); Bus::Shutdown();
TimingEvents::Shutdown(); TimingEvents::Shutdown();
Achievements::OnSystemDestroyed();
ClearRunningGame(); ClearRunningGame();
GPUThread::DestroyGPUBackend(); GPUThread::DestroyGPUBackend();
@ -2043,8 +2029,6 @@ void System::ClearRunningGame()
Host::OnGameChanged(s_state.running_game_path, s_state.running_game_serial, s_state.running_game_title, Host::OnGameChanged(s_state.running_game_path, s_state.running_game_serial, s_state.running_game_title,
s_state.running_game_hash); s_state.running_game_hash);
Achievements::GameChanged(s_state.running_game_path, nullptr, false);
UpdateRichPresence(true); UpdateRichPresence(true);
} }
@ -2780,7 +2764,7 @@ void System::InternalReset()
MDEC::Reset(); MDEC::Reset();
SIO::Reset(); SIO::Reset();
PCDrv::Reset(); PCDrv::Reset();
Achievements::Reset(); Achievements::OnSystemReset();
s_state.frame_number = 1; s_state.frame_number = 1;
s_state.internal_frame_number = 0; s_state.internal_frame_number = 0;
} }
@ -2963,7 +2947,7 @@ bool System::LoadStateFromBuffer(const SaveStateBuffer& buffer, Error* error, bo
ClearMemorySaveStates(false, false); ClearMemorySaveStates(false, false);
// Updating game/loading settings can turn on hardcore mode. Catch this. // Updating game/loading settings can turn on hardcore mode. Catch this.
Achievements::DisableHardcoreMode(); Achievements::DisableHardcoreMode(true, true);
return LoadStateDataFromBuffer(buffer.state_data.cspan(0, buffer.state_size), buffer.version, error, update_display); return LoadStateDataFromBuffer(buffer.state_data.cspan(0, buffer.state_size), buffer.version, error, update_display);
} }
@ -4205,18 +4189,18 @@ void System::UpdateRunningGame(const std::string& path, CDImage* image, bool boo
} }
UpdateGameSettingsLayer(); UpdateGameSettingsLayer();
ApplySettings(true);
if (!IsReplayingGPUDump()) if (!IsReplayingGPUDump())
{ {
Achievements::GameChanged(s_state.running_game_path, image, booting);
// Cheats are loaded later in Initialize(). // Cheats are loaded later in Initialize().
if (!booting) if (!booting)
{
Achievements::GameChanged(image);
Cheats::ReloadCheats(true, true, false, true, true); Cheats::ReloadCheats(true, true, false, true, true);
}
} }
ApplySettings(true);
if (s_state.running_game_serial != prev_serial) if (s_state.running_game_serial != prev_serial)
{ {
GPUThread::SetGameSerial(s_state.running_game_serial); GPUThread::SetGameSerial(s_state.running_game_serial);

View File

@ -56,7 +56,7 @@ void AchievementLoginDialog::cancelClicked()
{ {
Host::RunOnCPUThread([]() { Host::RunOnCPUThread([]() {
if (System::IsValid() && !Achievements::HasActiveGame()) if (System::IsValid() && !Achievements::HasActiveGame())
Achievements::DisableHardcoreMode(); Achievements::DisableHardcoreMode(false, false);
}); });
} }

View File

@ -138,9 +138,11 @@ void AchievementSettingsWidget::onHardcoreModeStateChanged()
return; return;
// don't bother prompting if the game doesn't have achievements // don't bother prompting if the game doesn't have achievements
auto lock = Achievements::GetLock(); {
if (!Achievements::HasActiveGame()) auto lock = Achievements::GetLock();
return; if (!Achievements::HasActiveGame())
return;
}
if (QMessageBox::question( if (QMessageBox::question(
QtUtils::GetRootWidget(this), tr("Reset System"), QtUtils::GetRootWidget(this), tr("Reset System"),

View File

@ -104,8 +104,10 @@ MainWindow* g_main_window = nullptr;
// UI thread VM validity. // UI thread VM validity.
static bool s_disable_window_rounded_corners = false; static bool s_disable_window_rounded_corners = false;
static bool s_system_starting = false;
static bool s_system_valid = false; static bool s_system_valid = false;
static bool s_system_paused = false; static bool s_system_paused = false;
static bool s_achievements_hardcore_mode = false;
static bool s_fullscreen_ui_started = false; static bool s_fullscreen_ui_started = false;
static std::atomic_uint32_t s_system_locked{false}; static std::atomic_uint32_t s_system_locked{false};
static QString s_current_game_title; static QString s_current_game_title;
@ -170,6 +172,7 @@ void MainWindow::initialize()
m_ui.setupUi(this); m_ui.setupUi(this);
setupAdditionalUi(); setupAdditionalUi();
updateToolbarActions(); updateToolbarActions();
updateEmulationActions(false, false, false);
connectSignals(); connectSignals();
restoreStateFromConfig(); restoreStateFromConfig();
@ -515,18 +518,20 @@ void MainWindow::onMouseModeRequested(bool relative_mode, bool hide_cursor)
void MainWindow::onSystemStarting() void MainWindow::onSystemStarting()
{ {
s_system_starting = true;
s_system_valid = false; s_system_valid = false;
s_system_paused = false; s_system_paused = false;
switchToEmulationView(); switchToEmulationView();
updateEmulationActions(true, false, Achievements::IsHardcoreModeActive()); updateEmulationActions(true, false, s_achievements_hardcore_mode);
} }
void MainWindow::onSystemStarted() void MainWindow::onSystemStarted()
{ {
m_was_disc_change_request = false; m_was_disc_change_request = false;
s_system_starting = false;
s_system_valid = true; s_system_valid = true;
updateEmulationActions(false, true, Achievements::IsHardcoreModeActive()); updateEmulationActions(false, true, s_achievements_hardcore_mode);
updateWindowTitle(); updateWindowTitle();
updateStatusBarWidgetVisibility(); updateStatusBarWidgetVisibility();
updateDisplayWidgetCursor(); updateDisplayWidgetCursor();
@ -574,6 +579,7 @@ void MainWindow::onSystemDestroyed()
m_ui.actionPause->setChecked(false); m_ui.actionPause->setChecked(false);
} }
s_system_starting = false;
s_system_valid = false; s_system_valid = false;
s_system_paused = false; s_system_paused = false;
@ -585,7 +591,7 @@ void MainWindow::onSystemDestroyed()
return; return;
} }
updateEmulationActions(false, false, Achievements::IsHardcoreModeActive()); updateEmulationActions(false, false, s_achievements_hardcore_mode);
if (m_display_widget) if (m_display_widget)
updateDisplayWidgetCursor(); updateDisplayWidgetCursor();
else else
@ -770,7 +776,7 @@ void MainWindow::recreate()
if (was_display_created) if (was_display_created)
{ {
g_emu_thread->setSurfaceless(false); g_emu_thread->setSurfaceless(false);
g_main_window->updateEmulationActions(false, System::IsValid(), Achievements::IsHardcoreModeActive()); g_main_window->updateEmulationActions(false, s_system_valid, s_achievements_hardcore_mode);
g_main_window->onFullscreenUIStartedOrStopped(s_fullscreen_ui_started); g_main_window->onFullscreenUIStartedOrStopped(s_fullscreen_ui_started);
} }
@ -821,7 +827,6 @@ void MainWindow::populateGameListContextMenu(const GameList::Entry* entry, QWidg
{ {
std::vector<SaveStateInfo> available_states(System::GetAvailableSaveStates(entry->serial)); std::vector<SaveStateInfo> available_states(System::GetAvailableSaveStates(entry->serial));
const QString timestamp_format = QLocale::system().dateTimeFormat(QLocale::ShortFormat); const QString timestamp_format = QLocale::system().dateTimeFormat(QLocale::ShortFormat);
const bool challenge_mode = Achievements::IsHardcoreModeActive();
for (SaveStateInfo& ssi : available_states) for (SaveStateInfo& ssi : available_states)
{ {
if (ssi.global) if (ssi.global)
@ -835,7 +840,6 @@ void MainWindow::populateGameListContextMenu(const GameList::Entry* entry, QWidg
if (slot < 0) if (slot < 0)
{ {
resume_action->setText(tr("Resume (%1)").arg(timestamp_str)); resume_action->setText(tr("Resume (%1)").arg(timestamp_str));
resume_action->setEnabled(!challenge_mode);
action = resume_action; action = resume_action;
} }
else else
@ -844,7 +848,7 @@ void MainWindow::populateGameListContextMenu(const GameList::Entry* entry, QWidg
action = load_state_menu->addAction(tr("Game Save %1 (%2)").arg(slot).arg(timestamp_str)); action = load_state_menu->addAction(tr("Game Save %1 (%2)").arg(slot).arg(timestamp_str));
} }
action->setDisabled(challenge_mode); action->setDisabled(s_achievements_hardcore_mode);
connect(action, &QAction::triggered, connect(action, &QAction::triggered,
[this, entry, path = std::move(ssi.path)]() { startFile(entry->path, std::move(path), std::nullopt); }); [this, entry, path = std::move(ssi.path)]() { startFile(entry->path, std::move(path), std::nullopt); });
} }
@ -1494,13 +1498,14 @@ void MainWindow::onGameListEntryContextMenuRequested(const QPoint& point)
g_emu_thread->bootSystem(std::move(boot_params)); g_emu_thread->bootSystem(std::move(boot_params));
}); });
if (m_ui.menuDebug->menuAction()->isVisible() && !Achievements::IsHardcoreModeActive()) if (m_ui.menuDebug->menuAction()->isVisible())
{ {
connect(menu.addAction(tr("Boot and Debug")), &QAction::triggered, [this, entry]() { connect(menu.addAction(tr("Boot and Debug")), &QAction::triggered, [this, entry]() {
openCPUDebugger(); openCPUDebugger();
std::shared_ptr<SystemBootParameters> boot_params = getSystemBootParameters(entry->path); std::shared_ptr<SystemBootParameters> boot_params = getSystemBootParameters(entry->path);
boot_params->override_start_paused = true; boot_params->override_start_paused = true;
boot_params->disable_achievements_hardcore_mode = true;
g_emu_thread->bootSystem(std::move(boot_params)); g_emu_thread->bootSystem(std::move(boot_params));
}); });
} }
@ -1805,14 +1810,14 @@ void MainWindow::onToolbarContextMenuRequested(const QPoint& pos)
updateToolbarActions(); updateToolbarActions();
} }
void MainWindow::updateEmulationActions(bool starting, bool running, bool cheevos_challenge_mode) void MainWindow::updateEmulationActions(bool starting, bool running, bool achievements_hardcore_mode)
{ {
const bool starting_or_running = (starting || running); const bool starting_or_running = (starting || running);
const bool starting_or_not_running = (starting || !running); const bool starting_or_not_running = (starting || !running);
m_ui.actionStartFile->setDisabled(starting_or_running); m_ui.actionStartFile->setDisabled(starting_or_running);
m_ui.actionStartDisc->setDisabled(starting_or_running); m_ui.actionStartDisc->setDisabled(starting_or_running);
m_ui.actionStartBios->setDisabled(starting_or_running); m_ui.actionStartBios->setDisabled(starting_or_running);
m_ui.actionResumeLastState->setDisabled(starting_or_running || cheevos_challenge_mode); m_ui.actionResumeLastState->setDisabled(starting_or_running || achievements_hardcore_mode);
m_ui.actionStartFullscreenUI->setDisabled(starting_or_running); m_ui.actionStartFullscreenUI->setDisabled(starting_or_running);
m_ui.actionStartFullscreenUI2->setDisabled(starting_or_running); m_ui.actionStartFullscreenUI2->setDisabled(starting_or_running);
@ -1821,16 +1826,16 @@ void MainWindow::updateEmulationActions(bool starting, bool running, bool cheevo
m_ui.actionReset->setDisabled(starting_or_not_running); m_ui.actionReset->setDisabled(starting_or_not_running);
m_ui.actionPause->setDisabled(starting_or_not_running); m_ui.actionPause->setDisabled(starting_or_not_running);
m_ui.actionChangeDisc->setDisabled(starting_or_not_running); m_ui.actionChangeDisc->setDisabled(starting_or_not_running);
m_ui.actionCheatsToolbar->setDisabled(starting_or_not_running || cheevos_challenge_mode); m_ui.actionCheatsToolbar->setDisabled(starting_or_not_running || achievements_hardcore_mode);
m_ui.actionScreenshot->setDisabled(starting_or_not_running); m_ui.actionScreenshot->setDisabled(starting_or_not_running);
m_ui.menuChangeDisc->setDisabled(starting_or_not_running); m_ui.menuChangeDisc->setDisabled(starting_or_not_running);
m_ui.menuCheats->setDisabled(starting_or_not_running || cheevos_challenge_mode); m_ui.menuCheats->setDisabled(starting_or_not_running || achievements_hardcore_mode);
m_ui.actionCPUDebugger->setDisabled(cheevos_challenge_mode); m_ui.actionCPUDebugger->setDisabled(achievements_hardcore_mode);
m_ui.actionMemoryScanner->setDisabled(cheevos_challenge_mode); m_ui.actionMemoryScanner->setDisabled(achievements_hardcore_mode);
m_ui.actionReloadTextureReplacements->setDisabled(starting_or_not_running); m_ui.actionReloadTextureReplacements->setDisabled(starting_or_not_running);
m_ui.actionDumpRAM->setDisabled(starting_or_not_running || cheevos_challenge_mode); m_ui.actionDumpRAM->setDisabled(starting_or_not_running || achievements_hardcore_mode);
m_ui.actionDumpVRAM->setDisabled(starting_or_not_running || cheevos_challenge_mode); m_ui.actionDumpVRAM->setDisabled(starting_or_not_running || achievements_hardcore_mode);
m_ui.actionDumpSPURAM->setDisabled(starting_or_not_running || cheevos_challenge_mode); m_ui.actionDumpSPURAM->setDisabled(starting_or_not_running || achievements_hardcore_mode);
m_ui.actionSaveState->setDisabled(starting_or_not_running); m_ui.actionSaveState->setDisabled(starting_or_not_running);
m_ui.menuSaveState->setDisabled(starting_or_not_running); m_ui.menuSaveState->setDisabled(starting_or_not_running);
@ -2045,8 +2050,6 @@ void MainWindow::switchToEmulationView()
void MainWindow::connectSignals() void MainWindow::connectSignals()
{ {
updateEmulationActions(false, false, Achievements::IsHardcoreModeActive());
connect(qApp, &QGuiApplication::applicationStateChanged, this, &MainWindow::onApplicationStateChanged); connect(qApp, &QGuiApplication::applicationStateChanged, this, &MainWindow::onApplicationStateChanged);
connect(m_ui.toolBar, &QToolBar::customContextMenuRequested, this, &MainWindow::onToolbarContextMenuRequested); connect(m_ui.toolBar, &QToolBar::customContextMenuRequested, this, &MainWindow::onToolbarContextMenuRequested);
@ -2165,8 +2168,8 @@ void MainWindow::connectSignals()
connect(g_emu_thread, &EmuThread::fullscreenUIStartedOrStopped, this, &MainWindow::onFullscreenUIStartedOrStopped); connect(g_emu_thread, &EmuThread::fullscreenUIStartedOrStopped, this, &MainWindow::onFullscreenUIStartedOrStopped);
connect(g_emu_thread, &EmuThread::achievementsLoginRequested, this, &MainWindow::onAchievementsLoginRequested); connect(g_emu_thread, &EmuThread::achievementsLoginRequested, this, &MainWindow::onAchievementsLoginRequested);
connect(g_emu_thread, &EmuThread::achievementsLoginSuccess, this, &MainWindow::onAchievementsLoginSuccess); connect(g_emu_thread, &EmuThread::achievementsLoginSuccess, this, &MainWindow::onAchievementsLoginSuccess);
connect(g_emu_thread, &EmuThread::achievementsChallengeModeChanged, this, connect(g_emu_thread, &EmuThread::achievementsHardcoreModeChanged, this,
&MainWindow::onAchievementsChallengeModeChanged); &MainWindow::onAchievementsHardcoreModeChanged);
connect(g_emu_thread, &EmuThread::onCoverDownloaderOpenRequested, this, &MainWindow::onToolsCoverDownloaderTriggered); connect(g_emu_thread, &EmuThread::onCoverDownloaderOpenRequested, this, &MainWindow::onToolsCoverDownloaderTriggered);
connect(g_emu_thread, &EmuThread::onCreateAuxiliaryRenderWindow, this, &MainWindow::onCreateAuxiliaryRenderWindow, connect(g_emu_thread, &EmuThread::onCreateAuxiliaryRenderWindow, this, &MainWindow::onCreateAuxiliaryRenderWindow,
Qt::BlockingQueuedConnection); Qt::BlockingQueuedConnection);
@ -2795,7 +2798,7 @@ void MainWindow::onAchievementsLoginSuccess(const QString& username, quint32 poi
} }
} }
void MainWindow::onAchievementsChallengeModeChanged(bool enabled) void MainWindow::onAchievementsHardcoreModeChanged(bool enabled)
{ {
if (enabled) if (enabled)
{ {
@ -2803,7 +2806,8 @@ void MainWindow::onAchievementsChallengeModeChanged(bool enabled)
QtUtils::CloseAndDeleteWindow(m_memory_scanner_window); QtUtils::CloseAndDeleteWindow(m_memory_scanner_window);
} }
updateEmulationActions(false, System::IsValid(), enabled); s_achievements_hardcore_mode = enabled;
updateEmulationActions(s_system_starting, s_system_valid, enabled);
} }
bool MainWindow::onCreateAuxiliaryRenderWindow(RenderAPI render_api, qint32 x, qint32 y, quint32 width, quint32 height, bool MainWindow::onCreateAuxiliaryRenderWindow(RenderAPI render_api, qint32 x, qint32 y, quint32 width, quint32 height,
@ -2889,7 +2893,7 @@ void MainWindow::onToolsMediaCaptureToggled(bool checked)
void MainWindow::onToolsMemoryScannerTriggered() void MainWindow::onToolsMemoryScannerTriggered()
{ {
if (Achievements::IsHardcoreModeActive()) if (s_achievements_hardcore_mode)
return; return;
if (!m_memory_scanner_window) if (!m_memory_scanner_window)
@ -2913,6 +2917,9 @@ void MainWindow::onToolsISOBrowserTriggered()
void MainWindow::openCPUDebugger() void MainWindow::openCPUDebugger()
{ {
if (s_achievements_hardcore_mode)
return;
if (!m_debugger_window) if (!m_debugger_window)
{ {
m_debugger_window = new DebuggerWindow(); m_debugger_window = new DebuggerWindow();

View File

@ -151,7 +151,7 @@ private Q_SLOTS:
void onMediaCaptureStopped(); void onMediaCaptureStopped();
void onAchievementsLoginRequested(Achievements::LoginRequestReason reason); void onAchievementsLoginRequested(Achievements::LoginRequestReason reason);
void onAchievementsLoginSuccess(const QString& username, quint32 points, quint32 sc_points, quint32 unread_messages); void onAchievementsLoginSuccess(const QString& username, quint32 points, quint32 sc_points, quint32 unread_messages);
void onAchievementsChallengeModeChanged(bool enabled); void onAchievementsHardcoreModeChanged(bool enabled);
bool onCreateAuxiliaryRenderWindow(RenderAPI render_api, qint32 x, qint32 y, quint32 width, quint32 height, bool onCreateAuxiliaryRenderWindow(RenderAPI render_api, qint32 x, qint32 y, quint32 width, quint32 height,
const QString& title, const QString& icon_name, const QString& title, const QString& icon_name,
Host::AuxiliaryRenderWindowUserData userdata, Host::AuxiliaryRenderWindowUserData userdata,

View File

@ -1677,7 +1677,7 @@ void Host::OnAchievementsRefreshed()
void Host::OnAchievementsHardcoreModeChanged(bool enabled) void Host::OnAchievementsHardcoreModeChanged(bool enabled)
{ {
emit g_emu_thread->achievementsChallengeModeChanged(enabled); emit g_emu_thread->achievementsHardcoreModeChanged(enabled);
} }
void Host::OnCoverDownloaderOpenRequested() void Host::OnCoverDownloaderOpenRequested()

View File

@ -155,7 +155,7 @@ Q_SIGNALS:
void achievementsLoginRequested(Achievements::LoginRequestReason reason); void achievementsLoginRequested(Achievements::LoginRequestReason reason);
void achievementsLoginSuccess(const QString& username, quint32 points, quint32 sc_points, quint32 unread_messages); void achievementsLoginSuccess(const QString& username, quint32 points, quint32 sc_points, quint32 unread_messages);
void achievementsRefreshed(quint32 id, const QString& game_info_string); void achievementsRefreshed(quint32 id, const QString& game_info_string);
void achievementsChallengeModeChanged(bool enabled); void achievementsHardcoreModeChanged(bool enabled);
void cheatEnabled(quint32 index, bool enabled); void cheatEnabled(quint32 index, bool enabled);
void mediaCaptureStarted(); void mediaCaptureStarted();
void mediaCaptureStopped(); void mediaCaptureStopped();