diff --git a/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs b/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs index b63c0a8196..ca84d00bb3 100644 --- a/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs +++ b/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs @@ -114,6 +114,30 @@ namespace osu.Game.Tests.Beatmaps AddUntilStep($"star difficulty -> {BASE_STARS + 1.75}", () => starDifficultyBindable.Value.Stars == BASE_STARS + 1.75); } + [Test] + public void TestStarDifficultyChangesOnModSettingsCorrectlyTrackAcrossReferenceChanges() + { + OsuModDoubleTime dt = null; + + AddStep("set computation function", () => difficultyCache.ComputeDifficulty = lookup => + { + var modRateAdjust = (ModRateAdjust)lookup.OrderedMods.SingleOrDefault(mod => mod is ModRateAdjust); + return new StarDifficulty(BASE_STARS + modRateAdjust?.SpeedChange.Value ?? 0, 0); + }); + + AddStep("change selected mod to DT", () => SelectedMods.Value = new[] { dt = new OsuModDoubleTime { SpeedChange = { Value = 1.5 } } }); + AddUntilStep($"star difficulty -> {BASE_STARS + 1.5}", () => starDifficultyBindable.Value.Stars == BASE_STARS + 1.5); + + AddStep("change DT speed to 1.25", () => dt.SpeedChange.Value = 1.25); + AddUntilStep($"star difficulty -> {BASE_STARS + 1.25}", () => starDifficultyBindable.Value.Stars == BASE_STARS + 1.25); + + AddStep("reconstruct DT mod with same settings", () => SelectedMods.Value = new[] { dt = (OsuModDoubleTime)dt.DeepClone() }); + AddUntilStep($"star difficulty -> {BASE_STARS + 1.25}", () => starDifficultyBindable.Value.Stars == BASE_STARS + 1.25); + + AddStep("change DT speed to 1.25", () => dt.SpeedChange.Value = 2); + AddUntilStep($"star difficulty -> {BASE_STARS + 2}", () => starDifficultyBindable.Value.Stars == BASE_STARS + 2); + } + [Test] public void TestStarDifficultyAdjustHashCodeConflict() { diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs index 291239e350..6ac8508036 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -76,7 +76,10 @@ namespace osu.Game.Beatmaps currentMods.BindValueChanged(mods => { // A change in bindable here doesn't guarantee that mods have actually changed. - if (mods.OldValue.SequenceEqual(mods.NewValue)) + // However, we *do* want to make sure that the mod *references* are the same; + // `SequenceEqual()` without a comparer would fall back to `IEquatable`. + // Failing to ensure reference equality can cause setting change tracking to fail later. + if (mods.OldValue.SequenceEqual(mods.NewValue, ReferenceEqualityComparer.Instance)) return; modSettingChangeTracker?.Dispose();