Files
ppsspp/Core/RetroAchievements.h
T
Henrik RydgårdandClaude Opus 5.5 c8b32dda0a Achievements: Keep a shut-down client alive until its requests finish
rc_client keeps raw pointers to itself in the callback data of award,
leaderboard submit and ping requests, so destroying it with one in flight
(disabling achievements mid-game) had the late callback write to freed
memory. Count requests per client, retire a busy client instead of
destroying it, and destroy it when its last request completes.

Request timeouts in naett are per operation and the plain HTTP client has
none for the body, so a retired client is also destroyed after 60 seconds
regardless. Its late callbacks are then dropped, leaking their data.
Clients are tracked by ID so a new client at a reused address can't get an
old one's callbacks. At app exit, pending requests are cancelled without
callbacks, so everything is destroyed immediately.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 10:26:00 -06:00

113 lines
4.1 KiB
C++

// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-2.0 OR GPL-3.0 OR CC-BY-NC-ND-4.0)
// Derived from Duckstation's RetroAchievements implementation by stenzek as can be seen
// above, relicensed to GPL 2.0.
// Modifications and deletions have been made where needed.
// Refactoring it into a more PPSSPP-like style may or may not be a good idea -
// it'll make it harder to copy new achievement features from Duckstation.
#pragma once
#include <string>
#include <set>
#include "Common/CommonTypes.h"
#include "Core/Loaders.h" // for IdentifiedFileType
class Path;
class PointerWrap;
class FileLoader;
struct rc_client_t;
namespace Achievements {
struct Statistics {
// Debug stats
int badMemoryAccessCount;
};
// Returns true if the user is logged in properly, and everything is set up for playing games with achievements.
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 encore-mode is active.
bool EncoreModeActive();
// 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.
// 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...)
// * Debugging
// * Cheats
// * and similar...
bool HardcoreModeActive();
// 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 Hardcore Mode" message will be shown.
// Also returns true if hardcore mode is active.
// Specify isSaveAction so we can still permit saves (but not loads) in hardcore mode if that option is enabled.
bool WarnUserIfHardcoreModeActive(bool isSaveStateAction, std::string_view message = "");
// Returns the length of the string. If (size_t)-1, there's no message.
size_t GetRichPresenceMessage(char *buffer, size_t bufSize);
// Returns true if the user has unsaved RAIntegration changes. Should prompt the user to be sure they want to exit.
bool RAIntegrationDirty();
// 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.
rc_client_t *GetClient();
void Initialize();
void InitializeRAIntegration(void *windowHandle); // This must be call after the main window exists.
void UpdateSettings();
bool LoginProblems(std::string *errorString);
bool HasToken();
/// Called when the system is being shut down. If Shutdown() returns false, the shutdown should be aborted if possible.
/// With waitForRequests, a client with requests in flight is kept alive until they complete (see Idle()).
/// Pass false only when pending requests are about to be cancelled, as at app exit.
bool Shutdown(bool waitForRequests = true);
void DownloadImageIfMissing(std::string_view url);
/// Called once a frame at vsync time on the CPU thread, during gameplay.
void FrameUpdate();
/// Called every frame to let background processing happen.
void Idle();
/// Saves/loads state.
void DoState(PointerWrap &p);
/// Returns true if the current game has any achievements or leaderboards.
bool HasAchievementsOrLeaderboards();
bool LoginAsync(const char *username, const char *password);
void Logout();
void SetGame(const Path &path, IdentifiedFileType fileType, FileLoader *fileLoader);
void ChangeUMD(const Path &path, FileLoader *fileLoader); // for in-game UMD change
void UnloadGame(); // Call when leaving a game.
Statistics GetStatistics();
std::string GetGameAchievementSummary(uint32_t subsetId);
std::set<uint32_t> GetActiveChallengeIDs();
} // namespace Achievements