* initial commit * add extern function for sending rank info to dolphin * minor bug fixes to fetch rank * bug fixes to dolphin rank getter * remove unnecessary imports * update rank getter to use new backend * fix rank request parser * clean up get rank ffi * fix bugs with refactor that prevented rank change * clear rank data on logout * minor cleanup * add response status field * wip rework rank manager to use separate thread * more refactoring of fetcher process * finish async fetch flow * fetch cleanup * push lock file * add new response status * misc cleanup, wip porting rust enums * cleanup rust side rank info * remove boxing when returning rank data * remove utils module and fix unused code errors * chore: run autoformatter * chore: remove unused fields * move thiserror to workspace dependency, simplify rank getter return * fix: always load new rating if it exists * Add a dedicated GraphQL call interface. - `APIClient` now has a `.graphql()` method that can be used to build a GraphQL request. - Updated `game-reporter` and `rank-info` to utilize the interface. - Removed/condensed specific error kinds in `game-reporter` and `rank-info` since they can more or less pass through to `GraphQLError` now. - Some initial cleanup of places where a lot of serialization logic was taking place that shouldn't be happening. This requires some testing - Fizzi is likely the best for that. * Minor cleanup * Make RankInfo default an invalid rank, general code cleanup * Remove clone calls where we can just copy, fix ffi variable name I missed * Remove wildcard import * Make RankInfo default 0, not -1. FFI interface subs in -1 instead since that's a C specific bit anyway. * add fetch status output and remove some fields * Remove unused error variant * Debug payload * fix query string * Allow passing in a pointer path to GraphQL requests instead of a single key * add proper fetch path for graphql response * remove fetch that doesnt work * Add support for a custom HTTP timeout on GraphQL requests * Code cleanup and slight reorg. - RankFetcher moved into free-standing functions to reduce boilerplate. - Applied `#[repr()]` tags to some enums to ensure things are consistent over time; they might work now but it's better for us to be sure if we're relying on conversion behavior. - Some further docstrings added, etc. * Refactored this slightly to make the module more organized. This is mostly moving types that are operated on in the rank fetcher into the module itself. All the fetching logic is mostly self contained now, and the lib.rs module acts as the bootstrapping/ffi barrier. This also handles a few small odds and ends, like making sure that we reset the FetchStatus on user sign out (very rare event I guess) and removes a method (`get_rank`) that doesn't appear to be used anymore (`get_rank_with_info` seems to be the one that's used). * Rename this module so it's naming convention is slightly more in line with the other modules * Add in retry logic for rank API calls. This migrates the constant background thread setup to one where we only spawn the background thread when we need to request the rank (i.e on the CSS). This is ultimately cleaner and makes retry logic easier to implement: before this change, leaving and re-entering the CSS screen would likely have hung the client since the background thread would be stuck in network request sleep timeouts and not be able to pick up the `Message::FetchRank` event. (Fizzi should probably tweak the retry timeout per his needs) * Remove extra status set * fix reporting regressions * exec retries if the rank takes time to update * add comment * chore: set fetching status synchronously * remove old fetching status set * fetch match result directly to ensure correctness before it would have been possible for some abandoned matches to get reported while we were in a game and the rating change would not have shown the impact of the last match but rather the impact of all matches combined. This could make it look like someone lost points when they won, for example. This new method avoids all of that. * clean up things that wont be needed soon * Move rank code in to user module * Fmt, fix commit * Refactor user and rank relation. - `UserManager` now holds `RankInfo` directly and threads it through calls that need access to it. - `RankFetcher` encompasses the logic we use for fetching updated rank data and merging it in to existing `RankInfo`. - `UserManager` will now update `RankInfo` on first user info update from server. - General code cleanup and formatting. * Properly (I think) set rank data on client start/sign-in. - This changes the rank fetcher status to default to `Fetched`, since we seem to expect there to be rank data in the initial sign-in request anyway; i.e there shouldn't really be a scenario where ranked data is `NotFetched`. - More general cleanup, I am too tired to write this commit. * fix issue where deserialization of rank could fail * Set rank fetcher status if we fail to overwrite user data from server. - Cleans up `overwrite_from_server` so it's less of a headache to read, and hoists the error so that we can just ensure rank fetcher status is error in the event of any problems. - Fixes some logging statements that didn't have their target set. - Removes some clones that we didn't need to be doing so eagerly. Sometimes I hate programming. * manage fetch status on user load * dont retry login on server failure --------- Co-authored-by: Jas Laferriere <Fizzi36@gmail.com> Co-authored-by: Ryan McGrath <ryan@rymc.io>
Slippi Jukebox
Slippi Jukebox serves as an integrated solution for playing Melee's OST in a way that's effectively independent from emulation.
How music is controlled
Three injections have been added game-side (not in this repo) to enable Jukebox to function. These injections send messages to the Slippi EXI Device:
- The first is in the
fileLoad_HPS
function. This runs once whenever the game is about to play a new song. - The second is in
Music_StopMusic
, which runs whenever the game wants to stop music playback. - The third is
DSP_Process
. This injection runs right after the game finishes calculating the "final music volume" variable which includes sound setting, pause multiplier, starman multiplier, etc. The function runs once per frame, so the previous volume is stored - this allows a message to be sent only when the value has changed.
The Slippi EXI Device will forward these messages to the Rust EXI device which may or may not be holding an instance of Jukebox (depending on if the player has music enabled or not.)
How music is played back
When a Jukebox
instance is created it will spawn a child thread which loops waiting to receive messages from the main thread. When a message to play a song is received, it reads from disk (completely independent of Dolphin) to load music data from the iso, decodes it into audio and plays it back with with the default audio device.
When the Jukebox
instance is dropped, the child thread terminates and the music stops.
Decoding Melee's Music
The logic for decoding Melee's music has been split out into a public library. See the hps_decode
crate for more. For general information about the .hps
file format, see here.