Skip to content

Commit

Permalink
Fix selection being dropped when changing carousel sort mode from dif…
Browse files Browse the repository at this point in the history
…ficulty sort

Closes ppy#29738.

This "regressed" in ppy#29639, but if I
didn't know better, I'd go as far as saying that this looks like a .NET
bug, because the fact that PR broke it looks not sane.

The TL;DR on this is that before the pull in question, the offending
`.Contains()` check that this commit modifies was called on a
`List<BeatmapSetInfo>`. The pull changed the collection type to
`BeatmapSetInfo[]`. That said, the call is a LINQ call, so all good,
right?

Not really. First off, the default overload resolution order means that
the previous code would call `List<T>.Contains()`, and not
`Enumerable.Contains<T>()`. Then again, why would that matter? In both
cases `T` is still `BeatmapSetInfo`, right? Well... about that...

It is difficult to tell for sure what precisely is happening here,
because of what looks like runtime magic. The end *symptom* is that the
old code ended up calling `Array<BeatmapSetInfo>.IndexOf()`, and the new
code ends up calling... `Array<object>.IndexOf()`.

So while yes, `BeatmapSetInfo` implements `IEquatable` and
the expectation would be that `Equals<BeatmapSetInfo>()` should be
getting called, the type elision to `object` means that we're back to
reference equality semantics, because that's what
`EqualityComparer.Default<object>` is.

A five-minute github search across dotnet/runtime yields this:

	https://github.com/dotnet/runtime/blob/c4792a228ea36792b90f87ddf7fce2477e827822/src/coreclr/vm/array.cpp#L984-L990

Now again, if I didn't know better, I'd see that "OPTIMIZATION:"
comment, see what transpired in this scenario, and call that
optimisation invalid, because it changes semantics. But I *probably*
know that the dotnet team knows better and am probably just going to
take it for what it is, because blame on that code looks to be years
old and it's probably not a new behaviour. (I haven't tested empirically
if it is.)

Instead the fix is just to tell the `.Contains()` method to use the
correct comparer.
  • Loading branch information
bdach committed Sep 8, 2024
1 parent 134bcc8 commit cefbc76
Showing 1 changed file with 1 addition and 1 deletion.
2 changes: 1 addition & 1 deletion osu.Game/Screens/Select/BeatmapCarousel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ private void loadNewRoot()
// We'll catch up on changes via subscriptions anyway.
BeatmapSetInfo[] loadableSets = detachedBeatmapSets!.ToArray();

if (selectedBeatmapSet != null && !loadableSets.Contains(selectedBeatmapSet.BeatmapSet))
if (selectedBeatmapSet != null && !loadableSets.Contains(selectedBeatmapSet.BeatmapSet, EqualityComparer<BeatmapSetInfo>.Default))
selectedBeatmapSet = null;

var selectedBeatmapBefore = selectedBeatmap?.BeatmapInfo;
Expand Down

0 comments on commit cefbc76

Please sign in to comment.