From 96dd7b3333014cb5e8e404d129b163de1f72f6e8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 30 Oct 2023 15:44:16 +0900 Subject: [PATCH 1/3] Update the last played date of a beatmap when importing a replay by the local user --- osu.Game.Tests/ImportTest.cs | 4 ++ osu.Game.Tests/Scores/IO/ImportScoreTest.cs | 59 +++++++++++++++++++++ osu.Game/Online/API/DummyAPIAccess.cs | 2 +- osu.Game/Scoring/ScoreImporter.cs | 3 ++ 4 files changed, 67 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/ImportTest.cs b/osu.Game.Tests/ImportTest.cs index 3f0f8a4f14..27b8d3f21e 100644 --- a/osu.Game.Tests/ImportTest.cs +++ b/osu.Game.Tests/ImportTest.cs @@ -9,6 +9,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions; using osu.Framework.Platform; using osu.Game.Database; +using osu.Game.Online.API; using osu.Game.Tests.Resources; namespace osu.Game.Tests @@ -46,12 +47,15 @@ namespace osu.Game.Tests public partial class TestOsuGameBase : OsuGameBase { public RealmAccess Realm => Dependencies.Get(); + public new IAPIProvider API => base.API; private readonly bool withBeatmap; public TestOsuGameBase(bool withBeatmap) { this.withBeatmap = withBeatmap; + + base.API = new DummyAPIAccess(); } [BackgroundDependencyLoader] diff --git a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs index 892ceea185..d1bacaaf69 100644 --- a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs +++ b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs @@ -12,6 +12,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions; using osu.Framework.Platform; using osu.Game.IO.Archives; +using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; @@ -67,6 +68,64 @@ namespace osu.Game.Tests.Scores.IO } } + [TestCase(false)] + [TestCase(true)] + public void TestLastPlayedUpdate(bool isLocalUser) + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + { + try + { + var osu = LoadOsuIntoHost(host, true); + + if (!isLocalUser) + osu.API.Logout(); + + var beatmap = BeatmapImportHelper.LoadOszIntoOsu(osu, TestResources.GetQuickTestBeatmapForImport()).GetResultSafely(); + var beatmapInfo = beatmap.Beatmaps.First(); + + DateTimeOffset replayDate = DateTimeOffset.Now; + + var toImport = new ScoreInfo + { + Rank = ScoreRank.B, + TotalScore = 987654, + Accuracy = 0.8, + MaxCombo = 500, + Combo = 250, + User = new APIUser + { + Username = "Test user", + Id = DummyAPIAccess.DUMMY_USER_ID, + }, + Date = replayDate, + OnlineID = 12345, + Ruleset = new OsuRuleset().RulesetInfo, + BeatmapInfo = beatmapInfo + }; + + var imported = LoadScoreIntoOsu(osu, toImport); + + Assert.AreEqual(toImport.Rank, imported.Rank); + Assert.AreEqual(toImport.TotalScore, imported.TotalScore); + Assert.AreEqual(toImport.Accuracy, imported.Accuracy); + Assert.AreEqual(toImport.MaxCombo, imported.MaxCombo); + Assert.AreEqual(toImport.User.Username, imported.User.Username); + Assert.AreEqual(toImport.Date, imported.Date); + Assert.AreEqual(toImport.OnlineID, imported.OnlineID); + + if (isLocalUser) + Assert.That(imported.BeatmapInfo!.LastPlayed, Is.EqualTo(replayDate)); + else + Assert.That(imported.BeatmapInfo!.LastPlayed, Is.Null); + } + finally + { + host.Exit(); + } + } + } + [Test] public void TestImportMods() { diff --git a/osu.Game/Online/API/DummyAPIAccess.cs b/osu.Game/Online/API/DummyAPIAccess.cs index 2764247f5c..d585124db6 100644 --- a/osu.Game/Online/API/DummyAPIAccess.cs +++ b/osu.Game/Online/API/DummyAPIAccess.cs @@ -112,7 +112,7 @@ namespace osu.Game.Online.API LocalUser.Value = new APIUser { Username = username, - Id = 1001, + Id = DUMMY_USER_ID, }; state.Value = APIState.Online; diff --git a/osu.Game/Scoring/ScoreImporter.cs b/osu.Game/Scoring/ScoreImporter.cs index 7473d887c3..32a528f218 100644 --- a/osu.Game/Scoring/ScoreImporter.cs +++ b/osu.Game/Scoring/ScoreImporter.cs @@ -87,6 +87,9 @@ namespace osu.Game.Scoring if (!model.Ruleset.IsManaged) model.Ruleset = realm.Find(model.Ruleset.ShortName)!; + if (api.IsLoggedIn && api.LocalUser.Value.OnlineID == model.UserID && (model.BeatmapInfo.LastPlayed == null || model.Date > model.BeatmapInfo.LastPlayed)) + model.BeatmapInfo.LastPlayed = model.Date; + // These properties are known to be non-null, but these final checks ensure a null hasn't come from somewhere (or the refetch has failed). // Under no circumstance do we want these to be written to realm as null. ArgumentNullException.ThrowIfNull(model.BeatmapInfo); From c1c8e2968f1f620851c4e608043051f07b944d8a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 30 Oct 2023 15:46:09 +0900 Subject: [PATCH 2/3] Move operation to after user population --- osu.Game/Scoring/ScoreImporter.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Scoring/ScoreImporter.cs b/osu.Game/Scoring/ScoreImporter.cs index 32a528f218..b216c0897e 100644 --- a/osu.Game/Scoring/ScoreImporter.cs +++ b/osu.Game/Scoring/ScoreImporter.cs @@ -87,9 +87,6 @@ namespace osu.Game.Scoring if (!model.Ruleset.IsManaged) model.Ruleset = realm.Find(model.Ruleset.ShortName)!; - if (api.IsLoggedIn && api.LocalUser.Value.OnlineID == model.UserID && (model.BeatmapInfo.LastPlayed == null || model.Date > model.BeatmapInfo.LastPlayed)) - model.BeatmapInfo.LastPlayed = model.Date; - // These properties are known to be non-null, but these final checks ensure a null hasn't come from somewhere (or the refetch has failed). // Under no circumstance do we want these to be written to realm as null. ArgumentNullException.ThrowIfNull(model.BeatmapInfo); @@ -185,6 +182,12 @@ namespace osu.Game.Scoring base.PostImport(model, realm, parameters); populateUserDetails(model); + + Debug.Assert(model.BeatmapInfo != null); + + // This needs to be run after user detail population to ensure we have a valid user id. + if (api.IsLoggedIn && api.LocalUser.Value.OnlineID == model.UserID && (model.BeatmapInfo.LastPlayed == null || model.Date > model.BeatmapInfo.LastPlayed)) + model.BeatmapInfo.LastPlayed = model.Date; } /// From 0dfb41b7966de7823982f3f18b2a5adfa38ac1c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 30 Oct 2023 09:28:37 +0100 Subject: [PATCH 3/3] Add test coverage for not updating `LastPlayed` due to newer plays --- osu.Game.Tests/Scores/IO/ImportScoreTest.cs | 54 +++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs index d1bacaaf69..dd724d268e 100644 --- a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs +++ b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs @@ -11,6 +11,8 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Extensions; using osu.Framework.Platform; +using osu.Game.Beatmaps; +using osu.Game.Database; using osu.Game.IO.Archives; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; @@ -126,6 +128,58 @@ namespace osu.Game.Tests.Scores.IO } } + [Test] + public void TestLastPlayedNotUpdatedDueToNewerPlays() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + { + try + { + var osu = LoadOsuIntoHost(host, true); + + var beatmap = BeatmapImportHelper.LoadOszIntoOsu(osu, TestResources.GetQuickTestBeatmapForImport()).GetResultSafely(); + var beatmapInfo = beatmap.Beatmaps.First(); + + var realmAccess = osu.Dependencies.Get(); + realmAccess.Write(r => r.Find(beatmapInfo.ID)!.LastPlayed = new DateTimeOffset(2023, 10, 30, 0, 0, 0, TimeSpan.Zero)); + + var toImport = new ScoreInfo + { + Rank = ScoreRank.B, + TotalScore = 987654, + Accuracy = 0.8, + MaxCombo = 500, + Combo = 250, + User = new APIUser + { + Username = "Test user", + Id = DummyAPIAccess.DUMMY_USER_ID, + }, + Date = new DateTimeOffset(2023, 10, 27, 0, 0, 0, TimeSpan.Zero), + OnlineID = 12345, + Ruleset = new OsuRuleset().RulesetInfo, + BeatmapInfo = beatmapInfo + }; + + var imported = LoadScoreIntoOsu(osu, toImport); + + Assert.AreEqual(toImport.Rank, imported.Rank); + Assert.AreEqual(toImport.TotalScore, imported.TotalScore); + Assert.AreEqual(toImport.Accuracy, imported.Accuracy); + Assert.AreEqual(toImport.MaxCombo, imported.MaxCombo); + Assert.AreEqual(toImport.User.Username, imported.User.Username); + Assert.AreEqual(toImport.Date, imported.Date); + Assert.AreEqual(toImport.OnlineID, imported.OnlineID); + + Assert.That(imported.BeatmapInfo!.LastPlayed, Is.EqualTo(new DateTimeOffset(2023, 10, 30, 0, 0, 0, TimeSpan.Zero))); + } + finally + { + host.Exit(); + } + } + } + [Test] public void TestImportMods() {