Răsfoiți Sursa

UI: Add descriptions of what each dynamic RPC formatter actually shows when hovering whether it has support in the game info popup

Evan Husted 1 an în urmă
părinte
comite
0d7d0e8092

+ 10 - 4
src/Ryujinx/UI/Controls/ApplicationDataView.axaml

@@ -119,17 +119,23 @@
                         TextWrapping="Wrap" >
                     </TextBlock>
                 </StackPanel>
-                <StackPanel Orientation="Horizontal" Spacing="5">
-                    <ui:SymbolIcon Foreground="ForestGreen" Symbol="Checkmark" IsVisible="{Binding AppData.HasDynamicRichPresenceSupport}"/>
+                <StackPanel Orientation="Horizontal" Spacing="5" ToolTip.Tip="{Binding DynamicRichPresenceDescription}">
+                    <ui:SymbolIcon 
+                        Foreground="ForestGreen" 
+                        Symbol="Checkmark" 
+                        IsVisible="{Binding AppData.HasDynamicRichPresenceSupport}"/>
                     <TextBlock
                         Foreground="ForestGreen"
                         HorizontalAlignment="Stretch"
                         IsVisible="{Binding AppData.HasDynamicRichPresenceSupport}"
                         Text="{ext:Locale GameInfoRpcDynamic}"
                         TextAlignment="Start"
-                        TextWrapping="Wrap" >
+                        TextWrapping="Wrap">
                     </TextBlock>
-                    <ui:SymbolIcon Foreground="Red" Symbol="Cancel" IsVisible="{Binding !AppData.HasDynamicRichPresenceSupport}"/>
+                    <ui:SymbolIcon 
+                        Foreground="Red" 
+                        Symbol="Cancel" 
+                        IsVisible="{Binding !AppData.HasDynamicRichPresenceSupport}"/>
                     <TextBlock
                         Foreground="Red"
                         HorizontalAlignment="Stretch"

+ 6 - 0
src/Ryujinx/UI/ViewModels/ApplicationDataViewModel.cs

@@ -1,6 +1,7 @@
 using Gommon;
 using Ryujinx.Ava.Common.Locale;
 using Ryujinx.Ava.Utilities.AppLibrary;
+using Ryujinx.Ava.Utilities.PlayReport;
 
 namespace Ryujinx.Ava.UI.ViewModels
 {
@@ -10,6 +11,11 @@ namespace Ryujinx.Ava.UI.ViewModels
 
         public ApplicationDataViewModel(ApplicationData appData) => AppData = appData;
 
+        public string DynamicRichPresenceDescription =>
+            AppData.HasDynamicRichPresenceSupport
+                ? AppData.RichPresenceSpec.Value.Description
+                : GameSpec.DefaultDescription;
+
         public string FormattedVersion => LocaleManager.Instance[LocaleKeys.GameListHeaderVersion].Format(AppData.Version);
         public string FormattedDeveloper => LocaleManager.Instance[LocaleKeys.GameListHeaderDeveloper].Format(AppData.Developer);
         public string FormattedFileExtension => LocaleManager.Instance[LocaleKeys.GameListHeaderFileExtension].Format(AppData.FileExtension);

+ 8 - 2
src/Ryujinx/Utilities/AppLibrary/ApplicationData.cs

@@ -10,6 +10,7 @@ using LibHac.Tools.FsSystem;
 using LibHac.Tools.FsSystem.NcaUtils;
 using Ryujinx.Ava.Common.Locale;
 using Ryujinx.Ava.Utilities.Compat;
+using Ryujinx.Ava.Utilities.PlayReport;
 using Ryujinx.Common.Logging;
 using Ryujinx.HLE.FileSystem;
 using Ryujinx.HLE.Loaders.Processes.Extensions;
@@ -35,9 +36,14 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
             {
                 _id = value;
 
-                Compatibility = CompatibilityCsv.Find(Id);
+                Compatibility = CompatibilityCsv.Find(value);
+                RichPresenceSpec = PlayReports.Analyzer.TryGetSpec(IdString, out GameSpec gameSpec) 
+                    ? gameSpec 
+                    : default(Optional<GameSpec>);
             }
         }
