diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneStatisticsPanel.cs b/osu.Game.Tests/Visual/Ranking/TestSceneStatisticsPanel.cs index f82b32167c..814c0519a3 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneStatisticsPanel.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneStatisticsPanel.cs @@ -8,11 +8,15 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; +using osu.Framework.Platform; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Graphics; @@ -27,6 +31,7 @@ using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu; using osu.Game.Scoring; using osu.Game.Screens.Ranking.Statistics; using osu.Game.Rulesets.Osu.Objects; @@ -43,6 +48,22 @@ namespace osu.Game.Tests.Visual.Ranking { private DummyAPIAccess dummyAPI => (DummyAPIAccess)API; + private ScoreManager scoreManager = null!; + private RulesetStore rulesetStore = null!; + private BeatmapManager beatmapManager = null!; + + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) + { + var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); + + dependencies.Cache(rulesetStore = new RealmRulesetStore(Realm)); + dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, Realm, null, dependencies.Get(), Resources, dependencies.Get(), Beatmap.Default)); + dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, Realm, API)); + Dependencies.Cache(Realm); + + return dependencies; + } + [Test] public void TestScoreWithPositionStatistics() { @@ -163,6 +184,24 @@ namespace osu.Game.Tests.Visual.Ranking { var score = TestResources.CreateTestScoreInfo(); + setUpTaggingRequests(() => score.BeatmapInfo); + AddStep("load panel", () => + { + Child = new PopoverContainer + { + RelativeSizeAxes = Axes.Both, + Child = new StatisticsPanel + { + RelativeSizeAxes = Axes.Both, + State = { Value = Visibility.Visible }, + Score = { Value = score }, + AchievedScore = score, + } + }; + }); + } + + private void setUpTaggingRequests(Func beatmap) => AddStep("set up network requests", () => { dummyAPI.HandleRequest = request => @@ -176,7 +215,11 @@ namespace osu.Game.Tests.Visual.Ranking Tags = [ new APITag { Id = 1, Name = "tech", Description = "Tests uncommon skills.", }, - new APITag { Id = 2, Name = "alt", Description = "Colloquial term for maps which use rhythms that encourage the player to alternate notes. Typically distinct from burst or stream maps.", }, + new APITag + { + Id = 2, Name = "alt", + Description = "Colloquial term for maps which use rhythms that encourage the player to alternate notes. Typically distinct from burst or stream maps.", + }, new APITag { Id = 3, Name = "aim", Description = "Category for difficulty relating to cursor movement.", }, new APITag { Id = 4, Name = "tap", Description = "Category for difficulty relating to tapping input.", }, ] @@ -186,7 +229,7 @@ namespace osu.Game.Tests.Visual.Ranking case GetBeatmapSetRequest getBeatmapSetRequest: { - var beatmapSet = CreateAPIBeatmapSet(score.BeatmapInfo); + var beatmapSet = CreateAPIBeatmapSet(beatmap.Invoke()); beatmapSet.Beatmaps.Single().TopTags = [ new APIBeatmapTag { TagId = 3, VoteCount = 9 }, @@ -206,21 +249,6 @@ namespace osu.Game.Tests.Visual.Ranking return false; }; }); - AddStep("load panel", () => - { - Child = new PopoverContainer - { - RelativeSizeAxes = Axes.Both, - Child = new StatisticsPanel - { - RelativeSizeAxes = Axes.Both, - State = { Value = Visibility.Visible }, - Score = { Value = score }, - AchievedScore = score, - } - }; - }); - } [Test] public void TestTaggingWhenRankTooLow() @@ -266,6 +294,78 @@ namespace osu.Game.Tests.Visual.Ranking }); } + [Test] + public void TestTaggingInteractionWithLocalScores() + { + BeatmapInfo beatmapInfo = null!; + + AddStep(@"Import beatmap", () => + { + beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely(); + beatmapInfo = beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps.First(); + }); + + AddStep("import bad score", () => + { + var score = TestResources.CreateTestScoreInfo(); + score.BeatmapInfo = beatmapInfo; + score.BeatmapHash = beatmapInfo.Hash; + score.Ruleset = beatmapInfo.Ruleset; + score.Rank = ScoreRank.D; + score.User = API.LocalUser.Value; + scoreManager.Import(score); + }); + + AddStep("import score by another user", () => + { + var score = TestResources.CreateTestScoreInfo(); + score.BeatmapInfo = beatmapInfo; + score.BeatmapHash = beatmapInfo.Hash; + score.Ruleset = beatmapInfo.Ruleset; + score.Rank = ScoreRank.D; + score.User = new APIUser { Username = "notme", Id = 5678 }; + scoreManager.Import(score); + }); + + AddStep("import convert score", () => + { + var score = TestResources.CreateTestScoreInfo(); + score.BeatmapInfo = beatmapInfo; + score.BeatmapHash = beatmapInfo.Hash; + score.Ruleset = new OsuRuleset().RulesetInfo; + score.User = API.LocalUser.Value; + scoreManager.Import(score); + }); + + AddStep("import correct score", () => + { + var score = TestResources.CreateTestScoreInfo(); + score.BeatmapInfo = beatmapInfo; + score.BeatmapHash = beatmapInfo.Hash; + score.Ruleset = beatmapInfo.Ruleset; + score.User = API.LocalUser.Value; + scoreManager.Import(score); + }); + + setUpTaggingRequests(() => beatmapInfo); + AddStep("load panel", () => + { + var score = TestResources.CreateTestScoreInfo(); + score.BeatmapInfo = beatmapInfo; + + Child = new PopoverContainer + { + RelativeSizeAxes = Axes.Both, + Child = new StatisticsPanel + { + RelativeSizeAxes = Axes.Both, + State = { Value = Visibility.Visible }, + Score = { Value = score }, + } + }; + }); + } + private void loadPanel(ScoreInfo score) => AddStep("load panel", () => { Child = new StatisticsPanel diff --git a/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs b/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs index 9ead9ce91c..ad868e58f0 100644 --- a/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs +++ b/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs @@ -14,14 +14,18 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; using osu.Game.Beatmaps; +using osu.Game.Database; +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; using osu.Game.Screens.Ranking.Statistics.User; using osuTK; +using Realms; namespace osu.Game.Screens.Ranking.Statistics { @@ -43,6 +47,9 @@ namespace osu.Game.Screens.Ranking.Statistics [Resolved] private BeatmapManager beatmapManager { get; set; } = null!; + [Resolved] + private RealmAccess realm { get; set; } = null!; + [Resolved] private IAPIProvider api { get; set; } = null!; @@ -231,17 +238,29 @@ namespace osu.Game.Screens.Ranking.Statistics }); } - if (AchievedScore != null - && newScore.BeatmapInfo!.OnlineID > 0 + if (newScore.BeatmapInfo!.OnlineID > 0 && api.IsLoggedIn) { string? preventTaggingReason = null; // We may want to iterate on the following conditions further in the future - if (AchievedScore.Ruleset.OnlineID != AchievedScore.BeatmapInfo!.Ruleset.OnlineID) + 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) + .AsEnumerable() + .OrderByDescending(score => score.Ruleset.MatchesOnlineID(newScore.BeatmapInfo.Ruleset)) + .ThenByDescending(score => score.Rank) + .FirstOrDefault()); + + if (localUserScore == null) + preventTaggingReason = "Play the beatmap to contribute to beatmap tags!"; + else if (localUserScore.Ruleset.OnlineID != newScore.BeatmapInfo!.Ruleset.OnlineID) preventTaggingReason = "Play the beatmap in its original ruleset to contribute to beatmap tags!"; - else if (AchievedScore.Rank < ScoreRank.C) + else if (localUserScore.Rank < ScoreRank.C) preventTaggingReason = "Set a better score to contribute to beatmap tags!"; if (preventTaggingReason == null)