From a3e09a1c31f5158e615fbacfa43de5c7c868693e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 30 Sep 2025 11:32:15 +0200 Subject: [PATCH] Fix song select carousel sometimes teleporting on beatmap set deletion Closes https://github.com/ppy/osu/issues/35010. The issue here does not reproduce consistently, and is more or less random in presentation. That said, using a large enough realm database more or less ensures that the issue will present itself (in testing on a large realm db, the failure rate is around ~50%). This actually regressed in https://github.com/ppy/osu/pull/34842. The core failure in this case is here: https://github.com/ppy/osu/blob/fd412618dba7399b1778b0902b73ffbae21a2399/osu.Game/Screens/SelectV2/BeatmapCarousel.cs#L161 The `CheckModelEquality()` call above is comparing two `BeatmapInfo`s, but a84c364e44d1e1f89c209da4f29e0ab524b3e2ab changed the `BeatmapInfo`-comparing path of `CheckModelEquality()` to use `GroupedBeatmap` instead. Due to this, `CheckModelEquality()` falls back to reference equality comparison for `BeatmapInfo`s. When that reference comparison fails, the carousel stops detecting that the current selection was deleted from under it correctly, and therefore the proximity-based selection logic never runs. Due to the human-obvious mechanism of failure and relatively easy manual reproduction I've decided not to try and add tests for this, as they are likely to take a long time to write due to the mechanism of failure being incorrect use of reference equality specifically. That said, I can try on request. --- osu.Game/Screens/SelectV2/BeatmapCarousel.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Screens/SelectV2/BeatmapCarousel.cs b/osu.Game/Screens/SelectV2/BeatmapCarousel.cs index d2b18b2f33..a5e187fed2 100644 --- a/osu.Game/Screens/SelectV2/BeatmapCarousel.cs +++ b/osu.Game/Screens/SelectV2/BeatmapCarousel.cs @@ -816,6 +816,11 @@ namespace osu.Game.Screens.SelectV2 if (x is GroupedBeatmap groupedBeatmapX && y is GroupedBeatmap groupedBeatmapY) return groupedBeatmapX.Equals(groupedBeatmapY); + // `BeatmapInfo` is no longer used directly in carousel items, but in rare circumstances still is used for model equality comparisons + // (see `beatmapSetsChanged()` deletion handling logic, which aims to find a beatmap close to the just-deleted one, disregarding grouping concerns) + if (x is BeatmapInfo beatmapInfoX && y is BeatmapInfo beatmapInfoY) + return beatmapInfoX.Equals(beatmapInfoY); + if (x is GroupDefinition groupX && y is GroupDefinition groupY) return groupX.Equals(groupY);