diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneTopLocalRank.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneTopLocalRank.cs
index 79baae53e8..93b9efed6a 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneTopLocalRank.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneTopLocalRank.cs
@@ -10,6 +10,8 @@ using osu.Framework.Graphics;
using osu.Framework.Platform;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
+using osu.Game.Online.API;
+using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets;
using osu.Game.Scoring;
using osu.Game.Screens.Select.Carousel;
@@ -161,5 +163,53 @@ namespace osu.Game.Tests.Visual.SongSelect
AddUntilStep("SS rank displayed", () => topLocalRank.DisplayedRank == ScoreRank.X);
}
+
+ [Test]
+ public void TestGuestScore()
+ {
+ AddStep("Add score for guest user", () =>
+ {
+ var testScoreInfo = TestResources.CreateTestScoreInfo(importedBeatmap);
+
+ testScoreInfo.User = new GuestUser();
+ testScoreInfo.Rank = ScoreRank.B;
+
+ scoreManager.Import(testScoreInfo);
+ });
+
+ AddUntilStep("B rank displayed", () => topLocalRank.DisplayedRank, () => Is.EqualTo(ScoreRank.B));
+ }
+
+ [Test]
+ public void TestUnknownUserScore()
+ {
+ AddStep("Add score for unknown user", () =>
+ {
+ var testScoreInfo = TestResources.CreateTestScoreInfo(importedBeatmap);
+
+ testScoreInfo.User = new APIUser { Username = "AAA", };
+ testScoreInfo.Rank = ScoreRank.S;
+
+ scoreManager.Import(testScoreInfo);
+ });
+
+ AddUntilStep("S rank displayed", () => topLocalRank.DisplayedRank, () => Is.EqualTo(ScoreRank.S));
+ }
+
+ [Test]
+ public void TestAnotherUserScore()
+ {
+ AddStep("Add score for not-current user", () =>
+ {
+ var testScoreInfo = TestResources.CreateTestScoreInfo(importedBeatmap);
+
+ testScoreInfo.User = new APIUser { Username = "notme", Id = 43, };
+ testScoreInfo.Rank = ScoreRank.S;
+
+ scoreManager.Import(testScoreInfo);
+ });
+
+ AddUntilStep("No rank displayed", () => topLocalRank.DisplayedRank, () => Is.Null);
+ }
}
}
diff --git a/osu.Game/Scoring/ScoreInfoExtensions.cs b/osu.Game/Scoring/ScoreInfoExtensions.cs
index dd08326742..2eec0399d6 100644
--- a/osu.Game/Scoring/ScoreInfoExtensions.cs
+++ b/osu.Game/Scoring/ScoreInfoExtensions.cs
@@ -5,8 +5,11 @@ using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
+using osu.Game.Models;
+using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Select.Leaderboards;
+using Realms;
namespace osu.Game.Scoring
{
@@ -64,5 +67,23 @@ namespace osu.Game.Scoring
/// The to compute the maximum achievable combo for.
/// The maximum achievable combo.
public static int GetMaximumAchievableCombo(this ScoreInfo score) => score.MaximumStatistics.Where(kvp => kvp.Key.AffectsCombo()).Sum(kvp => kvp.Value);
+
+ ///
+ /// Performs a realm filter that returns all scores that belong to the user with the given .
+ /// (for guests) is supported.
+ ///
+ ///
+ /// All guest scores (with user ID of ),
+ /// as well as scores of unknown provenance (with default user ID of 1, see ),
+ /// will be treated as if they belong to the local user.
+ /// This may not be necessarily considered fully correct in some circumstances, but in most cases it is the desired effect.
+ ///
+ public static IQueryable GetAllLocalScoresForUser(this Realm realm, int? userId)
+ {
+ return realm.All()
+ .Filter($@"({nameof(ScoreInfo.User)}.{nameof(RealmUser.OnlineID)} == $0 || {nameof(ScoreInfo.User)}.{nameof(RealmUser.OnlineID)} <= 1)"
+ + $@" && {nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.Hash)} == {nameof(ScoreInfo.BeatmapHash)}"
+ + $@" && {nameof(ScoreInfo.DeletePending)} == false", userId);
+ }
}
}
diff --git a/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs b/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs
index 3c1aec745d..5c5c814c5b 100644
--- a/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs
+++ b/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs
@@ -19,7 +19,6 @@ using osu.Game.Extensions;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
-using osu.Game.Models;
using osu.Game.Online.API;
using osu.Game.Online.Placeholders;
using osu.Game.Scoring;
@@ -246,11 +245,8 @@ namespace osu.Game.Screens.Ranking.Statistics
// We may want to iterate on the following conditions further in the future
var localUserScore = AchievedScore ?? realm.Run(r =>
- r.All()
- .Filter($@"{nameof(ScoreInfo.User)}.{nameof(RealmUser.OnlineID)} == $0"
- + $@" && {nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.ID)} == $1"
- + $@" && {nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.Hash)} == {nameof(ScoreInfo.BeatmapHash)}"
- + $@" && {nameof(ScoreInfo.DeletePending)} == false", api.LocalUser.Value.Id, newScore.BeatmapInfo.ID, newScore.BeatmapInfo.Ruleset.ShortName)
+ r.GetAllLocalScoresForUser(api.LocalUser.Value.Id)
+ .Filter($@"{nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.ID)} == $0", newScore.BeatmapInfo.ID)
.AsEnumerable()
.OrderByDescending(score => score.Ruleset.MatchesOnlineID(newScore.BeatmapInfo.Ruleset))
.ThenByDescending(score => score.Rank)
diff --git a/osu.Game/Screens/Select/Carousel/TopLocalRank.cs b/osu.Game/Screens/Select/Carousel/TopLocalRank.cs
index da9661f702..6f1f2e8370 100644
--- a/osu.Game/Screens/Select/Carousel/TopLocalRank.cs
+++ b/osu.Game/Screens/Select/Carousel/TopLocalRank.cs
@@ -9,7 +9,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Database;
-using osu.Game.Models;
using osu.Game.Online.API;
using osu.Game.Online.Leaderboards;
using osu.Game.Rulesets;
@@ -59,12 +58,9 @@ namespace osu.Game.Screens.Select.Carousel
{
scoreSubscription?.Dispose();
scoreSubscription = realm.RegisterForNotifications(r =>
- r.All()
- .Filter($"{nameof(ScoreInfo.User)}.{nameof(RealmUser.OnlineID)} == $0"
- + $" && {nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.ID)} == $1"
- + $" && {nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.Hash)} == {nameof(ScoreInfo.BeatmapHash)}"
- + $" && {nameof(ScoreInfo.Ruleset)}.{nameof(RulesetInfo.ShortName)} == $2"
- + $" && {nameof(ScoreInfo.DeletePending)} == false", api.LocalUser.Value.Id, beatmapInfo.ID, ruleset.Value.ShortName),
+ r.GetAllLocalScoresForUser(api.LocalUser.Value.Id)
+ .Filter($@"{nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.ID)} == $0"
+ + $" && {nameof(ScoreInfo.Ruleset)}.{nameof(RulesetInfo.ShortName)} == $1", beatmapInfo.ID, ruleset.Value.ShortName),
localScoresChanged);
}, true);
diff --git a/osu.Game/Screens/SelectV2/BeatmapCarousel.cs b/osu.Game/Screens/SelectV2/BeatmapCarousel.cs
index ab520525a5..952e545d0a 100644
--- a/osu.Game/Screens/SelectV2/BeatmapCarousel.cs
+++ b/osu.Game/Screens/SelectV2/BeatmapCarousel.cs
@@ -25,7 +25,6 @@ using osu.Game.Database;
using osu.Game.Graphics;
using osu.Game.Graphics.Carousel;
using osu.Game.Graphics.UserInterface;
-using osu.Game.Models;
using osu.Game.Rulesets;
using osu.Game.Scoring;
using osu.Game.Screens.Select;
@@ -758,11 +757,8 @@ namespace osu.Game.Screens.SelectV2
{
var topRankMapping = new Dictionary();
- var allLocalScores = r.All()
- .Filter($"{nameof(ScoreInfo.User)}.{nameof(RealmUser.OnlineID)} == $0"
- + $" && {nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.Hash)} == {nameof(ScoreInfo.BeatmapHash)}"
- + $" && {nameof(ScoreInfo.Ruleset)}.{nameof(RulesetInfo.ShortName)} == $1"
- + $" && {nameof(ScoreInfo.DeletePending)} == false", criteria.LocalUserId, criteria.Ruleset?.ShortName)
+ var allLocalScores = r.GetAllLocalScoresForUser(criteria.LocalUserId)
+ .Filter($@"{nameof(ScoreInfo.Ruleset)}.{nameof(RulesetInfo.ShortName)} == $0", criteria.Ruleset?.ShortName)
.OrderByDescending(s => s.TotalScore)
.ThenBy(s => s.Date);
diff --git a/osu.Game/Screens/SelectV2/PanelLocalRankDisplay.cs b/osu.Game/Screens/SelectV2/PanelLocalRankDisplay.cs
index 273f995794..c72835144f 100644
--- a/osu.Game/Screens/SelectV2/PanelLocalRankDisplay.cs
+++ b/osu.Game/Screens/SelectV2/PanelLocalRankDisplay.cs
@@ -9,7 +9,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Database;
-using osu.Game.Models;
using osu.Game.Online.API;
using osu.Game.Online.Leaderboards;
using osu.Game.Rulesets;
@@ -78,12 +77,9 @@ namespace osu.Game.Screens.SelectV2
return;
scoreSubscription = realm.RegisterForNotifications(r =>
- r.All()
- .Filter($"{nameof(ScoreInfo.User)}.{nameof(RealmUser.OnlineID)} == $0"
- + $" && {nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.ID)} == $1"
- + $" && {nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.Hash)} == {nameof(ScoreInfo.BeatmapHash)}"
- + $" && {nameof(ScoreInfo.Ruleset)}.{nameof(RulesetInfo.ShortName)} == $2"
- + $" && {nameof(ScoreInfo.DeletePending)} == false", api.LocalUser.Value.Id, beatmap.ID, ruleset.Value.ShortName),
+ r.GetAllLocalScoresForUser(api.LocalUser.Value.Id)
+ .Filter($@"{nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.ID)} == $0"
+ + $" && {nameof(ScoreInfo.Ruleset)}.{nameof(RulesetInfo.ShortName)} == $1", beatmap.ID, ruleset.Value.ShortName),
localScoresChanged);
}