1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 10:12:54 +08:00

Merge pull request #25943 from peppy/fix-song-select-sound-at-gameplay

Fix song select running updates when screen is not active
This commit is contained in:
Bartłomiej Dach 2023-12-20 12:20:06 +01:00 committed by GitHub
commit 22513e0070
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -301,6 +301,9 @@ namespace osu.Game.Screens.Select
if (loadedTestBeatmaps)
return;
var setsRequiringUpdate = new HashSet<BeatmapSetInfo>();
var setsRequiringRemoval = new HashSet<Guid>();
if (changes == null)
{
// During initial population, we must manually account for the fact that our original query was done on an async thread.
@ -314,67 +317,80 @@ namespace osu.Game.Screens.Select
foreach (var id in realmSets)
{
if (!root.BeatmapSetsByID.ContainsKey(id))
updateBeatmapSet(realm.Realm.Find<BeatmapSetInfo>(id)!.Detach());
setsRequiringUpdate.Add(realm.Realm.Find<BeatmapSetInfo>(id)!.Detach());
}
foreach (var id in root.BeatmapSetsByID.Keys)
{
if (!realmSets.Contains(id))
removeBeatmapSet(id);
setsRequiringRemoval.Add(id);
}
}
else
{
foreach (int i in changes.NewModifiedIndices)
setsRequiringUpdate.Add(sender[i].Detach());
invalidateAfterChange();
BeatmapSetsLoaded = true;
return;
foreach (int i in changes.InsertedIndices)
setsRequiringUpdate.Add(sender[i].Detach());
}
foreach (int i in changes.NewModifiedIndices)
updateBeatmapSet(sender[i].Detach());
foreach (int i in changes.InsertedIndices)
updateBeatmapSet(sender[i].Detach());
if (changes.DeletedIndices.Length > 0 && SelectedBeatmapInfo != null)
// All local operations must be scheduled.
//
// If we don't schedule, beatmaps getting changed while song select is suspended (ie. last played being updated)
// will cause unexpected sounds and operations to occur in the background.
Schedule(() =>
{
// If SelectedBeatmapInfo is non-null, the set should also be non-null.
Debug.Assert(SelectedBeatmapSet != null);
// To handle the beatmap update flow, attempt to track selection changes across delete-insert transactions.
// When an update occurs, the previous beatmap set is either soft or hard deleted.
// Check if the current selection was potentially deleted by re-querying its validity.
bool selectedSetMarkedDeleted = sender.Realm.Find<BeatmapSetInfo>(SelectedBeatmapSet.ID)?.DeletePending != false;
int[] modifiedAndInserted = changes.NewModifiedIndices.Concat(changes.InsertedIndices).ToArray();
if (selectedSetMarkedDeleted && modifiedAndInserted.Any())
try
{
// If it is no longer valid, make the bold assumption that an updated version will be available in the modified/inserted indices.
// This relies on the full update operation being in a single transaction, so please don't change that.
foreach (int i in modifiedAndInserted)
foreach (var set in setsRequiringRemoval)
removeBeatmapSet(set);
foreach (var set in setsRequiringUpdate)
updateBeatmapSet(set);
if (changes?.DeletedIndices.Length > 0 && SelectedBeatmapInfo != null)
{
var beatmapSetInfo = sender[i];
// If SelectedBeatmapInfo is non-null, the set should also be non-null.
Debug.Assert(SelectedBeatmapSet != null);
foreach (var beatmapInfo in beatmapSetInfo.Beatmaps)
// To handle the beatmap update flow, attempt to track selection changes across delete-insert transactions.
// When an update occurs, the previous beatmap set is either soft or hard deleted.
// Check if the current selection was potentially deleted by re-querying its validity.
bool selectedSetMarkedDeleted = realm.Run(r => r.Find<BeatmapSetInfo>(SelectedBeatmapSet.ID)?.DeletePending != false);
if (selectedSetMarkedDeleted && setsRequiringUpdate.Any())
{
if (!((IBeatmapMetadataInfo)beatmapInfo.Metadata).Equals(SelectedBeatmapInfo.Metadata))
continue;
// Best effort matching. We can't use ID because in the update flow a new version will get its own GUID.
if (beatmapInfo.DifficultyName == SelectedBeatmapInfo.DifficultyName)
// If it is no longer valid, make the bold assumption that an updated version will be available in the modified/inserted indices.
// This relies on the full update operation being in a single transaction, so please don't change that.
foreach (var set in setsRequiringUpdate)
{
SelectBeatmap(beatmapInfo);
return;
foreach (var beatmapInfo in set.Beatmaps)
{
if (!((IBeatmapMetadataInfo)beatmapInfo.Metadata).Equals(SelectedBeatmapInfo.Metadata))
continue;
// Best effort matching. We can't use ID because in the update flow a new version will get its own GUID.
if (beatmapInfo.DifficultyName == SelectedBeatmapInfo.DifficultyName)
{
SelectBeatmap(beatmapInfo);
return;
}
}
}
// If a direct selection couldn't be made, it's feasible that the difficulty name (or beatmap metadata) changed.
// Let's attempt to follow set-level selection anyway.
SelectBeatmap(setsRequiringUpdate.First().Beatmaps.First());
}
}
// If a direct selection couldn't be made, it's feasible that the difficulty name (or beatmap metadata) changed.
// Let's attempt to follow set-level selection anyway.
SelectBeatmap(sender[modifiedAndInserted.First()].Beatmaps.First());
}
}
invalidateAfterChange();
finally
{
BeatmapSetsLoaded = true;
invalidateAfterChange();
}
});
}
private void beatmapsChanged(IRealmCollection<BeatmapInfo> sender, ChangeSet? changes)