+        public Optional<GameSpec> RichPresenceSpec { get; set; }
+        
         public string Developer { get; set; } = "Unknown";
         public string Version { get; set; } = "0";
         public int PlayerCount { get; set; }
@@ -46,7 +52,7 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
         public bool HasLdnGames => PlayerCount != 0 && GameCount != 0;
 
         public bool HasRichPresenceAsset => DiscordIntegrationModule.HasAssetImage(IdString);
-        public bool HasDynamicRichPresenceSupport => DiscordIntegrationModule.HasAnalyzer(IdString);
+        public bool HasDynamicRichPresenceSupport => RichPresenceSpec.HasValue;
         
         public TimeSpan TimePlayed { get; set; }
         public DateTime? LastPlayed { get; set; }

+ 8 - 3
src/Ryujinx/Utilities/PlayReport/Analyzer.cs

@@ -20,6 +20,11 @@ namespace Ryujinx.Ava.Utilities.PlayReport
 
         public IReadOnlyList<GameSpec> Specs => new ReadOnlyCollection<GameSpec>(_specs);
 
+        public GameSpec GetSpec(string titleId) => _specs.First(x => x.TitleIds.ContainsIgnoreCase(titleId));
+
+        public bool TryGetSpec(string titleId, out GameSpec gameSpec)
+            => (gameSpec = _specs.FirstOrDefault(x => x.TitleIds.ContainsIgnoreCase(titleId))) != null;
+
         /// <summary>
         /// Add an analysis spec matching a specific game by title ID, with the provided spec configuration.
         /// </summary>
