Skip to content

Commit

Permalink
Implement RetroAchievements challenge mode restrictions, and fix rela…
Browse files Browse the repository at this point in the history
…ted bugs
  • Loading branch information
hrydgard committed Jul 8, 2023
1 parent 9e4387c commit c17b84b
Show file tree
Hide file tree
Showing 13 changed files with 189 additions and 109 deletions.
2 changes: 1 addition & 1 deletion Common/GPU/Vulkan/VulkanMemory.h
Expand Up @@ -5,6 +5,7 @@
#include <functional>
#include <vector>

#include "Common/Data/Collections/FastVec.h"
#include "Common/GPU/Vulkan/VulkanContext.h"
#include "Common/GPU/GPUBackendCommon.h"

Expand Down Expand Up @@ -205,4 +206,3 @@ class VulkanDescSetPool {
uint32_t usage_ = 0;
bool grow_;
};

15 changes: 14 additions & 1 deletion Common/System/OSD.cpp
Expand Up @@ -2,6 +2,7 @@

#include "Common/System/OSD.h"
#include "Common/TimeUtil.h"
#include "Common/Log.h"

OnScreenDisplay g_OSD;

Expand Down Expand Up @@ -146,6 +147,11 @@ void OnScreenDisplay::ShowChallengeIndicator(int achievementID, bool show) {
}
}

if (!show) {
// Sanity check
return;
}

// OK, let's make a new side-entry.
Entry entry;
entry.numericID = achievementID;
Expand All @@ -172,13 +178,20 @@ void OnScreenDisplay::ShowLeaderboardTracker(int leaderboardTrackerID, const cha
}
}

if (!show) {
// Sanity check
return;
}

// OK, let's make a new side-entry.
Entry entry;
entry.numericID = leaderboardTrackerID;
entry.type = OSDType::LEADERBOARD_TRACKER;
entry.startTime = now;
entry.endTime = now + 10000000.0; // Don't auto-fadeout
entry.text = trackerText;
if (trackerText) {
entry.text = trackerText;
}
sideEntries_.insert(sideEntries_.begin(), entry);
}

