mirror of
https://github.com/ppy/osu.git
synced 2026-05-26 08:10:35 +08:00
Merge pull request #36516 from peppy/fix-beatmap-invalidation-F-A-I-L
Fix star ratings sometimes not updating after editing a beatmap
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -15,6 +16,7 @@ using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Tests.Beatmaps.IO;
|
||||
using osu.Game.Tests.Visual;
|
||||
@@ -34,6 +36,12 @@ namespace osu.Game.Tests.Beatmaps
|
||||
|
||||
private IBindable<StarDifficulty> starDifficultyBindable;
|
||||
|
||||
[Resolved]
|
||||
private BeatmapManager beatmapManager { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private BeatmapDifficultyCache actualDifficultyCache { get; set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuGameBase osu)
|
||||
{
|
||||
@@ -55,6 +63,36 @@ namespace osu.Game.Tests.Beatmaps
|
||||
AddUntilStep($"star difficulty -> {BASE_STARS}", () => starDifficultyBindable.Value.Stars == BASE_STARS);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInvalidationFlow()
|
||||
{
|
||||
BeatmapInfo postEditBeatmapInfo = null;
|
||||
BeatmapInfo preEditBeatmapInfo = null;
|
||||
|
||||
IBindable<StarDifficulty> bindableDifficulty = null;
|
||||
|
||||
AddStep("get bindable stars", () =>
|
||||
{
|
||||
preEditBeatmapInfo = importedSet.Beatmaps.First();
|
||||
bindableDifficulty = actualDifficultyCache.GetBindableDifficulty(preEditBeatmapInfo);
|
||||
});
|
||||
|
||||
AddUntilStep("wait for stars retrieved", () => bindableDifficulty.Value.Stars, () => Is.GreaterThan(0));
|
||||
|
||||
AddStep("remove all hitobjects", () =>
|
||||
{
|
||||
var working = beatmapManager.GetWorkingBeatmap(preEditBeatmapInfo);
|
||||
|
||||
((IList<HitObject>)working.Beatmap.HitObjects).Clear();
|
||||
|
||||
beatmapManager.Save(working.BeatmapInfo, working.Beatmap);
|
||||
postEditBeatmapInfo = working.BeatmapInfo;
|
||||
});
|
||||
|
||||
AddAssert("stars is now zero", () => actualDifficultyCache.GetDifficultyAsync(postEditBeatmapInfo).GetResultSafely()!.Value.Stars, () => Is.Zero);
|
||||
AddUntilStep("bindable stars is now zero", () => bindableDifficulty.Value.Stars, () => Is.Zero);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStarDifficultyChangesOnModSettings()
|
||||
{
|
||||
@@ -122,8 +160,10 @@ namespace osu.Game.Tests.Beatmaps
|
||||
[Test]
|
||||
public void TestKeyDoesntEqualWithDifferentModSettings()
|
||||
{
|
||||
var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = guid }, new RulesetInfo { OnlineID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.1 } } });
|
||||
var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = guid }, new RulesetInfo { OnlineID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.9 } } });
|
||||
var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = guid }, new RulesetInfo { OnlineID = 0 },
|
||||
new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.1 } } });
|
||||
var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = guid }, new RulesetInfo { OnlineID = 0 },
|
||||
new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.9 } } });
|
||||
|
||||
Assert.That(key1, Is.Not.EqualTo(key2));
|
||||
Assert.That(key1.GetHashCode(), Is.Not.EqualTo(key2.GetHashCode()));
|
||||
@@ -132,8 +172,10 @@ namespace osu.Game.Tests.Beatmaps
|
||||
[Test]
|
||||
public void TestKeyEqualWithMatchingModSettings()
|
||||
{
|
||||
var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = guid }, new RulesetInfo { OnlineID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.25 } } });
|
||||
var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = guid }, new RulesetInfo { OnlineID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.25 } } });
|
||||
var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = guid }, new RulesetInfo { OnlineID = 0 },
|
||||
new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.25 } } });
|
||||
var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = guid }, new RulesetInfo { OnlineID = 0 },
|
||||
new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.25 } } });
|
||||
|
||||
Assert.That(key1, Is.EqualTo(key2));
|
||||
Assert.That(key1.GetHashCode(), Is.EqualTo(key2.GetHashCode()));
|
||||
|
||||
@@ -75,6 +75,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))
|
||||
return;
|
||||
|
||||
modSettingChangeTracker?.Dispose();
|
||||
|
||||
Scheduler.AddOnce(updateTrackedBindables);
|
||||
@@ -82,15 +86,37 @@ namespace osu.Game.Beatmaps
|
||||
modSettingChangeTracker = new ModSettingChangeTracker(mods.NewValue);
|
||||
modSettingChangeTracker.SettingChanged += _ =>
|
||||
{
|
||||
debouncedModSettingsChange?.Cancel();
|
||||
debouncedModSettingsChange = Scheduler.AddDelayed(updateTrackedBindables, 100);
|
||||
lock (bindableUpdateLock)
|
||||
{
|
||||
debouncedModSettingsChange?.Cancel();
|
||||
debouncedModSettingsChange = Scheduler.AddDelayed(updateTrackedBindables, 100);
|
||||
}
|
||||
};
|
||||
}, true);
|
||||
}
|
||||
|
||||
public void Invalidate(IBeatmapInfo beatmap)
|
||||
/// <summary>
|
||||
/// Notify this cache that a beatmap has been invalidated/updated.
|
||||
/// </summary>
|
||||
/// <param name="oldBeatmap">The old beatmap model.</param>
|
||||
/// <param name="newBeatmap">The updated beatmap model.</param>
|
||||
public void Invalidate(IBeatmapInfo oldBeatmap, IBeatmapInfo newBeatmap)
|
||||
{
|
||||
base.Invalidate(lookup => lookup.BeatmapInfo.Equals(beatmap));
|
||||
base.Invalidate(lookup => lookup.BeatmapInfo.Equals(oldBeatmap));
|
||||
|
||||
lock (bindableUpdateLock)
|
||||
{
|
||||
bool trackedBindablesRefreshRequired = false;
|
||||
|
||||
foreach (var bsd in trackedBindables.Where(bsd => bsd.BeatmapInfo.Equals(oldBeatmap)))
|
||||
{
|
||||
bsd.BeatmapInfo = newBeatmap;
|
||||
trackedBindablesRefreshRequired = true;
|
||||
}
|
||||
|
||||
if (trackedBindablesRefreshRequired)
|
||||
Scheduler.AddOnce(updateTrackedBindables);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -195,6 +221,9 @@ namespace osu.Game.Beatmaps
|
||||
{
|
||||
lock (bindableUpdateLock)
|
||||
{
|
||||
debouncedModSettingsChange?.Cancel();
|
||||
debouncedModSettingsChange = null;
|
||||
|
||||
trackedUpdateCancellationSource.Cancel();
|
||||
trackedUpdateCancellationSource = new CancellationTokenSource();
|
||||
|
||||
@@ -348,7 +377,7 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
private class BindableStarDifficulty : Bindable<StarDifficulty>
|
||||
{
|
||||
public readonly IBeatmapInfo BeatmapInfo;
|
||||
public IBeatmapInfo BeatmapInfo;
|
||||
public readonly CancellationToken CancellationToken;
|
||||
|
||||
public BindableStarDifficulty(IBeatmapInfo beatmapInfo, CancellationToken cancellationToken)
|
||||
|
||||
@@ -52,11 +52,11 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
foreach (BeatmapInfo beatmap in beatmapSet.Beatmaps)
|
||||
{
|
||||
difficultyCache.Invalidate(beatmap);
|
||||
|
||||
var working = workingBeatmapCache.GetWorkingBeatmap(beatmap);
|
||||
var ruleset = working.BeatmapInfo.Ruleset.CreateInstance();
|
||||
|
||||
difficultyCache.Invalidate(beatmap, working.BeatmapInfo);
|
||||
|
||||
var ruleset = working.BeatmapInfo.Ruleset.CreateInstance();
|
||||
var calculator = ruleset.CreateDifficultyCalculator(working);
|
||||
|
||||
beatmap.StarRating = calculator.Calculate().StarRating;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
@@ -104,6 +105,10 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
beatmapInfo = beatmapInfo.Detach();
|
||||
|
||||
// If this ever gets hit, a request has arrived with an outdated BeatmapInfo.
|
||||
// An outdated BeatmapInfo may contain a reference to a previous version of the beatmap's files on disk.
|
||||
Debug.Assert(confirmFileHashIsUpToDate(beatmapInfo), "working beatmap returned with outdated path");
|
||||
|
||||
workingCache.Add(working = new BeatmapManagerWorkingBeatmap(beatmapInfo, this));
|
||||
|
||||
// best effort; may be higher than expected.
|
||||
@@ -113,6 +118,12 @@ namespace osu.Game.Beatmaps
|
||||
}
|
||||
}
|
||||
|
||||
private bool confirmFileHashIsUpToDate(BeatmapInfo beatmapInfo)
|
||||
{
|
||||
string refetchPath = realm.Run(r => r.Find<BeatmapInfo>(beatmapInfo.ID)?.File?.File.Hash);
|
||||
return refetchPath == null || refetchPath == beatmapInfo.File?.File.Hash;
|
||||
}
|
||||
|
||||
#region IResourceStorageProvider
|
||||
|
||||
TextureStore IBeatmapResourceProvider.LargeTextureStore => largeTextureStore;
|
||||
|
||||
Reference in New Issue
Block a user