1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-13 19:54:15 +08:00

Merge pull request #32579 from bdach/allow-tagging-if-local-score-present

Allow tagging already played beatmaps without playing another time
This commit is contained in:
Dean Herbert
2025-03-26 19:32:53 +09:00
committed by GitHub
Unverified
2 changed files with 140 additions and 21 deletions
@@ -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<AudioManager>(), Resources, dependencies.Get<GameHost>(), 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<BeatmapInfo> 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
@@ -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<ScoreInfo>()
.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)