Not only was the extra call to the update callback in the AchievementEventHandler method unnecessary, it was getting called on events that don't even need to be tracked here, causing a lot of lag when it turned out one achievement was repeatedly spamming Achievement Reset Events as a shortcut.
Failing to do this was causing challenge icons to carry over into the next game if a game was closed while the icons were active, even if the next game to run was a completely different game entirely with completely different badges.
RetroAchievements plans to use the user_agent in unlock requests to determine which software version was used to play the game, and can filter older software versions out. As such, I have been given the go-ahead to remove the hardcoded line that forces hardcore to always be false.
Two minor updates to improve the Achievement Manager's handling of a player's completion rate.
One, UnlockStatus and the unlock map now track achievement category, such that TallyScore does not count unofficial achievements in counts/points.
Two, the determinations for mastery/completion are now improved to check (1) that the achievement triggering this is CORE (not UNOFFICIAL) and (2) that it has not already been unlocked at this level on the site, which should be sufficient to determine that the unlocking of this particular achievement completes/masters the game.
Most obviously, there is no longer a warning message to the player in the achievement window that achievements are disabled if a game is not currently running.
The Host URL setting in the RetroAchievements config will, if set, be used as the host URL for all server requests for achievements. This allows for an easy switch to the RetroAchievements staging server for testing.
Two fixes: verify that there's an API token before attempting to automatically log in, and don't attempt to hash a game and load it unless achievements are enabled and the player is logged in. This prevents multiple API requests that we already know will fail, one of which will display an unnecessary error message to the player.
Renames some variables to avoid shadowing warnings on gcc.
Also gets rid of a FilereaderState struct, since one is already defined
in the declaration of the AchievementManager class.
When an achievement is "primed", a challenge is active, for example completing a portion of the game in under a time limit or without taking damage or using certain items. This is stored in a map in the Achievement Manager (and removed when the achievement is unprimed) so a later commit can display it on screen.
The Disabled state sits between Game Closed and completely Shutdown - stronger than the former, as it refuses to let a game be opened again until AchievementManager is restored (which only happens upon a fresh core boot) but it isn't completely shut down and will still allow the player to be logged in and access the achievement settings and their (global) achievement header.
This change splits LoadGameAsync into three methods: two HashGame methods to accept either a string filepath or a volume, and a common LoadGameSync that both HashGames call to actually process the code. In the process, some minor cleanup, and the hash resolution now takes place on the work queue asynchronously.
Hardcore Mode is a RetroAchievements feature for enabling as close to original hardware as possible, to keep a fair, challenging, and competitive playing field for achievements (which get tallied differently and emphasized more in hardcore) and leaderboards (where it is mandatory) at the cost of several common emulator features that provide advantages, such as state loading and slower emulation speeds.
This commit just adds the flag to the AchievementSettings, with more to come.
Split the "welcome" messages letting players know achievements are active into a separate method that gets called (currently) after a number of frames to ensure that the emulator has started properly and has somewhere to display the messages.
FetchBoardInfo is called (via the work queue asynchronously) on a leaderboard every time it is activated or submitted to. It makes two calls to the RetroAchievements API for fetching leaderboard info, one that requests the top four entries in the leaderboard and another that requests the player's entry, the two entries above the player and the two entries below. All of these are inserted into a single map (resolving any overlaps) so the result can be exposed to the UI.
The leaderboard map created here contains information useful to displaying leaderboard stats in the Achievement dialog, including each leaderboard's name and description and a partial list of entries for display. The entire map is exposed to the UI in a single call for simplicity.
Both of these functions access `m_game_data` and don't lock themselves, so they must be called in a way that guarantees that `m_game_data` is not modified during the call.
Scope issue in the event callback from `rc_runtime_do_frame()`. The pointer points to a variable on the stack from inside `rc_runtime_do_frame()`, so that's a race condition between the thread calling `rc_runtime_do_frame()` and the event queue thread.
Added handling to Achievement Progress Events, which are generated when an achievement with a Measured field updates in value. For example, an achievement for collecting 120 stars will throw this event when the player collects each star, and with this handling, the player will get a message on screen informing them of this progress. This message will only appear if the newly added RA_PROGRESS_ENABLED setting is true.
Provided the badges are turned on in the settings, each achievement will have a badge next to it on the progress tab. There are different badges for locked and unlocked (usually locked is grayscale while unlocked is in color but not necessarily) and the badge chosen depends on the player's current unlock and hardcore status.
FetchBadges fetches all available badges (player, game, achievements) and stores them in AchievementManager's memory. New fields and accessors have been added as necessary. Badges for individual achievements are stored in the UnlockStatus map. The method is public so that the settings dialog can call it if badges are turned on after a game is started. Badges are deleted at game close and logout.
Image requests from RetroAchievements have a slightly different format from other requests, on account of being GET calls that return strings of image data, so this is a separate function that makes such calls. To handle this, I create a BadgeStatus object that ties each badge to a boolean flag for whether it is loaded and thus usable.
This refactors the Rich Presence generation to store to a member field that can be exposed to the UI to display the Rich Presence in the achievement header. It still updates at its original rate of once per two minutes, but calls an update on the dialog when that ticks.