diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs index 4bb2b557ffab..3b89c70a637a 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs @@ -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(), @@ -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().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().First().Text == BeatmapsetsStrings.ShowStatsCsMania); + AddStep("set ruleset to osu", () => advancedStats.Ruleset.Value = new OsuRuleset().RulesetInfo); + AddAssert("first bar text is correct", () => advancedStats.ChildrenOfType().First().Text == BeatmapsetsStrings.ShowStatsCs); } [Test] diff --git a/osu.Game/Overlays/BeatmapSet/Details.cs b/osu.Game/Overlays/BeatmapSet/Details.cs index cf78f605aa7c..d656a6b14be6 100644 --- a/osu.Game/Overlays/BeatmapSet/Details.cs +++ b/osu.Game/Overlays/BeatmapSet/Details.cs @@ -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; @@ -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(); } } @@ -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() @@ -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; diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 0d68a0ec3c13..1aba977f44f3 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -38,11 +38,6 @@ public partial class AdvancedStats : Container, IHasCustomTooltip> mods { get; set; } - [Resolved] - private OsuGameBase game { get; set; } - - private IBindable gameRuleset; - protected readonly StatisticRow FirstValue, HpDrain, Accuracy, ApproachRate; private readonly StatisticRow starDifficulty; @@ -64,6 +59,15 @@ public IBeatmapInfo BeatmapInfo } } + /// + /// Ruleset to be used for certain elements of display. + /// When set, this will override the set 's own ruleset. + /// + /// + /// No checks are done as to whether the ruleset specified is valid for the currently . + /// + public Bindable Ruleset { get; } = new Bindable(); + public AdvancedStats(int columns = 1) { switch (columns) @@ -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); } @@ -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); @@ -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()) 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. @@ -206,7 +203,6 @@ private void updateStatistics() FirstValue.Title = BeatmapsetsStrings.ShowStatsCsMania; FirstValue.Value = (keyCount, keyCount); - break; default: @@ -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(() => { diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index a603934a9dde..15469fad5bf2 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -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), }, } }, @@ -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.