mirror of
https://github.com/ppy/osu.git
synced 2026-05-13 20:33:35 +08:00
Allow tagging already played beatmaps without playing another time
Addresses https://github.com/ppy/osu/discussions/32568#discussioncomment-12610577. No changes in criteria (yet?), just allowing locally imported plays to count the same way as full beatmap completion does. The test scene is a bit rough / semi-manual but dealing with score imports is a bit of a pain in general. The way to semi-manually test with the test scene is to import a subset of scores, then recreate the statistics panel, and observe behaviour. I'm not sure it's worth it to be putting subscriptions in there, so the full recreation of the panel is necessary.
This commit is contained in:
@@ -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,79 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTaggingInteractionWithLocalScores()
|
||||
{
|
||||
BeatmapInfo beatmapInfo = null!;
|
||||
string originalHash = string.Empty;
|
||||
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user