Expand Down
1 change: 0 additions & 1 deletion Core/Config.cpp
Expand Up @@ -266,7 +266,6 @@ static bool DefaultSasThread() {

static const ConfigSetting achievementSettings[] = {
ConfigSetting("AchievementsEnable", &g_Config.bAchievementsEnable, true, CfgFlag::DEFAULT),
ConfigSetting("AchievementsRichPresence", &g_Config.bAchievementsRichPresence, true, CfgFlag::DEFAULT),
ConfigSetting("AchievementsChallengeMode", &g_Config.bAchievementsChallengeMode, false, CfgFlag::DEFAULT),
ConfigSetting("AchievementsEncoreMode", &g_Config.bAchievementsEncoreMode, false, CfgFlag::DEFAULT),
ConfigSetting("AchievementsUnofficial", &g_Config.bAchievementsUnofficial, false, CfgFlag::DEFAULT),
Expand Down
1 change: 0 additions & 1 deletion Core/Config.h
Expand Up @@ -488,7 +488,6 @@ struct Config {
// Retro Achievement settings
// Copied from Duckstation, we might want to remove some.
bool bAchievementsEnable;
bool bAchievementsRichPresence;
bool bAchievementsChallengeMode;
bool bAchievementsEncoreMode;
bool bAchievementsUnofficial;
Expand Down
14 changes: 10 additions & 4 deletions Core/CwCheat.cpp
Expand Up @@ -19,6 +19,7 @@
#include "Core/System.h"
#include "Core/HLE/sceCtrl.h"
#include "Core/MIPS/JitCommon/JitCommon.h"
#include "Core/RetroAchievements.h"
#include "GPU/Common/PostShader.h"

#ifdef _WIN32
Expand Down Expand Up @@ -319,9 +320,11 @@ void __CheatDoState(PointerWrap &p) {
}

void hleCheat(u64 userdata, int cyclesLate) {
if (cheatsEnabled != g_Config.bEnableCheats) {
bool shouldBeEnabled = !Achievements::ChallengeModeActive() && g_Config.bEnableCheats;

if (cheatsEnabled != shouldBeEnabled) {
// Okay, let's move to the desired state, then.
if (g_Config.bEnableCheats) {
if (shouldBeEnabled) {
__CheatStart();
} else {
__CheatStop();
Expand Down Expand Up @@ -1193,6 +1196,10 @@ void CWCheatEngine::ExecuteOp(const CheatOperation &op, const CheatCode &cheat,
}

void CWCheatEngine::Run() {
if (Achievements::ChallengeModeActive()) {
return;
}

for (CheatCode cheat : cheats_) {
// InterpretNextOp and ExecuteOp move i.
for (size_t i = 0; i < cheat.lines.size(); ) {
Expand All @@ -1207,8 +1214,7 @@ bool CWCheatEngine::HasCheats() {
}

bool CheatsInEffect() {
if (!cheatEngine || !cheatsEnabled)
if (!cheatEngine || !cheatsEnabled || Achievements::ChallengeModeActive())
return false;
return cheatEngine->HasCheats();
}

16 changes: 10 additions & 6 deletions Core/HLE/sceDisplay.cpp
Expand Up @@ -49,6 +49,7 @@
#include "Core/HLE/sceKernelInterrupt.h"
#include "Core/HW/Display.h"
#include "Core/Util/PPGeDraw.h"
#include "Core/RetroAchievements.h"

#include "GPU/GPU.h"
#include "GPU/GPUState.h"
Expand Down Expand Up @@ -348,12 +349,15 @@ void __DisplaySetWasPaused() {
}

static int FrameTimingLimit() {
if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1)
return g_Config.iFpsLimit1;
if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM2)
return g_Config.iFpsLimit2;
if (PSP_CoreParameter().fpsLimit == FPSLimit::ANALOG)
return PSP_CoreParameter().analogFpsLimit;
if (!Achievements::ChallengeModeActive()) {
if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1)
return g_Config.iFpsLimit1;
if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM2)
return g_Config.iFpsLimit2;
if (PSP_CoreParameter().fpsLimit == FPSLimit::ANALOG)
return PSP_CoreParameter().analogFpsLimit;
}
// Note: Fast-forward is OK in challenge mode.
if (PSP_CoreParameter().fastForward)
return 0;
return framerate;
Expand Down
55 changes: 32 additions & 23 deletions Core/RetroAchievements.cpp
Expand Up @@ -74,8 +74,6 @@ namespace Achievements {
// It's the name of the secret, not a secret name - the value is not secret :)
static const char *RA_TOKEN_SECRET_NAME = "retroachievements";

static u32 GetGameID();

static Achievements::Statistics g_stats;

const std::string g_gameIconCachePrefix = "game:";
Expand All @@ -84,8 +82,6 @@ const std::string g_iconCachePrefix = "badge:";
Path s_game_path;
std::string s_game_hash;

bool g_challengeMode = true;

bool g_isIdentifying = false;

// rc_client implementation
Expand All @@ -101,10 +97,6 @@ bool IsLoggedIn() {
return rc_client_get_user_info(g_rcClient) != nullptr;
}

bool ChallengeModeActive() {
return g_challengeMode;
}

bool EncoreModeActive() {
if (!g_rcClient) {
return false;
Expand All @@ -119,15 +111,33 @@ bool UnofficialEnabled() {
return rc_client_get_unofficial_enabled(g_rcClient);
}

bool IsActive() {
return GetGameID() != 0;
bool ChallengeModeActive() {
if (!g_rcClient) {
return false;
}
return rc_client_get_hardcore_enabled(g_rcClient);
}

bool WarnUserIfChallengeModeActive(const char *message) {
if (!ChallengeModeActive()) {
return false;
}

const char *showMessage = message;
if (!message) {
auto ac = GetI18NCategory(I18NCat::ACHIEVEMENTS);
showMessage = ac->T("This feature is not available in Challenge Mode");
}

g_OSD.Show(OSDType::MESSAGE_WARNING, showMessage, 3.0f);
return true;
}

bool IsBlockingExecution() {
return g_isIdentifying;
}

u32 GetGameID() {
static u32 GetGameID() {
if (!g_rcClient) {
return 0;
}
Expand All @@ -139,6 +149,10 @@ u32 GetGameID() {
return info->id; // 0 if not identified
}

bool IsActive() {
return GetGameID() != 0;
}

// This is the function the rc_client will use to read memory for the emulator. we don't need it yet,
// so just provide a dummy function that returns "no memory read".
static uint32_t read_memory_callback(uint32_t address, uint8_t *buffer, uint32_t num_bytes, rc_client_t *client) {
Expand Down Expand Up @@ -260,12 +274,12 @@ static void event_handler_callback(const rc_client_event_t *event, rc_client_t *
break;
case RC_CLIENT_EVENT_LEADERBOARD_FAILED:
NOTICE_LOG(ACHIEVEMENTS, "Leaderboard attempt failed: %s", event->leaderboard->title);
g_OSD.Show(OSDType::MESSAGE_INFO, ReplaceAll(ac->T("%1: Failed"), "%1", event->leaderboard->title), 3.0f);
g_OSD.Show(OSDType::MESSAGE_INFO, ReplaceAll(ac->T("%1: Leaderboard attempt failed"), "%1", event->leaderboard->title), 3.0f);
// A leaderboard attempt has failed.
break;
case RC_CLIENT_EVENT_LEADERBOARD_SUBMITTED:
NOTICE_LOG(ACHIEVEMENTS, "Leaderboard result submitted: %s", event->leaderboard->title);
g_OSD.Show(OSDType::MESSAGE_SUCCESS, ReplaceAll(ac->T("%1: Submitting successful score!"), "%1", event->leaderboard->title), event->leaderboard->description, 3.0f);
g_OSD.Show(OSDType::MESSAGE_SUCCESS, ReplaceAll(ReplaceAll(ac->T("%1: Submitting leaderboard score: %2!"), "%1", event->leaderboard->title), "%2", event->leaderboard->tracker_value), event->leaderboard->description, 3.0f);
// A leaderboard attempt was completed.The handler may show a message with the leaderboard title and /or description indicating the final value being submitted to the server.
break;
case RC_CLIENT_EVENT_ACHIEVEMENT_CHALLENGE_INDICATOR_SHOW:
Expand All @@ -288,7 +302,7 @@ static void event_handler_callback(const rc_client_event_t *event, rc_client_t *
g_OSD.ShowAchievementProgress(event->achievement->id, 2.0f);
break;
case RC_CLIENT_EVENT_LEADERBOARD_TRACKER_SHOW:
NOTICE_LOG(ACHIEVEMENTS, "Leaderboard tracker show: %s", event->leaderboard_tracker->display);
NOTICE_LOG(ACHIEVEMENTS, "Leaderboard tracker show: '%s' (id %d)", event->leaderboard_tracker->display, event->leaderboard_tracker->id);
// A leaderboard_tracker has become active. The handler should show the tracker text on screen.
// Multiple active leaderboards may share a single tracker if they have the same definition and value.
// As such, the leaderboard tracker IDs are unique amongst the leaderboard trackers, and have no correlation to the active leaderboard(s).
Expand All @@ -297,13 +311,13 @@ static void event_handler_callback(const rc_client_event_t *event, rc_client_t *
break;
case RC_CLIENT_EVENT_LEADERBOARD_TRACKER_HIDE:
// A leaderboard_tracker has become inactive. The handler should hide the tracker text from the screen.
NOTICE_LOG(ACHIEVEMENTS, "Leaderboard tracker hide: %s", event->leaderboard_tracker->display);
NOTICE_LOG(ACHIEVEMENTS, "Leaderboard tracker hide: '%s' (id %d)", event->leaderboard_tracker->display, event->leaderboard_tracker->id);
g_OSD.ShowLeaderboardTracker(event->leaderboard_tracker->id, nullptr, false);
break;
case RC_CLIENT_EVENT_LEADERBOARD_TRACKER_UPDATE:
// A leaderboard_tracker value has been updated. The handler should update the tracker text on the screen.
NOTICE_LOG(ACHIEVEMENTS, "Leaderboard tracker update: %s", event->leaderboard_tracker->display);
g_OSD.ShowLeaderboardTracker(event->leaderboard_tracker->id, event->leaderboard_tracker->display, false);
NOTICE_LOG(ACHIEVEMENTS, "Leaderboard tracker update: '%s' (id %d)", event->leaderboard_tracker->display, event->leaderboard_tracker->id);
g_OSD.ShowLeaderboardTracker(event->leaderboard_tracker->id, event->leaderboard_tracker->display, true);
break;
case RC_CLIENT_EVENT_RESET:
WARN_LOG(ACHIEVEMENTS, "Resetting game due to achievement setting change!");
Expand All @@ -328,16 +342,10 @@ void Initialize() {
}
_assert_msg_(g_Config.bAchievementsEnable, "Achievements are enabled");

g_challengeMode = true; // the default
g_rcClient = rc_client_create(read_memory_callback, server_call_callback);
// Provide a logging function to simplify debugging
rc_client_enable_logging(g_rcClient, RC_CLIENT_LOG_LEVEL_VERBOSE, log_message_callback);

// FOR NOW: Disable hardcore - if we goof something up in the implementation, we don't want our
// account disabled for cheating.
rc_client_set_hardcore_enabled(g_rcClient, 0);
g_challengeMode = false;

// Disable SSL for now.
rc_client_set_host(g_rcClient, "http://retroachievements.org");

Expand Down Expand Up @@ -590,6 +598,7 @@ void SetGame(const Path &path) {
g_isIdentifying = true;

// Apply pre-load settings.
rc_client_set_hardcore_enabled(g_rcClient, g_Config.bAchievementsChallengeMode ? 1 : 0);
rc_client_set_encore_mode_enabled(g_rcClient, g_Config.bAchievementsEncoreMode ? 1 : 0);
rc_client_set_unofficial_enabled(g_rcClient, g_Config.bAchievementsUnofficial ? 1 : 0);

Expand Down
16 changes: 13 additions & 3 deletions Core/RetroAchievements.h
Expand Up @@ -51,14 +51,24 @@ bool IsLoggedIn();
// Returns true if in a game, and achievements are active in the current game.
bool IsActive();

// Returns true if unofficial achievements are enabled.
bool UnofficialEnabled();

// Returns true if the emulator should hold off on executing game code, such as during game identification.
bool IsBlockingExecution();

/// Returns true if features such as save states should be disabled.
// Returns true if features such as save states should be disabled.
// This should only be used for controlling functionality of the following things, which are banned in Challenge/Hardcore mode:
//
// * Savestates
// * Slowdown time (though hard to fully prevent, could use crazy post shaders or software rendering...)
bool ChallengeModeActive();

// Returns true if unofficial achievements are enabled.
bool UnofficialEnabled();
// Same as ChallengeModeActive but comes with a convenient user message. Don't use for every-frame checks or UI enablement,
// only for shortcut keys and commands. You should look up the message in I18NCat::ACHIEVEMENTS.
// If no message is specified, a standard "This feature is not available in Challenge Mode" message will be shown.
// Also returns true if challenge mode is active.
bool WarnUserIfChallengeModeActive(const char *message = nullptr);

// The new API is so much nicer that we can use it directly instead of wrapping it. So let's expose the client.
// Will of course return nullptr if not active.
Expand Down
5 changes: 5 additions & 0 deletions Core/SaveState.cpp
Expand Up @@ -399,6 +399,11 @@ namespace SaveState

void Enqueue(SaveState::Operation op)
{
if (Achievements::ChallengeModeActive()) {
// No savestate operations are permitted, let's just ignore it.
return;
}

std::lock_guard<std::mutex> guard(mutex);
pending.push_back(op);

Expand Down

0 comments on commit c17b84b

Please sign in to comment.