Skip to content

Commit

Permalink
UI: RPC: Value Formatter V3
Browse files Browse the repository at this point in the history
- Allows the ability to bind a single PlayReportGameSpec to multiple title IDs, like for MK8D
- Allows the ability for the value formatters to tell the caller of the analyzer that they should reset the value, and also added the ability to explicitly not handle a value format.
  • Loading branch information
GreemDev committed Feb 3, 2025
1 parent fe43c32 commit b2eecd2
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 29 deletions.
18 changes: 13 additions & 5 deletions src/Ryujinx/DiscordIntegrationModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,13 +134,21 @@ private static void HandlePlayReport(MessagePackObject playReport)
if (!TitleIDs.CurrentApplication.Value.HasValue) return;
if (_discordPresencePlaying is null) return;

Optional<string> details = PlayReport.Analyzer.Run(TitleIDs.CurrentApplication.Value, _currentApp, playReport);
PlayReportFormattedValue value = PlayReport.Analyzer.Run(TitleIDs.CurrentApplication.Value, _currentApp, playReport);

if (!details.HasValue) return;

_discordPresencePlaying.Details = details;
if (!value.Handled) return;

if (value.Reset)
{
_discordPresencePlaying.Details = $"Playing {_currentApp.Title}";
Logger.Info?.Print(LogClass.UI, "Reset Discord RPC based on a supported play report value formatter.");
}
else
{
_discordPresencePlaying.Details = value.FormattedString;
Logger.Info?.Print(LogClass.UI, "Updated Discord RPC based on a supported play report.");
}
UpdatePlayingState();
Logger.Info?.Print(LogClass.UI, "Updated Discord RPC based on a supported play report.");
}

private static string TruncateToByteLength(string input)
Expand Down
75 changes: 51 additions & 24 deletions src/Ryujinx/Utilities/PlayReport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,28 +29,24 @@ public static class PlayReport
"010028600EBDA000",
spec => spec.AddValueFormatter("mode", SuperMario3DWorldOrBowsersFury)
)
.AddSpec( // Mario Kart 8 Deluxe
"0100152000022000",
spec => spec.AddValueFormatter("To", MarioKart8Deluxe_Mode)
)
.AddSpec( // Mario Kart 8 Deluxe (China)
"010075100E8EC000",
.AddSpec( // Mario Kart 8 Deluxe, Mario Kart 8 Deluxe (China)
["0100152000022000", "010075100E8EC000"],
spec => spec.AddValueFormatter("To", MarioKart8Deluxe_Mode)
);

private static string BreathOfTheWild_MasterMode(ref PlayReportValue value)
=> value.BoxedValue is 1 ? "Playing Master Mode" : "Playing Normal Mode";
private static PlayReportFormattedValue BreathOfTheWild_MasterMode(ref PlayReportValue value)
=> value.BoxedValue is 1 ? "Playing Master Mode" : PlayReportFormattedValue.ForceReset;

private static string SuperMarioOdyssey_AssistMode(ref PlayReportValue value)
private static PlayReportFormattedValue SuperMarioOdyssey_AssistMode(ref PlayReportValue value)
=> value.BoxedValue is 1 ? "Playing in Assist Mode" : "Playing in Regular Mode";

private static string SuperMarioOdysseyChina_AssistMode(ref PlayReportValue value)
private static PlayReportFormattedValue SuperMarioOdysseyChina_AssistMode(ref PlayReportValue value)
=> value.BoxedValue is 1 ? "Playing in 帮助模式" : "Playing in 普通模式";

private static string SuperMario3DWorldOrBowsersFury(ref PlayReportValue value)
private static PlayReportFormattedValue SuperMario3DWorldOrBowsersFury(ref PlayReportValue value)
=> value.BoxedValue is 0 ? "Playing Super Mario 3D World" : "Playing Bowser's Fury";

private static string MarioKart8Deluxe_Mode(ref PlayReportValue value)
private static PlayReportFormattedValue MarioKart8Deluxe_Mode(ref PlayReportValue value)
=> value.BoxedValue switch
{
// Single Player
Expand All @@ -75,7 +71,7 @@ private static string MarioKart8Deluxe_Mode(ref PlayReportValue value)
"Battle" => "Battle Mode",
"RaceStart" => "Selecting a Course",
"Race" => "Racing",
_ => $"Playing {value.Application.Title}"
_ => PlayReportFormattedValue.ForceReset
};
}