@@ -128,13 +133,13 @@ namespace Ryujinx.Ava.Utilities.PlayReport
         {
             if (!playReport.ReportData.IsDictionary)
                 return FormattedValue.Unhandled;
-
-            if (!_specs.TryGetFirst(s => runningGameId.EqualsAnyIgnoreCase(s.TitleIds), out GameSpec spec))
+            
+            if (!TryGetSpec(runningGameId, out GameSpec spec))
                 return FormattedValue.Unhandled;
 
             foreach (FormatterSpecBase formatSpec in spec.ValueFormatters.OrderBy(x => x.Priority))
             {
-                if (!formatSpec.Format(appMeta, playReport, out FormattedValue value))
+                if (!formatSpec.TryFormat(appMeta, playReport, out FormattedValue value))
                     continue;
 
                 return value;

+ 24 - 8
src/Ryujinx/Utilities/PlayReport/PlayReports.cs

@@ -17,6 +17,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport
                 .AddSpec(
                     "01007ef00011e000",
                     spec => spec
+                        .WithDescription("based on being in Master Mode.")
                         .AddValueFormatter("IsHardMode", BreathOfTheWild_MasterMode)
                         // reset to normal status when switching between normal & master mode in title screen
                         .AddValueFormatter("AoCVer", FormattedValue.SingleAlwaysResets)
@@ -24,34 +25,48 @@ namespace Ryujinx.Ava.Utilities.PlayReport
                 .AddSpec(
                     "0100f2c0115b6000",
                     spec => spec
+                        .WithDescription("based on where you are in Hyrule (Depths, Surface, Sky).")
                         .AddValueFormatter("PlayerPosY", TearsOfTheKingdom_CurrentField))
+                .AddSpec(
+                    "01002da013484000", 
+                    spec => spec
+                        .WithDescription("based on how many Rupees you have.")
+                        .AddValueFormatter("rupees", SkywardSwordHD_Rupees))
                 .AddSpec(
                     "0100000000010000",
-                    spec =>
-                        spec.AddValueFormatter("is_kids_mode", SuperMarioOdyssey_AssistMode)
+                    spec => spec
+                        .WithDescription("based on if you're playing with Assist Mode.")
+                        .AddValueFormatter("is_kids_mode", SuperMarioOdyssey_AssistMode)
                 )
                 .AddSpec(
                     "010075000ecbe000",
-                    spec =>
-                        spec.AddValueFormatter("is_kids_mode", SuperMarioOdysseyChina_AssistMode)
+                    spec => spec
+                        .WithDescription("based on if you're playing with Assist Mode.")
+                        .AddValueFormatter("is_kids_mode", SuperMarioOdysseyChina_AssistMode)
                 )
                 .AddSpec(
                     "010028600ebda000",
-                    spec => spec.AddValueFormatter("mode", SuperMario3DWorldOrBowsersFury)
+                    spec => spec
+                        .WithDescription("based on being in either Super Mario 3D World or Bowser's Fury.")
+                        .AddValueFormatter("mode", SuperMario3DWorldOrBowsersFury)
                 )
                 .AddSpec( // Global & China IDs
                     ["0100152000022000", "010075100e8ec000"],
-                    spec => spec.AddValueFormatter("To", MarioKart8Deluxe_Mode)
+                    spec => spec
+                        .WithDescription("based on what modes you're selecting in the menu & whether or not you're in a race.")
+                        .AddValueFormatter("To", MarioKart8Deluxe_Mode)
                 )
                 .AddSpec(
                     ["0100a3d008c5c000", "01008f6008c5e000"],
                     spec => spec
+                        .WithDescription("based on what area of Paldea you're exploring.")
                         .AddValueFormatter("area_no", PokemonSVArea)
                         .AddValueFormatter("team_circle", PokemonSVUnionCircle)
                 )
                 .AddSpec(
                     "01006a800016e000",
                     spec => spec
+                        .WithDescription("based on what mode you're playing, who won, and what characters were present.")
                         .AddSparseMultiValueFormatter(
                             [
                                 // Metadata to figure out what PlayReport we have.
@@ -72,9 +87,10 @@ namespace Ryujinx.Ava.Utilities.PlayReport
                         "0100c9a00ece6000", "01008d300c50c000", "0100d870045b6000",
                         "010012f017576000", "0100c62011050000", "0100b3c014bda000"
                     ],
-                    spec => spec.AddValueFormatter("launch_title_id", NsoEmulator_LaunchedGame)
+                    spec => spec
+                        .WithDescription("based on what game you first launch.\n\nNSO emulators do not print any Play Report information past the first game launch so it's all we got.")
+                        .AddValueFormatter("launch_title_id", NsoEmulator_LaunchedGame)
                 )
-                .AddSpec("01002da013484000", spec => spec.AddValueFormatter("rupees", SkywardSwordHD_Rupees))
         );
 
         private static string Playing(string game) => $"Playing {game}";

+ 15 - 1
src/Ryujinx/Utilities/PlayReport/Specs.cs

@@ -23,6 +23,20 @@ namespace Ryujinx.Ava.Utilities.PlayReport
 
         public required string[] TitleIds { get; init; }
 
+        public const string DefaultDescription = "Formats the details on your Discord presence based on logged data from the game.";
+
+        private string _valueDescription;
+
+        public string Description => _valueDescription ?? DefaultDescription;
+
+        public GameSpec WithDescription(string description)
+        {
+            _valueDescription = description != null
+                ? $"Formats the details on your Discord presence {description}"
+                : null;
+            return this;
+        }
+        
         public List<FormatterSpecBase> ValueFormatters { get; } = [];
 
 
@@ -197,7 +211,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport
         public string[] ReportKeys { get; init; }
         public Delegate Formatter { get; init; }
 
-        public bool Format(ApplicationMetadata appMeta, Horizon.Prepo.Types.PlayReport playReport,
+        public bool TryFormat(ApplicationMetadata appMeta, Horizon.Prepo.Types.PlayReport playReport,
             out FormattedValue formattedValue)
         {
             formattedValue = default;