Skip to content

Commit

Permalink
Merge pull request #27380 from frenzibyte/fix-advanced-stats-display
Browse files Browse the repository at this point in the history
Fix advanced stats in beatmap info overlay showing "key count" on non-mania beatmaps
  • Loading branch information
bdach committed Feb 28, 2024
2 parents 1569cd8 + 8e33661 commit a5948d3
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 76 deletions.
49 changes: 5 additions & 44 deletions osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs
Expand Up @@ -37,15 +37,9 @@ public partial class TestSceneAdvancedStats : OsuTestScene
[SetUp]
public void Setup() => Schedule(() => Child = advancedStats = new TestAdvancedStats
{
Width = 500
Width = 500,
});

[SetUpSteps]
public void SetUpSteps()
{
AddStep("reset game ruleset", () => Ruleset.Value = new OsuRuleset().RulesetInfo);
}

private BeatmapInfo exampleBeatmapInfo => new BeatmapInfo
{
Ruleset = rulesets.AvailableRulesets.First(),
Expand Down Expand Up @@ -74,45 +68,12 @@ public void TestNoMod()
}

[Test]
public void TestManiaFirstBarTextManiaBeatmap()
public void TestFirstBarText()
{
AddStep("set game ruleset to mania", () => Ruleset.Value = new ManiaRuleset().RulesetInfo);

AddStep("set beatmap", () => advancedStats.BeatmapInfo = new BeatmapInfo
{
Ruleset = rulesets.GetRuleset(3) ?? throw new InvalidOperationException("osu!mania ruleset not found"),
Difficulty = new BeatmapDifficulty
{
CircleSize = 5,
DrainRate = 4.3f,
OverallDifficulty = 4.5f,
ApproachRate = 3.1f
},
StarRating = 8
});

AddAssert("first bar text is correct", () => advancedStats.ChildrenOfType<SpriteText>().First().Text == BeatmapsetsStrings.ShowStatsCsMania);
}

[Test]
public void TestManiaFirstBarTextConvert()
{
AddStep("set game ruleset to mania", () => Ruleset.Value = new ManiaRuleset().RulesetInfo);

AddStep("set beatmap", () => advancedStats.BeatmapInfo = new BeatmapInfo
{
Ruleset = new OsuRuleset().RulesetInfo,
Difficulty = new BeatmapDifficulty
{
CircleSize = 5,
DrainRate = 4.3f,
OverallDifficulty = 4.5f,
ApproachRate = 3.1f
},
StarRating = 8
});

AddStep("set ruleset to mania", () => advancedStats.Ruleset.Value = new ManiaRuleset().RulesetInfo);
AddAssert("first bar text is correct", () => advancedStats.ChildrenOfType<SpriteText>().First().Text == BeatmapsetsStrings.ShowStatsCsMania);
AddStep("set ruleset to osu", () => advancedStats.Ruleset.Value = new OsuRuleset().RulesetInfo);
AddAssert("first bar text is correct", () => advancedStats.ChildrenOfType<SpriteText>().First().Text == BeatmapsetsStrings.ShowStatsCs);
}

[Test]
Expand Down
30 changes: 19 additions & 11 deletions osu.Game/Overlays/BeatmapSet/Details.cs
Expand Up @@ -10,6 +10,7 @@
using osu.Game.Beatmaps;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays.BeatmapSet.Buttons;
using osu.Game.Rulesets;
using osu.Game.Screens.Select.Details;
using osuTK;

Expand All @@ -33,10 +34,10 @@ public APIBeatmapSet BeatmapSet
{
if (value == beatmapSet) return;

beatmapSet = value;
basic.BeatmapSet = preview.BeatmapSet = beatmapSet = value;

basic.BeatmapSet = preview.BeatmapSet = BeatmapSet;
updateDisplay();
if (IsLoaded)
updateDisplay();
}
}

Expand All @@ -50,13 +51,10 @@ public IBeatmapInfo BeatmapInfo
if (value == beatmapInfo) return;

basic.BeatmapInfo = advanced.BeatmapInfo = beatmapInfo = value;
}
}

private void updateDisplay()
{
Ratings.Ratings = BeatmapSet?.Ratings;
ratingBox.Alpha = BeatmapSet?.Status > 0 ? 1 : 0;
if (IsLoaded)
updateDisplay();
}
}

public Details()
Expand Down Expand Up @@ -101,12 +99,22 @@ public Details()
};
}

[BackgroundDependencyLoader]
private void load()
[Resolved]
private RulesetStore rulesets { get; set; }

protected override void LoadComplete()
{
base.LoadComplete();
updateDisplay();
}

private void updateDisplay()
{
Ratings.Ratings = BeatmapSet?.Ratings;
ratingBox.Alpha = BeatmapSet?.Status > 0 ? 1 : 0;
advanced.Ruleset.Value = rulesets.GetRuleset(beatmapInfo?.Ruleset.OnlineID ?? 0);
}

private partial class DetailBox : Container
{
private readonly Container content;
Expand Down
36 changes: 16 additions & 20 deletions osu.Game/Screens/Select/Details/AdvancedStats.cs
Expand Up @@ -38,11 +38,6 @@ public partial class AdvancedStats : Container, IHasCustomTooltip<AdjustedAttrib
[Resolved]
private IBindable<IReadOnlyList<Mod>> mods { get; set; }

[Resolved]
private OsuGameBase game { get; set; }

private IBindable<RulesetInfo> gameRuleset;

protected readonly StatisticRow FirstValue, HpDrain, Accuracy, ApproachRate;
private readonly StatisticRow starDifficulty;

Expand All @@ -64,6 +59,15 @@ public IBeatmapInfo BeatmapInfo
}
}