Expand All @@ -87,23 +83,35 @@ public class PlayReportAnalyzer

public PlayReportAnalyzer AddSpec(string titleId, Func<PlayReportGameSpec, PlayReportGameSpec> transform)
{
_specs.Add(transform(new PlayReportGameSpec { TitleIdStr = titleId }));
_specs.Add(transform(new PlayReportGameSpec { TitleIds = [titleId] }));
return this;
}

public PlayReportAnalyzer AddSpec(string titleId, Action<PlayReportGameSpec> transform)
{
_specs.Add(new PlayReportGameSpec { TitleIdStr = titleId }.Apply(transform));
_specs.Add(new PlayReportGameSpec { TitleIds = [titleId] }.Apply(transform));
return this;
}

public PlayReportAnalyzer AddSpec(IEnumerable<string> titleIds, Func<PlayReportGameSpec, PlayReportGameSpec> transform)
{
_specs.Add(transform(new PlayReportGameSpec { TitleIds = [..titleIds] }));
return this;
}

public PlayReportAnalyzer AddSpec(IEnumerable<string> titleIds, Action<PlayReportGameSpec> transform)
{
_specs.Add(new PlayReportGameSpec { TitleIds = [..titleIds] }.Apply(transform));
return this;
}

public Optional<string> Run(string runningGameId, ApplicationMetadata appMeta, MessagePackObject playReport)
public PlayReportFormattedValue Run(string runningGameId, ApplicationMetadata appMeta, MessagePackObject playReport)
{
if (!playReport.IsDictionary)
return Optional<string>.None;
return PlayReportFormattedValue.Unhandled;

if (!_specs.TryGetFirst(s => s.TitleIdStr.EqualsIgnoreCase(runningGameId), out PlayReportGameSpec spec))
return Optional<string>.None;
if (!_specs.TryGetFirst(s => runningGameId.EqualsAnyIgnoreCase(s.TitleIds), out PlayReportGameSpec spec))
return PlayReportFormattedValue.Unhandled;

foreach (PlayReportValueFormatterSpec formatSpec in spec.Analyses.OrderBy(x => x.Priority))
{
Expand All @@ -119,14 +127,14 @@ public Optional<string> Run(string runningGameId, ApplicationMetadata appMeta, M
return formatSpec.ValueFormatter(ref value);
}

return Optional<string>.None;
return PlayReportFormattedValue.Unhandled;
}

}

public class PlayReportGameSpec
{
public required string TitleIdStr { get; init; }
public required string[] TitleIds { get; init; }
public List<PlayReportValueFormatterSpec> Analyses { get; } = [];

public PlayReportGameSpec AddValueFormatter(string reportKey, PlayReportValueFormatter valueFormatter)
Expand Down Expand Up @@ -158,14 +166,33 @@ public struct PlayReportValue
public object BoxedValue { get; init; }
}

public struct PlayReportFormattedValue
{
public bool Handled { get; private init; }

public bool Reset { get; private init; }

public string FormattedString { get; private init; }

public static implicit operator PlayReportFormattedValue(string formattedValue)
=> new() { Handled = true, FormattedString = formattedValue };

public static PlayReportFormattedValue Unhandled => default;
public static PlayReportFormattedValue ForceReset => new() { Handled = true, Reset = true };

public static PlayReportValueFormatter AlwaysResets = AlwaysResetsImpl;

private static PlayReportFormattedValue AlwaysResetsImpl(ref PlayReportValue _) => ForceReset;
}

public struct PlayReportValueFormatterSpec
{
public required int Priority { get; init; }
public required string ReportKey { get; init; }
public required PlayReportValueFormatter ValueFormatter { get; init; }
public PlayReportValueFormatter ValueFormatter { get; init; }
}

public delegate string PlayReportValueFormatter(ref PlayReportValue value);
public delegate PlayReportFormattedValue PlayReportValueFormatter(ref PlayReportValue value);

#endregion
}

0 comments on commit b2eecd2

Please sign in to comment.