-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Implement "rank achieved" grouping mode #34548
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
pretty scary since users will hit this number in no time |
|
Sorry, I meant it will take too long for the test to import all those beatmaps and scores. I've still waited patiently and tested 12k scores, it had almost no effect on the filter time. |
8d1ae6e to
c23856c
Compare
| #region Rank Achieved grouping | ||
|
|
||
| [Test] | ||
| public void TestRankAchievedGrouping() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not too sure why tests have started to be added to this class and not the non-test-scene one. Did you run into an issue which meant you couldn't put them there..?
It's also literally the only reason the realm instance is provided as nullable. I don't like making arguments nullable just for tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've set up this test scene to take away the complications of setting up a local temporary database and API access, I've also stated that in the xmldoc of the test scene class.
This test scene also helps confirming that the grouping filter is correctly hooked up with those components (especially cases like a user logging out or collections being modified, which isn't necessarily identified by the grouping filter class itself, but rather signaled by FilterControl / components of the screen).
osu.Game/Beatmaps/BeatmapInfo.cs
Outdated
|
|
||
| public bool Equals(IBeatmapInfo? other) => other is BeatmapInfo b && Equals(b); | ||
|
|
||
| public override int GetHashCode() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this..? Reading on I can see why this might have been added, but I do not think it's a good idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've went with this approach following BeatmapSetInfo and the SetItems dictionary set up in the grouping filter, which uses BeatmapSetInfo as a key.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That dictionary is a bit different because usages need to read the BeatmapSetInfo content. That's not the case for this private usage so let's not. It's just extra complexity to think about. I see a class being used as a dinctionary key and my brain goes into panic.
| new BeatmapCarouselFilterMatching(() => Criteria!), | ||
| new BeatmapCarouselFilterSorting(() => Criteria!), | ||
| grouping = new BeatmapCarouselFilterGrouping(() => Criteria!, () => detachedCollections()) | ||
| grouping = new BeatmapCarouselFilterGrouping(() => Criteria!, () => realm) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why was this moved?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since I'm including realm directly, I thought there's not really any reason to still have the detachedCollections func. If you don't think that's the correct approach and the realm func will eventually be removed, then I'll revert it.
| private Sample? spinSample; | ||
| private Sample? randomSelectSample; | ||
|
|
||
| private Func<List<BeatmapCollection>> detachedCollections = null!; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why?
| { | ||
| var allLocalScores = r.All<ScoreInfo>() | ||
| .Filter($"{nameof(ScoreInfo.User)}.{nameof(RealmUser.OnlineID)} == $0" | ||
| + $" && {nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.Hash)} == {nameof(ScoreInfo.BeatmapHash)}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's this conditional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Carried across from TopLocalRank (also exists in LeaderboardManager), it seems to be for discarding scores of a beatmap version that doesn't match the current one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inline comment mentioning this is the case, please. I can't read brains.
| + $" && {nameof(ScoreInfo.Ruleset)}.{nameof(RulesetInfo.ShortName)} == $1" | ||
| + $" && {nameof(ScoreInfo.DeletePending)} == false", criteria.LocalUserId, criteria.Ruleset?.ShortName) | ||
| .OrderByDescending(s => s.TotalScore) | ||
| .ThenBy(s => s.Date); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Date has limited precision. Online score id should be preferred whenever available.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also carried from https://github.com/ppy/osu/blob/cb354685ca323f4e429e60617077ed5a5175ee3d/osu.Game/Screens/Select/Carousel/TopLocalRank.cs#L78-L77, I can switch it to fall back to ID but we should probably update that code as well? If so, should I bundle it in this PR or in a separate one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update both places, and add a comment inline here mentioning the important fact that this code is shared.
| { | ||
| Debug.Assert(score.BeatmapInfo != null); | ||
|
|
||
| if (topRankMapping.ContainsKey(score.BeatmapInfo)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason to not use hash here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was debating between hash and the database ID, but the latter seemed to represent the beatmap in the local user's database better. This seems to be an either/or situation to me?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue I have here is using a model class as the key. I don't care if you use ID or hash, but please use it directly.
Rank animation is played for new panels when scrolling down, showing the previous rank belonging to the previous beatmap assigned to that specific panel instance.
49bb206 to
8239294
Compare
| #region Benchmarks | ||
|
|
||
| [Test] | ||
| public void TestPerformance() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just for your own improvement, when i asked for a benchmark i hoped to be able to run something headless via nunit.
FilterCriteria.LocalUserIdRFC. This does not cover online scores not present in the database. Implementation is done as discussed, using a dictionary to achieve O(n) time complexity, as proposed by @peppy.
Performance is tested through
TestSceneSongSelectGrouping.TestPerformance, which loads a total of 10k difficulties and 1.2k scores to a temporary realm database (going any higher takes too long to load). I've also confirmed the generated databases are in a temporary folder (/var/folders/...) since the database grows up to 7MB to contain all of the imported models.Code-wise this PR violates rules to achieve the end result, by including realm directly in the grouping class. I think we're at the point where it doesn't really matter if realm lookups are done directly inside the grouping class or encapsulated by a delegate, and if delegates are still favoured then we'll need a delegate which wraps
realm.Run, and the signature for that won't be pretty (I proposed something similar for collection grouping before and it was faced with disgust).I've also took the time to tidy up
TestSceneSongSelectGroupingas it keeps containing more realm/API-based tests.