/// <summary>
/// Ruleset to be used for certain elements of display.
/// When set, this will override the set <see cref="Beatmap"/>'s own ruleset.
/// </summary>
/// <remarks>
/// No checks are done as to whether the ruleset specified is valid for the currently <see cref="BeatmapInfo"/>.
/// </remarks>
public Bindable<RulesetInfo> Ruleset { get; } = new Bindable<RulesetInfo>();

public AdvancedStats(int columns = 1)
{
switch (columns)
Expand Down Expand Up @@ -137,12 +141,7 @@ protected override void LoadComplete()
{
base.LoadComplete();

// the cached ruleset bindable might be a decoupled bindable provided by SongSelect,
// which we can't rely on in combination with the game-wide selected mods list,
// since mods could be updated to the new ruleset instances while the decoupled bindable is held behind,
// therefore resulting in performing difficulty calculation with invalid states.
gameRuleset = game.Ruleset.GetBoundCopy();
gameRuleset.BindValueChanged(_ => updateStatistics());
Ruleset.BindValueChanged(_ => updateStatistics());

mods.BindValueChanged(modsChanged, true);
}
Expand All @@ -169,8 +168,6 @@ private void updateStatistics()
IBeatmapDifficultyInfo baseDifficulty = BeatmapInfo?.Difficulty;
BeatmapDifficulty adjustedDifficulty = null;

IRulesetInfo ruleset = gameRuleset?.Value ?? beatmapInfo.Ruleset;

if (baseDifficulty != null)
{
BeatmapDifficulty originalDifficulty = new BeatmapDifficulty(baseDifficulty);
Expand All @@ -180,24 +177,24 @@ private void updateStatistics()

adjustedDifficulty = originalDifficulty;

if (gameRuleset != null)
if (Ruleset.Value != null)
{
double rate = 1;
foreach (var mod in mods.Value.OfType<IApplicableToRate>())
rate = mod.ApplyToRate(0, rate);

adjustedDifficulty = ruleset.CreateInstance().GetRateAdjustedDisplayDifficulty(originalDifficulty, rate);
adjustedDifficulty = Ruleset.Value.CreateInstance().GetRateAdjustedDisplayDifficulty(originalDifficulty, rate);

TooltipContent = new AdjustedAttributesTooltip.Data(originalDifficulty, adjustedDifficulty);
}
}

switch (ruleset.OnlineID)
switch (Ruleset.Value?.OnlineID)
{
case 3:
// Account for mania differences locally for now.
// Eventually this should be handled in a more modular way, allowing rulesets to return arbitrary difficulty attributes.
ILegacyRuleset legacyRuleset = (ILegacyRuleset)ruleset.CreateInstance();
ILegacyRuleset legacyRuleset = (ILegacyRuleset)Ruleset.Value.CreateInstance();

// For the time being, the key count is static no matter what, because:
// a) The method doesn't have knowledge of the active keymods. Doing so may require considerations for filtering.
Expand All @@ -206,7 +203,6 @@ private void updateStatistics()

FirstValue.Title = BeatmapsetsStrings.ShowStatsCsMania;
FirstValue.Value = (keyCount, keyCount);

break;

default:
Expand Down Expand Up @@ -240,8 +236,8 @@ private void updateStatistics()
starDifficultyCancellationSource = new CancellationTokenSource();
var normalStarDifficultyTask = difficultyCache.GetDifficultyAsync(BeatmapInfo, gameRuleset.Value, null, starDifficultyCancellationSource.Token);
var moddedStarDifficultyTask = difficultyCache.GetDifficultyAsync(BeatmapInfo, gameRuleset.Value, mods.Value, starDifficultyCancellationSource.Token);
var normalStarDifficultyTask = difficultyCache.GetDifficultyAsync(BeatmapInfo, Ruleset.Value, null, starDifficultyCancellationSource.Token);
var moddedStarDifficultyTask = difficultyCache.GetDifficultyAsync(BeatmapInfo, Ruleset.Value, mods.Value, starDifficultyCancellationSource.Token);
Task.WhenAll(normalStarDifficultyTask, moddedStarDifficultyTask).ContinueWith(_ => Schedule(() =>
{
Expand Down
7 changes: 6 additions & 1 deletion osu.Game/Screens/Select/SongSelect.cs
Expand Up @@ -286,7 +286,7 @@ private void load(AudioManager audio, OsuColour colours, ManageCollectionsDialog
AutoSizeAxes = Axes.Y,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Padding = new MarginPadding(10)
Padding = new MarginPadding(10),
},
}
},
Expand Down Expand Up @@ -585,6 +585,11 @@ private void performUpdateSelected()
beatmapInfoPrevious = beatmap;
}

// we can't run this in the debounced run due to the selected mods bindable not being debounced,
// since mods could be updated to the new ruleset instances while the decoupled bindable is held behind,
// therefore resulting in performing difficulty calculation with invalid states.
advancedStats.Ruleset.Value = ruleset;

void run()
{
// clear pending task immediately to track any potential nested debounce operation.
Expand Down

0 comments on commit a5948d3

Please sign in to comment.