DoFrame is a function called every frame by the emulator so that rcheevos can be properly updated and processed. It requires a memory peeker and an event handler to be passed in; the memory peeker is called repeatedly each frame to measure what's in memory and compare to achievement definitions, and any events thrown by that comparison are sent to the event handler.
Also, DoFrame checks for the current system time to determine when to ping rich presence. Rich Presence on the RetroAchievements website updates every two minutes, so if two minutes have elapsed since the previous ping, another ping is sent.
GenerateRichPresence calls rc_runtime_get_richpresence in rhceevos on the achievement runtime to get the current Rich Presence string, a description of the player's current in-game state based on its memory as fed into a custom-developed script downloaded via FetchGameData. This gets passed into PingRichPresence, but is separated into its own method so it can be used elsewhere locally.
MemoryPeeker is a function passed by pointer into rcheevos DoFrame functionality that forms the lynchpin of the rcheevos runtime - it provides the interface by which rcheevos accesses memory and determines if the fields provided by achievement, leaderboard, and rich presence definitions are meeting the criteria needed.
AchievementEventHandler simply checks which kind of event is triggered and calls the appropriate function. Its primary purpose is as a function to be pointed to.
HandleAchievementTriggeredEvent is an asynchronous method that processes an event and places a synchronous AwardAchievement call on the work queue. In the process, it also updates the unlock map and makes the ActivateDeactivateAchievement call to determine and adjust the achievement's current active state.
PingRichPresence makes a "ping" API request to the RetroAchievements website with the provided RichPresence string parameter. While there has been talk about tying ping in with session, in its current state the primary purpose of ping is to send the player's Rich Presence to the website.
AwardAchievement performs the API call to notify the site that an achievement has been unlocked. As one of the parameters is the game hash (something I overlooked previously; I thought it was the game ID) this change also moves the game hash into a member field.
This reads Steam Deck controls bypassing Steam Input. This allows for access to
motion controls as well as independent access to thumb sticks, trackpads, and
back grip buttons.
This reverts commit cfe3683668.
On Windows systems with 125% DPI scaling, this was causing both icons
and the OSD to be upscaled by 25% using nearest neighbor scaling, which
looks really bad. shuffle2 has said that this will be improved later,
but we should revert it for now for the sake of the upcoming beta build.
The old tooltip description box used GraphicsWidget to provide shared
code to the Graphics config panes for adding descriptions to their
settings.
The description box has been replaced by BalloonTips and serves no
further purpose, so remove it and have the Graphics panes derive from
QWidget instead.
GraphicsInteger is used by the panes in the Graphics config window to
create spin boxes that change their associated config setting, and
update their own state when something else changes the config setting.
Despite its current name nothing about this class is particular to the
Graphics window, so renaming it to ConfigInteger better reflects its
purpose. This should also make it less confusing when ConfigIntegers
are added to other config windows.
This fixes a crash I found in the Request function of AchievementManager where under certain conditions init_request would return an api_request with null post data, and Post would crash if it attempted to access it. Now the function aborts before the Post and returns an INVALID_REQUEST response type.
GraphicsSlider is used by the panes in the Graphics config window to
create sliders that change their associated config setting, and update
their own state when something else changes the config setting.
Despite its current name nothing about this class is particular to the
Graphics window, so renaming it to ConfigSlider better reflects its
purpose. This should also make it less confusing when ConfigSliders are
added to other config panes.
GraphicsRadioInt is used by the panes in the Graphics config window to
create radio buttons that change their associated config setting, and
update their own state when something else changes the config setting.
Despite its current name nothing about this class is particular to the
Graphics window, so renaming it to ConfigRadioInt better reflects its
purpose. This should also make it less confusing when ConfigRadioInts
are added to other config panes.
m_xrr_config was used by the GeneralWidget::GetAvailableResolutions
function to get the list of supported fullscreen resolutions when
HAVE_XRANDR was set. aa4088a removed the ability to set that resolution
in the UI, leaving the GetAvailableResolutions function unused.
m_xrr_config is initialized in MainWindow which still uses it to toggle
fullscreen, but the references in GeneralWidget and GraphicsWidget
(which just ferried m_xrr_config from MainWindow to GeneralWidget) are
now unnecessary and removed in this commit.
Fixes dynamically changing dpi scaling.
Load resources from svg if possible.
Currently svg support is not in Qt build in Externals,
and image files need to be added later.
GraphicsChoice is used by the panes in the Graphics config window to
create combo boxes that change their associated config setting, and
update their own state when something else changes the config setting.
Despite its current name nothing about this class is particular to the
Graphics window, so renaming it to ConfigChoice better reflects its
purpose. This should also make it less confusing when ConfigChoices are
eventually added to the other config windows.
QApplication should parse and remove and args it recognizes
before dolphin starts inspecting the args.
This allows using e.g. -style <style> on the commandline.
PR #11183 regressed the lookup table reconstruction and, for some reason, added an else clause that clobbered the dCache whenever dCache emulation is turned on.
GraphicsBool is used by the panes in the Graphics config window to
create checkboxes that change their associated config setting, and
update their own state when something else changes the config setting.
Despite its current name nothing about this class is particular to the
Graphics window, so renaming it to ConfigBool better reflects its
purpose. This should also make it less confusing when ConfigBools are
eventually added to the other config windows.
In older versions of Dolphin GraphicsBoolEx was used to create a pair of
radio buttons selecting one of Virtual XFB and Real XFB, but this was
removed with the introduction of Hybrid XFB in 65cd085f.
In the meantime GraphicsRadioInt was introduced to allow for Graphics
radio buttons with multiple options, so GraphicsBoolEx is now redundant.
Previously this was using the default deleter (which just calls delete
on the pointer), which is incorrect, since the ENetHost instance is
allocated through ENet's C API, so we need to use its functions to
deallocate the host instead.
Before, any call of Settings::SetDebugModeEnabled(true) would show it. This means that if the debugging UI is enabled, but the user manually closed the code widget, then toggling any option on the interface pane (such as "Pause on Focus Loss") would cause the code widget to reappear. Additionally, closing and reopening dolphin did not call SetDebugModeEnabled, so the code widget did not reappear in that case (it only appeared after touching the interface pane). This is a bit silly, so now only enabling the debugger does it.
This also somewhat resolves an inconsistency introduced by the previous commit: prior to it, --debugger would call SetDebugModeEnabled(true) and thus show the code pane; after these commits, it does not, as it acts like a config change. This is a behavior difference, but not a particularly important one.
Before, Settings::SetDebugModeEnabled was used; this calls SetBaseOrCurrent() which will usually permanently change the base configuration setting for the debugger to true. Thus, the debugger would remain active even if the --debugger command line option was removed. Now, it remains active only for the current run, like other command-line options.
Note that SetBaseOrCurrent is also used by the "Show Debugging UI" option under Options -> Interface; this means that if the debugger is turned off (or off and then back on) by the user while --debugger is specified, this will be reset to whatever the base configuration had when Dolphin is closed and reopened. This behavior is consistent with the rest of the UI.
To my understanding, the --debugger option is something from 5.0 stable/DolphinWx where there was no way to toggle the debug UI in the settings (and the command-line option was the only way of enabling it). It's less useful nowadays.
This isn't used anywhere and not really a generic utility, so we can get
rid of it.
This also lets us remove MathUtil.cpp, since this was the only thing
within that file.
RetroAchievements Rich Presence is a script that is run periodically on a game's memory to provide a detailed text description of what the player is doing. Existing Discord presence on Dolphin would update a player's Discord status to say not just that they are using Dolphin but that they are playing, for example, Sonic Adventure 2 Battle; Rich Presence would detail that the player is in City Escape with 5 lives and 142 rings.
Activating this in the runtime simply entails loading that text script, as returned by the FetchGameData API call, into the runtime, here only determined by whether rich presence is enabled in the achievement settings. Deactivating this is done via the same rcheevos method by setting the rich presence to an empty string.
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).
XInput2 was created to support multiple pointer/keyboard pairs (often
called MPX for multi-pointer X). Dolphin's XInput2 implementation has
always supported MPX by creating a KeyboardMouse object per master
pointer. Since commit bbb12a7, Dolphin's keyboard state is filtered by
the output of XQueryKeymap. As a core X function, XQueryKeymap queries
"the" keyboard, which by default is the first master keyboard. As a
result, Dolphin will ignore keys pressed on other master keyboards
unless the first master is simultaneously pressing the same keys.
XInput2 doesn't provide a function to query the keyboard state. There
is no XIQueryKeymap and the current state is not a member of the
XIKeyClassInfo returned by XIQueryDevice. Instead, XInput2 allows a
master pointer to be nominated as "the" pointer on a per-client basis,
with "the" keyboard automatically becoming the associated master
keyboard. The "documentation" [1] says passing None for the window is
only for debugging purposes, but it is documented in the
XISetClientPointer man page and seems to be the only way to query
keyboards beyond the first.
With this commit, Dolphin correctly reads keys from keyboards other than
the first master keyboard. To test, use the xinput command-line utility
to create a master pointer and reattach a keyboard to the associated
master keyboard.
[1]: https://who-t.blogspot.com/2009/07/xi2-recipes-part-6.html
(the XInput2 developer's blog)
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.
Fixes us forgetting to add its include directories, which could result in linking to a dylib from MacPorts while using the system's header, and failing to link because they use different function names
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.