This activates or deactivates leaderboards in the rcheevos runtime similarly to achievements. The logic is much more straightforward - all leaderboards are active together; there is nothing requiring some leaderboards to be active while others are unactive, and even a leaderboard that has been submitted to in this session is still active to be submitted to again. The only criteria are that leaderboards must be enabled in the settings, and hardcore mode must be on, the latter of which is false until a future PR.
LoadUnlockData and ActivateDeactivateAchievements are the public API components responding to the FetchUnlocks and A/DAchievement (singular) private methods.
LoadUnlockData is asynchronous and performs both a hardcore and a softcore unlock call, updating the unlock map and the active status of any achievements returned from these calls.
ActivateDeactivateAchievements calls ActivateDeactivateAchievement on every achievement ID found in m_game_data, initializing the unlock map for each ID if not already found.
Both of these are currently called in LoadGameByFilenameAsync once the game has been loaded properly. There's a lock around this, to ensure that the unlock map is initialized properly by ActivateDeactivate Achievements before FetchUnlockData makes modifications to it without stalling the async portions of FetchUnlockData.
FetchUnlockData is an API call to RetroAchievements that downloads a list of achievement IDs for a game that the user has already unlocked and published to the site. It accepts a parameter for whether or not hardcore or softcore achievements are being requested, so that must be provided as well. Once it has the requested list on hand, it updates each achievement's status in the unlock map and will activate or deactivate achievements as necessary.
ActivateDeactivateAchievement is passed an Achievement ID as returned from the FetchGameData API call and determines whether to activate it, deactivate it, or leave it where it is based on its current known state and what settings are enabled.
Activating or deactivating an achievement entails calling a method provided by rcheevos that performs this on the rcheevos runtime. Activating an achievement loads its memory signature into the runtime; now the runtime will process the achievement each time the rc_runtime_do_frame function is called (this will be in a future PR) to determine when the achievement's requirements are met. Deactivating an achievement unloads it from the runtime.
The specific logic to determine whether an achievement is active operates over many fields but is documented in detail inside the function. There are multiple settings flags for which achievements are enabled (one flag for all achievements, an "unofficial" flag for enabling achievements marked as unofficial i.e. those that have logic on the site but have not yet been officially approved, and an "encore" flag that enables achievements the player has already unlocked) and this function also evaluates whether the achievement has been unlocked in hardcore mode or softcore mode (though currently every reference to the current hardcore mode state is hardcoded as false).
LoadGameByFilenameAsync sets up a volume reader and hashes the volume, then uses that hash to make the three consecutive API requests to resolve hash, start session and load game data.
CloseGame resets the m_is_game_loaded flag, wipes the queue, and destroys all the game data responses.
FetchGameData is the big one - this retrieves the logic for all the achievements, leaderboards, and rich presence, and all the relevant metadata for the game.
Added a call to the RetroAchievements Start Session API to AchievementManager. This is primarily for client-side activation, so it doesn't return much of value, aside from its success/error information, but I'm storing the return structure in case this changes in the future.
Added the ResolveHash method to AchievementManager. This is a blocking function to send a hash string to the RetroAchievements server to verify it and get a game ID back.
This was previously copying each pair out of the vector returned by
GetInterfaceListInternal() when we just need to emplace the first entry
of each pair.
bSupports2DTextureStorageMultisample is completely unused, while bSupports3DTextureStorageMultisample is practically unused. In the past, these were checked and fell back to sampler2DMS instead of sampler2DMSArray on GLES 3.1, but this path was removed in f039149198 and Dolphin always uses array textures now.
See https://bugs.dolphin-emu.org/issues/13232; this was introduced in 7dde0c3c31. Apparently, providing a parent for a widget that is not visible makes your new widget visible when the parent is later made visible, in addition to managing the deletion of the widget; the documentation does not specify this (only that if the parent is visible you need to explicitly show it).
The m_checkbox_lock_mouse QCheckBox was only conditionally being added
to the layout, leaving it unmanaged and leaking
Setting the parent will allow it to be managed.
The m_verbosity_debug button was only conditionally being added as
widget, this was done in order to hide the object, but this left it
unmanaged.
Unconditionally adding it to the layout and controlling it's visibility
will resolve these issues
Added AchievementManager class. Upon startup (currently only in DolphinQt), logs into RetroAchievements with the login credentials stored in achievements.ini.
Co-authored-by: AdmiralCurtiss <AdmiralCurtiss@users.noreply.github.com>
Added AchievementSettings in Config with RA_INTEGRATION_ENABLED, RA_USERNAME, and RA_API_TOKEN. Includes code to load and store from Achievements.ini file in config folder.
The QByteArray returned by QString::toUtf8() was being freed so the char
pointer was pointing to freed memory.
Found via ASan, didn't notice any issues during normal runtime.
This was triggered after hitting a key combo with alt (ex. toggle
fullscreen) probably happens with others
This fixes a crash when recording fifologs, as the mutex is acquired when BPWritten calls AfterFrameEvent::Trigger, but then acquired again when FifoRecorder::EndFrame calls m_end_of_frame_event.reset(). std::mutex does not allow calling lock() if the thread already owns the mutex, while std::recursive_mutex does allow this.
This is a regression from #11522 (which introduced the HookableEvent system).
Adds the rcheevos library from RetroAchievements to the Dolphin Externals as a submodule. Change was verified to import correctly and build both via Visual Studio and via cmake ninja.
We have these for a reason. I think this also fixes a theoretical
problem when `ABI_PARAM1 == loop_counter` where the first MOV destroys
the second's value; I'm not sure if this can actually happen in practice
though.